v0.10.2 7f6f138

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	}
125	pthreadSymbols = []Symbol{
126		{"pthread_attr_init", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32", nil},
127		{"pthread_create", [5]Arg{{"thread", "*pthread_t"}, {"attr", "*pthread_attr_t"}, {"start", "unsafe.Pointer"}, {"arg", "unsafe.Pointer"}}, "int32", nil},
128		{"pthread_detach", [5]Arg{{"thread", "pthread_t"}}, "int32", nil},
129		{"pthread_sigmask", [5]Arg{{"how", "sighow"}, {"ign", "*sigset_t"}, {"oset", "*sigset_t"}}, "int32", nil},
130		{"pthread_self", [5]Arg{}, "pthread_t", []string{"darwin"}},
131		{"pthread_get_stacksize_np", [5]Arg{{"thread", "pthread_t"}}, "size_t", []string{"darwin"}},
132		{"pthread_attr_getstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"stacksize", "*size_t"}}, "int32", []string{"linux", "freebsd", "netbsd"}},
133		{"pthread_attr_setstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"size", "size_t"}}, "int32", []string{"darwin"}},
134		{"pthread_attr_destroy", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32", []string{"linux", "freebsd", "netbsd"}},
135		{"pthread_mutex_lock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32", nil},
136		{"pthread_mutex_unlock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32", nil},
137		{"pthread_cond_broadcast", [5]Arg{{"cond", "*pthread_cond_t"}}, "int32", nil},
138		{"pthread_setspecific", [5]Arg{{"key", "pthread_key_t"}, {"value", "unsafe.Pointer"}}, "int32", nil},
139	}
140)
141
142var funcs = map[string]any{
143	"hasPrefix": strings.HasPrefix,
144	"imports":   imports,
145}
146
147var GOOSes = []string{"darwin", "freebsd", "linux", "netbsd"}
148
149func imports(symbols []Symbol) string {
150	if len(symbols) == 0 {
151		return ""
152	}
153	imports := make(map[string]struct{})
154	imports["unsafe"] = struct{}{}
155	for _, s := range symbols {
156		for _, arg := range s.Args {
157			if pkg, _, found := strings.Cut(arg.Type, "."); found {
158				pkg = strings.TrimPrefix(pkg, "*")
159				imports[pkg] = struct{}{}
160			}
161		}
162	}
163	if len(imports) == 1 {
164		return `import "unsafe"`
165	}
166	result := make([]string, 0, len(imports))
167	for imp := range imports {
168		result = append(result, imp)
169	}
170	sort.Strings(result)
171	var sb strings.Builder
172	sb.WriteString("import (")
173	for _, imp := range result {
174		sb.WriteString("\n\t\"")
175		sb.WriteString(imp)
176		sb.WriteString("\"")
177	}
178	sb.WriteString("\n)")
179	return sb.String()
180}
181
182// filterSymbols filters the symbols by GOOS.
183// If goos is empty, it returns symbols that are not specific to any GOOS.
184func filterSymbols(symbols []Symbol, goos string) []Symbol {
185	filtered := make([]Symbol, 0, len(symbols))
186	for _, s := range symbols {
187		if (s.GOOSes == nil && goos == "") || contains(s.GOOSes, goos) {
188			filtered = append(filtered, s)
189		}
190	}
191	return filtered
192}
193
194func contains(slice []string, item string) bool {
195	for _, s := range slice {
196		if s == item {
197			return true
198		}
199	}
200	return false
201}
202
203func execute(t *template.Template, path string, data any) error {
204	var buf bytes.Buffer
205	if err := t.Execute(&buf, data); err != nil {
206		return err
207	}
208	if filepath.Ext(path) == ".go" {
209		source, err := format.Source(buf.Bytes())
210		if err != nil {
211			return err
212		}
213		buf.Reset()
214		buf.Write(source)
215	}
216	return os.WriteFile(path, buf.Bytes(), 0644)
217}
218
219func symbolsNames(symbols []Symbol) []string {
220	names := make([]string, len(symbols))
221	for i, s := range symbols {
222		names[i] = s.Name
223	}
224	return names
225}
226
227type dynamicImport struct {
228	Name    string
229	Symbols []string
230}
231
232type fileContent struct {
233	Tag            string
234	Symbols        []Symbol
235	DynamicImports []dynamicImport
236}
237
238func run() error {
239	allSymbols := append(append([]Symbol{}, libcSymbols...), pthreadSymbols...)
240	goosAgnosticContent := fileContent{
241		Symbols: filterSymbols(allSymbols, ""),
242		Tag:     "!cgo && (" + strings.Join(GOOSes, " || ") + ")",
243	}
244	if err := execute(templateSymbols, "zsymbols.go", goosAgnosticContent); err != nil {
245		return err
246	}
247	if err := execute(templateTrampolinesStubs, "ztrampolines_stubs.s", goosAgnosticContent); err != nil {
248		return err
249	}
250	goosAgnosticLibCSymbols := filterSymbols(libcSymbols, "")
251	goosAgnosticPthreadSymbols := filterSymbols(pthreadSymbols, "")
252	for _, goos := range GOOSes {
253		goosSymbols := filterSymbols(allSymbols, goos)
254		var libcSO, pthreadSO string
255		switch goos {
256		case "darwin":
257			libcSO = "/usr/lib/libSystem.B.dylib"
258			pthreadSO = "/usr/lib/libSystem.B.dylib"
259		case "freebsd":
260			libcSO = "libc.so.7"
261			pthreadSO = "libthr.so.3"
262		case "linux":
263			libcSO = "libc.so.6"
264			pthreadSO = "libpthread.so.0"
265		case "netbsd":
266			libcSO = "libc.so"
267			pthreadSO = "libpthread.so"
268		default:
269			return fmt.Errorf("unsupported OS: %s", goos)
270		}
271		located := fileContent{
272			DynamicImports: []dynamicImport{
273				{libcSO, symbolsNames(append(goosAgnosticLibCSymbols, filterSymbols(libcSymbols, goos)...))},
274				{pthreadSO, symbolsNames(append(goosAgnosticPthreadSymbols, filterSymbols(pthreadSymbols, goos)...))},
275			},
276			Symbols: goosSymbols,
277			Tag:     "!cgo",
278		}
279		if err := execute(templateSymbols, fmt.Sprintf("zsymbols_%s.go", goos), located); err != nil {
280			return err
281		}
282		if len(goosSymbols) != 0 {
283			if err := execute(templateTrampolinesStubs, fmt.Sprintf("ztrampolines_%s.s", goos), located); err != nil {
284				return err
285			}
286		}
287	}
288	return nil
289}
290
291func main() {
292	if err := run(); err != nil {
293		log.Fatal(err)
294	}
295}

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