v0.11.0-alpha.11 63002e0

gen.go RawHistoryFolder

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
3
4//go:build ignore
5
6package main
7
8import (
9	"bytes"
10	"fmt"
11	"go/format"
12	"log"
13	"os"
14	"path/filepath"
15	"sort"
16	"strings"
17	"text/template"
18)
19
20var templateSymbols = template.Must(template.New("symbols").Funcs(funcs).Parse(
21	`// Code generated by 'go generate' with gen.go. DO NOT EDIT.
22
23// SPDX-License-Identifier: Apache-2.0
24// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
25
26//go:build {{.Tag}}
27
28package fakecgo
29
30{{ imports .Symbols }}
31
32{{- range $di := .DynamicImports }}
33{{- range $sym := $di.Symbols }}
34//go:cgo_import_dynamic purego_{{ $sym }} {{ $sym }} "{{ $di.Name }}"
35{{- end }}
36{{- end }}
37
38{{ range .Symbols -}}
39//go:nosplit
40//go:norace
41func {{.Name}}(
42{{- range .Args -}}
43	{{- if .Name -}}
44		{{.Name}} {{.Type}},
45	{{- end -}}
46{{- end -}}) {{.Return}} {
47	{{- if .Return -}}
48		{{- if eq .Return "unsafe.Pointer" -}}
49			ret :=
50		{{- else -}}
51			return {{.Return}}(
52		{{- end -}}
53	{{- end -}}
54call5({{.Name}}ABI0,
55{{- range .Args}}
56	{{- if .Name -}}
57		{{- if hasPrefix .Type "*" -}}
58			uintptr(unsafe.Pointer({{.Name}})),
59		{{- else -}}
60			uintptr({{.Name}}),
61		{{- end -}}
62	{{- else -}}
63		0,
64	{{- end -}}
65{{- end -}}
66	) {{/* end of call5 */}}
67{{- if .Return -}}
68	{{- if eq .Return "unsafe.Pointer"}}
69		// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
70		return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
71	{{- else -}}
72		) {{/* end of cast */}}
73	{{- end -}}
74{{- end}}
75}
76
77{{end}}
78{{- range .Symbols }}
79//go:linkname _{{.Name}} _{{.Name}}
80var _{{.Name}} uint8
81var {{.Name}}ABI0 = uintptr(unsafe.Pointer(&_{{.Name}}))
82{{ end }}
83`))
84
85var templateTrampolinesStubs = template.Must(template.New("trampolines").Parse(
86	`// Code generated by 'go generate' with gen.go. DO NOT EDIT.
87
88// SPDX-License-Identifier: Apache-2.0
89// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
90
91//go:build {{.Tag}}
92
93#include "textflag.h"
94
95// these stubs are here because it is not possible to go:linkname directly the C functions
96{{ range .Symbols }}
97TEXT _{{.Name}}(SB), NOSPLIT|NOFRAME, $0-0
98	JMP purego_{{.Name}}(SB)
99{{ end -}}
100`))
101
102type Arg struct {
103	Name string
104	Type string
105}
106
107type Symbol struct {
108	Name   string
109	Args   [5]Arg
110	Return string
111	GOOSes []string // empty means all GOOSes
112}
113
114var (
115	libcSymbols = []Symbol{
116		{"malloc", [5]Arg{{"size", "uintptr"}}, "unsafe.Pointer", nil},
117		{"free", [5]Arg{{"ptr", "unsafe.Pointer"}}, "", nil},
118		{"setenv", [5]Arg{{"name", "*byte"}, {"value", "*byte"}, {"overwrite", "int32"}}, "int32", nil},
119		{"unsetenv", [5]Arg{{"name", "*byte"}}, "int32", nil},
120		{"sigfillset", [5]Arg{{"set", "*sigset_t"}}, "int32", nil},
121		{"nanosleep", [5]Arg{{"ts", "*syscall.Timespec"}, {"rem", "*syscall.Timespec"}}, "int32", nil},
122		{"abort", [5]Arg{}, "", nil},
123		{"sigaltstack", [5]Arg{{"ss", "*stack_t"}, {"old_ss", "*stack_t"}}, "int32", []string{"netbsd"}},
124		{"__errno_location", [5]Arg{}, "uintptr", []string{"linux"}},
125		{"setegid", [5]Arg{{"egid", "uint32"}}, "int32", []string{"linux"}},
126		{"seteuid", [5]Arg{{"euid", "uint32"}}, "int32", []string{"linux"}},
127		{"setgid", [5]Arg{{"gid", "uint32"}}, "int32", []string{"linux"}},
128		{"setregid", [5]Arg{{"rgid", "uint32"}, {"egid", "uint32"}}, "int32", []string{"linux"}},
129		{"setresgid", [5]Arg{{"rgid", "uint32"}, {"egid", "uint32"}, {"sgid", "uint32"}}, "int32", []string{"linux"}},
130		{"setresuid", [5]Arg{{"ruid", "uint32"}, {"euid", "uint32"}, {"suid", "uint32"}}, "int32", []string{"linux"}},
131		{"setreuid", [5]Arg{{"ruid", "uint32"}, {"euid", "uint32"}}, "int32", []string{"linux"}},
132		{"setuid", [5]Arg{{"uid", "uint32"}}, "int32", []string{"linux"}},
133		{"setgroups", [5]Arg{{"ngid", "uint32"}, {"gidset", "*uint32"}}, "int32", []string{"linux"}},
134	}
135	pthreadSymbols = []Symbol{
136		{"pthread_attr_init", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32", nil},
137		{"pthread_create", [5]Arg{{"thread", "*pthread_t"}, {"attr", "*pthread_attr_t"}, {"start", "unsafe.Pointer"}, {"arg", "unsafe.Pointer"}}, "int32", nil},
138		{"pthread_detach", [5]Arg{{"thread", "pthread_t"}}, "int32", nil},
139		{"pthread_sigmask", [5]Arg{{"how", "sighow"}, {"ign", "*sigset_t"}, {"oset", "*sigset_t"}}, "int32", nil},
140		{"pthread_self", [5]Arg{}, "pthread_t", []string{"darwin"}},
141		{"pthread_get_stacksize_np", [5]Arg{{"thread", "pthread_t"}}, "size_t", []string{"darwin"}},
142		{"pthread_attr_getstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"stacksize", "*size_t"}}, "int32", []string{"linux", "freebsd", "netbsd"}},
143		{"pthread_attr_setstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"size", "size_t"}}, "int32", []string{"darwin"}},
144		{"pthread_attr_destroy", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32", []string{"linux", "freebsd", "netbsd"}},
145		{"pthread_mutex_lock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32", nil},
146		{"pthread_mutex_unlock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32", nil},
147		{"pthread_cond_broadcast", [5]Arg{{"cond", "*pthread_cond_t"}}, "int32", nil},
148		{"pthread_setspecific", [5]Arg{{"key", "pthread_key_t"}, {"value", "unsafe.Pointer"}}, "int32", nil},
149	}
150)
151
152var funcs = map[string]any{
153	"hasPrefix": strings.HasPrefix,
154	"imports":   imports,
155}
156
157var GOOSes = []string{"darwin", "freebsd", "linux", "netbsd"}
158
159func imports(symbols []Symbol) string {
160	if len(symbols) == 0 {
161		return ""
162	}
163	imports := make(map[string]struct{})
164	imports["unsafe"] = struct{}{}
165	for _, s := range symbols {
166		for _, arg := range s.Args {
167			if pkg, _, found := strings.Cut(arg.Type, "."); found {
168				pkg = strings.TrimPrefix(pkg, "*")
169				imports[pkg] = struct{}{}
170			}
171		}
172	}
173	if len(imports) == 1 {
174		return `import "unsafe"`
175	}
176	result := make([]string, 0, len(imports))
177	for imp := range imports {
178		result = append(result, imp)
179	}
180	sort.Strings(result)
181	var sb strings.Builder
182	sb.WriteString("import (")
183	for _, imp := range result {
184		sb.WriteString("\n\t\"")
185		sb.WriteString(imp)
186		sb.WriteString("\"")
187	}
188	sb.WriteString("\n)")
189	return sb.String()
190}
191
192// filterSymbols filters the symbols by GOOS.
193// If goos is empty, it returns symbols that are not specific to any GOOS.
194func filterSymbols(symbols []Symbol, goos string) []Symbol {
195	filtered := make([]Symbol, 0, len(symbols))
196	for _, s := range symbols {
197		if (s.GOOSes == nil && goos == "") || contains(s.GOOSes, goos) {
198			filtered = append(filtered, s)
199		}
200	}
201	return filtered
202}
203
204func contains(slice []string, item string) bool {
205	for _, s := range slice {
206		if s == item {
207			return true
208		}
209	}
210	return false
211}
212
213func execute(t *template.Template, path string, data any) error {
214	var buf bytes.Buffer
215	if err := t.Execute(&buf, data); err != nil {
216		return err
217	}
218	if filepath.Ext(path) == ".go" {
219		source, err := format.Source(buf.Bytes())
220		if err != nil {
221			return err
222		}
223		buf.Reset()
224		buf.Write(source)
225	}
226	return os.WriteFile(path, buf.Bytes(), 0644)
227}
228
229func symbolsNames(symbols []Symbol) []string {
230	names := make([]string, len(symbols))
231	for i, s := range symbols {
232		names[i] = s.Name
233	}
234	return names
235}
236
237type dynamicImport struct {
238	Name    string
239	Symbols []string
240}
241
242type fileContent struct {
243	Tag            string
244	Symbols        []Symbol
245	DynamicImports []dynamicImport
246}
247
248func run() error {
249	allSymbols := append(append([]Symbol{}, libcSymbols...), pthreadSymbols...)
250	goosAgnosticContent := fileContent{
251		Symbols: filterSymbols(allSymbols, ""),
252		Tag:     "!cgo && (" + strings.Join(GOOSes, " || ") + ")",
253	}
254	if err := execute(templateSymbols, "zsymbols.go", goosAgnosticContent); err != nil {
255		return err
256	}
257	if err := execute(templateTrampolinesStubs, "ztrampolines_stubs.s", goosAgnosticContent); err != nil {
258		return err
259	}
260	goosAgnosticLibCSymbols := filterSymbols(libcSymbols, "")
261	goosAgnosticPthreadSymbols := filterSymbols(pthreadSymbols, "")
262	for _, goos := range GOOSes {
263		goosSymbols := filterSymbols(allSymbols, goos)
264		var libcSO, pthreadSO string
265		switch goos {
266		case "darwin":
267			libcSO = "/usr/lib/libSystem.B.dylib"
268			pthreadSO = "/usr/lib/libSystem.B.dylib"
269		case "freebsd":
270			libcSO = "libc.so.7"
271			pthreadSO = "libthr.so.3"
272		case "linux":
273			libcSO = "libc.so.6"
274			pthreadSO = "libpthread.so.0"
275		case "netbsd":
276			libcSO = "libc.so"
277			pthreadSO = "libpthread.so"
278		default:
279			return fmt.Errorf("unsupported OS: %s", goos)
280		}
281		located := fileContent{
282			DynamicImports: []dynamicImport{
283				{libcSO, symbolsNames(append(goosAgnosticLibCSymbols, filterSymbols(libcSymbols, goos)...))},
284				{pthreadSO, symbolsNames(append(goosAgnosticPthreadSymbols, filterSymbols(pthreadSymbols, goos)...))},
285			},
286			Symbols: goosSymbols,
287			Tag:     "!cgo",
288		}
289		if err := execute(templateSymbols, fmt.Sprintf("zsymbols_%s.go", goos), located); err != nil {
290			return err
291		}
292		if len(goosSymbols) != 0 {
293			if err := execute(templateTrampolinesStubs, fmt.Sprintf("ztrampolines_%s.s", goos), located); err != nil {
294				return err
295			}
296		}
297	}
298	return nil
299}
300
301func main() {
302	if err := run(); err != nil {
303		log.Fatal(err)
304	}
305}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close