source: code/trunk/vendor/modernc.org/ccgo/v3/lib/ccgo.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 55.2 KB
Line 
1// Copyright 2020 The CCGO Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:generate stringer -output stringer.go -type=exprMode,opKind
6
7// Package ccgo implements the ccgo command.
8package ccgo // import "modernc.org/ccgo/v3/lib"
9
10import (
11 "bufio"
12 "bytes"
13 "encoding/csv"
14 "encoding/json"
15 "fmt"
16 "go/ast"
17 "go/build"
18 "go/parser"
19 "go/token"
20 "io"
21 "io/ioutil"
22 "os"
23 "os/exec"
24 "path/filepath"
25 "regexp"
26 "runtime"
27 "runtime/debug"
28 "sort"
29 "strconv"
30 "strings"
31 "time"
32
33 "github.com/kballard/go-shellquote"
34 "golang.org/x/tools/go/packages"
35 "modernc.org/cc/v3"
36 "modernc.org/libc"
37 "modernc.org/opt"
38)
39
40const (
41 Version = "3.12.6-20210922111124"
42
43 experimentsEnvVar = "CCGO_EXPERIMENT"
44 maxSourceLine = 1 << 20
45)
46
47var (
48 _ = libc.Xstdin
49
50 coverExperiment bool
51)
52
53func init() {
54 s := strings.TrimSpace(os.Getenv(experimentsEnvVar))
55 if s == "" {
56 return
57 }
58
59 for _, v := range strings.Split(s, ",") {
60 switch strings.TrimSpace(v) {
61 case "cover":
62 coverExperiment = true
63 }
64 }
65}
66
67//TODO CPython
68//TODO Cython
69//TODO gmp
70//TODO gofrontend
71//TODO gsl
72//TODO minigmp
73//TODO mpc
74//TODO mpfr
75//TODO pcre
76//TODO pcre2
77//TODO quickjs
78//TODO redis
79//TODO sdl2
80//TODO wolfssl
81//TODO zdat
82//TODO zstd
83
84//TODO 2020-07-17
85//
86// Fix += and friends
87//
88// Audit all unsafe.Pointer conversions
89//
90// Remove double dereferencing **
91//
92// Shifts must not use n.Promote on left opearand
93//
94// Un-array
95//
96// Pass more CSmith tests.
97
98//TODO merge VaList slots of distinct top level statements.
99
100//TODO turn void
101//
102// a = b = c = d
103//
104// where all but the first and last of a, b, c, ... are declarators, into
105//
106// c = d
107// b = c
108// a = b
109
110//TODO define and use all tagged struct types, including inner ones, for example SQLite's SrcList_item.
111
112//TODO rewrite return conditionalExpression so it has no closures. Partially done.
113
114//TODO define and restore simple named constants. Having
115//
116// #define FOO 42
117// ...
118// case FOO:
119//
120// we do not yet define FOO and generate
121//
122// case 42:
123
124//TODO do not generate a terminating semicolon for empty statements.
125
126//TODO replace
127//
128// var sqlite3_data_directory uintptr = uintptr(0) /* sqlite3.c:156345:17 */
129//
130// by
131//
132// var sqlite3_data_directory uintptr = 0 /* sqlite3.c:156345:17 */
133//
134// or
135//
136// var sqlite3_data_directory = uintptr(0) /* sqlite3.c:156345:17 */
137
138//TODO drop all non-referenced declarators unless forced by a command line flag.
139
140const (
141 builtin = `
142#ifdef __PTRDIFF_TYPE__
143typedef __PTRDIFF_TYPE__ ptrdiff_t;
144#else
145#error __PTRDIFF_TYPE__ undefined
146#endif
147
148#ifdef __SIZE_TYPE__
149typedef __SIZE_TYPE__ size_t;
150#else
151#error __SIZE_TYPE__ undefined
152#endif
153
154#ifdef __WCHAR_TYPE__
155typedef __WCHAR_TYPE__ wchar_t;
156#else
157#error __WCHAR_TYPE__ undefined
158#endif
159
160#ifdef __SIZEOF_INT128__
161typedef struct { __INT64_TYPE__ lo, hi; } __int128_t; // must match modernc.org/mathutil.Int128
162typedef struct { __UINT64_TYPE__ lo, hi; } __uint128_t; // must match modernc.org/mathutil.Int128
163#endif;
164
165#define _FILE_OFFSET_BITS 64
166#define __FUNCTION__ __func__
167#define __PRETTY_FUNCTION__ __func__
168#define __asm __asm__
169#define __builtin_constant_p(x) __builtin_constant_p_impl(0, x)
170#define __builtin_offsetof(type, member) ((__SIZE_TYPE__)&(((type*)0)->member))
171#define __builtin_va_arg(ap, type) ((type)__ccgo_va_arg(ap))
172#define __builtin_va_copy(dst, src) dst = src
173#define __builtin_va_end(ap) __ccgo_va_end(ap)
174#define __builtin_va_start(ap, v) __ccgo_va_start(ap)
175#define __ccgo_fd_zero(set) __builtin_memset(set, 0, sizeof(fd_set))
176#define __ccgo_tcl_default_double_rounding(set) ((void)0)
177#define __ccgo_tcl_ieee_double_rounding(set) ((void)0)
178#define __extension__
179#define __has_include(...) __has_include_impl(#__VA_ARGS__)
180#define __has_include_impl(x)
181#define __inline__ inline
182#define __signed signed
183#define asm __asm__
184#define in6addr_any (*__ccgo_in6addr_anyp())
185
186typedef void *__builtin_va_list;
187typedef long double __float128;
188
189#if defined(__MINGW32__) || defined(__MINGW64__)
190typedef __builtin_va_list va_list;
191int gnu_printf(const char *format, ...);
192int gnu_scanf(const char *format, ...);
193int ms_printf(const char *format, ...);
194int ms_scanf(const char *format, ...);
195#define _VA_LIST_DEFINED
196#define __extension__
197#endif
198
199__UINT16_TYPE__ __builtin_bswap16 (__UINT16_TYPE__ x);
200__UINT32_TYPE__ __builtin_bswap32 (__UINT32_TYPE__ x);
201__UINT64_TYPE__ __builtin_bswap64 (__UINT64_TYPE__ x);
202char *__builtin___strcat_chk (char *dest, const char *src, size_t os);
203char *__builtin___strcpy_chk (char *dest, const char *src, size_t os);
204char *__builtin___strncpy_chk(char *dest, char *src, size_t n, size_t os);
205char *__builtin_strchr(const char *s, int c);
206char *__builtin_strcpy(char *dest, const char *src);
207double __builtin_copysign ( double x, double y );
208double __builtin_copysignl (long double x, long double y );
209double __builtin_fabs(double x);
210double __builtin_huge_val (void);
211double __builtin_inf (void);
212double __builtin_nan (const char *str);
213float __builtin_copysignf ( float x, float y );
214float __builtin_fabsf(float x);
215float __builtin_huge_valf (void);
216float __builtin_inff (void);
217float __builtin_nanf (const char *str);
218int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, ...);
219int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...);
220int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, __builtin_va_list ap);
221int __builtin__snprintf_chk(char * str, size_t maxlen, int flag, size_t strlen, const char * format);
222int __builtin_abs(int j);
223int __builtin_add_overflow();
224int __builtin_clz (unsigned);
225int __builtin_isunordered(double x, double y);
226int __builtin_clzl (unsigned long);
227int __builtin_clzll (unsigned long long);
228int __builtin_constant_p_impl(int, ...);
229int __builtin_getentropy(void*, size_t);
230int __builtin_isnan(double);
231int __builtin_memcmp(const void *s1, const void *s2, size_t n);
232int __builtin_mul_overflow();
233int __builtin_popcount (unsigned int x);
234int __builtin_popcountl (unsigned long x);
235int __builtin_printf(const char *format, ...);
236int __builtin_snprintf(char *str, size_t size, const char *format, ...);
237int __builtin_sprintf(char *str, const char *format, ...);
238int __builtin_strcmp(const char *s1, const char *s2);
239int __builtin_sub_overflow();
240long __builtin_expect (long exp, long c);
241long double __builtin_fabsl(long double x);
242long double __builtin_nanl (const char *str);
243long long __builtin_llabs(long long j);
244size_t __builtin_object_size (void * ptr, int type);
245size_t __builtin_strlen(const char *s);
246void *__builtin___memcpy_chk (void *dest, const void *src, size_t n, size_t os);
247void *__builtin___memmove_chk (void *dest, const void *src, size_t n, size_t os);
248void *__builtin___memset_chk (void *dstpp, int c, size_t len, size_t dstlen);
249void *__builtin_malloc(size_t size);
250void *__builtin_memcpy(void *dest, const void *src, size_t n);
251void *__builtin_memset(void *s, int c, size_t n);
252void *__builtin_mmap(void *addr, size_t length, int prot, int flags, int fd, __INTPTR_TYPE__ offset);
253void *__ccgo_va_arg(__builtin_va_list ap);
254void __builtin_abort(void);
255void __builtin_bzero(void *s, size_t n);
256void __builtin_exit(int status);
257void __builtin_free(void *ptr);
258void __builtin_prefetch (const void *addr, ...);
259void __builtin_trap (void);
260void __builtin_unreachable (void);
261void __ccgo_dmesg(char*, ...);
262void __ccgo_va_end(__builtin_va_list ap);
263void __ccgo_va_start(__builtin_va_list ap);
264
265#define __sync_add_and_fetch(ptr, val) \
266 __builtin_choose_expr( \
267 __builtin_types_compatible_p(typeof(*ptr), unsigned), \
268 __sync_add_and_fetch_uint32(ptr, val), \
269 __TODO__ \
270 )
271
272#define __sync_fetch_and_add(ptr, val) \
273 __TODO__ \
274
275#define __sync_sub_and_fetch(ptr, val) \
276 __builtin_choose_expr( \
277 __builtin_types_compatible_p(typeof(*ptr), unsigned), \
278 __sync_sub_and_fetch_uint32(ptr, val), \
279 __TODO__ \
280 )
281
282unsigned __sync_add_and_fetch_uint32(unsigned*, unsigned);
283unsigned __sync_sub_and_fetch_uint32(unsigned*, unsigned);
284
285#ifdef __APPLE__
286int (*__darwin_check_fd_set_overflow)(int, void *, int);
287#endif
288
289`
290 defaultCrt = "modernc.org/libc"
291)
292
293func origin(skip int) string {
294 pc, fn, fl, _ := runtime.Caller(skip)
295 f := runtime.FuncForPC(pc)
296 var fns string
297 if f != nil {
298 fns = f.Name()
299 if x := strings.LastIndex(fns, "."); x > 0 {
300 fns = fns[x+1:]
301 }
302 }
303 return fmt.Sprintf("%s:%d:%s", fn, fl, fns)
304}
305
306func todo(s string, args ...interface{}) string {
307 switch {
308 case s == "":
309 s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
310 default:
311 s = fmt.Sprintf(s, args...)
312 }
313 r := fmt.Sprintf("%s\n\tTODO %s", origin(2), s) //TODOOK
314 fmt.Fprintf(os.Stdout, "%s\n", r)
315 os.Stdout.Sync()
316 return r
317}
318
319func trc(s string, args ...interface{}) string {
320 switch {
321 case s == "":
322 s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...)
323 default:
324 s = fmt.Sprintf(s, args...)
325 }
326 r := fmt.Sprintf("%s: TRC %s", origin(2), s)
327 fmt.Fprintf(os.Stderr, "%s\n", r)
328 os.Stderr.Sync()
329 return r
330}
331
332// Task represents a compilation job.
333type Task struct {
334 D []string // -D
335 I []string // -I
336 U []string // -U
337 ar string // $AR, default "ar"
338 arLookPath string // LookPath(ar)
339 args []string
340 asts []*cc.AST
341 capif string
342 saveConfig string // -save-config
343 saveConfigErr error
344 cc string // $CC, default "gcc"
345 ccLookPath string // LookPath(cc)
346 cdb string // foo.json, use compile DB
347 cfg *cc.Config
348 compiledb string // -compiledb
349 crt string
350 crtImportPath string // -crt-import-path
351 exportDefines string // -export-defines
352 exportEnums string // -export-enums
353 exportExterns string // -export-externs
354 exportFields string // -export-fields
355 exportStructs string // -export-structs
356 exportTypedefs string // -export-typedefs
357 goarch string
358 goos string
359 hide map[string]struct{} // -hide
360 hostConfigCmd string // -host-config-cmd
361 hostConfigOpts string // -host-config-opts
362 hostIncludes []string
363 hostPredefined string
364 hostSysIncludes []string
365 ignoredIncludes string // -ignored-includes
366 ignoredObjects map[string]struct{} // -ignore-object
367 imported []*imported
368 includedFiles map[string]struct{}
369 l []string // -l
370 loadConfig string // --load-config
371 o string // -o
372 out io.Writer
373 pkgName string // -pkgname
374 replaceFdZero string // -replace-fd-zero
375 replaceTclDefaultDoubleRounding string // -replace-tcl-default-double-rounding
376 replaceTclIeeeDoubleRounding string // -replace-tcl-default-double-rounding
377 scriptFn string // -script
378 sources []cc.Source
379 staticLocalsPrefix string // -static-locals-prefix
380 stderr io.Writer
381 stdout io.Writer
382 symSearchOrder []int // >= 0: asts[i], < 0 : imported[-i-1]
383 verboseCompiledb bool // -verbose-compiledb
384 volatiles map[cc.StringID]struct{} // -volatile
385
386 // Path to a binary that will be called instead of executing
387 // Task.Main(). Intended to support TestGenerate in stable vs latest
388 // modes. This is _not_ supposed to be used when the Task instance is
389 // constructed by a ccgo _command_ (ccgo/v3) - it should never set this
390 // field. Only programs importing ccgo/v3/lib that opt-in into this
391 // feature should ever set it.
392 CallOutBinary string
393
394 E bool // -E
395 allErrors bool // -all-errors
396 compiledbValid bool // -compiledb present
397 configSaved bool
398 configured bool // hostPredefined, hostIncludes, hostSysIncludes are valid
399 cover bool // -cover-instrumentation
400 coverC bool // -cover-instrumentation-c
401 defaultUnExport bool // -unexported-by-default
402 errTrace bool // -err-trace
403 exportDefinesValid bool // -export-defines present
404 exportEnumsValid bool // -export-enums present
405 exportExternsValid bool // -export-externs present
406 exportFieldsValid bool // -export-fields present
407 exportStructsValid bool // -export-structs present
408 exportTypedefsValid bool // -export-typedefs present
409 fullPathComments bool // -full-path-comments
410 funcSig bool // -func-sig
411 header bool // -header
412 ignoreUnsupportedAligment bool // -ignore-unsupported-alignment
413 isScripted bool
414 mingw bool
415 noCapi bool // -nocapi
416 nostdinc bool // -nostdinc
417 nostdlib bool // -nostdlib
418 panicStubs bool // -panic-stubs
419 tracePinning bool // -trace-pinning
420 traceTranslationUnits bool // -trace-translation-units
421 verifyStructs bool // -verify-structs
422 version bool // -version
423 watch bool // -watch-instrumentation
424 windows bool // -windows
425}
426
427// NewTask returns a newly created Task.
428func NewTask(args []string, stdout, stderr io.Writer) *Task {
429 if dmesgs {
430 dmesg("%v: %v", origin(1), args)
431 }
432 if stdout == nil {
433 stdout = os.Stdout
434 }
435 if stderr == nil {
436 stderr = os.Stderr
437 }
438 return &Task{
439 args: args,
440 cfg: &cc.Config{
441 Config3: cc.Config3{
442 MaxSourceLine: maxSourceLine,
443 },
444 DoNotTypecheckAsm: true,
445 EnableAssignmentCompatibilityChecking: true,
446 LongDoubleIsDouble: true,
447 SharedFunctionDefinitions: &cc.SharedFunctionDefinitions{},
448 },
449 ar: env("AR", "ar"),
450 cc: env("CC", "gcc"),
451 crt: "libc.",
452 crtImportPath: defaultCrt,
453 goarch: env("TARGET_GOARCH", env("GOARCH", runtime.GOARCH)),
454 goos: env("TARGET_GOOS", env("GOOS", runtime.GOOS)),
455 hide: map[string]struct{}{},
456 hostConfigCmd: env("CCGO_CPP", ""),
457 pkgName: "main",
458 stderr: stderr,
459 stdout: stdout,
460 volatiles: map[cc.StringID]struct{}{},
461 }
462}
463
464func env(name, deflt string) (r string) {
465 r = deflt
466 if s := os.Getenv(name); s != "" {
467 r = s
468 }
469 return r
470}
471
472// Get exported symbols from package having import path 'path'.
473func (t *Task) capi(path string) (pkgName string, exports map[string]struct{}, err error) {
474 // defer func() {
475 // var a []string
476 // for k := range exports {
477 // a = append(a, k)
478 // }
479 // sort.Strings(a)
480 // trc("%s\n%s", path, strings.Join(a, "\n"))
481 // }()
482 var errModule, errGopath error
483 defer func() {
484 if err != nil {
485 a := []string{err.Error()}
486 if errModule != nil {
487 a = append(a, fmt.Sprintf("module mode error: %s", errModule))
488 }
489 if errGopath != nil {
490 a = append(a, fmt.Sprintf("gopath mode error: %s", errGopath))
491 }
492 wd, err2 := os.Getwd()
493 err = fmt.Errorf(
494 "(wd %q, %v): loading C exports from %s (GOPATH=%v GO111MODULE=%v): %v",
495 wd, err2, path, os.Getenv("GOPATH"), os.Getenv("GO111MODULE"), strings.Join(a, "\n\t"),
496 )
497 }
498 }()
499
500 mod := os.Getenv("GO111MODULE")
501 if mod == "" || mod == "on" {
502 var pkgs []*packages.Package
503 pkgs, errModule = packages.Load(
504 &packages.Config{
505 Mode: packages.NeedFiles,
506 Env: append(os.Environ(), fmt.Sprintf("GOOS=%s", t.goos), fmt.Sprintf("GOARCH=%s", t.goarch)),
507 },
508 path,
509 )
510 switch {
511 case errModule == nil:
512 if len(pkgs) != 1 {
513 errModule = fmt.Errorf("expected one package, loaded %d", len(pkgs))
514 break
515 }
516
517 pkg := pkgs[0]
518 if len(pkg.Errors) != 0 {
519 var a []string
520 for _, v := range pkg.Errors {
521 a = append(a, v.Error())
522 }
523 errModule = fmt.Errorf("%s", strings.Join(a, "\n"))
524 break
525 }
526
527 return t.capi2(pkg.GoFiles)
528 }
529 }
530
531 gopath0 := os.Getenv("GOPATH")
532 for _, gopath := range strings.Split(gopath0, string(os.PathListSeparator)) {
533 if gopath == "" || !filepath.IsAbs(gopath) {
534 continue
535 }
536
537 ctx := build.Context{
538 GOARCH: t.goarch,
539 GOOS: t.goos,
540 GOPATH: gopath,
541 Compiler: "gc",
542 }
543 arg := filepath.Join(gopath, "src", path)
544 pkg, err := ctx.ImportDir(arg, 0)
545 if err != nil {
546 errGopath = err
547 continue
548 }
549
550 for i, v := range pkg.GoFiles {
551 pkg.GoFiles[i] = filepath.Join(gopath, "src", path, v)
552 }
553 return t.capi2(pkg.GoFiles)
554 }
555 return "", nil, fmt.Errorf("cannot load CAPI")
556}
557
558func (t *Task) capi2(files []string) (pkgName string, exports map[string]struct{}, err error) {
559 exports = map[string]struct{}{}
560 base := fmt.Sprintf("capi_%s_%s.go", t.goos, t.goarch)
561 var fn string
562 for _, v := range files {
563 if filepath.Base(v) == base {
564 fn = v
565 break
566 }
567 }
568 if fn == "" {
569 return "", nil, fmt.Errorf("file %s not found", base)
570 }
571
572 fset := token.NewFileSet()
573 file, err := parser.ParseFile(fset, fn, nil, 0)
574 if err != nil {
575 return "", nil, err
576 }
577
578 obj, ok := file.Scope.Objects["CAPI"]
579 if !ok {
580 return "", nil, fmt.Errorf("CAPI not declared in %s", fn)
581 }
582
583 switch obj.Kind {
584 case ast.Var:
585 // ok
586 default:
587 return "", nil, fmt.Errorf("unexpected CAPI object kind: %v", obj.Kind)
588 }
589
590 spec, ok := obj.Decl.(*ast.ValueSpec)
591 if !ok {
592 return "", nil, fmt.Errorf("unexpected CAPI object type: %T", obj.Decl)
593 }
594
595 if len(spec.Values) != 1 {
596 return "", nil, fmt.Errorf("expected one CAPI expression, got %v", len(spec.Values))
597 }
598
599 ast.Inspect(spec.Values[0], func(n ast.Node) bool {
600 if x, ok := n.(*ast.BasicLit); ok {
601 var key string
602 if key, err = strconv.Unquote(x.Value); err != nil {
603 err = fmt.Errorf("invalid CAPI key value: %s", x.Value)
604 return false
605 }
606
607 exports[key] = struct{}{}
608 }
609 return true
610 })
611 return file.Name.String(), exports, err
612}
613
614// Main executes task.
615func (t *Task) Main() (err error) {
616 if dmesgs {
617 defer func() {
618 if err != nil {
619 // trc("FAIL %p: %q: %v", t, t.args, err)
620 dmesg("%v: returning from Task.Main: %v", origin(1), err)
621 }
622 }()
623
624 }
625
626 defer func() {
627 if t.saveConfigErr != nil && err == nil {
628 err = t.saveConfigErr
629 }
630 }()
631
632 if !t.isScripted && coverExperiment {
633 defer func() {
634 fmt.Fprintf(os.Stderr, "cover report:\n%s\n", coverReport())
635 }()
636 }
637 if t.CallOutBinary != "" {
638 if dmesgs {
639 dmesg("%v: calling out '%s' instead", origin(1))
640 }
641 cmd := exec.Command(t.CallOutBinary, t.args[1:]...)
642 out, err := cmd.CombinedOutput()
643 if err != nil {
644 err = fmt.Errorf("%v\n%s", err, out)
645 }
646 return err
647 }
648
649 opts := opt.NewSet()
650 opts.Arg("D", true, func(arg, value string) error { t.D = append(t.D, value); return nil })
651 opts.Arg("I", true, func(opt, arg string) error { t.I = append(t.I, arg); return nil })
652 opts.Arg("U", true, func(arg, value string) error { t.U = append(t.U, value); return nil })
653 opts.Arg("compiledb", false, func(arg, value string) error { t.compiledb = value; t.compiledbValid = true; return opt.Skip(nil) })
654 opts.Arg("crt-import-path", false, func(arg, value string) error { t.crtImportPath = value; return nil })
655 opts.Arg("export-defines", false, func(arg, value string) error { t.exportDefines = value; t.exportDefinesValid = true; return nil })
656 opts.Arg("export-enums", false, func(arg, value string) error { t.exportEnums = value; t.exportEnumsValid = true; return nil })
657 opts.Arg("export-externs", false, func(arg, value string) error { t.exportExterns = value; t.exportExternsValid = true; return nil })
658 opts.Arg("export-fields", false, func(arg, value string) error { t.exportFields = value; t.exportFieldsValid = true; return nil })
659 opts.Arg("export-structs", false, func(arg, value string) error { t.exportStructs = value; t.exportStructsValid = true; return nil })
660 opts.Arg("export-typedefs", false, func(arg, value string) error { t.exportTypedefs = value; t.exportTypedefsValid = true; return nil })
661 opts.Arg("host-config-cmd", false, func(arg, value string) error { t.hostConfigCmd = value; return nil })
662 opts.Arg("host-config-opts", false, func(arg, value string) error { t.hostConfigOpts = value; return nil })
663 opts.Arg("ignored-includes", false, func(arg, value string) error { t.ignoredIncludes = value; return nil })
664 opts.Arg("pkgname", false, func(arg, value string) error { t.pkgName = value; return nil })
665 opts.Arg("replace-fd-zero", false, func(arg, value string) error { t.replaceFdZero = value; return nil })
666 opts.Arg("replace-tcl-default-double-rounding", false, func(arg, value string) error { t.replaceTclDefaultDoubleRounding = value; return nil })
667 opts.Arg("replace-tcl-ieee-double-rounding", false, func(arg, value string) error { t.replaceTclIeeeDoubleRounding = value; return nil })
668 opts.Arg("script", false, func(arg, value string) error { t.scriptFn = value; return nil })
669 opts.Arg("static-locals-prefix", false, func(arg, value string) error { t.staticLocalsPrefix = value; return nil })
670
671 opts.Opt("E", func(opt string) error { t.E = true; return nil })
672 opts.Opt("all-errors", func(opt string) error { t.allErrors = true; return nil })
673 opts.Opt("cover-instrumentation", func(opt string) error { t.cover = true; return nil })
674 opts.Opt("cover-instrumentation-c", func(opt string) error { t.coverC = true; return nil })
675 opts.Opt("err-trace", func(opt string) error { t.errTrace = true; return nil })
676 opts.Opt("full-path-comments", func(opt string) error { t.fullPathComments = true; return nil })
677 opts.Opt("func-sig", func(opt string) error { t.funcSig = true; return nil })
678 opts.Opt("header", func(opt string) error { t.header = true; return nil })
679 opts.Opt("ignore-unsupported-alignment", func(opt string) error { t.ignoreUnsupportedAligment = true; return nil })
680 opts.Opt("nocapi", func(opt string) error { t.noCapi = true; return nil })
681 opts.Opt("nostdinc", func(opt string) error { t.nostdinc = true; return nil })
682 opts.Opt("panic-stubs", func(opt string) error { t.panicStubs = true; return nil })
683 opts.Opt("trace-pinning", func(opt string) error { t.tracePinning = true; return nil })
684 opts.Opt("trace-translation-units", func(opt string) error { t.traceTranslationUnits = true; return nil })
685 opts.Opt("unexported-by-default", func(opt string) error { t.defaultUnExport = true; return nil })
686 opts.Opt("verbose-compiledb", func(opt string) error { t.verboseCompiledb = true; return nil })
687 opts.Opt("verify-structs", func(opt string) error { t.verifyStructs = true; return nil })
688 opts.Opt("version", func(opt string) error { t.version = true; return nil })
689 opts.Opt("watch-instrumentation", func(opt string) error { t.watch = true; return nil })
690 opts.Opt("windows", func(opt string) error { t.windows = true; return nil })
691
692 opts.Opt("trace-included-files", func(opt string) error {
693 if t.includedFiles == nil {
694 t.includedFiles = map[string]struct{}{}
695 }
696 prev := t.cfg.IncludeFileHandler
697 t.cfg.IncludeFileHandler = func(pos token.Position, pathName string) {
698 if prev != nil {
699 prev(pos, pathName)
700 }
701 if _, ok := t.includedFiles[pathName]; !ok {
702 t.includedFiles[pathName] = struct{}{}
703 fmt.Fprintf(os.Stderr, "#include %s\n", pathName)
704 }
705 }
706 return nil
707 })
708 opts.Arg("ignore-object", false, func(arg, value string) error {
709 if t.ignoredObjects == nil {
710 t.ignoredObjects = map[string]struct{}{}
711 }
712 t.ignoredObjects[value] = struct{}{}
713 return nil
714 })
715 opts.Arg("save-config", false, func(arg, value string) error {
716 if value == "" {
717 return nil
718 }
719
720 abs, err := filepath.Abs(value)
721 if err != nil {
722 return err
723 }
724
725 t.saveConfig = abs
726 if t.includedFiles == nil {
727 t.includedFiles = map[string]struct{}{}
728 }
729 prev := t.cfg.IncludeFileHandler
730 t.cfg.IncludeFileHandler = func(pos token.Position, pathName string) {
731 if prev != nil {
732 prev(pos, pathName)
733 }
734 if _, ok := t.includedFiles[pathName]; !ok {
735 t.includedFiles[pathName] = struct{}{}
736 full := filepath.Join(abs, pathName)
737 switch _, err := os.Stat(full); {
738 case err != nil && os.IsNotExist(err):
739 // ok
740 case err != nil:
741 t.saveConfigErr = err
742 return
743 default:
744 return
745 }
746
747 b, err := ioutil.ReadFile(pathName)
748 if err != nil {
749 t.saveConfigErr = err
750 return
751 }
752
753 dir, _ := filepath.Split(full)
754 if err := os.MkdirAll(dir, 0700); err != nil {
755 t.saveConfigErr = err
756 return
757 }
758
759 if err := ioutil.WriteFile(full, b, 0600); err != nil {
760 t.saveConfigErr = err
761 }
762 }
763 }
764 return nil
765 })
766 opts.Arg("-load-config", false, func(arg, value string) error {
767 if value == "" {
768 return nil
769 }
770
771 abs, err := filepath.Abs(value)
772 if err != nil {
773 return err
774 }
775
776 t.loadConfig = abs
777 return nil
778 })
779 opts.Arg("volatile", false, func(arg, value string) error {
780 for _, v := range strings.Split(strings.TrimSpace(value), ",") {
781 t.volatiles[cc.String(v)] = struct{}{}
782 }
783 return nil
784 })
785 opts.Opt("nostdlib", func(opt string) error {
786 t.nostdlib = true
787 t.crt = ""
788 t.crtImportPath = ""
789 return nil
790 })
791 opts.Arg("hide", false, func(arg, value string) error {
792 value = strings.TrimSpace(value)
793 a := strings.Split(value, ",")
794 for _, v := range a {
795 t.hide[v] = struct{}{}
796 }
797 return nil
798 })
799 opts.Arg("l", true, func(arg, value string) error {
800 value = strings.TrimSpace(value)
801 a := strings.Split(value, ",")
802 for _, v := range a {
803 t.l = append(t.l, v)
804 t.symSearchOrder = append(t.symSearchOrder, -len(t.l))
805 }
806 return nil
807 })
808 opts.Arg("o", false, func(arg, value string) error {
809 if t.o != "" {
810 return fmt.Errorf("multiple argument: -o %s", value)
811 }
812
813 t.o = value
814 return nil
815 })
816 if err := opts.Parse(t.args[1:], func(arg string) error {
817 if strings.HasPrefix(arg, "-") {
818 return fmt.Errorf("unexpected option: %s", arg)
819 }
820
821 switch filepath.Ext(arg) {
822 case ".h":
823 t.symSearchOrder = append(t.symSearchOrder, len(t.sources))
824 t.sources = append(t.sources, cc.Source{Name: arg})
825 case ".c":
826 t.symSearchOrder = append(t.symSearchOrder, len(t.sources))
827 t.sources = append(t.sources, cc.Source{Name: arg, DoNotCache: true})
828 case ".json":
829 t.cdb = arg
830 return opt.Skip(nil)
831 default:
832 return fmt.Errorf("unexpected file type: %s", arg)
833 }
834
835 return nil
836 }); err != nil {
837 switch x := err.(type) {
838 case opt.Skip:
839 switch {
840 case t.compiledbValid: // -compiledb foo.json, create DB
841 cmd := []string(x)[1:]
842 if len(cmd) == 0 {
843 return fmt.Errorf("missing command after -compiledb <file>")
844 }
845
846 return t.createCompileDB(cmd)
847 case t.cdb != "": // foo.json ..., use DB
848 if err := t.configure(); err != nil {
849 return err
850 }
851
852 return t.useCompileDB(t.cdb, x)
853 }
854
855 return err
856 default:
857 return err
858 }
859 }
860
861 if t.version {
862 gobin, err := exec.LookPath("go")
863 var b []byte
864 if err == nil {
865 var bin string
866 bin, err = exec.LookPath(os.Args[0])
867 if err == nil {
868 b, err = exec.Command(gobin, "version", "-m", bin).CombinedOutput()
869 }
870 }
871 if err == nil {
872 fmt.Fprintf(t.stdout, "%s", b)
873 return nil
874 }
875
876 fmt.Fprintf(t.stdout, "%s\n", Version)
877 return nil
878 }
879
880 if t.scriptFn != "" {
881 return t.scriptBuild(t.scriptFn)
882 }
883
884 if len(t.sources) == 0 {
885 return fmt.Errorf("no input files specified")
886 }
887
888 if t.crtImportPath != "" {
889 t.l = append(t.l, t.crtImportPath)
890 t.symSearchOrder = append(t.symSearchOrder, -len(t.l))
891 m := map[string]struct{}{}
892 for _, v := range t.l {
893 v = strings.TrimSpace(v)
894 if _, ok := m[v]; !ok {
895 t.imported = append(t.imported, &imported{path: v})
896 m[v] = struct{}{}
897 }
898 }
899 t.imported[len(t.imported)-1].used = true // crt is always imported
900 }
901
902 if err := t.configure(); err != nil {
903 return err
904 }
905
906 abi, err := cc.NewABI(t.goos, t.goarch)
907 if err != nil {
908 return err
909 }
910 abi.Types[cc.LongDouble] = abi.Types[cc.Double]
911
912 var re *regexp.Regexp
913 if t.ignoredIncludes != "" {
914 if re, err = regexp.Compile(t.ignoredIncludes); err != nil {
915 return err
916 }
917 }
918
919 t.cfg.ABI = abi
920 t.cfg.ReplaceMacroFdZero = t.replaceFdZero
921 t.cfg.ReplaceMacroTclDefaultDoubleRounding = t.replaceTclDefaultDoubleRounding
922 t.cfg.ReplaceMacroTclIeeeDoubleRounding = t.replaceTclIeeeDoubleRounding
923 t.cfg.Config3.IgnoreInclude = re
924 t.cfg.Config3.NoFieldAndBitfieldOverlap = true
925 t.cfg.Config3.PreserveWhiteSpace = t.saveConfig == ""
926 t.cfg.Config3.UnsignedEnums = true
927
928 if t.mingw = detectMingw(t.hostPredefined); t.mingw {
929 t.windows = true
930 }
931 if t.nostdinc {
932 t.hostIncludes = nil
933 t.hostSysIncludes = nil
934 }
935 var sources []cc.Source
936 if t.hostPredefined != "" {
937 sources = append(sources, cc.Source{Name: "<predefined>", Value: t.hostPredefined})
938 }
939 sources = append(sources, cc.Source{Name: "<builtin>", Value: builtin})
940 if len(t.D) != 0 {
941 var a []string
942 for _, v := range t.D {
943 if i := strings.IndexByte(v, '='); i > 0 {
944 a = append(a, fmt.Sprintf("#define %s %s", v[:i], v[i+1:]))
945 continue
946 }
947
948 a = append(a, fmt.Sprintf("#define %s 1", v))
949 }
950 a = append(a, "\n")
951 sources = append(sources, cc.Source{Name: "<defines>", Value: strings.Join(a, "\n"), DoNotCache: true})
952 }
953 if len(t.U) != 0 {
954 var a []string
955 for _, v := range t.U {
956 a = append(a, fmt.Sprintf("#undef %s", v))
957 }
958 a = append(a, "\n")
959 sources = append(sources, cc.Source{Name: "<undefines>", Value: strings.Join(a, "\n"), DoNotCache: true})
960 }
961
962 // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html
963 //
964 // Headers whose names are enclosed in double-quotes ( "" ) shall be
965 // searched for first in the directory of the file with the #include
966 // line, then in directories named in -I options, and last in the usual
967 // places
968 includePaths := append([]string{"@"}, t.I...)
969 includePaths = append(includePaths, t.hostIncludes...)
970 includePaths = append(includePaths, t.hostSysIncludes...)
971 // For headers whose names are enclosed in angle brackets ( "<>" ), the
972 // header shall be searched for only in directories named in -I options
973 // and then in the usual places.
974 sysIncludePaths := append(t.I, t.hostSysIncludes...)
975 if t.traceTranslationUnits {
976 fmt.Printf("target: %s/%s\n", t.goos, t.goarch)
977 if t.hostConfigCmd != "" {
978 fmt.Printf("host config cmd: %s\n", t.hostConfigCmd)
979 }
980 }
981 for i, v := range t.sources {
982 tuSources := append(sources, v)
983 out := t.stdout
984 if t.saveConfig != "" {
985 out = io.Discard
986 t.E = true
987 }
988 if t.E {
989 t.cfg.PreprocessOnly = true
990 if err := cc.Preprocess(t.cfg, includePaths, sysIncludePaths, tuSources, out); err != nil {
991 return err
992 }
993 memGuard(i, t.isScripted)
994 continue
995 }
996
997 var t0 time.Time
998 if t.traceTranslationUnits {
999 fmt.Printf("C front end %d/%d: %s ... ", i+1, len(t.sources), v.Name)
1000 t0 = time.Now()
1001 }
1002 ast, err := cc.Translate(t.cfg, includePaths, sysIncludePaths, tuSources)
1003 if err != nil {
1004 return err
1005 }
1006
1007 if t.traceTranslationUnits {
1008 fmt.Println(time.Since(t0))
1009 }
1010 t.asts = append(t.asts, ast)
1011 memGuard(i, t.isScripted)
1012 }
1013 if t.E || t.isScripted {
1014 return nil
1015 }
1016
1017 return t.link()
1018}
1019
1020func (t *Task) configure() (err error) {
1021 if t.configured {
1022 return nil
1023 }
1024
1025 type jsonConfig struct {
1026 Predefined string
1027 IncludePaths []string
1028 SysIncludePaths []string
1029 OS string
1030 Arch string
1031 }
1032
1033 t.configured = true
1034 if t.loadConfig != "" {
1035 path := filepath.Join(t.loadConfig, "config.json")
1036 // trc("%p: LOAD_CONFIG(%s)", t, path)
1037 b, err := ioutil.ReadFile(path)
1038 if err != nil {
1039 return err
1040 }
1041
1042 loadConfig := &jsonConfig{}
1043 if err := json.Unmarshal(b, loadConfig); err != nil {
1044 return err
1045 }
1046
1047 t.goos = loadConfig.OS
1048 t.goarch = loadConfig.Arch
1049 for _, v := range loadConfig.IncludePaths {
1050 t.hostIncludes = append(t.hostIncludes, filepath.Join(t.loadConfig, v))
1051 }
1052 for _, v := range loadConfig.SysIncludePaths {
1053 t.hostSysIncludes = append(t.hostSysIncludes, filepath.Join(t.loadConfig, v))
1054 }
1055 t.hostPredefined = loadConfig.Predefined
1056 return nil
1057 }
1058
1059 hostConfigOpts := strings.Split(t.hostConfigOpts, ",")
1060 if t.hostConfigOpts == "" {
1061 hostConfigOpts = nil
1062 }
1063 if t.hostPredefined, t.hostIncludes, t.hostSysIncludes, err = cc.HostConfig(t.hostConfigCmd, hostConfigOpts...); err != nil {
1064 return err
1065 }
1066
1067 if t.saveConfig != "" && !t.configSaved {
1068 t.configSaved = true
1069 // trc("%p: SAVE_CONFIG(%s)", t, t.saveConfig)
1070 cfg := &jsonConfig{
1071 Predefined: t.hostPredefined,
1072 IncludePaths: t.hostIncludes,
1073 SysIncludePaths: t.hostSysIncludes,
1074 OS: t.goos,
1075 Arch: t.goarch,
1076 }
1077 b, err := json.Marshal(cfg)
1078 if err != nil {
1079 return err
1080 }
1081
1082 full := filepath.Join(t.saveConfig, "config.json")
1083 if err := os.MkdirAll(t.saveConfig, 0700); err != nil {
1084 return err
1085 }
1086
1087 if err := ioutil.WriteFile(full, b, 0600); err != nil {
1088 return err
1089 }
1090 }
1091
1092 return nil
1093}
1094
1095func (t *Task) setLookPaths() (err error) {
1096 if t.ccLookPath, err = exec.LookPath(t.cc); err != nil {
1097 return err
1098 }
1099
1100 t.arLookPath, err = exec.LookPath(t.ar)
1101 return err
1102}
1103
1104func (t *Task) link() (err error) {
1105 if len(t.asts) == 0 {
1106 return fmt.Errorf("no objects to link")
1107 }
1108
1109 if t.o == "" {
1110 t.o = fmt.Sprintf("a_%s_%s.go", t.goos, t.goarch)
1111 }
1112 dir := filepath.Dir(t.o)
1113 t.capif = filepath.Join(dir, fmt.Sprintf("capi_%s_%s.go", t.goos, t.goarch))
1114 f, err2 := os.Create(t.o)
1115 if err2 != nil {
1116 return err2
1117 }
1118
1119 defer func() {
1120 if e := f.Close(); e != nil && err == nil {
1121 err = e
1122 return
1123 }
1124
1125 if out, e := exec.Command("gofmt", "-r", "(x) -> x", "-l", "-s", "-w", t.o).CombinedOutput(); e != nil && err == nil {
1126 err = fmt.Errorf(strings.Join([]string{string(out), e.Error()}, ": "))
1127 }
1128 if out, e := exec.Command("gofmt", "-l", "-s", "-w", t.o).CombinedOutput(); e != nil && err == nil {
1129 err = fmt.Errorf(strings.Join([]string{string(out), e.Error()}, ": "))
1130 }
1131 }()
1132
1133 w := bufio.NewWriter(f)
1134
1135 defer func() {
1136 if e := w.Flush(); e != nil && err == nil {
1137 err = e
1138 }
1139 }()
1140
1141 t.out = w
1142 p, err := newProject(t)
1143 if err != nil {
1144 return err
1145 }
1146
1147 return p.main()
1148}
1149
1150func (t *Task) scriptBuild(fn string) error {
1151 f, err := os.Open(fn)
1152 if err != nil {
1153 return err
1154 }
1155
1156 defer f.Close()
1157
1158 r := csv.NewReader(f)
1159 r.Comment = '#'
1160 r.FieldsPerRecord = -1
1161 r.TrimLeadingSpace = true
1162 script, err := r.ReadAll()
1163 if err != nil {
1164 return err
1165 }
1166
1167 return t.scriptBuild2(script)
1168}
1169
1170func (t *Task) scriptBuild2(script [][]string) error {
1171 var ldir string
1172 ccgo := []string{t.args[0]}
1173 for i, line := range script {
1174 dir := line[0]
1175 args := line[1:]
1176 for _, v := range args {
1177 if strings.HasSuffix(v, ".c") || strings.HasSuffix(v, ".h") {
1178 v = filepath.Join(dir, v)
1179 t.symSearchOrder = append(t.symSearchOrder, len(t.sources))
1180 t.sources = append(t.sources, cc.Source{Name: v})
1181 }
1182 }
1183 cmd := append(ccgo, args...)
1184 if t.traceTranslationUnits {
1185 if dir != ldir {
1186 fmt.Println(dir)
1187 ldir = dir
1188 }
1189 fmt.Printf("%s\n", cmd)
1190 }
1191 t2 := NewTask(append(ccgo, args...), t.stdout, t.stderr)
1192 t2.cfg.IncludeFileHandler = t.cfg.IncludeFileHandler
1193 t2.cfg.SharedFunctionDefinitions = t.cfg.SharedFunctionDefinitions
1194 t2.configSaved = t.configSaved
1195 t2.configured = t.configured
1196 t2.hostIncludes = t.hostIncludes
1197 t2.hostPredefined = t.hostPredefined
1198 t2.hostSysIncludes = t.hostSysIncludes
1199 t2.includedFiles = t.includedFiles
1200 t2.isScripted = true
1201 t2.loadConfig = t.loadConfig
1202 t2.replaceFdZero = t.replaceFdZero
1203 t2.replaceTclDefaultDoubleRounding = t.replaceTclDefaultDoubleRounding
1204 t2.replaceTclIeeeDoubleRounding = t.replaceTclIeeeDoubleRounding
1205 t2.saveConfig = t.saveConfig
1206 if err := inDir(dir, t2.Main); err != nil {
1207 return err
1208 }
1209
1210 t.asts = append(t.asts, t2.asts...)
1211 if i == 0 {
1212 t.cfg = t2.cfg
1213 }
1214 }
1215 if t.crtImportPath != "" {
1216 t.l = append(t.l, t.crtImportPath)
1217 t.symSearchOrder = append(t.symSearchOrder, -len(t.l))
1218 m := map[string]struct{}{}
1219 for _, v := range t.l {
1220 v = strings.TrimSpace(v)
1221 if _, ok := m[v]; !ok {
1222 t.imported = append(t.imported, &imported{path: v})
1223 m[v] = struct{}{}
1224 }
1225 }
1226 t.imported[len(t.imported)-1].used = true // crt is always imported
1227 }
1228 if t.saveConfig != "" {
1229 return nil
1230 }
1231
1232 return t.link()
1233}
1234
1235type cdb struct {
1236 items []*cdbItem
1237 outputIndex map[string][]*cdbItem
1238}
1239
1240func (db *cdb) find(obj map[string]*cdbItem, nm string, ver, seqLimit int, path []string, cc, ar string, ignored map[string]struct{}) error {
1241 // trc("%v: nm %q ver %v seqLimit %v path %q cc %q ar %q", origin(1), nm, ver, seqLimit, path, cc, ar)
1242 var item *cdbItem
1243 var k string
1244 switch {
1245 case ver < 0:
1246 // find highest ver with .seq < seqLimit
1247 for i, v := range db.outputIndex[nm] {
1248 if v.seq >= seqLimit {
1249 break
1250 }
1251
1252 item = v
1253 ver = i
1254 }
1255 if item == nil {
1256 ver = -1
1257 for _, v := range db.items {
1258 if seqLimit >= 0 && v.seq >= seqLimit {
1259 break
1260 }
1261
1262 if filepath.Base(v.Output) == filepath.Base(nm) {
1263 item = v
1264 ver = v.ver
1265 break
1266 }
1267 }
1268 }
1269
1270 k = fmt.Sprintf("%s#%d", nm, ver)
1271 default:
1272 // match ver exactly
1273 k = fmt.Sprintf("%s#%d", nm, ver)
1274 if obj[k] != nil {
1275 return nil
1276 }
1277
1278 items := db.outputIndex[nm]
1279 switch {
1280 case ver < len(items):
1281 panic(todo("", nm, ver, seqLimit))
1282 default:
1283 n := -1
1284 for _, v := range db.items {
1285 if seqLimit >= 0 && v.seq >= seqLimit {
1286 break
1287 }
1288
1289 if filepath.Base(v.Output) == filepath.Base(nm) {
1290 n++
1291 if n == ver {
1292 item = v
1293 break
1294 }
1295 }
1296 }
1297 }
1298 }
1299 if item == nil {
1300 for k := range ignored {
1301 if k == nm || strings.HasSuffix(nm, k) {
1302 return nil
1303 }
1304 }
1305
1306 return fmt.Errorf("not found in compile DB: %s (max seq %d), path %v", k, seqLimit, path)
1307 }
1308
1309 if obj[k] != nil {
1310 return nil
1311 }
1312
1313 obj[k] = item
1314 var errs []string
1315 for _, v := range item.sources(cc, ar) {
1316 if err := db.find(obj, v, -1, item.seq, append(path, nm), cc, ar, ignored); err != nil {
1317 errs = append(errs, err.Error())
1318 }
1319 }
1320 if len(errs) != 0 {
1321 sort.Strings(errs)
1322 w := 0
1323 for _, v := range errs {
1324 if w == 0 || w > 0 && v != errs[w-1] {
1325 errs[w] = v
1326 w++
1327 }
1328 }
1329 errs = errs[:w]
1330 return fmt.Errorf("%s", strings.Join(errs, "\n"))
1331 }
1332
1333 return nil
1334}
1335
1336func suffixNum(s string, dflt int) (string, int) {
1337 x := strings.LastIndexByte(s, '#')
1338 if x < 0 {
1339 return s, dflt
1340 }
1341
1342 // foo#42
1343 // 012345
1344 // x == 3
1345 num := s[x+1:]
1346 n, err := strconv.ParseUint(num, 10, 32)
1347 if err != nil {
1348 return s, dflt
1349 }
1350
1351 return s[:x], int(n)
1352}
1353
1354func (t *Task) useCompileDB(fn string, args []string) error {
1355 if err := t.setLookPaths(); err != nil {
1356 return err
1357 }
1358
1359 var cdb cdb
1360 f, err := os.Open(fn)
1361 if err != nil {
1362 return err
1363 }
1364
1365 de := json.NewDecoder(f)
1366 err = de.Decode(&cdb.items)
1367 f.Close()
1368 if err != nil {
1369 return err
1370 }
1371
1372 cdb.outputIndex = map[string][]*cdbItem{}
1373 for i, v := range cdb.items {
1374 v.seq = i
1375 if len(v.Arguments) == 0 {
1376 if len(v.Command) == 0 {
1377 return fmt.Errorf("either arguments or command is required: %+v", v)
1378 }
1379
1380 if v.Arguments, err = shellquote.Split(v.Command); err != nil {
1381 return err
1382 }
1383 }
1384
1385 k := v.output(t.ccLookPath, t.arLookPath)
1386 a := cdb.outputIndex[k]
1387 v.ver = len(a)
1388 cdb.outputIndex[k] = append(a, v)
1389 }
1390 obj := map[string]*cdbItem{}
1391 notFound := false
1392 for _, v := range args {
1393 v, ver := suffixNum(v, 0)
1394 if err := cdb.find(obj, v, ver, -1, nil, t.ccLookPath, t.arLookPath, t.ignoredObjects); err != nil {
1395 notFound = true
1396 fmt.Fprintln(os.Stderr, err)
1397 }
1398 }
1399 if notFound {
1400 var a []string
1401 for k, v := range cdb.outputIndex {
1402 for _, w := range v {
1403 a = append(a, fmt.Sprintf("%5d %s", w.seq, k))
1404 }
1405 }
1406 sort.Strings(a)
1407 fmt.Fprintf(os.Stderr, "compile DB index:\n\t%s\n", strings.Join(a, "\n\t"))
1408 }
1409
1410 var a []string
1411 for k := range obj {
1412 a = append(a, k)
1413 }
1414 sort.Strings(a)
1415 return t.cdbBuild(obj, a)
1416}
1417
1418func (t *Task) cdbBuild(obj map[string]*cdbItem, list []string) error {
1419 var script [][]string
1420 for _, nm := range list {
1421 it := obj[nm]
1422 if !strings.HasSuffix(it.Output, ".o") || it.Arguments[0] != t.cc {
1423 continue
1424 }
1425
1426 args, err := it.ccgoArgs(t.cc)
1427 if err != nil {
1428 return err
1429 }
1430
1431 for _, v := range t.D {
1432 args = append(args, "-D"+v)
1433 }
1434 for _, v := range t.U {
1435 args = append(args, "-U"+v)
1436 }
1437
1438 line := append([]string{it.Directory}, args...)
1439 script = append(script, line)
1440 }
1441 return t.scriptBuild2(script)
1442}
1443
1444func (t *Task) createCompileDB(command []string) (rerr error) {
1445 if err := t.setLookPaths(); err != nil {
1446 return err
1447 }
1448
1449 cwd, err := os.Getwd()
1450 if err != nil {
1451 return err
1452 }
1453
1454 f, err := os.Create(t.compiledb)
1455 if err != nil {
1456 return err
1457 }
1458
1459 defer func() {
1460 if err := f.Close(); err != nil && rerr == nil {
1461 rerr = err
1462 }
1463 }()
1464
1465 cwr := newCDBWriter(f)
1466
1467 defer func() {
1468 if err := cwr.finish(); err != nil && rerr == nil {
1469 rerr = err
1470 }
1471 }()
1472
1473 var cmd *exec.Cmd
1474 var parser func(s string) ([]string, error)
1475out:
1476 switch t.goos {
1477 case "darwin", "freebsd", "netbsd":
1478 switch command[0] {
1479 case "make", "gmake":
1480 // ok
1481 default:
1482 return fmt.Errorf("usupported build command: %s", command[0])
1483 }
1484
1485 sh, err := exec.LookPath("sh")
1486 if err != nil {
1487 return err
1488 }
1489
1490 command = append([]string{sh, "-c"}, join(" ", command[0], "SHELL='sh -x'", command[1:]))
1491 cmd = exec.Command(command[0], command[1:]...)
1492 parser = makeXParser
1493 case "openbsd":
1494 switch command[0] {
1495 case "make", "gmake":
1496 // ok
1497 default:
1498 return fmt.Errorf("usupported build command: %s", command[0])
1499 }
1500
1501 sh, err := exec.LookPath("sh")
1502 if err != nil {
1503 return err
1504 }
1505
1506 command = append([]string{sh, "-c"}, join(" ", command[0], "SHELL='sh -x'", command[1:]))
1507 cmd = exec.Command(command[0], command[1:]...)
1508 parser = makeXParser2
1509 case "windows":
1510 if command[0] != "make" && command[0] != "make.exe" {
1511 return fmt.Errorf("usupported build command: %s", command[0])
1512 }
1513
1514 switch s := runtime.GOOS; s {
1515 case "windows":
1516 argv := append([]string{"-d"}, command[1:]...)
1517 if !strings.HasSuffix(command[0], ".exe") {
1518 command[0] += ".exe"
1519 }
1520 cmd = exec.Command(command[0], argv...)
1521 parser = makeDParser
1522 break out
1523 case "linux":
1524 // ok
1525 default:
1526 return fmt.Errorf("usupported cross compile host: %s", s)
1527 }
1528
1529 fallthrough
1530 default:
1531 strace, err := exec.LookPath("strace")
1532 if err != nil {
1533 return err
1534 }
1535
1536 argv := append([]string{"-f", "-s1000000", "-e", "trace=execve"}, command...)
1537 cmd = exec.Command(strace, argv...)
1538 parser = straceParser
1539 }
1540 cmd.Env = append(os.Environ(), "LC_ALL=C")
1541 cw := t.newCdbMakeWriter(cwr, cwd, parser)
1542 switch {
1543 case t.verboseCompiledb:
1544 cmd.Stdout = io.MultiWriter(cw, os.Stdout)
1545 default:
1546 cmd.Stdout = cw
1547 }
1548 cmd.Stderr = cmd.Stdout
1549 if dmesgs {
1550 dmesg("%v: %v", origin(1), cmd.Args)
1551 }
1552 if err := cmd.Run(); err != nil {
1553 if dmesgs {
1554 dmesg("%v: cmd.Run: %v", origin(1), err)
1555 }
1556 return err
1557 }
1558
1559 return cw.err
1560}
1561
1562func makeDParser(s string) ([]string, error) {
1563 const prefix = "CreateProcess("
1564 if !strings.HasPrefix(s, prefix) {
1565 return nil, nil
1566 }
1567
1568 // s: `CreateProcess(C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe,gcc -O3 -Wall -c -o compress.o compress.c,...)`
1569 s = s[len(prefix):]
1570 // s: `C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe,gcc -O3 -Wall -c -o compress.o compress.c,...)`
1571 x := strings.IndexByte(s, ',')
1572 if x < 0 {
1573 return nil, nil
1574 }
1575
1576 cmd := s[:x]
1577 // cmd: `C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe`
1578
1579 s = s[x+1:]
1580 // s: `gcc -O3 -Wall -c -o compress.o compress.c,...)`
1581 if x = strings.LastIndexByte(s, ','); x < 0 {
1582 return nil, nil
1583 }
1584
1585 s = s[:x]
1586 // s: `gcc -O3 -Wall -c -o compress.o compress.c`
1587 a, err := shellquote.Split(strings.TrimSpace(s))
1588 if err != nil || len(a) == 0 {
1589 return nil, err
1590 }
1591
1592 return append([]string{cmd}, a[1:]...), nil
1593}
1594
1595func isCreateArchive(s string) bool {
1596 // ar modifiers may be in any order so sort characters in s before checking.
1597 // This turns eg `rc` into `cr`.
1598 b := []byte(s)
1599 sort.Slice(b, func(i, j int) bool { return b[i] < b[j] })
1600 switch string(b) {
1601 case "cq", "cr", "crs", "cru", "r":
1602 return true
1603 }
1604 return false
1605}
1606
1607func hasPlusPrefix(s string) (n int, r string) {
1608 for strings.HasPrefix(s, "+") {
1609 n++
1610 s = s[1:]
1611 }
1612 return n, s
1613}
1614
1615func makeXParser(s string) (r []string, err error) {
1616 switch {
1617 case strings.HasPrefix(s, "libtool: link: ar "):
1618 s = s[len("libtool: link:"):]
1619 case strings.HasPrefix(s, "libtool: compile: "):
1620 s = s[len("libtool: compile:"):]
1621 for strings.HasPrefix(s, " ") {
1622 s = s[1:]
1623 }
1624 default:
1625 var n int
1626 if n, s = hasPlusPrefix(s); n == 0 {
1627 return nil, nil
1628 }
1629 }
1630
1631 if !strings.HasPrefix(s, " ") {
1632 return nil, nil
1633 }
1634
1635 s = s[1:]
1636 if dmesgs {
1637 dmesg("%v: source line `%s`, caller %v:", origin(1), s, origin(2))
1638 }
1639 r, err = shellquote.Split(s)
1640 if dmesgs {
1641 dmesg("%v: shellquote.Split -> %v %[2]q, %v", origin(1), r, err)
1642 }
1643 if err != nil {
1644 if strings.Contains(err.Error(), "Unterminated single-quoted string") {
1645 return nil, nil // ignore
1646 }
1647 }
1648 if len(r) != 0 && filepath.Base(r[0]) == "libtool" {
1649 r[0] = "libtool"
1650 }
1651 return r, err
1652}
1653
1654func makeXParser2(s string) (r []string, err error) {
1655 s = strings.TrimSpace(s)
1656 switch {
1657 case strings.HasPrefix(s, "libtool: link: ar "):
1658 s = s[len("libtool: link:"):]
1659 case strings.HasPrefix(s, "libtool: compile: "):
1660 s = s[len("libtool: compile:"):]
1661 for strings.HasPrefix(s, " ") {
1662 s = s[1:]
1663 }
1664 default:
1665 var n int
1666 if n, s = hasPlusPrefix(s); n != 0 {
1667 return nil, nil
1668 }
1669 }
1670
1671 if dmesgs {
1672 dmesg("%v: source line `%s`, caller %v:", origin(1), s, origin(2))
1673 }
1674 r, err = shellquote.Split(s)
1675 if dmesgs {
1676 dmesg("%v: shellquote.Split -> %v %[2]q, %v", origin(1), r, err)
1677 }
1678 if err != nil {
1679 if strings.Contains(err.Error(), "Unterminated single-quoted string") {
1680 return nil, nil // ignore
1681 }
1682 }
1683 if len(r) != 0 && filepath.Base(r[0]) == "libtool" {
1684 r[0] = "libtool"
1685 }
1686 return r, err
1687}
1688
1689func straceParser(s string) ([]string, error) {
1690 prefix := "execve("
1691 if strings.HasPrefix(s, "[pid ") {
1692 s = strings.TrimSpace(s[strings.IndexByte(s, ']')+1:])
1693 }
1694 if !strings.HasPrefix(s, prefix) || !strings.HasSuffix(s, ") = 0") {
1695 return nil, nil
1696 }
1697
1698 // s: `execve("/usr/bin/ar", ["ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0`
1699 s = s[len(prefix):]
1700 // s: `"/usr/bin/ar", ["ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0`
1701 a := strings.SplitN(s, ", [", 2)
1702 // a[0]: `"/usr/bin/ar"`, a[1]: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0`
1703 args := a[1]
1704 // args: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0`
1705 args = args[:strings.LastIndex(args, "], ")]
1706 // args: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"`
1707 argv, err := shellquote.Split(args)
1708 if err != nil {
1709 return nil, err
1710 }
1711
1712 words, err := shellquote.Split(a[0])
1713 if err != nil {
1714 return nil, err
1715 }
1716
1717 argv[0] = words[0]
1718 for i, v := range argv {
1719 if strings.HasSuffix(v, ",") {
1720 v = v[:len(v)-1]
1721 }
1722 if v2, err := strconv.Unquote(`"` + v + `"`); err == nil {
1723 v = v2
1724 }
1725 argv[i] = v
1726 }
1727
1728 return argv, nil
1729}
1730
1731type cdbItem struct {
1732 Arguments []string `json:"arguments"`
1733 Command string `json:"command,omitempty"`
1734 Directory string `json:"directory"`
1735 File string `json:"file"`
1736 Output string `json:"output,omitempty"`
1737
1738 seq int
1739 ver int
1740}
1741
1742func (it *cdbItem) cmpString() string { return fmt.Sprint(*it) }
1743
1744func (it *cdbItem) ccgoArgs(cc string) (r []string, err error) {
1745 switch it.Arguments[0] {
1746 case cc:
1747 set := opt.NewSet()
1748 set.Arg("D", true, func(opt, arg string) error { r = append(r, "-D"+arg); return nil })
1749 set.Arg("I", true, func(opt, arg string) error { r = append(r, "-I"+arg); return nil })
1750 set.Arg("MF", true, func(opt, arg string) error { return nil })
1751 set.Arg("MT", true, func(opt, arg string) error { return nil })
1752 set.Arg("O", true, func(opt, arg string) error { return nil })
1753 set.Arg("U", true, func(opt, arg string) error { r = append(r, "-U"+arg); return nil })
1754 set.Arg("o", true, func(opt, arg string) error { return nil })
1755 set.Arg("std", true, func(opt, arg string) error { return nil })
1756 set.Opt("MD", func(opt string) error { return nil })
1757 set.Opt("MMD", func(opt string) error { return nil })
1758 set.Opt("MP", func(opt string) error { return nil })
1759 set.Opt("ansi", func(opt string) error { return nil })
1760 set.Opt("c", func(opt string) error { return nil })
1761 set.Opt("g", func(opt string) error { return nil })
1762 set.Opt("pedantic", func(opt string) error { return nil })
1763 set.Opt("pipe", func(opt string) error { return nil })
1764 set.Opt("pthread", func(opt string) error { return nil })
1765 set.Opt("s", func(opt string) error { return nil })
1766 set.Opt("w", func(opt string) error { return nil })
1767 if err := set.Parse(it.Arguments[1:], func(arg string) error {
1768 switch {
1769 case strings.HasSuffix(arg, ".c"):
1770 r = append(r, arg)
1771 case
1772
1773 strings.HasPrefix(arg, "-W"),
1774 strings.HasPrefix(arg, "-f"),
1775 strings.HasPrefix(arg, "-m"):
1776
1777 // nop
1778 case strings.HasPrefix(arg, ">"):
1779 return opt.Skip(nil)
1780 default:
1781 return fmt.Errorf("unknown/unsupported CC option: %s", arg)
1782 }
1783
1784 return nil
1785 }); err != nil {
1786 switch err.(type) {
1787 case opt.Skip:
1788 // ok
1789 default:
1790 return nil, err
1791 }
1792 }
1793
1794 return r, nil
1795 default:
1796 return nil, fmt.Errorf("command not supported: %q", it.Arguments[0])
1797 }
1798}
1799
1800func (it *cdbItem) output(cc, ar string) (r string) {
1801 if it.Output != "" {
1802 return it.Output
1803 }
1804
1805 if len(it.Arguments) == 0 {
1806 return ""
1807 }
1808
1809 switch it.Arguments[0] {
1810 case cc:
1811 for i, v := range it.Arguments {
1812 if v == "-o" && i < len(it.Arguments)-1 {
1813 it.Output = filepath.Join(it.Directory, it.Arguments[i+1])
1814 break
1815 }
1816 }
1817 if it.Output == "" && strings.HasSuffix(it.File, ".c") {
1818 for _, v := range it.Arguments {
1819 if v == "-c" {
1820 bn := filepath.Base(it.File)
1821 it.Output = filepath.Join(it.Directory, bn[:len(bn)-2]+".o")
1822 break
1823 }
1824 }
1825 }
1826 case ar:
1827 if isCreateArchive(it.Arguments[1]) {
1828 it.Output = filepath.Join(it.Directory, it.Arguments[2])
1829 }
1830 case "libtool":
1831 for i, v := range it.Arguments {
1832 if v == "-o" && i < len(it.Arguments)-1 {
1833 it.Output = filepath.Join(it.Directory, it.Arguments[i+1])
1834 }
1835 }
1836 }
1837 return it.Output
1838}
1839
1840func (it *cdbItem) sources(cc, ar string) (r []string) {
1841 if len(it.Arguments) == 0 {
1842 return nil
1843 }
1844
1845 switch arg0 := it.Arguments[0]; arg0 {
1846 case
1847 "libtool",
1848 ar,
1849 filepath.Base(ar),
1850 cc:
1851
1852 var prev string
1853 for _, v := range it.Arguments {
1854 switch prev {
1855 case "-o", "-MT", "-MF":
1856 // nop
1857 default:
1858 if strings.HasSuffix(v, ".o") {
1859 r = append(r, filepath.Join(it.Directory, v))
1860 }
1861 }
1862 prev = v
1863 }
1864 return r
1865 default:
1866 panic(todo("cc: %q ar: %q it: %+v", cc, ar, it))
1867 }
1868}
1869
1870type cdbMakeWriter struct {
1871 ar string
1872 arBase string
1873 b bytes.Buffer
1874 cc string
1875 dir string
1876 err error
1877 it cdbItem
1878 parser func(s string) ([]string, error)
1879 prefix string
1880 sc *bufio.Scanner
1881 t *Task
1882 w *cdbWriter
1883}
1884
1885func (t *Task) newCdbMakeWriter(w *cdbWriter, dir string, parser func(s string) ([]string, error)) *cdbMakeWriter {
1886 const sz = 1 << 16
1887 r := &cdbMakeWriter{
1888 ar: t.arLookPath,
1889 arBase: filepath.Base(t.arLookPath),
1890 cc: t.ccLookPath,
1891 dir: dir,
1892 parser: parser,
1893 t: t,
1894 w: w,
1895 }
1896 r.sc = bufio.NewScanner(&r.b)
1897 r.sc.Buffer(make([]byte, sz), sz)
1898 return r
1899}
1900
1901func (w *cdbMakeWriter) fail(err error) {
1902 if w.err == nil {
1903 w.err = fmt.Errorf("%v (%v)", err, origin(2))
1904 }
1905}
1906
1907func (w *cdbMakeWriter) Write(b []byte) (int, error) {
1908 w.b.Write(b)
1909 for bytes.Contains(w.b.Bytes(), []byte{'\n'}) {
1910 if !w.sc.Scan() {
1911 panic(todo("internal error"))
1912 }
1913
1914 s := w.sc.Text()
1915 if strings.HasSuffix(s, "\\") {
1916 w.prefix += s[:len(s)-1]
1917 continue
1918 }
1919
1920 s = w.prefix + s
1921 w.prefix = ""
1922 s = strings.TrimSpace(s)
1923 if edx := strings.Index(s, "Entering directory"); edx >= 0 {
1924 s = s[edx+len("Entering directory"):]
1925 s = strings.TrimSpace(s)
1926 if len(s) == 0 {
1927 continue
1928 }
1929
1930 if (s[0] == '\'' || s[0] == '`') && s[len(s)-1] == '\'' {
1931 s = s[1:]
1932 if len(s) == 0 {
1933 continue
1934 }
1935
1936 s = s[:len(s)-1]
1937 }
1938 s = `"` + s + `"`
1939 dir, err := strconv.Unquote(s)
1940 if err != nil {
1941 w.fail(err)
1942 continue
1943 }
1944
1945 dir = filepath.Clean(dir)
1946 if dir == w.dir {
1947 continue
1948 }
1949
1950 w.dir = dir
1951 fmt.Printf("cd %s\n", dir)
1952 continue
1953 }
1954
1955 if dmesgs {
1956 dmesg("%v: source line `%s`", origin(1), s)
1957 }
1958 args, err := w.parser(s)
1959 if dmesgs {
1960 dmesg("%v: parser -> %v %[2]q, %v", origin(1), args, err)
1961 }
1962 if err != nil {
1963 w.fail(err)
1964 continue
1965 }
1966
1967 if len(args) == 0 {
1968 continue
1969 }
1970
1971 // TODO: change so eg handleGCC returns []cdbItem, skip if none.
1972
1973 w.it = cdbItem{}
1974
1975 err = nil
1976 switch args[0] {
1977 case w.cc:
1978 if w.t.verboseCompiledb {
1979 fmt.Printf("source line: %q\n", s)
1980 }
1981 fmt.Printf("CCGO CC: %q\n", args)
1982 err = w.handleGCC(args)
1983 case w.ar:
1984 fallthrough
1985 case w.arBase:
1986 if isCreateArchive(args[1]) {
1987 if w.t.verboseCompiledb {
1988 fmt.Printf("source line: %q\n", s)
1989 }
1990 fmt.Printf("CCGO AR: %q\n", args)
1991 err = w.handleAR(args)
1992 }
1993 case "libtool":
1994 if w.t.verboseCompiledb {
1995 fmt.Printf("source line: %q\n", s)
1996 }
1997 fmt.Printf("CCGO LIBTOOL: %q\n", args)
1998 err = w.handleLibtool(args)
1999 }
2000 if err != nil {
2001 w.fail(err)
2002 continue
2003 }
2004
2005 if w.it.Output != "" {
2006 w.w.add(w.it)
2007 }
2008 }
2009 return len(b), nil
2010}
2011
2012func (w *cdbMakeWriter) handleLibtool(args []string) error {
2013 w.it = cdbItem{
2014 Arguments: args,
2015 Directory: w.dir,
2016 }
2017 for i, v := range args {
2018 switch {
2019 case v == "-o" && i < len(args)-1:
2020 w.it.Output = filepath.Join(w.dir, args[i+1])
2021 }
2022 }
2023 w.it.output(w.cc, w.ar)
2024 return nil
2025}
2026
2027func (w *cdbMakeWriter) handleAR(args []string) error {
2028 w.it = cdbItem{
2029 Arguments: args,
2030 Directory: w.dir,
2031 }
2032 // TODO: assumes isCreateArchive has already been checked
2033 w.it.Output = filepath.Join(w.dir, args[2])
2034 return nil
2035}
2036
2037func (w *cdbMakeWriter) handleGCC(args []string) error {
2038 w.it = cdbItem{
2039 Arguments: args,
2040 Directory: w.dir,
2041 }
2042 for i, v := range args {
2043 switch {
2044 case v == "-o" && i < len(args)-1:
2045 w.it.Output = filepath.Join(w.dir, args[i+1])
2046 case strings.HasSuffix(v, ".c"):
2047 if w.it.File != "" {
2048 return fmt.Errorf("multiple .c files: %s", v)
2049 }
2050
2051 w.it.File = filepath.Clean(v)
2052 }
2053 }
2054 w.it.output(w.cc, w.ar)
2055 return nil
2056}
2057
2058type cdbWriter struct {
2059 w *bufio.Writer
2060 items []cdbItem
2061}
2062
2063func newCDBWriter(w io.Writer) *cdbWriter {
2064 return &cdbWriter{w: bufio.NewWriter(w)}
2065}
2066
2067func (w *cdbWriter) add(item cdbItem) {
2068 w.items = append(w.items, item)
2069}
2070
2071func (w *cdbWriter) finish() error {
2072 enc := json.NewEncoder(w.w)
2073 enc.SetIndent("", " ")
2074 if err := enc.Encode(w.items); err != nil {
2075 return err
2076 }
2077 return w.w.Flush()
2078}
2079
2080func join(sep string, a ...interface{}) string {
2081 var b []string
2082 for _, v := range a {
2083 switch x := v.(type) {
2084 case string:
2085 b = append(b, x)
2086 case []string:
2087 b = append(b, x...)
2088 default:
2089 panic(todo("internal error: %T", x))
2090 }
2091 }
2092 return strings.Join(b, sep)
2093}
2094
2095func inDir(dir string, f func() error) (err error) {
2096 var cwd string
2097 if cwd, err = os.Getwd(); err != nil {
2098 return err
2099 }
2100
2101 defer func() {
2102 if err2 := os.Chdir(cwd); err2 != nil {
2103 err = err2
2104 }
2105 }()
2106
2107 if err = os.Chdir(dir); err != nil {
2108 return err
2109 }
2110
2111 return f()
2112}
2113
2114func detectMingw(s string) bool {
2115 return strings.Contains(s, "#define __MINGW")
2116}
2117
2118func memGuard(i int, force bool) {
2119 if totalRam == 0 || totalRam > 64e9 {
2120 return
2121 }
2122
2123 var ms runtime.MemStats
2124 runtime.ReadMemStats(&ms)
2125 switch {
2126 case ms.Alloc < totalRam/2:
2127 return
2128 case ms.Alloc < (8*totalRam)/10:
2129 if force {
2130 break
2131 }
2132
2133 switch {
2134 case totalRam < 1e9:
2135 // ok
2136 case totalRam < 16e9:
2137 if i&1 == 1 {
2138 return
2139 }
2140 default:
2141 if i&3 != 3 {
2142 return
2143 }
2144 }
2145 }
2146
2147 debug.FreeOSMemory()
2148}
Note: See TracBrowser for help on using the repository browser.