• 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	"fmt"
9	"io"
10	"path/filepath"
11	"strings"
12)
13
14func callCompiler(env env, cfg *config, inputCmd *command) int {
15	var compilerErr error
16
17	if !filepath.IsAbs(inputCmd.Path) && !strings.HasPrefix(inputCmd.Path, ".") &&
18		!strings.ContainsRune(inputCmd.Path, filepath.Separator) {
19		if resolvedPath, err := resolveAgainstPathEnv(env, inputCmd.Path); err == nil {
20			inputCmd = &command{
21				Path:       resolvedPath,
22				Args:       inputCmd.Args,
23				EnvUpdates: inputCmd.EnvUpdates,
24			}
25		} else {
26			compilerErr = err
27		}
28	}
29	exitCode := 0
30	if compilerErr == nil {
31		exitCode, compilerErr = callCompilerInternal(env, cfg, inputCmd)
32	}
33	if compilerErr != nil {
34		printCompilerError(env.stderr(), compilerErr)
35		exitCode = 1
36	}
37	return exitCode
38}
39
40func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
41	if err := checkUnsupportedFlags(inputCmd); err != nil {
42		return 0, err
43	}
44	mainBuilder, err := newCommandBuilder(env, cfg, inputCmd)
45	if err != nil {
46		return 0, err
47	}
48	processPrintConfigFlag(mainBuilder)
49	processPrintCmdlineFlag(mainBuilder)
50	env = mainBuilder.env
51	var compilerCmd *command
52	clangSyntax := processClangSyntaxFlag(mainBuilder)
53	if cfg.isAndroidWrapper {
54		// FIXME: This combination of using the directory of the symlink but the
55		// basename of the link target is strange but is the logic that old android
56		// wrapper uses. Change this to use directory and basename either from the
57		// absWrapperPath or from the builder.path, but don't mix anymore.
58		mainBuilder.path = filepath.Join(filepath.Dir(mainBuilder.path), filepath.Base(mainBuilder.absWrapperPath)+".real")
59
60		switch mainBuilder.target.compilerType {
61		case clangType:
62			mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
63			mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
64			if _, err := processGomaCccFlags(mainBuilder); err != nil {
65				return 0, err
66			}
67			compilerCmd = mainBuilder.build()
68		case clangTidyType:
69			compilerCmd = mainBuilder.build()
70		default:
71			return 0, newErrorwithSourceLocf("unsupported compiler: %s", mainBuilder.target.compiler)
72		}
73	} else if mainBuilder.target.compilerType == clangType {
74		cSrcFile, useClangTidy := processClangTidyFlags(mainBuilder)
75		sysroot, err := prepareClangCommand(mainBuilder)
76		if err != nil {
77			return 0, err
78		}
79		allowCCache := true
80		if useClangTidy {
81			allowCCache = false
82			clangCmdWithoutGomaAndCCache := mainBuilder.build()
83			if err := runClangTidy(env, clangCmdWithoutGomaAndCCache, cSrcFile); err != nil {
84				return 0, err
85			}
86		}
87		if err := processGomaCCacheFlags(sysroot, allowCCache, mainBuilder); err != nil {
88			return 0, err
89		}
90		compilerCmd = mainBuilder.build()
91	} else {
92		if clangSyntax {
93			allowCCache := false
94			clangCmd, err := calcClangCommand(allowCCache, mainBuilder.clone())
95			if err != nil {
96				return 0, err
97			}
98			gccCmd, err := calcGccCommand(mainBuilder)
99			if err != nil {
100				return 0, err
101			}
102			return checkClangSyntax(env, clangCmd, gccCmd)
103		}
104		compilerCmd, err = calcGccCommand(mainBuilder)
105		if err != nil {
106			return 0, err
107		}
108	}
109	rusageLogfileName := getRusageLogFilename(env)
110	bisectStage := getBisectStage(env)
111	if shouldForceDisableWError(env) {
112		if rusageLogfileName != "" {
113			return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
114		}
115		if bisectStage != "" {
116			return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
117		}
118		return doubleBuildWithWNoError(env, cfg, compilerCmd)
119	}
120	if shouldCompileWithFallback(env) {
121		if rusageLogfileName != "" {
122			return 0, newUserErrorf("GETRUSAGE is meaningless with FORCE_DISABLE_WERROR")
123		}
124		if bisectStage != "" {
125			return 0, newUserErrorf("BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR")
126		}
127		return compileWithFallback(env, cfg, compilerCmd, mainBuilder.absWrapperPath)
128	}
129	if rusageLogfileName != "" {
130		if bisectStage != "" {
131			return 0, newUserErrorf("BISECT_STAGE is meaningless with GETRUSAGE")
132		}
133		return logRusage(env, rusageLogfileName, compilerCmd)
134	}
135	if bisectStage != "" {
136		compilerCmd, err = calcBisectCommand(env, cfg, bisectStage, compilerCmd)
137		if err != nil {
138			return 0, err
139		}
140	}
141	// Note: We return an exit code only if the underlying env is not
142	// really doing an exec, e.g. commandRecordingEnv.
143	return wrapSubprocessErrorWithSourceLoc(compilerCmd, env.exec(compilerCmd))
144}
145
146func prepareClangCommand(builder *commandBuilder) (sysroot string, err error) {
147	sysroot = ""
148	if !builder.cfg.isHostWrapper {
149		sysroot = processSysrootFlag(builder)
150	}
151	builder.addPreUserArgs(builder.cfg.clangFlags...)
152	builder.addPostUserArgs(builder.cfg.clangPostFlags...)
153	calcCommonPreUserArgs(builder)
154	if err := processClangFlags(builder); err != nil {
155		return "", err
156	}
157	return sysroot, nil
158}
159
160func calcClangCommand(allowCCache bool, builder *commandBuilder) (*command, error) {
161	sysroot, err := prepareClangCommand(builder)
162	if err != nil {
163		return nil, err
164	}
165	if err := processGomaCCacheFlags(sysroot, allowCCache, builder); err != nil {
166		return nil, err
167	}
168	return builder.build(), nil
169}
170
171func calcGccCommand(builder *commandBuilder) (*command, error) {
172	sysroot := ""
173	if !builder.cfg.isHostWrapper {
174		sysroot = processSysrootFlag(builder)
175	}
176	builder.addPreUserArgs(builder.cfg.gccFlags...)
177	if !builder.cfg.isHostWrapper {
178		calcCommonPreUserArgs(builder)
179	}
180	processGccFlags(builder)
181	if !builder.cfg.isHostWrapper {
182		allowCCache := true
183		if err := processGomaCCacheFlags(sysroot, allowCCache, builder); err != nil {
184			return nil, err
185		}
186	}
187	return builder.build(), nil
188}
189
190func calcCommonPreUserArgs(builder *commandBuilder) {
191	builder.addPreUserArgs(builder.cfg.commonFlags...)
192	if !builder.cfg.isHostWrapper {
193		processPieFlags(builder)
194		processThumbCodeFlags(builder)
195		processStackProtectorFlags(builder)
196		processX86Flags(builder)
197	}
198	processSanitizerFlags(builder)
199}
200
201func processGomaCCacheFlags(sysroot string, allowCCache bool, builder *commandBuilder) (err error) {
202	gomaccUsed := false
203	if !builder.cfg.isHostWrapper {
204		gomaccUsed, err = processGomaCccFlags(builder)
205		if err != nil {
206			return err
207		}
208	}
209	if !gomaccUsed && allowCCache {
210		processCCacheFlag(sysroot, builder)
211	}
212	return nil
213}
214
215func getAbsWrapperPath(env env, wrapperCmd *command) (string, error) {
216	wrapperPath := getAbsCmdPath(env, wrapperCmd)
217	evaledCmdPath, err := filepath.EvalSymlinks(wrapperPath)
218	if err != nil {
219		return "", wrapErrorwithSourceLocf(err, "failed to evaluate symlinks for %s", wrapperPath)
220	}
221	return evaledCmdPath, nil
222}
223
224func printCompilerError(writer io.Writer, compilerErr error) {
225	if _, ok := compilerErr.(userError); ok {
226		fmt.Fprintf(writer, "%s\n", compilerErr)
227	} else {
228		fmt.Fprintf(writer,
229			"Internal error. Please report to chromeos-toolchain@google.com.\n%s\n",
230			compilerErr)
231	}
232}
233
234func teeStdinIfNeeded(env env, inputCmd *command, dest io.Writer) io.Reader {
235	// We can't use io.TeeReader unconditionally, as that would block
236	// calls to exec.Cmd.Run(), even if the underlying process has already
237	// terminated. See https://github.com/golang/go/issues/7990 for more details.
238	lastArg := ""
239	for _, arg := range inputCmd.Args {
240		if arg == "-" && lastArg != "-o" {
241			return io.TeeReader(env.stdin(), dest)
242		}
243		lastArg = arg
244	}
245	return env.stdin()
246}
247