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 | }
|
---|