1*9bb1b549SSpandan Das// Copyright 2019 The Bazel Authors. All rights reserved. 2*9bb1b549SSpandan Das// 3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License"); 4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License. 5*9bb1b549SSpandan Das// You may obtain a copy of the License at 6*9bb1b549SSpandan Das// 7*9bb1b549SSpandan Das// http://www.apache.org/licenses/LICENSE-2.0 8*9bb1b549SSpandan Das// 9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software 10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS, 11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and 13*9bb1b549SSpandan Das// limitations under the License. 14*9bb1b549SSpandan Das 15*9bb1b549SSpandan Das// compilepkg compiles a complete Go package from Go, C, and assembly files. It 16*9bb1b549SSpandan Das// supports cgo, coverage, and nogo. It is invoked by the Go rules as an action. 17*9bb1b549SSpandan Daspackage main 18*9bb1b549SSpandan Das 19*9bb1b549SSpandan Dasimport ( 20*9bb1b549SSpandan Das "bytes" 21*9bb1b549SSpandan Das "context" 22*9bb1b549SSpandan Das "errors" 23*9bb1b549SSpandan Das "flag" 24*9bb1b549SSpandan Das "fmt" 25*9bb1b549SSpandan Das "io/ioutil" 26*9bb1b549SSpandan Das "os" 27*9bb1b549SSpandan Das "os/exec" 28*9bb1b549SSpandan Das "path" 29*9bb1b549SSpandan Das "path/filepath" 30*9bb1b549SSpandan Das "sort" 31*9bb1b549SSpandan Das "strings" 32*9bb1b549SSpandan Das) 33*9bb1b549SSpandan Das 34*9bb1b549SSpandan Dastype nogoResult int 35*9bb1b549SSpandan Das 36*9bb1b549SSpandan Dasconst ( 37*9bb1b549SSpandan Das nogoNotRun nogoResult = iota 38*9bb1b549SSpandan Das nogoError 39*9bb1b549SSpandan Das nogoFailed 40*9bb1b549SSpandan Das nogoSucceeded 41*9bb1b549SSpandan Das) 42*9bb1b549SSpandan Das 43*9bb1b549SSpandan Dasfunc compilePkg(args []string) error { 44*9bb1b549SSpandan Das // Parse arguments. 45*9bb1b549SSpandan Das args, _, err := expandParamsFiles(args) 46*9bb1b549SSpandan Das if err != nil { 47*9bb1b549SSpandan Das return err 48*9bb1b549SSpandan Das } 49*9bb1b549SSpandan Das 50*9bb1b549SSpandan Das fs := flag.NewFlagSet("GoCompilePkg", flag.ExitOnError) 51*9bb1b549SSpandan Das goenv := envFlags(fs) 52*9bb1b549SSpandan Das var unfilteredSrcs, coverSrcs, embedSrcs, embedLookupDirs, embedRoots, recompileInternalDeps multiFlag 53*9bb1b549SSpandan Das var deps archiveMultiFlag 54*9bb1b549SSpandan Das var importPath, packagePath, nogoPath, packageListPath, coverMode string 55*9bb1b549SSpandan Das var outPath, outFactsPath, cgoExportHPath string 56*9bb1b549SSpandan Das var testFilter string 57*9bb1b549SSpandan Das var gcFlags, asmFlags, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags quoteMultiFlag 58*9bb1b549SSpandan Das var coverFormat string 59*9bb1b549SSpandan Das fs.Var(&unfilteredSrcs, "src", ".go, .c, .cc, .m, .mm, .s, or .S file to be filtered and compiled") 60*9bb1b549SSpandan Das fs.Var(&coverSrcs, "cover", ".go file that should be instrumented for coverage (must also be a -src)") 61*9bb1b549SSpandan Das fs.Var(&embedSrcs, "embedsrc", "file that may be compiled into the package with a //go:embed directive") 62*9bb1b549SSpandan Das fs.Var(&embedLookupDirs, "embedlookupdir", "Root-relative paths to directories relative to which //go:embed directives are resolved") 63*9bb1b549SSpandan Das fs.Var(&embedRoots, "embedroot", "Bazel output root under which a file passed via -embedsrc resides") 64*9bb1b549SSpandan Das fs.Var(&deps, "arc", "Import path, package path, and file name of a direct dependency, separated by '='") 65*9bb1b549SSpandan Das fs.StringVar(&importPath, "importpath", "", "The import path of the package being compiled. Not passed to the compiler, but may be displayed in debug data.") 66*9bb1b549SSpandan Das fs.StringVar(&packagePath, "p", "", "The package path (importmap) of the package being compiled") 67*9bb1b549SSpandan Das fs.Var(&gcFlags, "gcflags", "Go compiler flags") 68*9bb1b549SSpandan Das fs.Var(&asmFlags, "asmflags", "Go assembler flags") 69*9bb1b549SSpandan Das fs.Var(&cppFlags, "cppflags", "C preprocessor flags") 70*9bb1b549SSpandan Das fs.Var(&cFlags, "cflags", "C compiler flags") 71*9bb1b549SSpandan Das fs.Var(&cxxFlags, "cxxflags", "C++ compiler flags") 72*9bb1b549SSpandan Das fs.Var(&objcFlags, "objcflags", "Objective-C compiler flags") 73*9bb1b549SSpandan Das fs.Var(&objcxxFlags, "objcxxflags", "Objective-C++ compiler flags") 74*9bb1b549SSpandan Das fs.Var(&ldFlags, "ldflags", "C linker flags") 75*9bb1b549SSpandan Das fs.StringVar(&nogoPath, "nogo", "", "The nogo binary. If unset, nogo will not be run.") 76*9bb1b549SSpandan Das fs.StringVar(&packageListPath, "package_list", "", "The file containing the list of standard library packages") 77*9bb1b549SSpandan Das fs.StringVar(&coverMode, "cover_mode", "", "The coverage mode to use. Empty if coverage instrumentation should not be added.") 78*9bb1b549SSpandan Das fs.StringVar(&outPath, "o", "", "The output archive file to write compiled code") 79*9bb1b549SSpandan Das fs.StringVar(&outFactsPath, "x", "", "The output archive file to write export data and nogo facts") 80*9bb1b549SSpandan Das fs.StringVar(&cgoExportHPath, "cgoexport", "", "The _cgo_exports.h file to write") 81*9bb1b549SSpandan Das fs.StringVar(&testFilter, "testfilter", "off", "Controls test package filtering") 82*9bb1b549SSpandan Das fs.StringVar(&coverFormat, "cover_format", "", "Emit source file paths in coverage instrumentation suitable for the specified coverage format") 83*9bb1b549SSpandan Das fs.Var(&recompileInternalDeps, "recompile_internal_deps", "The import path of the direct dependencies that needs to be recompiled.") 84*9bb1b549SSpandan Das if err := fs.Parse(args); err != nil { 85*9bb1b549SSpandan Das return err 86*9bb1b549SSpandan Das } 87*9bb1b549SSpandan Das if err := goenv.checkFlags(); err != nil { 88*9bb1b549SSpandan Das return err 89*9bb1b549SSpandan Das } 90*9bb1b549SSpandan Das if importPath == "" { 91*9bb1b549SSpandan Das importPath = packagePath 92*9bb1b549SSpandan Das } 93*9bb1b549SSpandan Das cgoEnabled := os.Getenv("CGO_ENABLED") == "1" 94*9bb1b549SSpandan Das cc := os.Getenv("CC") 95*9bb1b549SSpandan Das outPath = abs(outPath) 96*9bb1b549SSpandan Das for i := range unfilteredSrcs { 97*9bb1b549SSpandan Das unfilteredSrcs[i] = abs(unfilteredSrcs[i]) 98*9bb1b549SSpandan Das } 99*9bb1b549SSpandan Das for i := range embedSrcs { 100*9bb1b549SSpandan Das embedSrcs[i] = abs(embedSrcs[i]) 101*9bb1b549SSpandan Das } 102*9bb1b549SSpandan Das 103*9bb1b549SSpandan Das // Filter sources. 104*9bb1b549SSpandan Das srcs, err := filterAndSplitFiles(unfilteredSrcs) 105*9bb1b549SSpandan Das if err != nil { 106*9bb1b549SSpandan Das return err 107*9bb1b549SSpandan Das } 108*9bb1b549SSpandan Das 109*9bb1b549SSpandan Das // TODO(jayconrod): remove -testfilter flag. The test action should compile 110*9bb1b549SSpandan Das // the main, internal, and external packages by calling compileArchive 111*9bb1b549SSpandan Das // with the correct sources for each. 112*9bb1b549SSpandan Das switch testFilter { 113*9bb1b549SSpandan Das case "off": 114*9bb1b549SSpandan Das case "only": 115*9bb1b549SSpandan Das testSrcs := make([]fileInfo, 0, len(srcs.goSrcs)) 116*9bb1b549SSpandan Das for _, f := range srcs.goSrcs { 117*9bb1b549SSpandan Das if strings.HasSuffix(f.pkg, "_test") { 118*9bb1b549SSpandan Das testSrcs = append(testSrcs, f) 119*9bb1b549SSpandan Das } 120*9bb1b549SSpandan Das } 121*9bb1b549SSpandan Das srcs.goSrcs = testSrcs 122*9bb1b549SSpandan Das case "exclude": 123*9bb1b549SSpandan Das libSrcs := make([]fileInfo, 0, len(srcs.goSrcs)) 124*9bb1b549SSpandan Das for _, f := range srcs.goSrcs { 125*9bb1b549SSpandan Das if !strings.HasSuffix(f.pkg, "_test") { 126*9bb1b549SSpandan Das libSrcs = append(libSrcs, f) 127*9bb1b549SSpandan Das } 128*9bb1b549SSpandan Das } 129*9bb1b549SSpandan Das srcs.goSrcs = libSrcs 130*9bb1b549SSpandan Das default: 131*9bb1b549SSpandan Das return fmt.Errorf("invalid test filter %q", testFilter) 132*9bb1b549SSpandan Das } 133*9bb1b549SSpandan Das 134*9bb1b549SSpandan Das return compileArchive( 135*9bb1b549SSpandan Das goenv, 136*9bb1b549SSpandan Das importPath, 137*9bb1b549SSpandan Das packagePath, 138*9bb1b549SSpandan Das srcs, 139*9bb1b549SSpandan Das deps, 140*9bb1b549SSpandan Das coverMode, 141*9bb1b549SSpandan Das coverSrcs, 142*9bb1b549SSpandan Das embedSrcs, 143*9bb1b549SSpandan Das embedLookupDirs, 144*9bb1b549SSpandan Das embedRoots, 145*9bb1b549SSpandan Das cgoEnabled, 146*9bb1b549SSpandan Das cc, 147*9bb1b549SSpandan Das gcFlags, 148*9bb1b549SSpandan Das asmFlags, 149*9bb1b549SSpandan Das cppFlags, 150*9bb1b549SSpandan Das cFlags, 151*9bb1b549SSpandan Das cxxFlags, 152*9bb1b549SSpandan Das objcFlags, 153*9bb1b549SSpandan Das objcxxFlags, 154*9bb1b549SSpandan Das ldFlags, 155*9bb1b549SSpandan Das nogoPath, 156*9bb1b549SSpandan Das packageListPath, 157*9bb1b549SSpandan Das outPath, 158*9bb1b549SSpandan Das outFactsPath, 159*9bb1b549SSpandan Das cgoExportHPath, 160*9bb1b549SSpandan Das coverFormat, 161*9bb1b549SSpandan Das recompileInternalDeps) 162*9bb1b549SSpandan Das} 163*9bb1b549SSpandan Das 164*9bb1b549SSpandan Dasfunc compileArchive( 165*9bb1b549SSpandan Das goenv *env, 166*9bb1b549SSpandan Das importPath string, 167*9bb1b549SSpandan Das packagePath string, 168*9bb1b549SSpandan Das srcs archiveSrcs, 169*9bb1b549SSpandan Das deps []archive, 170*9bb1b549SSpandan Das coverMode string, 171*9bb1b549SSpandan Das coverSrcs []string, 172*9bb1b549SSpandan Das embedSrcs []string, 173*9bb1b549SSpandan Das embedLookupDirs []string, 174*9bb1b549SSpandan Das embedRoots []string, 175*9bb1b549SSpandan Das cgoEnabled bool, 176*9bb1b549SSpandan Das cc string, 177*9bb1b549SSpandan Das gcFlags []string, 178*9bb1b549SSpandan Das asmFlags []string, 179*9bb1b549SSpandan Das cppFlags []string, 180*9bb1b549SSpandan Das cFlags []string, 181*9bb1b549SSpandan Das cxxFlags []string, 182*9bb1b549SSpandan Das objcFlags []string, 183*9bb1b549SSpandan Das objcxxFlags []string, 184*9bb1b549SSpandan Das ldFlags []string, 185*9bb1b549SSpandan Das nogoPath string, 186*9bb1b549SSpandan Das packageListPath string, 187*9bb1b549SSpandan Das outPath string, 188*9bb1b549SSpandan Das outXPath string, 189*9bb1b549SSpandan Das cgoExportHPath string, 190*9bb1b549SSpandan Das coverFormat string, 191*9bb1b549SSpandan Das recompileInternalDeps []string, 192*9bb1b549SSpandan Das) error { 193*9bb1b549SSpandan Das workDir, cleanup, err := goenv.workDir() 194*9bb1b549SSpandan Das if err != nil { 195*9bb1b549SSpandan Das return err 196*9bb1b549SSpandan Das } 197*9bb1b549SSpandan Das defer cleanup() 198*9bb1b549SSpandan Das 199*9bb1b549SSpandan Das // As part of compilation process, rules_go does generate and/or rewrite code 200*9bb1b549SSpandan Das // based on the original source files. We should only run static analysis 201*9bb1b549SSpandan Das // over original source files and not the generated source as end users have 202*9bb1b549SSpandan Das // little control over the generated source. 203*9bb1b549SSpandan Das // 204*9bb1b549SSpandan Das // nogoSrcsOrigin maps generated/rewritten source files back to original source. 205*9bb1b549SSpandan Das // If the original source path is an empty string, exclude generated source from nogo run. 206*9bb1b549SSpandan Das nogoSrcsOrigin := make(map[string]string) 207*9bb1b549SSpandan Das 208*9bb1b549SSpandan Das if len(srcs.goSrcs) == 0 { 209*9bb1b549SSpandan Das // We need to run the compiler to create a valid archive, even if there's nothing in it. 210*9bb1b549SSpandan Das // Otherwise, GoPack will complain if we try to add assembly or cgo objects. 211*9bb1b549SSpandan Das // A truly empty archive does not include any references to source file paths, which 212*9bb1b549SSpandan Das // ensures hermeticity even though the temp file path is random. 213*9bb1b549SSpandan Das emptyGoFile, err := os.CreateTemp(filepath.Dir(outPath), "*.go") 214*9bb1b549SSpandan Das if err != nil { 215*9bb1b549SSpandan Das return err 216*9bb1b549SSpandan Das } 217*9bb1b549SSpandan Das defer os.Remove(emptyGoFile.Name()) 218*9bb1b549SSpandan Das defer emptyGoFile.Close() 219*9bb1b549SSpandan Das if _, err := emptyGoFile.WriteString("package empty\n"); err != nil { 220*9bb1b549SSpandan Das return err 221*9bb1b549SSpandan Das } 222*9bb1b549SSpandan Das if err := emptyGoFile.Close(); err != nil { 223*9bb1b549SSpandan Das return err 224*9bb1b549SSpandan Das } 225*9bb1b549SSpandan Das 226*9bb1b549SSpandan Das srcs.goSrcs = append(srcs.goSrcs, fileInfo{ 227*9bb1b549SSpandan Das filename: emptyGoFile.Name(), 228*9bb1b549SSpandan Das ext: goExt, 229*9bb1b549SSpandan Das matched: true, 230*9bb1b549SSpandan Das pkg: "empty", 231*9bb1b549SSpandan Das }) 232*9bb1b549SSpandan Das 233*9bb1b549SSpandan Das nogoSrcsOrigin[emptyGoFile.Name()] = "" 234*9bb1b549SSpandan Das } 235*9bb1b549SSpandan Das packageName := srcs.goSrcs[0].pkg 236*9bb1b549SSpandan Das var goSrcs, cgoSrcs []string 237*9bb1b549SSpandan Das for _, src := range srcs.goSrcs { 238*9bb1b549SSpandan Das if src.isCgo { 239*9bb1b549SSpandan Das cgoSrcs = append(cgoSrcs, src.filename) 240*9bb1b549SSpandan Das } else { 241*9bb1b549SSpandan Das goSrcs = append(goSrcs, src.filename) 242*9bb1b549SSpandan Das } 243*9bb1b549SSpandan Das } 244*9bb1b549SSpandan Das cSrcs := make([]string, len(srcs.cSrcs)) 245*9bb1b549SSpandan Das for i, src := range srcs.cSrcs { 246*9bb1b549SSpandan Das cSrcs[i] = src.filename 247*9bb1b549SSpandan Das } 248*9bb1b549SSpandan Das cxxSrcs := make([]string, len(srcs.cxxSrcs)) 249*9bb1b549SSpandan Das for i, src := range srcs.cxxSrcs { 250*9bb1b549SSpandan Das cxxSrcs[i] = src.filename 251*9bb1b549SSpandan Das } 252*9bb1b549SSpandan Das objcSrcs := make([]string, len(srcs.objcSrcs)) 253*9bb1b549SSpandan Das for i, src := range srcs.objcSrcs { 254*9bb1b549SSpandan Das objcSrcs[i] = src.filename 255*9bb1b549SSpandan Das } 256*9bb1b549SSpandan Das objcxxSrcs := make([]string, len(srcs.objcxxSrcs)) 257*9bb1b549SSpandan Das for i, src := range srcs.objcxxSrcs { 258*9bb1b549SSpandan Das objcxxSrcs[i] = src.filename 259*9bb1b549SSpandan Das } 260*9bb1b549SSpandan Das sSrcs := make([]string, len(srcs.sSrcs)) 261*9bb1b549SSpandan Das for i, src := range srcs.sSrcs { 262*9bb1b549SSpandan Das sSrcs[i] = src.filename 263*9bb1b549SSpandan Das } 264*9bb1b549SSpandan Das hSrcs := make([]string, len(srcs.hSrcs)) 265*9bb1b549SSpandan Das for i, src := range srcs.hSrcs { 266*9bb1b549SSpandan Das hSrcs[i] = src.filename 267*9bb1b549SSpandan Das } 268*9bb1b549SSpandan Das haveCgo := len(cgoSrcs)+len(cSrcs)+len(cxxSrcs)+len(objcSrcs)+len(objcxxSrcs) > 0 269*9bb1b549SSpandan Das 270*9bb1b549SSpandan Das // Instrument source files for coverage. 271*9bb1b549SSpandan Das if coverMode != "" { 272*9bb1b549SSpandan Das relCoverPath := make(map[string]string) 273*9bb1b549SSpandan Das for _, s := range coverSrcs { 274*9bb1b549SSpandan Das relCoverPath[abs(s)] = s 275*9bb1b549SSpandan Das } 276*9bb1b549SSpandan Das 277*9bb1b549SSpandan Das combined := append([]string{}, goSrcs...) 278*9bb1b549SSpandan Das if cgoEnabled { 279*9bb1b549SSpandan Das combined = append(combined, cgoSrcs...) 280*9bb1b549SSpandan Das } 281*9bb1b549SSpandan Das for i, origSrc := range combined { 282*9bb1b549SSpandan Das if _, ok := relCoverPath[origSrc]; !ok { 283*9bb1b549SSpandan Das continue 284*9bb1b549SSpandan Das } 285*9bb1b549SSpandan Das 286*9bb1b549SSpandan Das var srcName string 287*9bb1b549SSpandan Das switch coverFormat { 288*9bb1b549SSpandan Das case "go_cover": 289*9bb1b549SSpandan Das srcName = origSrc 290*9bb1b549SSpandan Das if importPath != "" { 291*9bb1b549SSpandan Das srcName = path.Join(importPath, filepath.Base(origSrc)) 292*9bb1b549SSpandan Das } 293*9bb1b549SSpandan Das case "lcov": 294*9bb1b549SSpandan Das // Bazel merges lcov reports across languages and thus assumes 295*9bb1b549SSpandan Das // that the source file paths are relative to the exec root. 296*9bb1b549SSpandan Das srcName = relCoverPath[origSrc] 297*9bb1b549SSpandan Das default: 298*9bb1b549SSpandan Das return fmt.Errorf("invalid value for -cover_format: %q", coverFormat) 299*9bb1b549SSpandan Das } 300*9bb1b549SSpandan Das 301*9bb1b549SSpandan Das stem := filepath.Base(origSrc) 302*9bb1b549SSpandan Das if ext := filepath.Ext(stem); ext != "" { 303*9bb1b549SSpandan Das stem = stem[:len(stem)-len(ext)] 304*9bb1b549SSpandan Das } 305*9bb1b549SSpandan Das coverVar := fmt.Sprintf("Cover_%s_%d_%s", sanitizePathForIdentifier(importPath), i, sanitizePathForIdentifier(stem)) 306*9bb1b549SSpandan Das coverVar = strings.ReplaceAll(coverVar, "_", "Z") 307*9bb1b549SSpandan Das coverSrc := filepath.Join(workDir, fmt.Sprintf("cover_%d.go", i)) 308*9bb1b549SSpandan Das if err := instrumentForCoverage(goenv, origSrc, srcName, coverVar, coverMode, coverSrc); err != nil { 309*9bb1b549SSpandan Das return err 310*9bb1b549SSpandan Das } 311*9bb1b549SSpandan Das 312*9bb1b549SSpandan Das if i < len(goSrcs) { 313*9bb1b549SSpandan Das goSrcs[i] = coverSrc 314*9bb1b549SSpandan Das nogoSrcsOrigin[coverSrc] = origSrc 315*9bb1b549SSpandan Das continue 316*9bb1b549SSpandan Das } 317*9bb1b549SSpandan Das 318*9bb1b549SSpandan Das cgoSrcs[i-len(goSrcs)] = coverSrc 319*9bb1b549SSpandan Das } 320*9bb1b549SSpandan Das } 321*9bb1b549SSpandan Das 322*9bb1b549SSpandan Das // If we have cgo, generate separate C and go files, and compile the 323*9bb1b549SSpandan Das // C files. 324*9bb1b549SSpandan Das var objFiles []string 325*9bb1b549SSpandan Das if cgoEnabled && haveCgo { 326*9bb1b549SSpandan Das // TODO(#2006): Compile .s and .S files with cgo2, not the Go assembler. 327*9bb1b549SSpandan Das // If cgo is not enabled or we don't have other cgo sources, don't 328*9bb1b549SSpandan Das // compile .S files. 329*9bb1b549SSpandan Das var srcDir string 330*9bb1b549SSpandan Das srcDir, goSrcs, objFiles, err = cgo2(goenv, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, nil, hSrcs, packagePath, packageName, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags, cgoExportHPath) 331*9bb1b549SSpandan Das if err != nil { 332*9bb1b549SSpandan Das return err 333*9bb1b549SSpandan Das } 334*9bb1b549SSpandan Das 335*9bb1b549SSpandan Das gcFlags = append(gcFlags, createTrimPath(gcFlags, srcDir)) 336*9bb1b549SSpandan Das } else { 337*9bb1b549SSpandan Das if cgoExportHPath != "" { 338*9bb1b549SSpandan Das if err := ioutil.WriteFile(cgoExportHPath, nil, 0o666); err != nil { 339*9bb1b549SSpandan Das return err 340*9bb1b549SSpandan Das } 341*9bb1b549SSpandan Das } 342*9bb1b549SSpandan Das gcFlags = append(gcFlags, createTrimPath(gcFlags, ".")) 343*9bb1b549SSpandan Das } 344*9bb1b549SSpandan Das 345*9bb1b549SSpandan Das // Check that the filtered sources don't import anything outside of 346*9bb1b549SSpandan Das // the standard library and the direct dependencies. 347*9bb1b549SSpandan Das imports, err := checkImports(srcs.goSrcs, deps, packageListPath, importPath, recompileInternalDeps) 348*9bb1b549SSpandan Das if err != nil { 349*9bb1b549SSpandan Das return err 350*9bb1b549SSpandan Das } 351*9bb1b549SSpandan Das if cgoEnabled && len(cgoSrcs) != 0 { 352*9bb1b549SSpandan Das // cgo generated code imports some extra packages. 353*9bb1b549SSpandan Das imports["runtime/cgo"] = nil 354*9bb1b549SSpandan Das imports["syscall"] = nil 355*9bb1b549SSpandan Das imports["unsafe"] = nil 356*9bb1b549SSpandan Das } 357*9bb1b549SSpandan Das if coverMode != "" { 358*9bb1b549SSpandan Das if coverMode == "atomic" { 359*9bb1b549SSpandan Das imports["sync/atomic"] = nil 360*9bb1b549SSpandan Das } 361*9bb1b549SSpandan Das const coverdataPath = "github.com/bazelbuild/rules_go/go/tools/coverdata" 362*9bb1b549SSpandan Das var coverdata *archive 363*9bb1b549SSpandan Das for i := range deps { 364*9bb1b549SSpandan Das if deps[i].importPath == coverdataPath { 365*9bb1b549SSpandan Das coverdata = &deps[i] 366*9bb1b549SSpandan Das break 367*9bb1b549SSpandan Das } 368*9bb1b549SSpandan Das } 369*9bb1b549SSpandan Das if coverdata == nil { 370*9bb1b549SSpandan Das return errors.New("coverage requested but coverdata dependency not provided") 371*9bb1b549SSpandan Das } 372*9bb1b549SSpandan Das imports[coverdataPath] = coverdata 373*9bb1b549SSpandan Das } 374*9bb1b549SSpandan Das 375*9bb1b549SSpandan Das // Build an importcfg file for the compiler. 376*9bb1b549SSpandan Das importcfgPath, err := buildImportcfgFileForCompile(imports, goenv.installSuffix, filepath.Dir(outPath)) 377*9bb1b549SSpandan Das if err != nil { 378*9bb1b549SSpandan Das return err 379*9bb1b549SSpandan Das } 380*9bb1b549SSpandan Das if !goenv.shouldPreserveWorkDir { 381*9bb1b549SSpandan Das defer os.Remove(importcfgPath) 382*9bb1b549SSpandan Das } 383*9bb1b549SSpandan Das 384*9bb1b549SSpandan Das // Build an embedcfg file mapping embed patterns to filenames. 385*9bb1b549SSpandan Das // Embed patterns are relative to any one of a list of root directories 386*9bb1b549SSpandan Das // that may contain embeddable files. Source files containing embed patterns 387*9bb1b549SSpandan Das // must be in one of these root directories so the pattern appears to be 388*9bb1b549SSpandan Das // relative to the source file. Due to transitions, source files can reside 389*9bb1b549SSpandan Das // under Bazel roots different from both those of the go srcs and those of 390*9bb1b549SSpandan Das // the compilation output. Thus, we have to consider all combinations of 391*9bb1b549SSpandan Das // Bazel roots embedsrcs and root-relative paths of source files and the 392*9bb1b549SSpandan Das // output binary. 393*9bb1b549SSpandan Das var embedRootDirs []string 394*9bb1b549SSpandan Das for _, root := range embedRoots { 395*9bb1b549SSpandan Das for _, lookupDir := range embedLookupDirs { 396*9bb1b549SSpandan Das embedRootDir := abs(filepath.Join(root, lookupDir)) 397*9bb1b549SSpandan Das // Since we are iterating over all combinations of roots and 398*9bb1b549SSpandan Das // root-relative paths, some resulting paths may not exist and 399*9bb1b549SSpandan Das // should be filtered out before being passed to buildEmbedcfgFile. 400*9bb1b549SSpandan Das // Since Bazel uniquified both the roots and the root-relative 401*9bb1b549SSpandan Das // paths, the combinations are automatically unique. 402*9bb1b549SSpandan Das if _, err := os.Stat(embedRootDir); err == nil { 403*9bb1b549SSpandan Das embedRootDirs = append(embedRootDirs, embedRootDir) 404*9bb1b549SSpandan Das } 405*9bb1b549SSpandan Das } 406*9bb1b549SSpandan Das } 407*9bb1b549SSpandan Das embedcfgPath, err := buildEmbedcfgFile(srcs.goSrcs, embedSrcs, embedRootDirs, workDir) 408*9bb1b549SSpandan Das if err != nil { 409*9bb1b549SSpandan Das return err 410*9bb1b549SSpandan Das } 411*9bb1b549SSpandan Das if embedcfgPath != "" { 412*9bb1b549SSpandan Das if !goenv.shouldPreserveWorkDir { 413*9bb1b549SSpandan Das defer os.Remove(embedcfgPath) 414*9bb1b549SSpandan Das } 415*9bb1b549SSpandan Das } 416*9bb1b549SSpandan Das 417*9bb1b549SSpandan Das // Run nogo concurrently. 418*9bb1b549SSpandan Das var nogoChan chan error 419*9bb1b549SSpandan Das outFactsPath := filepath.Join(workDir, nogoFact) 420*9bb1b549SSpandan Das nogoSrcs := make([]string, 0, len(goSrcs)) 421*9bb1b549SSpandan Das for _, goSrc := range goSrcs { 422*9bb1b549SSpandan Das // If source is found in the origin map, that means it's likely to be a generated source file 423*9bb1b549SSpandan Das // so feed the original source file to static analyzers instead of the generated one. 424*9bb1b549SSpandan Das // 425*9bb1b549SSpandan Das // If origin is empty, that means the generated source file is not based on a user-provided source file 426*9bb1b549SSpandan Das // thus ignore that entry entirely. 427*9bb1b549SSpandan Das if originSrc, ok := nogoSrcsOrigin[goSrc]; ok { 428*9bb1b549SSpandan Das if originSrc != "" { 429*9bb1b549SSpandan Das nogoSrcs = append(nogoSrcs, originSrc) 430*9bb1b549SSpandan Das } 431*9bb1b549SSpandan Das continue 432*9bb1b549SSpandan Das } 433*9bb1b549SSpandan Das 434*9bb1b549SSpandan Das // TODO(sluongng): most likely what remains here are CGO-generated source files as the result of calling cgo2() 435*9bb1b549SSpandan Das // Need to determine whether we want to feed these CGO-generated files into static analyzers. 436*9bb1b549SSpandan Das // 437*9bb1b549SSpandan Das // Add unknown origin source files into the mix. 438*9bb1b549SSpandan Das nogoSrcs = append(nogoSrcs, goSrc) 439*9bb1b549SSpandan Das } 440*9bb1b549SSpandan Das if nogoPath != "" && len(nogoSrcs) > 0 { 441*9bb1b549SSpandan Das ctx, cancel := context.WithCancel(context.Background()) 442*9bb1b549SSpandan Das nogoChan = make(chan error) 443*9bb1b549SSpandan Das go func() { 444*9bb1b549SSpandan Das nogoChan <- runNogo(ctx, workDir, nogoPath, nogoSrcs, deps, packagePath, importcfgPath, outFactsPath) 445*9bb1b549SSpandan Das }() 446*9bb1b549SSpandan Das defer func() { 447*9bb1b549SSpandan Das if nogoChan != nil { 448*9bb1b549SSpandan Das cancel() 449*9bb1b549SSpandan Das <-nogoChan 450*9bb1b549SSpandan Das } 451*9bb1b549SSpandan Das }() 452*9bb1b549SSpandan Das } 453*9bb1b549SSpandan Das 454*9bb1b549SSpandan Das // If there are assembly files, and this is go1.12+, generate symbol ABIs. 455*9bb1b549SSpandan Das asmHdrPath := "" 456*9bb1b549SSpandan Das if len(srcs.sSrcs) > 0 { 457*9bb1b549SSpandan Das asmHdrPath = filepath.Join(workDir, "go_asm.h") 458*9bb1b549SSpandan Das } 459*9bb1b549SSpandan Das symabisPath, err := buildSymabisFile(goenv, srcs.sSrcs, srcs.hSrcs, asmHdrPath) 460*9bb1b549SSpandan Das if symabisPath != "" { 461*9bb1b549SSpandan Das if !goenv.shouldPreserveWorkDir { 462*9bb1b549SSpandan Das defer os.Remove(symabisPath) 463*9bb1b549SSpandan Das } 464*9bb1b549SSpandan Das } 465*9bb1b549SSpandan Das if err != nil { 466*9bb1b549SSpandan Das return err 467*9bb1b549SSpandan Das } 468*9bb1b549SSpandan Das 469*9bb1b549SSpandan Das // Compile the filtered .go files. 470*9bb1b549SSpandan Das if err := compileGo(goenv, goSrcs, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath, gcFlags, outPath); err != nil { 471*9bb1b549SSpandan Das return err 472*9bb1b549SSpandan Das } 473*9bb1b549SSpandan Das 474*9bb1b549SSpandan Das // Compile the .s files. 475*9bb1b549SSpandan Das if len(srcs.sSrcs) > 0 { 476*9bb1b549SSpandan Das includeSet := map[string]struct{}{ 477*9bb1b549SSpandan Das filepath.Join(os.Getenv("GOROOT"), "pkg", "include"): {}, 478*9bb1b549SSpandan Das workDir: {}, 479*9bb1b549SSpandan Das } 480*9bb1b549SSpandan Das for _, hdr := range srcs.hSrcs { 481*9bb1b549SSpandan Das includeSet[filepath.Dir(hdr.filename)] = struct{}{} 482*9bb1b549SSpandan Das } 483*9bb1b549SSpandan Das includes := make([]string, len(includeSet)) 484*9bb1b549SSpandan Das for inc := range includeSet { 485*9bb1b549SSpandan Das includes = append(includes, inc) 486*9bb1b549SSpandan Das } 487*9bb1b549SSpandan Das sort.Strings(includes) 488*9bb1b549SSpandan Das for _, inc := range includes { 489*9bb1b549SSpandan Das asmFlags = append(asmFlags, "-I", inc) 490*9bb1b549SSpandan Das } 491*9bb1b549SSpandan Das for i, sSrc := range srcs.sSrcs { 492*9bb1b549SSpandan Das obj := filepath.Join(workDir, fmt.Sprintf("s%d.o", i)) 493*9bb1b549SSpandan Das if err := asmFile(goenv, sSrc.filename, packagePath, asmFlags, obj); err != nil { 494*9bb1b549SSpandan Das return err 495*9bb1b549SSpandan Das } 496*9bb1b549SSpandan Das objFiles = append(objFiles, obj) 497*9bb1b549SSpandan Das } 498*9bb1b549SSpandan Das } 499*9bb1b549SSpandan Das 500*9bb1b549SSpandan Das // Pack .o files into the archive. These may come from cgo generated code, 501*9bb1b549SSpandan Das // cgo dependencies (cdeps), or assembly. 502*9bb1b549SSpandan Das if len(objFiles) > 0 { 503*9bb1b549SSpandan Das if err := appendFiles(goenv, outPath, objFiles); err != nil { 504*9bb1b549SSpandan Das return err 505*9bb1b549SSpandan Das } 506*9bb1b549SSpandan Das } 507*9bb1b549SSpandan Das 508*9bb1b549SSpandan Das // Check results from nogo. 509*9bb1b549SSpandan Das nogoStatus := nogoNotRun 510*9bb1b549SSpandan Das if nogoChan != nil { 511*9bb1b549SSpandan Das err := <-nogoChan 512*9bb1b549SSpandan Das nogoChan = nil // no cancellation needed 513*9bb1b549SSpandan Das if err != nil { 514*9bb1b549SSpandan Das nogoStatus = nogoFailed 515*9bb1b549SSpandan Das // TODO: should we still create the .x file without nogo facts in this case? 516*9bb1b549SSpandan Das return err 517*9bb1b549SSpandan Das } 518*9bb1b549SSpandan Das nogoStatus = nogoSucceeded 519*9bb1b549SSpandan Das } 520*9bb1b549SSpandan Das 521*9bb1b549SSpandan Das // Extract the export data file and pack it in an .x archive together with the 522*9bb1b549SSpandan Das // nogo facts file (if there is one). This allows compile actions to depend 523*9bb1b549SSpandan Das // on .x files only, so we don't need to recompile a package when one of its 524*9bb1b549SSpandan Das // imports changes in a way that doesn't affect export data. 525*9bb1b549SSpandan Das // TODO(golang/go#33820): After Go 1.16 is the minimum supported version, 526*9bb1b549SSpandan Das // use -linkobj to tell the compiler to create separate .a and .x files for 527*9bb1b549SSpandan Das // compiled code and export data. Before that version, the linker needed 528*9bb1b549SSpandan Das // export data in the .a file when building a plugin. To work around that, 529*9bb1b549SSpandan Das // we copy the export data into .x ourselves. 530*9bb1b549SSpandan Das if err = extractFileFromArchive(outPath, workDir, pkgDef); err != nil { 531*9bb1b549SSpandan Das return err 532*9bb1b549SSpandan Das } 533*9bb1b549SSpandan Das pkgDefPath := filepath.Join(workDir, pkgDef) 534*9bb1b549SSpandan Das if nogoStatus == nogoSucceeded { 535*9bb1b549SSpandan Das return appendFiles(goenv, outXPath, []string{pkgDefPath, outFactsPath}) 536*9bb1b549SSpandan Das } 537*9bb1b549SSpandan Das return appendFiles(goenv, outXPath, []string{pkgDefPath}) 538*9bb1b549SSpandan Das} 539*9bb1b549SSpandan Das 540*9bb1b549SSpandan Dasfunc compileGo(goenv *env, srcs []string, packagePath, importcfgPath, embedcfgPath, asmHdrPath, symabisPath string, gcFlags []string, outPath string) error { 541*9bb1b549SSpandan Das args := goenv.goTool("compile") 542*9bb1b549SSpandan Das args = append(args, "-p", packagePath, "-importcfg", importcfgPath, "-pack") 543*9bb1b549SSpandan Das if embedcfgPath != "" { 544*9bb1b549SSpandan Das args = append(args, "-embedcfg", embedcfgPath) 545*9bb1b549SSpandan Das } 546*9bb1b549SSpandan Das if asmHdrPath != "" { 547*9bb1b549SSpandan Das args = append(args, "-asmhdr", asmHdrPath) 548*9bb1b549SSpandan Das } 549*9bb1b549SSpandan Das if symabisPath != "" { 550*9bb1b549SSpandan Das args = append(args, "-symabis", symabisPath) 551*9bb1b549SSpandan Das } 552*9bb1b549SSpandan Das args = append(args, gcFlags...) 553*9bb1b549SSpandan Das args = append(args, "-o", outPath) 554*9bb1b549SSpandan Das args = append(args, "--") 555*9bb1b549SSpandan Das args = append(args, srcs...) 556*9bb1b549SSpandan Das absArgs(args, []string{"-I", "-o", "-trimpath", "-importcfg"}) 557*9bb1b549SSpandan Das return goenv.runCommand(args) 558*9bb1b549SSpandan Das} 559*9bb1b549SSpandan Das 560*9bb1b549SSpandan Dasfunc runNogo(ctx context.Context, workDir string, nogoPath string, srcs []string, deps []archive, packagePath, importcfgPath, outFactsPath string) error { 561*9bb1b549SSpandan Das args := []string{nogoPath} 562*9bb1b549SSpandan Das args = append(args, "-p", packagePath) 563*9bb1b549SSpandan Das args = append(args, "-importcfg", importcfgPath) 564*9bb1b549SSpandan Das for _, dep := range deps { 565*9bb1b549SSpandan Das args = append(args, "-fact", fmt.Sprintf("%s=%s", dep.importPath, dep.file)) 566*9bb1b549SSpandan Das } 567*9bb1b549SSpandan Das args = append(args, "-x", outFactsPath) 568*9bb1b549SSpandan Das args = append(args, srcs...) 569*9bb1b549SSpandan Das 570*9bb1b549SSpandan Das paramsFile := filepath.Join(workDir, "nogo.param") 571*9bb1b549SSpandan Das if err := writeParamsFile(paramsFile, args[1:]); err != nil { 572*9bb1b549SSpandan Das return fmt.Errorf("error writing nogo params file: %v", err) 573*9bb1b549SSpandan Das } 574*9bb1b549SSpandan Das 575*9bb1b549SSpandan Das cmd := exec.CommandContext(ctx, args[0], "-param="+paramsFile) 576*9bb1b549SSpandan Das out := &bytes.Buffer{} 577*9bb1b549SSpandan Das cmd.Stdout, cmd.Stderr = out, out 578*9bb1b549SSpandan Das if err := cmd.Run(); err != nil { 579*9bb1b549SSpandan Das if exitErr, ok := err.(*exec.ExitError); ok { 580*9bb1b549SSpandan Das if !exitErr.Exited() { 581*9bb1b549SSpandan Das cmdLine := strings.Join(args, " ") 582*9bb1b549SSpandan Das return fmt.Errorf("nogo command '%s' exited unexpectedly: %s", cmdLine, exitErr.String()) 583*9bb1b549SSpandan Das } 584*9bb1b549SSpandan Das return errors.New(string(relativizePaths(out.Bytes()))) 585*9bb1b549SSpandan Das } else { 586*9bb1b549SSpandan Das if out.Len() != 0 { 587*9bb1b549SSpandan Das fmt.Fprintln(os.Stderr, out.String()) 588*9bb1b549SSpandan Das } 589*9bb1b549SSpandan Das return fmt.Errorf("error running nogo: %v", err) 590*9bb1b549SSpandan Das } 591*9bb1b549SSpandan Das } 592*9bb1b549SSpandan Das return nil 593*9bb1b549SSpandan Das} 594*9bb1b549SSpandan Das 595*9bb1b549SSpandan Dasfunc createTrimPath(gcFlags []string, path string) string { 596*9bb1b549SSpandan Das for _, flag := range gcFlags { 597*9bb1b549SSpandan Das if strings.HasPrefix(flag, "-trimpath=") { 598*9bb1b549SSpandan Das return flag + ":" + path 599*9bb1b549SSpandan Das } 600*9bb1b549SSpandan Das } 601*9bb1b549SSpandan Das 602*9bb1b549SSpandan Das return "-trimpath=" + path 603*9bb1b549SSpandan Das} 604*9bb1b549SSpandan Das 605*9bb1b549SSpandan Dasfunc sanitizePathForIdentifier(path string) string { 606*9bb1b549SSpandan Das return strings.Map(func(r rune) rune { 607*9bb1b549SSpandan Das if 'A' <= r && r <= 'Z' || 608*9bb1b549SSpandan Das 'a' <= r && r <= 'z' || 609*9bb1b549SSpandan Das '0' <= r && r <= '9' || 610*9bb1b549SSpandan Das r == '_' { 611*9bb1b549SSpandan Das return r 612*9bb1b549SSpandan Das } 613*9bb1b549SSpandan Das return '_' 614*9bb1b549SSpandan Das }, path) 615*9bb1b549SSpandan Das} 616