gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ccgo.go (56560B)


      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 }