diff options
Diffstat (limited to 'vendor/modernc.org/ccgo/v3/lib/ccgo.go')
-rw-r--r-- | vendor/modernc.org/ccgo/v3/lib/ccgo.go | 2148 |
1 files changed, 0 insertions, 2148 deletions
diff --git a/vendor/modernc.org/ccgo/v3/lib/ccgo.go b/vendor/modernc.org/ccgo/v3/lib/ccgo.go deleted file mode 100644 index a2efca3e0..000000000 --- a/vendor/modernc.org/ccgo/v3/lib/ccgo.go +++ /dev/null @@ -1,2148 +0,0 @@ -// Copyright 2020 The CCGO Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate stringer -output stringer.go -type=exprMode,opKind - -// Package ccgo implements the ccgo command. -package ccgo // import "modernc.org/ccgo/v3/lib" - -import ( - "bufio" - "bytes" - "encoding/csv" - "encoding/json" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "runtime/debug" - "sort" - "strconv" - "strings" - "time" - - "github.com/kballard/go-shellquote" - "golang.org/x/tools/go/packages" - "modernc.org/cc/v3" - "modernc.org/libc" - "modernc.org/opt" -) - -const ( - Version = "3.12.6-20210922111124" - - experimentsEnvVar = "CCGO_EXPERIMENT" - maxSourceLine = 1 << 20 -) - -var ( - _ = libc.Xstdin - - coverExperiment bool -) - -func init() { - s := strings.TrimSpace(os.Getenv(experimentsEnvVar)) - if s == "" { - return - } - - for _, v := range strings.Split(s, ",") { - switch strings.TrimSpace(v) { - case "cover": - coverExperiment = true - } - } -} - -//TODO CPython -//TODO Cython -//TODO gmp -//TODO gofrontend -//TODO gsl -//TODO minigmp -//TODO mpc -//TODO mpfr -//TODO pcre -//TODO pcre2 -//TODO quickjs -//TODO redis -//TODO sdl2 -//TODO wolfssl -//TODO zdat -//TODO zstd - -//TODO 2020-07-17 -// -// Fix += and friends -// -// Audit all unsafe.Pointer conversions -// -// Remove double dereferencing ** -// -// Shifts must not use n.Promote on left opearand -// -// Un-array -// -// Pass more CSmith tests. - -//TODO merge VaList slots of distinct top level statements. - -//TODO turn void -// -// a = b = c = d -// -// where all but the first and last of a, b, c, ... are declarators, into -// -// c = d -// b = c -// a = b - -//TODO define and use all tagged struct types, including inner ones, for example SQLite's SrcList_item. - -//TODO rewrite return conditionalExpression so it has no closures. Partially done. - -//TODO define and restore simple named constants. Having -// -// #define FOO 42 -// ... -// case FOO: -// -// we do not yet define FOO and generate -// -// case 42: - -//TODO do not generate a terminating semicolon for empty statements. - -//TODO replace -// -// var sqlite3_data_directory uintptr = uintptr(0) /* sqlite3.c:156345:17 */ -// -// by -// -// var sqlite3_data_directory uintptr = 0 /* sqlite3.c:156345:17 */ -// -// or -// -// var sqlite3_data_directory = uintptr(0) /* sqlite3.c:156345:17 */ - -//TODO drop all non-referenced declarators unless forced by a command line flag. - -const ( - builtin = ` -#ifdef __PTRDIFF_TYPE__ -typedef __PTRDIFF_TYPE__ ptrdiff_t; -#else -#error __PTRDIFF_TYPE__ undefined -#endif - -#ifdef __SIZE_TYPE__ -typedef __SIZE_TYPE__ size_t; -#else -#error __SIZE_TYPE__ undefined -#endif - -#ifdef __WCHAR_TYPE__ -typedef __WCHAR_TYPE__ wchar_t; -#else -#error __WCHAR_TYPE__ undefined -#endif - -#ifdef __SIZEOF_INT128__ -typedef struct { __INT64_TYPE__ lo, hi; } __int128_t; // must match modernc.org/mathutil.Int128 -typedef struct { __UINT64_TYPE__ lo, hi; } __uint128_t; // must match modernc.org/mathutil.Int128 -#endif; - -#define _FILE_OFFSET_BITS 64 -#define __FUNCTION__ __func__ -#define __PRETTY_FUNCTION__ __func__ -#define __asm __asm__ -#define __builtin_constant_p(x) __builtin_constant_p_impl(0, x) -#define __builtin_offsetof(type, member) ((__SIZE_TYPE__)&(((type*)0)->member)) -#define __builtin_va_arg(ap, type) ((type)__ccgo_va_arg(ap)) -#define __builtin_va_copy(dst, src) dst = src -#define __builtin_va_end(ap) __ccgo_va_end(ap) -#define __builtin_va_start(ap, v) __ccgo_va_start(ap) -#define __ccgo_fd_zero(set) __builtin_memset(set, 0, sizeof(fd_set)) -#define __ccgo_tcl_default_double_rounding(set) ((void)0) -#define __ccgo_tcl_ieee_double_rounding(set) ((void)0) -#define __extension__ -#define __has_include(...) __has_include_impl(#__VA_ARGS__) -#define __has_include_impl(x) -#define __inline__ inline -#define __signed signed -#define asm __asm__ -#define in6addr_any (*__ccgo_in6addr_anyp()) - -typedef void *__builtin_va_list; -typedef long double __float128; - -#if defined(__MINGW32__) || defined(__MINGW64__) -typedef __builtin_va_list va_list; -int gnu_printf(const char *format, ...); -int gnu_scanf(const char *format, ...); -int ms_printf(const char *format, ...); -int ms_scanf(const char *format, ...); -#define _VA_LIST_DEFINED -#define __extension__ -#endif - -__UINT16_TYPE__ __builtin_bswap16 (__UINT16_TYPE__ x); -__UINT32_TYPE__ __builtin_bswap32 (__UINT32_TYPE__ x); -__UINT64_TYPE__ __builtin_bswap64 (__UINT64_TYPE__ x); -char *__builtin___strcat_chk (char *dest, const char *src, size_t os); -char *__builtin___strcpy_chk (char *dest, const char *src, size_t os); -char *__builtin___strncpy_chk(char *dest, char *src, size_t n, size_t os); -char *__builtin_strchr(const char *s, int c); -char *__builtin_strcpy(char *dest, const char *src); -double __builtin_copysign ( double x, double y ); -double __builtin_copysignl (long double x, long double y ); -double __builtin_fabs(double x); -double __builtin_huge_val (void); -double __builtin_inf (void); -double __builtin_nan (const char *str); -float __builtin_copysignf ( float x, float y ); -float __builtin_fabsf(float x); -float __builtin_huge_valf (void); -float __builtin_inff (void); -float __builtin_nanf (const char *str); -int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, ...); -int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...); -int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, __builtin_va_list ap); -int __builtin__snprintf_chk(char * str, size_t maxlen, int flag, size_t strlen, const char * format); -int __builtin_abs(int j); -int __builtin_add_overflow(); -int __builtin_clz (unsigned); -int __builtin_isunordered(double x, double y); -int __builtin_clzl (unsigned long); -int __builtin_clzll (unsigned long long); -int __builtin_constant_p_impl(int, ...); -int __builtin_getentropy(void*, size_t); -int __builtin_isnan(double); -int __builtin_memcmp(const void *s1, const void *s2, size_t n); -int __builtin_mul_overflow(); -int __builtin_popcount (unsigned int x); -int __builtin_popcountl (unsigned long x); -int __builtin_printf(const char *format, ...); -int __builtin_snprintf(char *str, size_t size, const char *format, ...); -int __builtin_sprintf(char *str, const char *format, ...); -int __builtin_strcmp(const char *s1, const char *s2); -int __builtin_sub_overflow(); -long __builtin_expect (long exp, long c); -long double __builtin_fabsl(long double x); -long double __builtin_nanl (const char *str); -long long __builtin_llabs(long long j); -size_t __builtin_object_size (void * ptr, int type); -size_t __builtin_strlen(const char *s); -void *__builtin___memcpy_chk (void *dest, const void *src, size_t n, size_t os); -void *__builtin___memmove_chk (void *dest, const void *src, size_t n, size_t os); -void *__builtin___memset_chk (void *dstpp, int c, size_t len, size_t dstlen); -void *__builtin_malloc(size_t size); -void *__builtin_memcpy(void *dest, const void *src, size_t n); -void *__builtin_memset(void *s, int c, size_t n); -void *__builtin_mmap(void *addr, size_t length, int prot, int flags, int fd, __INTPTR_TYPE__ offset); -void *__ccgo_va_arg(__builtin_va_list ap); -void __builtin_abort(void); -void __builtin_bzero(void *s, size_t n); -void __builtin_exit(int status); -void __builtin_free(void *ptr); -void __builtin_prefetch (const void *addr, ...); -void __builtin_trap (void); -void __builtin_unreachable (void); -void __ccgo_dmesg(char*, ...); -void __ccgo_va_end(__builtin_va_list ap); -void __ccgo_va_start(__builtin_va_list ap); - -#define __sync_add_and_fetch(ptr, val) \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(*ptr), unsigned), \ - __sync_add_and_fetch_uint32(ptr, val), \ - __TODO__ \ - ) - -#define __sync_fetch_and_add(ptr, val) \ - __TODO__ \ - -#define __sync_sub_and_fetch(ptr, val) \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(*ptr), unsigned), \ - __sync_sub_and_fetch_uint32(ptr, val), \ - __TODO__ \ - ) - -unsigned __sync_add_and_fetch_uint32(unsigned*, unsigned); -unsigned __sync_sub_and_fetch_uint32(unsigned*, unsigned); - -#ifdef __APPLE__ -int (*__darwin_check_fd_set_overflow)(int, void *, int); -#endif - -` - defaultCrt = "modernc.org/libc" -) - -func origin(skip int) string { - pc, fn, fl, _ := runtime.Caller(skip) - f := runtime.FuncForPC(pc) - var fns string - if f != nil { - fns = f.Name() - if x := strings.LastIndex(fns, "."); x > 0 { - fns = fns[x+1:] - } - } - return fmt.Sprintf("%s:%d:%s", fn, fl, fns) -} - -func todo(s string, args ...interface{}) string { - switch { - case s == "": - s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) - default: - s = fmt.Sprintf(s, args...) - } - r := fmt.Sprintf("%s\n\tTODO %s", origin(2), s) //TODOOK - fmt.Fprintf(os.Stdout, "%s\n", r) - os.Stdout.Sync() - return r -} - -func trc(s string, args ...interface{}) string { - switch { - case s == "": - s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) - default: - s = fmt.Sprintf(s, args...) - } - r := fmt.Sprintf("%s: TRC %s", origin(2), s) - fmt.Fprintf(os.Stderr, "%s\n", r) - os.Stderr.Sync() - return r -} - -// Task represents a compilation job. -type Task struct { - D []string // -D - I []string // -I - U []string // -U - ar string // $AR, default "ar" - arLookPath string // LookPath(ar) - args []string - asts []*cc.AST - capif string - saveConfig string // -save-config - saveConfigErr error - cc string // $CC, default "gcc" - ccLookPath string // LookPath(cc) - cdb string // foo.json, use compile DB - cfg *cc.Config - compiledb string // -compiledb - crt string - crtImportPath string // -crt-import-path - exportDefines string // -export-defines - exportEnums string // -export-enums - exportExterns string // -export-externs - exportFields string // -export-fields - exportStructs string // -export-structs - exportTypedefs string // -export-typedefs - goarch string - goos string - hide map[string]struct{} // -hide - hostConfigCmd string // -host-config-cmd - hostConfigOpts string // -host-config-opts - hostIncludes []string - hostPredefined string - hostSysIncludes []string - ignoredIncludes string // -ignored-includes - ignoredObjects map[string]struct{} // -ignore-object - imported []*imported - includedFiles map[string]struct{} - l []string // -l - loadConfig string // --load-config - o string // -o - out io.Writer - pkgName string // -pkgname - replaceFdZero string // -replace-fd-zero - replaceTclDefaultDoubleRounding string // -replace-tcl-default-double-rounding - replaceTclIeeeDoubleRounding string // -replace-tcl-default-double-rounding - scriptFn string // -script - sources []cc.Source - staticLocalsPrefix string // -static-locals-prefix - stderr io.Writer - stdout io.Writer - symSearchOrder []int // >= 0: asts[i], < 0 : imported[-i-1] - verboseCompiledb bool // -verbose-compiledb - volatiles map[cc.StringID]struct{} // -volatile - - // Path to a binary that will be called instead of executing - // Task.Main(). Intended to support TestGenerate in stable vs latest - // modes. This is _not_ supposed to be used when the Task instance is - // constructed by a ccgo _command_ (ccgo/v3) - it should never set this - // field. Only programs importing ccgo/v3/lib that opt-in into this - // feature should ever set it. - CallOutBinary string - - E bool // -E - allErrors bool // -all-errors - compiledbValid bool // -compiledb present - configSaved bool - configured bool // hostPredefined, hostIncludes, hostSysIncludes are valid - cover bool // -cover-instrumentation - coverC bool // -cover-instrumentation-c - defaultUnExport bool // -unexported-by-default - errTrace bool // -err-trace - exportDefinesValid bool // -export-defines present - exportEnumsValid bool // -export-enums present - exportExternsValid bool // -export-externs present - exportFieldsValid bool // -export-fields present - exportStructsValid bool // -export-structs present - exportTypedefsValid bool // -export-typedefs present - fullPathComments bool // -full-path-comments - funcSig bool // -func-sig - header bool // -header - ignoreUnsupportedAligment bool // -ignore-unsupported-alignment - isScripted bool - mingw bool - noCapi bool // -nocapi - nostdinc bool // -nostdinc - nostdlib bool // -nostdlib - panicStubs bool // -panic-stubs - tracePinning bool // -trace-pinning - traceTranslationUnits bool // -trace-translation-units - verifyStructs bool // -verify-structs - version bool // -version - watch bool // -watch-instrumentation - windows bool // -windows -} - -// NewTask returns a newly created Task. -func NewTask(args []string, stdout, stderr io.Writer) *Task { - if dmesgs { - dmesg("%v: %v", origin(1), args) - } - if stdout == nil { - stdout = os.Stdout - } - if stderr == nil { - stderr = os.Stderr - } - return &Task{ - args: args, - cfg: &cc.Config{ - Config3: cc.Config3{ - MaxSourceLine: maxSourceLine, - }, - DoNotTypecheckAsm: true, - EnableAssignmentCompatibilityChecking: true, - LongDoubleIsDouble: true, - SharedFunctionDefinitions: &cc.SharedFunctionDefinitions{}, - }, - ar: env("AR", "ar"), - cc: env("CC", "gcc"), - crt: "libc.", - crtImportPath: defaultCrt, - goarch: env("TARGET_GOARCH", env("GOARCH", runtime.GOARCH)), - goos: env("TARGET_GOOS", env("GOOS", runtime.GOOS)), - hide: map[string]struct{}{}, - hostConfigCmd: env("CCGO_CPP", ""), - pkgName: "main", - stderr: stderr, - stdout: stdout, - volatiles: map[cc.StringID]struct{}{}, - } -} - -func env(name, deflt string) (r string) { - r = deflt - if s := os.Getenv(name); s != "" { - r = s - } - return r -} - -// Get exported symbols from package having import path 'path'. -func (t *Task) capi(path string) (pkgName string, exports map[string]struct{}, err error) { - // defer func() { - // var a []string - // for k := range exports { - // a = append(a, k) - // } - // sort.Strings(a) - // trc("%s\n%s", path, strings.Join(a, "\n")) - // }() - var errModule, errGopath error - defer func() { - if err != nil { - a := []string{err.Error()} - if errModule != nil { - a = append(a, fmt.Sprintf("module mode error: %s", errModule)) - } - if errGopath != nil { - a = append(a, fmt.Sprintf("gopath mode error: %s", errGopath)) - } - wd, err2 := os.Getwd() - err = fmt.Errorf( - "(wd %q, %v): loading C exports from %s (GOPATH=%v GO111MODULE=%v): %v", - wd, err2, path, os.Getenv("GOPATH"), os.Getenv("GO111MODULE"), strings.Join(a, "\n\t"), - ) - } - }() - - mod := os.Getenv("GO111MODULE") - if mod == "" || mod == "on" { - var pkgs []*packages.Package - pkgs, errModule = packages.Load( - &packages.Config{ - Mode: packages.NeedFiles, - Env: append(os.Environ(), fmt.Sprintf("GOOS=%s", t.goos), fmt.Sprintf("GOARCH=%s", t.goarch)), - }, - path, - ) - switch { - case errModule == nil: - if len(pkgs) != 1 { - errModule = fmt.Errorf("expected one package, loaded %d", len(pkgs)) - break - } - - pkg := pkgs[0] - if len(pkg.Errors) != 0 { - var a []string - for _, v := range pkg.Errors { - a = append(a, v.Error()) - } - errModule = fmt.Errorf("%s", strings.Join(a, "\n")) - break - } - - return t.capi2(pkg.GoFiles) - } - } - - gopath0 := os.Getenv("GOPATH") - for _, gopath := range strings.Split(gopath0, string(os.PathListSeparator)) { - if gopath == "" || !filepath.IsAbs(gopath) { - continue - } - - ctx := build.Context{ - GOARCH: t.goarch, - GOOS: t.goos, - GOPATH: gopath, - Compiler: "gc", - } - arg := filepath.Join(gopath, "src", path) - pkg, err := ctx.ImportDir(arg, 0) - if err != nil { - errGopath = err - continue - } - - for i, v := range pkg.GoFiles { - pkg.GoFiles[i] = filepath.Join(gopath, "src", path, v) - } - return t.capi2(pkg.GoFiles) - } - return "", nil, fmt.Errorf("cannot load CAPI") -} - -func (t *Task) capi2(files []string) (pkgName string, exports map[string]struct{}, err error) { - exports = map[string]struct{}{} - base := fmt.Sprintf("capi_%s_%s.go", t.goos, t.goarch) - var fn string - for _, v := range files { - if filepath.Base(v) == base { - fn = v - break - } - } - if fn == "" { - return "", nil, fmt.Errorf("file %s not found", base) - } - - fset := token.NewFileSet() - file, err := parser.ParseFile(fset, fn, nil, 0) - if err != nil { - return "", nil, err - } - - obj, ok := file.Scope.Objects["CAPI"] - if !ok { - return "", nil, fmt.Errorf("CAPI not declared in %s", fn) - } - - switch obj.Kind { - case ast.Var: - // ok - default: - return "", nil, fmt.Errorf("unexpected CAPI object kind: %v", obj.Kind) - } - - spec, ok := obj.Decl.(*ast.ValueSpec) - if !ok { - return "", nil, fmt.Errorf("unexpected CAPI object type: %T", obj.Decl) - } - - if len(spec.Values) != 1 { - return "", nil, fmt.Errorf("expected one CAPI expression, got %v", len(spec.Values)) - } - - ast.Inspect(spec.Values[0], func(n ast.Node) bool { - if x, ok := n.(*ast.BasicLit); ok { - var key string - if key, err = strconv.Unquote(x.Value); err != nil { - err = fmt.Errorf("invalid CAPI key value: %s", x.Value) - return false - } - - exports[key] = struct{}{} - } - return true - }) - return file.Name.String(), exports, err -} - -// Main executes task. -func (t *Task) Main() (err error) { - if dmesgs { - defer func() { - if err != nil { - // trc("FAIL %p: %q: %v", t, t.args, err) - dmesg("%v: returning from Task.Main: %v", origin(1), err) - } - }() - - } - - defer func() { - if t.saveConfigErr != nil && err == nil { - err = t.saveConfigErr - } - }() - - if !t.isScripted && coverExperiment { - defer func() { - fmt.Fprintf(os.Stderr, "cover report:\n%s\n", coverReport()) - }() - } - if t.CallOutBinary != "" { - if dmesgs { - dmesg("%v: calling out '%s' instead", origin(1)) - } - cmd := exec.Command(t.CallOutBinary, t.args[1:]...) - out, err := cmd.CombinedOutput() - if err != nil { - err = fmt.Errorf("%v\n%s", err, out) - } - return err - } - - opts := opt.NewSet() - opts.Arg("D", true, func(arg, value string) error { t.D = append(t.D, value); return nil }) - opts.Arg("I", true, func(opt, arg string) error { t.I = append(t.I, arg); return nil }) - opts.Arg("U", true, func(arg, value string) error { t.U = append(t.U, value); return nil }) - opts.Arg("compiledb", false, func(arg, value string) error { t.compiledb = value; t.compiledbValid = true; return opt.Skip(nil) }) - opts.Arg("crt-import-path", false, func(arg, value string) error { t.crtImportPath = value; return nil }) - opts.Arg("export-defines", false, func(arg, value string) error { t.exportDefines = value; t.exportDefinesValid = true; return nil }) - opts.Arg("export-enums", false, func(arg, value string) error { t.exportEnums = value; t.exportEnumsValid = true; return nil }) - opts.Arg("export-externs", false, func(arg, value string) error { t.exportExterns = value; t.exportExternsValid = true; return nil }) - opts.Arg("export-fields", false, func(arg, value string) error { t.exportFields = value; t.exportFieldsValid = true; return nil }) - opts.Arg("export-structs", false, func(arg, value string) error { t.exportStructs = value; t.exportStructsValid = true; return nil }) - opts.Arg("export-typedefs", false, func(arg, value string) error { t.exportTypedefs = value; t.exportTypedefsValid = true; return nil }) - opts.Arg("host-config-cmd", false, func(arg, value string) error { t.hostConfigCmd = value; return nil }) - opts.Arg("host-config-opts", false, func(arg, value string) error { t.hostConfigOpts = value; return nil }) - opts.Arg("ignored-includes", false, func(arg, value string) error { t.ignoredIncludes = value; return nil }) - opts.Arg("pkgname", false, func(arg, value string) error { t.pkgName = value; return nil }) - opts.Arg("replace-fd-zero", false, func(arg, value string) error { t.replaceFdZero = value; return nil }) - opts.Arg("replace-tcl-default-double-rounding", false, func(arg, value string) error { t.replaceTclDefaultDoubleRounding = value; return nil }) - opts.Arg("replace-tcl-ieee-double-rounding", false, func(arg, value string) error { t.replaceTclIeeeDoubleRounding = value; return nil }) - opts.Arg("script", false, func(arg, value string) error { t.scriptFn = value; return nil }) - opts.Arg("static-locals-prefix", false, func(arg, value string) error { t.staticLocalsPrefix = value; return nil }) - - opts.Opt("E", func(opt string) error { t.E = true; return nil }) - opts.Opt("all-errors", func(opt string) error { t.allErrors = true; return nil }) - opts.Opt("cover-instrumentation", func(opt string) error { t.cover = true; return nil }) - opts.Opt("cover-instrumentation-c", func(opt string) error { t.coverC = true; return nil }) - opts.Opt("err-trace", func(opt string) error { t.errTrace = true; return nil }) - opts.Opt("full-path-comments", func(opt string) error { t.fullPathComments = true; return nil }) - opts.Opt("func-sig", func(opt string) error { t.funcSig = true; return nil }) - opts.Opt("header", func(opt string) error { t.header = true; return nil }) - opts.Opt("ignore-unsupported-alignment", func(opt string) error { t.ignoreUnsupportedAligment = true; return nil }) - opts.Opt("nocapi", func(opt string) error { t.noCapi = true; return nil }) - opts.Opt("nostdinc", func(opt string) error { t.nostdinc = true; return nil }) - opts.Opt("panic-stubs", func(opt string) error { t.panicStubs = true; return nil }) - opts.Opt("trace-pinning", func(opt string) error { t.tracePinning = true; return nil }) - opts.Opt("trace-translation-units", func(opt string) error { t.traceTranslationUnits = true; return nil }) - opts.Opt("unexported-by-default", func(opt string) error { t.defaultUnExport = true; return nil }) - opts.Opt("verbose-compiledb", func(opt string) error { t.verboseCompiledb = true; return nil }) - opts.Opt("verify-structs", func(opt string) error { t.verifyStructs = true; return nil }) - opts.Opt("version", func(opt string) error { t.version = true; return nil }) - opts.Opt("watch-instrumentation", func(opt string) error { t.watch = true; return nil }) - opts.Opt("windows", func(opt string) error { t.windows = true; return nil }) - - opts.Opt("trace-included-files", func(opt string) error { - if t.includedFiles == nil { - t.includedFiles = map[string]struct{}{} - } - prev := t.cfg.IncludeFileHandler - t.cfg.IncludeFileHandler = func(pos token.Position, pathName string) { - if prev != nil { - prev(pos, pathName) - } - if _, ok := t.includedFiles[pathName]; !ok { - t.includedFiles[pathName] = struct{}{} - fmt.Fprintf(os.Stderr, "#include %s\n", pathName) - } - } - return nil - }) - opts.Arg("ignore-object", false, func(arg, value string) error { - if t.ignoredObjects == nil { - t.ignoredObjects = map[string]struct{}{} - } - t.ignoredObjects[value] = struct{}{} - return nil - }) - opts.Arg("save-config", false, func(arg, value string) error { - if value == "" { - return nil - } - - abs, err := filepath.Abs(value) - if err != nil { - return err - } - - t.saveConfig = abs - if t.includedFiles == nil { - t.includedFiles = map[string]struct{}{} - } - prev := t.cfg.IncludeFileHandler - t.cfg.IncludeFileHandler = func(pos token.Position, pathName string) { - if prev != nil { - prev(pos, pathName) - } - if _, ok := t.includedFiles[pathName]; !ok { - t.includedFiles[pathName] = struct{}{} - full := filepath.Join(abs, pathName) - switch _, err := os.Stat(full); { - case err != nil && os.IsNotExist(err): - // ok - case err != nil: - t.saveConfigErr = err - return - default: - return - } - - b, err := ioutil.ReadFile(pathName) - if err != nil { - t.saveConfigErr = err - return - } - - dir, _ := filepath.Split(full) - if err := os.MkdirAll(dir, 0700); err != nil { - t.saveConfigErr = err - return - } - - if err := ioutil.WriteFile(full, b, 0600); err != nil { - t.saveConfigErr = err - } - } - } - return nil - }) - opts.Arg("-load-config", false, func(arg, value string) error { - if value == "" { - return nil - } - - abs, err := filepath.Abs(value) - if err != nil { - return err - } - - t.loadConfig = abs - return nil - }) - opts.Arg("volatile", false, func(arg, value string) error { - for _, v := range strings.Split(strings.TrimSpace(value), ",") { - t.volatiles[cc.String(v)] = struct{}{} - } - return nil - }) - opts.Opt("nostdlib", func(opt string) error { - t.nostdlib = true - t.crt = "" - t.crtImportPath = "" - return nil - }) - opts.Arg("hide", false, func(arg, value string) error { - value = strings.TrimSpace(value) - a := strings.Split(value, ",") - for _, v := range a { - t.hide[v] = struct{}{} - } - return nil - }) - opts.Arg("l", true, func(arg, value string) error { - value = strings.TrimSpace(value) - a := strings.Split(value, ",") - for _, v := range a { - t.l = append(t.l, v) - t.symSearchOrder = append(t.symSearchOrder, -len(t.l)) - } - return nil - }) - opts.Arg("o", false, func(arg, value string) error { - if t.o != "" { - return fmt.Errorf("multiple argument: -o %s", value) - } - - t.o = value - return nil - }) - if err := opts.Parse(t.args[1:], func(arg string) error { - if strings.HasPrefix(arg, "-") { - return fmt.Errorf("unexpected option: %s", arg) - } - - switch filepath.Ext(arg) { - case ".h": - t.symSearchOrder = append(t.symSearchOrder, len(t.sources)) - t.sources = append(t.sources, cc.Source{Name: arg}) - case ".c": - t.symSearchOrder = append(t.symSearchOrder, len(t.sources)) - t.sources = append(t.sources, cc.Source{Name: arg, DoNotCache: true}) - case ".json": - t.cdb = arg - return opt.Skip(nil) - default: - return fmt.Errorf("unexpected file type: %s", arg) - } - - return nil - }); err != nil { - switch x := err.(type) { - case opt.Skip: - switch { - case t.compiledbValid: // -compiledb foo.json, create DB - cmd := []string(x)[1:] - if len(cmd) == 0 { - return fmt.Errorf("missing command after -compiledb <file>") - } - - return t.createCompileDB(cmd) - case t.cdb != "": // foo.json ..., use DB - if err := t.configure(); err != nil { - return err - } - - return t.useCompileDB(t.cdb, x) - } - - return err - default: - return err - } - } - - if t.version { - gobin, err := exec.LookPath("go") - var b []byte - if err == nil { - var bin string - bin, err = exec.LookPath(os.Args[0]) - if err == nil { - b, err = exec.Command(gobin, "version", "-m", bin).CombinedOutput() - } - } - if err == nil { - fmt.Fprintf(t.stdout, "%s", b) - return nil - } - - fmt.Fprintf(t.stdout, "%s\n", Version) - return nil - } - - if t.scriptFn != "" { - return t.scriptBuild(t.scriptFn) - } - - if len(t.sources) == 0 { - return fmt.Errorf("no input files specified") - } - - if t.crtImportPath != "" { - t.l = append(t.l, t.crtImportPath) - t.symSearchOrder = append(t.symSearchOrder, -len(t.l)) - m := map[string]struct{}{} - for _, v := range t.l { - v = strings.TrimSpace(v) - if _, ok := m[v]; !ok { - t.imported = append(t.imported, &imported{path: v}) - m[v] = struct{}{} - } - } - t.imported[len(t.imported)-1].used = true // crt is always imported - } - - if err := t.configure(); err != nil { - return err - } - - abi, err := cc.NewABI(t.goos, t.goarch) - if err != nil { - return err - } - abi.Types[cc.LongDouble] = abi.Types[cc.Double] - - var re *regexp.Regexp - if t.ignoredIncludes != "" { - if re, err = regexp.Compile(t.ignoredIncludes); err != nil { - return err - } - } - - t.cfg.ABI = abi - t.cfg.ReplaceMacroFdZero = t.replaceFdZero - t.cfg.ReplaceMacroTclDefaultDoubleRounding = t.replaceTclDefaultDoubleRounding - t.cfg.ReplaceMacroTclIeeeDoubleRounding = t.replaceTclIeeeDoubleRounding - t.cfg.Config3.IgnoreInclude = re - t.cfg.Config3.NoFieldAndBitfieldOverlap = true - t.cfg.Config3.PreserveWhiteSpace = t.saveConfig == "" - t.cfg.Config3.UnsignedEnums = true - - if t.mingw = detectMingw(t.hostPredefined); t.mingw { - t.windows = true - } - if t.nostdinc { - t.hostIncludes = nil - t.hostSysIncludes = nil - } - var sources []cc.Source - if t.hostPredefined != "" { - sources = append(sources, cc.Source{Name: "<predefined>", Value: t.hostPredefined}) - } - sources = append(sources, cc.Source{Name: "<builtin>", Value: builtin}) - if len(t.D) != 0 { - var a []string - for _, v := range t.D { - if i := strings.IndexByte(v, '='); i > 0 { - a = append(a, fmt.Sprintf("#define %s %s", v[:i], v[i+1:])) - continue - } - - a = append(a, fmt.Sprintf("#define %s 1", v)) - } - a = append(a, "\n") - sources = append(sources, cc.Source{Name: "<defines>", Value: strings.Join(a, "\n"), DoNotCache: true}) - } - if len(t.U) != 0 { - var a []string - for _, v := range t.U { - a = append(a, fmt.Sprintf("#undef %s", v)) - } - a = append(a, "\n") - sources = append(sources, cc.Source{Name: "<undefines>", Value: strings.Join(a, "\n"), DoNotCache: true}) - } - - // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html - // - // Headers whose names are enclosed in double-quotes ( "" ) shall be - // searched for first in the directory of the file with the #include - // line, then in directories named in -I options, and last in the usual - // places - includePaths := append([]string{"@"}, t.I...) - includePaths = append(includePaths, t.hostIncludes...) - includePaths = append(includePaths, t.hostSysIncludes...) - // For headers whose names are enclosed in angle brackets ( "<>" ), the - // header shall be searched for only in directories named in -I options - // and then in the usual places. - sysIncludePaths := append(t.I, t.hostSysIncludes...) - if t.traceTranslationUnits { - fmt.Printf("target: %s/%s\n", t.goos, t.goarch) - if t.hostConfigCmd != "" { - fmt.Printf("host config cmd: %s\n", t.hostConfigCmd) - } - } - for i, v := range t.sources { - tuSources := append(sources, v) - out := t.stdout - if t.saveConfig != "" { - out = io.Discard - t.E = true - } - if t.E { - t.cfg.PreprocessOnly = true - if err := cc.Preprocess(t.cfg, includePaths, sysIncludePaths, tuSources, out); err != nil { - return err - } - memGuard(i, t.isScripted) - continue - } - - var t0 time.Time - if t.traceTranslationUnits { - fmt.Printf("C front end %d/%d: %s ... ", i+1, len(t.sources), v.Name) - t0 = time.Now() - } - ast, err := cc.Translate(t.cfg, includePaths, sysIncludePaths, tuSources) - if err != nil { - return err - } - - if t.traceTranslationUnits { - fmt.Println(time.Since(t0)) - } - t.asts = append(t.asts, ast) - memGuard(i, t.isScripted) - } - if t.E || t.isScripted { - return nil - } - - return t.link() -} - -func (t *Task) configure() (err error) { - if t.configured { - return nil - } - - type jsonConfig struct { - Predefined string - IncludePaths []string - SysIncludePaths []string - OS string - Arch string - } - - t.configured = true - if t.loadConfig != "" { - path := filepath.Join(t.loadConfig, "config.json") - // trc("%p: LOAD_CONFIG(%s)", t, path) - b, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - loadConfig := &jsonConfig{} - if err := json.Unmarshal(b, loadConfig); err != nil { - return err - } - - t.goos = loadConfig.OS - t.goarch = loadConfig.Arch - for _, v := range loadConfig.IncludePaths { - t.hostIncludes = append(t.hostIncludes, filepath.Join(t.loadConfig, v)) - } - for _, v := range loadConfig.SysIncludePaths { - t.hostSysIncludes = append(t.hostSysIncludes, filepath.Join(t.loadConfig, v)) - } - t.hostPredefined = loadConfig.Predefined - return nil - } - - hostConfigOpts := strings.Split(t.hostConfigOpts, ",") - if t.hostConfigOpts == "" { - hostConfigOpts = nil - } - if t.hostPredefined, t.hostIncludes, t.hostSysIncludes, err = cc.HostConfig(t.hostConfigCmd, hostConfigOpts...); err != nil { - return err - } - - if t.saveConfig != "" && !t.configSaved { - t.configSaved = true - // trc("%p: SAVE_CONFIG(%s)", t, t.saveConfig) - cfg := &jsonConfig{ - Predefined: t.hostPredefined, - IncludePaths: t.hostIncludes, - SysIncludePaths: t.hostSysIncludes, - OS: t.goos, - Arch: t.goarch, - } - b, err := json.Marshal(cfg) - if err != nil { - return err - } - - full := filepath.Join(t.saveConfig, "config.json") - if err := os.MkdirAll(t.saveConfig, 0700); err != nil { - return err - } - - if err := ioutil.WriteFile(full, b, 0600); err != nil { - return err - } - } - - return nil -} - -func (t *Task) setLookPaths() (err error) { - if t.ccLookPath, err = exec.LookPath(t.cc); err != nil { - return err - } - - t.arLookPath, err = exec.LookPath(t.ar) - return err -} - -func (t *Task) link() (err error) { - if len(t.asts) == 0 { - return fmt.Errorf("no objects to link") - } - - if t.o == "" { - t.o = fmt.Sprintf("a_%s_%s.go", t.goos, t.goarch) - } - dir := filepath.Dir(t.o) - t.capif = filepath.Join(dir, fmt.Sprintf("capi_%s_%s.go", t.goos, t.goarch)) - f, err2 := os.Create(t.o) - if err2 != nil { - return err2 - } - - defer func() { - if e := f.Close(); e != nil && err == nil { - err = e - return - } - - if out, e := exec.Command("gofmt", "-r", "(x) -> x", "-l", "-s", "-w", t.o).CombinedOutput(); e != nil && err == nil { - err = fmt.Errorf(strings.Join([]string{string(out), e.Error()}, ": ")) - } - if out, e := exec.Command("gofmt", "-l", "-s", "-w", t.o).CombinedOutput(); e != nil && err == nil { - err = fmt.Errorf(strings.Join([]string{string(out), e.Error()}, ": ")) - } - }() - - w := bufio.NewWriter(f) - - defer func() { - if e := w.Flush(); e != nil && err == nil { - err = e - } - }() - - t.out = w - p, err := newProject(t) - if err != nil { - return err - } - - return p.main() -} - -func (t *Task) scriptBuild(fn string) error { - f, err := os.Open(fn) - if err != nil { - return err - } - - defer f.Close() - - r := csv.NewReader(f) - r.Comment = '#' - r.FieldsPerRecord = -1 - r.TrimLeadingSpace = true - script, err := r.ReadAll() - if err != nil { - return err - } - - return t.scriptBuild2(script) -} - -func (t *Task) scriptBuild2(script [][]string) error { - var ldir string - ccgo := []string{t.args[0]} - for i, line := range script { - dir := line[0] - args := line[1:] - for _, v := range args { - if strings.HasSuffix(v, ".c") || strings.HasSuffix(v, ".h") { - v = filepath.Join(dir, v) - t.symSearchOrder = append(t.symSearchOrder, len(t.sources)) - t.sources = append(t.sources, cc.Source{Name: v}) - } - } - cmd := append(ccgo, args...) - if t.traceTranslationUnits { - if dir != ldir { - fmt.Println(dir) - ldir = dir - } - fmt.Printf("%s\n", cmd) - } - t2 := NewTask(append(ccgo, args...), t.stdout, t.stderr) - t2.cfg.IncludeFileHandler = t.cfg.IncludeFileHandler - t2.cfg.SharedFunctionDefinitions = t.cfg.SharedFunctionDefinitions - t2.configSaved = t.configSaved - t2.configured = t.configured - t2.hostIncludes = t.hostIncludes - t2.hostPredefined = t.hostPredefined - t2.hostSysIncludes = t.hostSysIncludes - t2.includedFiles = t.includedFiles - t2.isScripted = true - t2.loadConfig = t.loadConfig - t2.replaceFdZero = t.replaceFdZero - t2.replaceTclDefaultDoubleRounding = t.replaceTclDefaultDoubleRounding - t2.replaceTclIeeeDoubleRounding = t.replaceTclIeeeDoubleRounding - t2.saveConfig = t.saveConfig - if err := inDir(dir, t2.Main); err != nil { - return err - } - - t.asts = append(t.asts, t2.asts...) - if i == 0 { - t.cfg = t2.cfg - } - } - if t.crtImportPath != "" { - t.l = append(t.l, t.crtImportPath) - t.symSearchOrder = append(t.symSearchOrder, -len(t.l)) - m := map[string]struct{}{} - for _, v := range t.l { - v = strings.TrimSpace(v) - if _, ok := m[v]; !ok { - t.imported = append(t.imported, &imported{path: v}) - m[v] = struct{}{} - } - } - t.imported[len(t.imported)-1].used = true // crt is always imported - } - if t.saveConfig != "" { - return nil - } - - return t.link() -} - -type cdb struct { - items []*cdbItem - outputIndex map[string][]*cdbItem -} - -func (db *cdb) find(obj map[string]*cdbItem, nm string, ver, seqLimit int, path []string, cc, ar string, ignored map[string]struct{}) error { - // trc("%v: nm %q ver %v seqLimit %v path %q cc %q ar %q", origin(1), nm, ver, seqLimit, path, cc, ar) - var item *cdbItem - var k string - switch { - case ver < 0: - // find highest ver with .seq < seqLimit - for i, v := range db.outputIndex[nm] { - if v.seq >= seqLimit { - break - } - - item = v - ver = i - } - if item == nil { - ver = -1 - for _, v := range db.items { - if seqLimit >= 0 && v.seq >= seqLimit { - break - } - - if filepath.Base(v.Output) == filepath.Base(nm) { - item = v - ver = v.ver - break - } - } - } - - k = fmt.Sprintf("%s#%d", nm, ver) - default: - // match ver exactly - k = fmt.Sprintf("%s#%d", nm, ver) - if obj[k] != nil { - return nil - } - - items := db.outputIndex[nm] - switch { - case ver < len(items): - panic(todo("", nm, ver, seqLimit)) - default: - n := -1 - for _, v := range db.items { - if seqLimit >= 0 && v.seq >= seqLimit { - break - } - - if filepath.Base(v.Output) == filepath.Base(nm) { - n++ - if n == ver { - item = v - break - } - } - } - } - } - if item == nil { - for k := range ignored { - if k == nm || strings.HasSuffix(nm, k) { - return nil - } - } - - return fmt.Errorf("not found in compile DB: %s (max seq %d), path %v", k, seqLimit, path) - } - - if obj[k] != nil { - return nil - } - - obj[k] = item - var errs []string - for _, v := range item.sources(cc, ar) { - if err := db.find(obj, v, -1, item.seq, append(path, nm), cc, ar, ignored); err != nil { - errs = append(errs, err.Error()) - } - } - if len(errs) != 0 { - sort.Strings(errs) - w := 0 - for _, v := range errs { - if w == 0 || w > 0 && v != errs[w-1] { - errs[w] = v - w++ - } - } - errs = errs[:w] - return fmt.Errorf("%s", strings.Join(errs, "\n")) - } - - return nil -} - -func suffixNum(s string, dflt int) (string, int) { - x := strings.LastIndexByte(s, '#') - if x < 0 { - return s, dflt - } - - // foo#42 - // 012345 - // x == 3 - num := s[x+1:] - n, err := strconv.ParseUint(num, 10, 32) - if err != nil { - return s, dflt - } - - return s[:x], int(n) -} - -func (t *Task) useCompileDB(fn string, args []string) error { - if err := t.setLookPaths(); err != nil { - return err - } - - var cdb cdb - f, err := os.Open(fn) - if err != nil { - return err - } - - de := json.NewDecoder(f) - err = de.Decode(&cdb.items) - f.Close() - if err != nil { - return err - } - - cdb.outputIndex = map[string][]*cdbItem{} - for i, v := range cdb.items { - v.seq = i - if len(v.Arguments) == 0 { - if len(v.Command) == 0 { - return fmt.Errorf("either arguments or command is required: %+v", v) - } - - if v.Arguments, err = shellquote.Split(v.Command); err != nil { - return err - } - } - - k := v.output(t.ccLookPath, t.arLookPath) - a := cdb.outputIndex[k] - v.ver = len(a) - cdb.outputIndex[k] = append(a, v) - } - obj := map[string]*cdbItem{} - notFound := false - for _, v := range args { - v, ver := suffixNum(v, 0) - if err := cdb.find(obj, v, ver, -1, nil, t.ccLookPath, t.arLookPath, t.ignoredObjects); err != nil { - notFound = true - fmt.Fprintln(os.Stderr, err) - } - } - if notFound { - var a []string - for k, v := range cdb.outputIndex { - for _, w := range v { - a = append(a, fmt.Sprintf("%5d %s", w.seq, k)) - } - } - sort.Strings(a) - fmt.Fprintf(os.Stderr, "compile DB index:\n\t%s\n", strings.Join(a, "\n\t")) - } - - var a []string - for k := range obj { - a = append(a, k) - } - sort.Strings(a) - return t.cdbBuild(obj, a) -} - -func (t *Task) cdbBuild(obj map[string]*cdbItem, list []string) error { - var script [][]string - for _, nm := range list { - it := obj[nm] - if !strings.HasSuffix(it.Output, ".o") || it.Arguments[0] != t.cc { - continue - } - - args, err := it.ccgoArgs(t.cc) - if err != nil { - return err - } - - for _, v := range t.D { - args = append(args, "-D"+v) - } - for _, v := range t.U { - args = append(args, "-U"+v) - } - - line := append([]string{it.Directory}, args...) - script = append(script, line) - } - return t.scriptBuild2(script) -} - -func (t *Task) createCompileDB(command []string) (rerr error) { - if err := t.setLookPaths(); err != nil { - return err - } - - cwd, err := os.Getwd() - if err != nil { - return err - } - - f, err := os.Create(t.compiledb) - if err != nil { - return err - } - - defer func() { - if err := f.Close(); err != nil && rerr == nil { - rerr = err - } - }() - - cwr := newCDBWriter(f) - - defer func() { - if err := cwr.finish(); err != nil && rerr == nil { - rerr = err - } - }() - - var cmd *exec.Cmd - var parser func(s string) ([]string, error) -out: - switch t.goos { - case "darwin", "freebsd", "netbsd": - switch command[0] { - case "make", "gmake": - // ok - default: - return fmt.Errorf("usupported build command: %s", command[0]) - } - - sh, err := exec.LookPath("sh") - if err != nil { - return err - } - - command = append([]string{sh, "-c"}, join(" ", command[0], "SHELL='sh -x'", command[1:])) - cmd = exec.Command(command[0], command[1:]...) - parser = makeXParser - case "openbsd": - switch command[0] { - case "make", "gmake": - // ok - default: - return fmt.Errorf("usupported build command: %s", command[0]) - } - - sh, err := exec.LookPath("sh") - if err != nil { - return err - } - - command = append([]string{sh, "-c"}, join(" ", command[0], "SHELL='sh -x'", command[1:])) - cmd = exec.Command(command[0], command[1:]...) - parser = makeXParser2 - case "windows": - if command[0] != "make" && command[0] != "make.exe" { - return fmt.Errorf("usupported build command: %s", command[0]) - } - - switch s := runtime.GOOS; s { - case "windows": - argv := append([]string{"-d"}, command[1:]...) - if !strings.HasSuffix(command[0], ".exe") { - command[0] += ".exe" - } - cmd = exec.Command(command[0], argv...) - parser = makeDParser - break out - case "linux": - // ok - default: - return fmt.Errorf("usupported cross compile host: %s", s) - } - - fallthrough - default: - strace, err := exec.LookPath("strace") - if err != nil { - return err - } - - argv := append([]string{"-f", "-s1000000", "-e", "trace=execve"}, command...) - cmd = exec.Command(strace, argv...) - parser = straceParser - } - cmd.Env = append(os.Environ(), "LC_ALL=C") - cw := t.newCdbMakeWriter(cwr, cwd, parser) - switch { - case t.verboseCompiledb: - cmd.Stdout = io.MultiWriter(cw, os.Stdout) - default: - cmd.Stdout = cw - } - cmd.Stderr = cmd.Stdout - if dmesgs { - dmesg("%v: %v", origin(1), cmd.Args) - } - if err := cmd.Run(); err != nil { - if dmesgs { - dmesg("%v: cmd.Run: %v", origin(1), err) - } - return err - } - - return cw.err -} - -func makeDParser(s string) ([]string, error) { - const prefix = "CreateProcess(" - if !strings.HasPrefix(s, prefix) { - return nil, nil - } - - // s: `CreateProcess(C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe,gcc -O3 -Wall -c -o compress.o compress.c,...)` - s = s[len(prefix):] - // s: `C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe,gcc -O3 -Wall -c -o compress.o compress.c,...)` - x := strings.IndexByte(s, ',') - if x < 0 { - return nil, nil - } - - cmd := s[:x] - // cmd: `C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe` - - s = s[x+1:] - // s: `gcc -O3 -Wall -c -o compress.o compress.c,...)` - if x = strings.LastIndexByte(s, ','); x < 0 { - return nil, nil - } - - s = s[:x] - // s: `gcc -O3 -Wall -c -o compress.o compress.c` - a, err := shellquote.Split(strings.TrimSpace(s)) - if err != nil || len(a) == 0 { - return nil, err - } - - return append([]string{cmd}, a[1:]...), nil -} - -func isCreateArchive(s string) bool { - // ar modifiers may be in any order so sort characters in s before checking. - // This turns eg `rc` into `cr`. - b := []byte(s) - sort.Slice(b, func(i, j int) bool { return b[i] < b[j] }) - switch string(b) { - case "cq", "cr", "crs", "cru", "r": - return true - } - return false -} - -func hasPlusPrefix(s string) (n int, r string) { - for strings.HasPrefix(s, "+") { - n++ - s = s[1:] - } - return n, s -} - -func makeXParser(s string) (r []string, err error) { - switch { - case strings.HasPrefix(s, "libtool: link: ar "): - s = s[len("libtool: link:"):] - case strings.HasPrefix(s, "libtool: compile: "): - s = s[len("libtool: compile:"):] - for strings.HasPrefix(s, " ") { - s = s[1:] - } - default: - var n int - if n, s = hasPlusPrefix(s); n == 0 { - return nil, nil - } - } - - if !strings.HasPrefix(s, " ") { - return nil, nil - } - - s = s[1:] - if dmesgs { - dmesg("%v: source line `%s`, caller %v:", origin(1), s, origin(2)) - } - r, err = shellquote.Split(s) - if dmesgs { - dmesg("%v: shellquote.Split -> %v %[2]q, %v", origin(1), r, err) - } - if err != nil { - if strings.Contains(err.Error(), "Unterminated single-quoted string") { - return nil, nil // ignore - } - } - if len(r) != 0 && filepath.Base(r[0]) == "libtool" { - r[0] = "libtool" - } - return r, err -} - -func makeXParser2(s string) (r []string, err error) { - s = strings.TrimSpace(s) - switch { - case strings.HasPrefix(s, "libtool: link: ar "): - s = s[len("libtool: link:"):] - case strings.HasPrefix(s, "libtool: compile: "): - s = s[len("libtool: compile:"):] - for strings.HasPrefix(s, " ") { - s = s[1:] - } - default: - var n int - if n, s = hasPlusPrefix(s); n != 0 { - return nil, nil - } - } - - if dmesgs { - dmesg("%v: source line `%s`, caller %v:", origin(1), s, origin(2)) - } - r, err = shellquote.Split(s) - if dmesgs { - dmesg("%v: shellquote.Split -> %v %[2]q, %v", origin(1), r, err) - } - if err != nil { - if strings.Contains(err.Error(), "Unterminated single-quoted string") { - return nil, nil // ignore - } - } - if len(r) != 0 && filepath.Base(r[0]) == "libtool" { - r[0] = "libtool" - } - return r, err -} - -func straceParser(s string) ([]string, error) { - prefix := "execve(" - if strings.HasPrefix(s, "[pid ") { - s = strings.TrimSpace(s[strings.IndexByte(s, ']')+1:]) - } - if !strings.HasPrefix(s, prefix) || !strings.HasSuffix(s, ") = 0") { - return nil, nil - } - - // 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` - s = s[len(prefix):] - // 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` - a := strings.SplitN(s, ", [", 2) - // 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` - args := a[1] - // args: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0` - args = args[:strings.LastIndex(args, "], ")] - // args: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"` - argv, err := shellquote.Split(args) - if err != nil { - return nil, err - } - - words, err := shellquote.Split(a[0]) - if err != nil { - return nil, err - } - - argv[0] = words[0] - for i, v := range argv { - if strings.HasSuffix(v, ",") { - v = v[:len(v)-1] - } - if v2, err := strconv.Unquote(`"` + v + `"`); err == nil { - v = v2 - } - argv[i] = v - } - - return argv, nil -} - -type cdbItem struct { - Arguments []string `json:"arguments"` - Command string `json:"command,omitempty"` - Directory string `json:"directory"` - File string `json:"file"` - Output string `json:"output,omitempty"` - - seq int - ver int -} - -func (it *cdbItem) cmpString() string { return fmt.Sprint(*it) } - -func (it *cdbItem) ccgoArgs(cc string) (r []string, err error) { - switch it.Arguments[0] { - case cc: - set := opt.NewSet() - set.Arg("D", true, func(opt, arg string) error { r = append(r, "-D"+arg); return nil }) - set.Arg("I", true, func(opt, arg string) error { r = append(r, "-I"+arg); return nil }) - set.Arg("MF", true, func(opt, arg string) error { return nil }) - set.Arg("MT", true, func(opt, arg string) error { return nil }) - set.Arg("O", true, func(opt, arg string) error { return nil }) - set.Arg("U", true, func(opt, arg string) error { r = append(r, "-U"+arg); return nil }) - set.Arg("o", true, func(opt, arg string) error { return nil }) - set.Arg("std", true, func(opt, arg string) error { return nil }) - set.Opt("MD", func(opt string) error { return nil }) - set.Opt("MMD", func(opt string) error { return nil }) - set.Opt("MP", func(opt string) error { return nil }) - set.Opt("ansi", func(opt string) error { return nil }) - set.Opt("c", func(opt string) error { return nil }) - set.Opt("g", func(opt string) error { return nil }) - set.Opt("pedantic", func(opt string) error { return nil }) - set.Opt("pipe", func(opt string) error { return nil }) - set.Opt("pthread", func(opt string) error { return nil }) - set.Opt("s", func(opt string) error { return nil }) - set.Opt("w", func(opt string) error { return nil }) - if err := set.Parse(it.Arguments[1:], func(arg string) error { - switch { - case strings.HasSuffix(arg, ".c"): - r = append(r, arg) - case - - strings.HasPrefix(arg, "-W"), - strings.HasPrefix(arg, "-f"), - strings.HasPrefix(arg, "-m"): - - // nop - case strings.HasPrefix(arg, ">"): - return opt.Skip(nil) - default: - return fmt.Errorf("unknown/unsupported CC option: %s", arg) - } - - return nil - }); err != nil { - switch err.(type) { - case opt.Skip: - // ok - default: - return nil, err - } - } - - return r, nil - default: - return nil, fmt.Errorf("command not supported: %q", it.Arguments[0]) - } -} - -func (it *cdbItem) output(cc, ar string) (r string) { - if it.Output != "" { - return it.Output - } - - if len(it.Arguments) == 0 { - return "" - } - - switch it.Arguments[0] { - case cc: - for i, v := range it.Arguments { - if v == "-o" && i < len(it.Arguments)-1 { - it.Output = filepath.Join(it.Directory, it.Arguments[i+1]) - break - } - } - if it.Output == "" && strings.HasSuffix(it.File, ".c") { - for _, v := range it.Arguments { - if v == "-c" { - bn := filepath.Base(it.File) - it.Output = filepath.Join(it.Directory, bn[:len(bn)-2]+".o") - break - } - } - } - case ar: - if isCreateArchive(it.Arguments[1]) { - it.Output = filepath.Join(it.Directory, it.Arguments[2]) - } - case "libtool": - for i, v := range it.Arguments { - if v == "-o" && i < len(it.Arguments)-1 { - it.Output = filepath.Join(it.Directory, it.Arguments[i+1]) - } - } - } - return it.Output -} - -func (it *cdbItem) sources(cc, ar string) (r []string) { - if len(it.Arguments) == 0 { - return nil - } - - switch arg0 := it.Arguments[0]; arg0 { - case - "libtool", - ar, - filepath.Base(ar), - cc: - - var prev string - for _, v := range it.Arguments { - switch prev { - case "-o", "-MT", "-MF": - // nop - default: - if strings.HasSuffix(v, ".o") { - r = append(r, filepath.Join(it.Directory, v)) - } - } - prev = v - } - return r - default: - panic(todo("cc: %q ar: %q it: %+v", cc, ar, it)) - } -} - -type cdbMakeWriter struct { - ar string - arBase string - b bytes.Buffer - cc string - dir string - err error - it cdbItem - parser func(s string) ([]string, error) - prefix string - sc *bufio.Scanner - t *Task - w *cdbWriter -} - -func (t *Task) newCdbMakeWriter(w *cdbWriter, dir string, parser func(s string) ([]string, error)) *cdbMakeWriter { - const sz = 1 << 16 - r := &cdbMakeWriter{ - ar: t.arLookPath, - arBase: filepath.Base(t.arLookPath), - cc: t.ccLookPath, - dir: dir, - parser: parser, - t: t, - w: w, - } - r.sc = bufio.NewScanner(&r.b) - r.sc.Buffer(make([]byte, sz), sz) - return r -} - -func (w *cdbMakeWriter) fail(err error) { - if w.err == nil { - w.err = fmt.Errorf("%v (%v)", err, origin(2)) - } -} - -func (w *cdbMakeWriter) Write(b []byte) (int, error) { - w.b.Write(b) - for bytes.Contains(w.b.Bytes(), []byte{'\n'}) { - if !w.sc.Scan() { - panic(todo("internal error")) - } - - s := w.sc.Text() - if strings.HasSuffix(s, "\\") { - w.prefix += s[:len(s)-1] - continue - } - - s = w.prefix + s - w.prefix = "" - s = strings.TrimSpace(s) - if edx := strings.Index(s, "Entering directory"); edx >= 0 { - s = s[edx+len("Entering directory"):] - s = strings.TrimSpace(s) - if len(s) == 0 { - continue - } - - if (s[0] == '\'' || s[0] == '`') && s[len(s)-1] == '\'' { - s = s[1:] - if len(s) == 0 { - continue - } - - s = s[:len(s)-1] - } - s = `"` + s + `"` - dir, err := strconv.Unquote(s) - if err != nil { - w.fail(err) - continue - } - - dir = filepath.Clean(dir) - if dir == w.dir { - continue - } - - w.dir = dir - fmt.Printf("cd %s\n", dir) - continue - } - - if dmesgs { - dmesg("%v: source line `%s`", origin(1), s) - } - args, err := w.parser(s) - if dmesgs { - dmesg("%v: parser -> %v %[2]q, %v", origin(1), args, err) - } - if err != nil { - w.fail(err) - continue - } - - if len(args) == 0 { - continue - } - - // TODO: change so eg handleGCC returns []cdbItem, skip if none. - - w.it = cdbItem{} - - err = nil - switch args[0] { - case w.cc: - if w.t.verboseCompiledb { - fmt.Printf("source line: %q\n", s) - } - fmt.Printf("CCGO CC: %q\n", args) - err = w.handleGCC(args) - case w.ar: - fallthrough - case w.arBase: - if isCreateArchive(args[1]) { - if w.t.verboseCompiledb { - fmt.Printf("source line: %q\n", s) - } - fmt.Printf("CCGO AR: %q\n", args) - err = w.handleAR(args) - } - case "libtool": - if w.t.verboseCompiledb { - fmt.Printf("source line: %q\n", s) - } - fmt.Printf("CCGO LIBTOOL: %q\n", args) - err = w.handleLibtool(args) - } - if err != nil { - w.fail(err) - continue - } - - if w.it.Output != "" { - w.w.add(w.it) - } - } - return len(b), nil -} - -func (w *cdbMakeWriter) handleLibtool(args []string) error { - w.it = cdbItem{ - Arguments: args, - Directory: w.dir, - } - for i, v := range args { - switch { - case v == "-o" && i < len(args)-1: - w.it.Output = filepath.Join(w.dir, args[i+1]) - } - } - w.it.output(w.cc, w.ar) - return nil -} - -func (w *cdbMakeWriter) handleAR(args []string) error { - w.it = cdbItem{ - Arguments: args, - Directory: w.dir, - } - // TODO: assumes isCreateArchive has already been checked - w.it.Output = filepath.Join(w.dir, args[2]) - return nil -} - -func (w *cdbMakeWriter) handleGCC(args []string) error { - w.it = cdbItem{ - Arguments: args, - Directory: w.dir, - } - for i, v := range args { - switch { - case v == "-o" && i < len(args)-1: - w.it.Output = filepath.Join(w.dir, args[i+1]) - case strings.HasSuffix(v, ".c"): - if w.it.File != "" { - return fmt.Errorf("multiple .c files: %s", v) - } - - w.it.File = filepath.Clean(v) - } - } - w.it.output(w.cc, w.ar) - return nil -} - -type cdbWriter struct { - w *bufio.Writer - items []cdbItem -} - -func newCDBWriter(w io.Writer) *cdbWriter { - return &cdbWriter{w: bufio.NewWriter(w)} -} - -func (w *cdbWriter) add(item cdbItem) { - w.items = append(w.items, item) -} - -func (w *cdbWriter) finish() error { - enc := json.NewEncoder(w.w) - enc.SetIndent("", " ") - if err := enc.Encode(w.items); err != nil { - return err - } - return w.w.Flush() -} - -func join(sep string, a ...interface{}) string { - var b []string - for _, v := range a { - switch x := v.(type) { - case string: - b = append(b, x) - case []string: - b = append(b, x...) - default: - panic(todo("internal error: %T", x)) - } - } - return strings.Join(b, sep) -} - -func inDir(dir string, f func() error) (err error) { - var cwd string - if cwd, err = os.Getwd(); err != nil { - return err - } - - defer func() { - if err2 := os.Chdir(cwd); err2 != nil { - err = err2 - } - }() - - if err = os.Chdir(dir); err != nil { - return err - } - - return f() -} - -func detectMingw(s string) bool { - return strings.Contains(s, "#define __MINGW") -} - -func memGuard(i int, force bool) { - if totalRam == 0 || totalRam > 64e9 { - return - } - - var ms runtime.MemStats - runtime.ReadMemStats(&ms) - switch { - case ms.Alloc < totalRam/2: - return - case ms.Alloc < (8*totalRam)/10: - if force { - break - } - - switch { - case totalRam < 1e9: - // ok - case totalRam < 16e9: - if i&1 == 1 { - return - } - default: - if i&3 != 3 { - return - } - } - } - - debug.FreeOSMemory() -} |