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