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