• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package main
6
7import (
8	"bytes"
9	"fmt"
10	"io"
11	"path/filepath"
12	"strings"
13)
14
15func callCompiler(env env, cfg *config, inputCmd *command) int {
16	var compilerErr error
17
18	if !filepath.IsAbs(inputCmd.Path) && !strings.HasPrefix(inputCmd.Path, ".") &&
19		!strings.ContainsRune(inputCmd.Path, filepath.Separator) {
20		if resolvedPath, err := resolveAgainstPathEnv(env, inputCmd.Path); err == nil {
21			inputCmd = &command{
22				Path:       resolvedPath,
23				Args:       inputCmd.Args,
24				EnvUpdates: inputCmd.EnvUpdates,
25			}
26		} else {
27			compilerErr = err
28		}
29	}
30	exitCode := 0
31	if compilerErr == nil {
32		exitCode, compilerErr = callCompilerInternal(env, cfg, inputCmd)
33	}
34	if compilerErr != nil {
35		printCompilerError(env.stderr(), compilerErr)
36		exitCode = 1
37	}
38	return exitCode
39}
40
41// Given the main builder path and the absolute path to our wrapper, returns the path to the
42// 'real' compiler we should invoke.
43func calculateAndroidWrapperPath(mainBuilderPath string, absWrapperPath string) string {
44	// FIXME: This combination of using the directory of the symlink but the basename of the
45	// link target is strange but is the logic that old android wrapper uses. Change this to use
46	// directory and basename either from the absWrapperPath or from the builder.path, but don't
47	// mix anymore.
48
49	// We need to be careful here: path.Join Clean()s its result, so `./foo` will get
50	// transformed to `foo`, which isn't good since we're passing this path to exec.
51	basePart := filepath.Base(absWrapperPath) + ".real"
52	if !strings.ContainsRune(mainBuilderPath, filepath.Separator) {
53		return basePart
54	}
55
56	dirPart := filepath.Dir(mainBuilderPath)
57	if cleanResult := filepath.Join(dirPart, basePart); strings.ContainsRune(cleanResult, filepath.Separator) {
58		return cleanResult
59	}
60
61	return "." + string(filepath.Separator) + basePart
62}
63
64func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
65	if err := checkUnsupportedFlags(inputCmd); err != nil {
66		return 0, err
67	}
68	mainBuilder, err := newCommandBuilder(env, cfg, inputCmd)
69	if err != nil {
70		return 0, err
71	}
72	processPrintConfigFlag(mainBuilder)
73	processPrintCmdlineFlag(mainBuilder)
74	env = mainBuilder.env
75	var compilerCmd *command
76	clangSyntax := processClangSyntaxFlag(mainBuilder)
77	if cfg.isAndroidWrapper {
78		mainBuilder.path = calculateAndroidWrapperPath(mainBuilder.path, mainBuilder.absWrapperPath)
79		switch mainBuilder.target.compilerType {
80		case clangType:
81			mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
82			mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
83			mainBuilder.addPostUserArgs(mainBuilder.cfg.clangPostFlags...)
84			if _, err := processGomaCccFlags(mainBuilder); err != nil {
85				return 0, err
86			}
87			compilerCmd = mainBuilder.build()
88		case clangTidyType:
89			compilerCmd = mainBuilder.build()
90		default:
91			return 0, newErrorwithSourceLocf("unsupported compiler: %s", mainBuilder.target.compiler)
92		}
93	} else {
94		cSrcFile, tidyFlags, tidyMode := processClangTidyFlags(mainBuilder)
95		if mainBuilder.target.compilerType == clangType {
96			err := prepareClangCommand(mainBuilder)
97			if err != nil {
98				return 0, err
99			}
100			allowCCache := true
101			if tidyMode != tidyModeNone {
102				allowCCache = false
103				clangCmdWithoutGomaAndCCache := mainBuilder.build()
104				var err error
105				switch tidyMode {
106				case tidyModeTricium:
107					if cfg.triciumNitsDir == "" {
108						return 0, newErrorwithSourceLocf("tricium linting was requested, but no nits directory is configured")
109					}
110					err = runClangTidyForTricium(env, clangCmdWithoutGomaAndCCache, cSrcFile, cfg.triciumNitsDir, tidyFlags, cfg.crashArtifactsDir)
111				case tidyModeAll:
112					err = runClangTidy(env, clangCmdWithoutGomaAndCCache, cSrcFile, tidyFlags)
113				default:
114					panic(fmt.Sprintf("Unknown tidy mode: %v", tidyMode))
115				}
116
117				if err != nil {
118					return 0, err
119				}
120			}
121			if err := processGomaCCacheFlags(allowCCache, mainBuilder); err != nil {
122				return 0, err
123			}
124			compilerCmd = mainBuilder.build()
125		} else {
126			if clangSyntax {
127				allowCCache := false
128				clangCmd, err := calcClangCommand(allowCCache, mainBuilder.clone())
129				if err != nil {
130					return 0, err
131				}
132				gccCmd, err := calcGccCommand(mainBuilder)
133				if err != nil {
134					return 0, err
135				}
136				return checkClangSyntax(env, clangCmd, gccCmd)
137			}
138			compilerCmd, err = calcGccCommand(mainBuilder)
139			if err != nil {
140				return 0, err
141			}
142		}
143	}
144	rusageLogfileName := getRusageLogFilename(env)
145	bisectStage := getBisectStage(env)
146	if shouldForceDisableWerror(env, cfg) {
147		if rusageLogfileName != "" {
148			return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
149		}
150		if bisectStage != "" {
151			return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
152		}
153		return doubleBuildWithWNoError(env, cfg, compilerCmd)
154	}
155	if shouldCompileWithFallback(env) {
156		if rusageLogfileName != "" {
157			return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
158		}
159		if bisectStage != "" {
160			return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
161		}
162		return compileWithFallback(env, cfg, compilerCmd, mainBuilder.absWrapperPath)
163	}
164	if rusageLogfileName != "" {
165		if bisectStage != "" {
166			return 0, newUserErrorf("BISECT_STAGE is meaningless with GETRUSAGE")
167		}
168		return logRusage(env, rusageLogfileName, compilerCmd)
169	}
170	if bisectStage != "" {
171		compilerCmd, err = calcBisectCommand(env, cfg, bisectStage, compilerCmd)
172		if err != nil {
173			return 0, err
174		}
175	}
176	// Note: We return an exit code only if the underlying env is not
177	// really doing an exec, e.g. commandRecordingEnv.
178	return wrapSubprocessErrorWithSourceLoc(compilerCmd, env.exec(compilerCmd))
179}
180
181func prepareClangCommand(builder *commandBuilder) (err error) {
182	if !builder.cfg.isHostWrapper {
183		processSysrootFlag(builder)
184	}
185	builder.addPreUserArgs(builder.cfg.clangFlags...)
186	if builder.cfg.crashArtifactsDir != "" {
187		builder.addPreUserArgs("-fcrash-diagnostics-dir=" + builder.cfg.crashArtifactsDir)
188	}
189	builder.addPostUserArgs(builder.cfg.clangPostFlags...)
190	calcCommonPreUserArgs(builder)
191	return processClangFlags(builder)
192}
193
194func calcClangCommand(allowCCache bool, builder *commandBuilder) (*command, error) {
195	err := prepareClangCommand(builder)
196	if err != nil {
197		return nil, err
198	}
199	if err := processGomaCCacheFlags(allowCCache, builder); err != nil {
200		return nil, err
201	}
202	return builder.build(), nil
203}
204
205func calcGccCommand(builder *commandBuilder) (*command, error) {
206	if !builder.cfg.isHostWrapper {
207		processSysrootFlag(builder)
208	}
209	builder.addPreUserArgs(builder.cfg.gccFlags...)
210	if !builder.cfg.isHostWrapper {
211		calcCommonPreUserArgs(builder)
212	}
213	processGccFlags(builder)
214	if !builder.cfg.isHostWrapper {
215		allowCCache := true
216		if err := processGomaCCacheFlags(allowCCache, builder); err != nil {
217			return nil, err
218		}
219	}
220	return builder.build(), nil
221}
222
223func calcCommonPreUserArgs(builder *commandBuilder) {
224	builder.addPreUserArgs(builder.cfg.commonFlags...)
225	if !builder.cfg.isHostWrapper {
226		processPieFlags(builder)
227		processThumbCodeFlags(builder)
228		processStackProtectorFlags(builder)
229		processX86Flags(builder)
230	}
231	processSanitizerFlags(builder)
232}
233
234func processGomaCCacheFlags(allowCCache bool, builder *commandBuilder) (err error) {
235	gomaccUsed := false
236	if !builder.cfg.isHostWrapper {
237		gomaccUsed, err = processGomaCccFlags(builder)
238		if err != nil {
239			return err
240		}
241	}
242	if !gomaccUsed && allowCCache {
243		processCCacheFlag(builder)
244	}
245	return nil
246}
247
248func getAbsWrapperPath(env env, wrapperCmd *command) (string, error) {
249	wrapperPath := getAbsCmdPath(env, wrapperCmd)
250	evaledCmdPath, err := filepath.EvalSymlinks(wrapperPath)
251	if err != nil {
252		return "", wrapErrorwithSourceLocf(err, "failed to evaluate symlinks for %s", wrapperPath)
253	}
254	return evaledCmdPath, nil
255}
256
257func printCompilerError(writer io.Writer, compilerErr error) {
258	if _, ok := compilerErr.(userError); ok {
259		fmt.Fprintf(writer, "%s\n", compilerErr)
260	} else {
261		emailAccount := "chromeos-toolchain"
262		if isAndroidConfig() {
263			emailAccount = "android-llvm"
264		}
265		fmt.Fprintf(writer,
266			"Internal error. Please report to %s@google.com.\n%s\n",
267			emailAccount, compilerErr)
268	}
269}
270
271func needStdinTee(inputCmd *command) bool {
272	lastArg := ""
273	for _, arg := range inputCmd.Args {
274		if arg == "-" && lastArg != "-o" {
275			return true
276		}
277		lastArg = arg
278	}
279	return false
280}
281
282func prebufferStdinIfNeeded(env env, inputCmd *command) (getStdin func() io.Reader, err error) {
283	// We pre-buffer the entirety of stdin, since the compiler may exit mid-invocation with an
284	// error, which may leave stdin partially read.
285	if !needStdinTee(inputCmd) {
286		// This won't produce deterministic input to the compiler, but stdin shouldn't
287		// matter in this case, so...
288		return env.stdin, nil
289	}
290
291	stdinBuffer := &bytes.Buffer{}
292	if _, err := stdinBuffer.ReadFrom(env.stdin()); err != nil {
293		return nil, wrapErrorwithSourceLocf(err, "prebuffering stdin")
294	}
295
296	return func() io.Reader { return bytes.NewReader(stdinBuffer.Bytes()) }, nil
297}
298