modernc.org/ccgo/v3@v3.16.14/lib/ccgo.go (about)

     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.
     8  package ccgo // import "modernc.org/ccgo/v3/lib"
     9  
    10  import (
    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  
    40  const (
    41  	Version = "3.12.6-20210922111124"
    42  
    43  	experimentsEnvVar = "CCGO_EXPERIMENT"
    44  	maxSourceLine     = 1 << 20
    45  )
    46  
    47  var (
    48  	_ = libc.Xstdin
    49  
    50  	coverExperiment bool
    51  )
    52  
    53  func 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  
   140  const (
   141  	builtin = `
   142  #ifdef __PTRDIFF_TYPE__
   143  typedef __PTRDIFF_TYPE__ ptrdiff_t;
   144  #else
   145  #error __PTRDIFF_TYPE__ undefined
   146  #endif
   147  
   148  #ifdef __SIZE_TYPE__
   149  typedef __SIZE_TYPE__ size_t;
   150  #else
   151  #error __SIZE_TYPE__ undefined
   152  #endif
   153  
   154  #ifdef __WCHAR_TYPE__
   155  typedef __WCHAR_TYPE__ wchar_t;
   156  #else
   157  #error __WCHAR_TYPE__ undefined
   158  #endif
   159  
   160  #ifdef __SIZEOF_INT128__
   161  typedef struct { __INT64_TYPE__ lo, hi; } __int128_t;   // must match modernc.org/mathutil.Int128
   162  typedef 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  
   186  typedef void *__builtin_va_list;
   187  typedef long double __float128;
   188  
   189  #if defined(__MINGW32__) || defined(__MINGW64__)
   190  typedef __builtin_va_list va_list;
   191  int gnu_printf(const char *format, ...);
   192  int gnu_scanf(const char *format, ...);
   193  int ms_printf(const char *format, ...);
   194  int 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);
   202  char *__builtin___strcat_chk (char *dest, const char *src, size_t os);
   203  char *__builtin___strcpy_chk (char *dest, const char *src, size_t os);
   204  char *__builtin___strncpy_chk(char *dest, char *src, size_t n, size_t os);
   205  char *__builtin_strchr(const char *s, int c);
   206  char *__builtin_strcpy(char *dest, const char *src);
   207  double __builtin_copysign ( double x, double y );
   208  double __builtin_copysignl (long double x, long double y );
   209  double __builtin_fabs(double x);
   210  double __builtin_huge_val (void);
   211  double __builtin_inf (void);
   212  double __builtin_nan (const char *str);
   213  float __builtin_copysignf ( float x, float y );
   214  float __builtin_fabsf(float x);
   215  float __builtin_huge_valf (void);
   216  float __builtin_inff (void);
   217  float __builtin_nanf (const char *str);
   218  int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, ...);
   219  int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...);
   220  int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, __builtin_va_list ap);
   221  int __builtin__snprintf_chk(char * str, size_t maxlen, int flag, size_t strlen, const char * format);
   222  int __builtin_abs(int j);
   223  int __builtin_add_overflow();
   224  int __builtin_clz (unsigned);
   225  int __builtin_isunordered(double x, double y);
   226  int __builtin_clzl (unsigned long);
   227  int __builtin_clzll (unsigned long long);
   228  int __builtin_constant_p_impl(int, ...);
   229  int __builtin_getentropy(void*, size_t);
   230  int __builtin_isnan(double);
   231  int __builtin_memcmp(const void *s1, const void *s2, size_t n);
   232  int __builtin_mul_overflow();
   233  int __builtin_popcount (unsigned int x);
   234  int __builtin_popcountl (unsigned long x);
   235  int __builtin_printf(const char *format, ...);
   236  int __builtin_snprintf(char *str, size_t size, const char *format, ...);
   237  int __builtin_sprintf(char *str, const char *format, ...);
   238  int __builtin_strcmp(const char *s1, const char *s2);
   239  int __builtin_sub_overflow();
   240  long __builtin_expect (long exp, long c);
   241  long double __builtin_fabsl(long double x);
   242  long double __builtin_nanl (const char *str);
   243  long long __builtin_llabs(long long j);
   244  size_t __builtin_object_size (void * ptr, int type);
   245  size_t __builtin_strlen(const char *s);
   246  void *__builtin___memcpy_chk (void *dest, const void *src, size_t n, size_t os);
   247  void *__builtin___memmove_chk (void *dest, const void *src, size_t n, size_t os);
   248  void *__builtin___memset_chk (void *dstpp, int c, size_t len, size_t dstlen);
   249  void *__builtin_malloc(size_t size);
   250  void *__builtin_memcpy(void *dest, const void *src, size_t n);
   251  void *__builtin_memset(void *s, int c, size_t n);
   252  void *__builtin_mmap(void *addr, size_t length, int prot, int flags, int fd, __INTPTR_TYPE__ offset);
   253  void *__ccgo_va_arg(__builtin_va_list ap);
   254  void __builtin_abort(void);
   255  void __builtin_bzero(void *s, size_t n);
   256  void __builtin_exit(int status);
   257  void __builtin_free(void *ptr);
   258  void __builtin_prefetch (const void *addr, ...);
   259  void __builtin_trap (void);
   260  void __builtin_unreachable (void);
   261  void __ccgo_dmesg(char*, ...);
   262  void __ccgo_va_end(__builtin_va_list ap);
   263  void __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  
   282  unsigned __sync_add_and_fetch_uint32(unsigned*, unsigned);
   283  unsigned __sync_sub_and_fetch_uint32(unsigned*, unsigned);
   284  
   285  #ifdef __APPLE__
   286  int (*__darwin_check_fd_set_overflow)(int, void *, int);
   287  #endif
   288  
   289  `
   290  	defaultCrt = "modernc.org/libc"
   291  )
   292  
   293  func 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  
   306  func 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  
   319  func 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.
   333  type 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.
   428  func 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  
   464  func 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'.
   473  func (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  
   558  func (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.
   615  func (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  
  1020  func (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  
  1095  func (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  
  1104  func (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  
  1150  func (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  
  1170  func (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  
  1235  type cdb struct {
  1236  	items       []*cdbItem
  1237  	outputIndex map[string][]*cdbItem
  1238  }
  1239  
  1240  func (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  
  1336  func 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  
  1354  func (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  
  1418  func (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  
  1444  func (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)
  1475  out:
  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  
  1562  func 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  
  1595  func 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  
  1607  func 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  
  1615  func 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  
  1654  func 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  
  1689  func 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  
  1731  type 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  
  1742  func (it *cdbItem) cmpString() string { return fmt.Sprint(*it) }
  1743  
  1744  func (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  
  1800  func (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  
  1840  func (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  
  1870  type 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  
  1885  func (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  
  1901  func (w *cdbMakeWriter) fail(err error) {
  1902  	if w.err == nil {
  1903  		w.err = fmt.Errorf("%v (%v)", err, origin(2))
  1904  	}
  1905  }
  1906  
  1907  func (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  
  2012  func (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  
  2027  func (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  
  2037  func (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  
  2058  type cdbWriter struct {
  2059  	w     *bufio.Writer
  2060  	items []cdbItem
  2061  }
  2062  
  2063  func newCDBWriter(w io.Writer) *cdbWriter {
  2064  	return &cdbWriter{w: bufio.NewWriter(w)}
  2065  }
  2066  
  2067  func (w *cdbWriter) add(item cdbItem) {
  2068  	w.items = append(w.items, item)
  2069  }
  2070  
  2071  func (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  
  2080  func 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  
  2095  func 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  
  2114  func detectMingw(s string) bool {
  2115  	return strings.Contains(s, "#define __MINGW")
  2116  }
  2117  
  2118  func 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  }