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// cgo2.go provides new cgo functionality for use by the GoCompilePkg action. 16*9bb1b549SSpandan Das// We can't use the functionality in cgo.go, since it relies too heavily 17*9bb1b549SSpandan Das// on logic in cgo.bzl. Ideally, we'd be able to replace cgo.go with this 18*9bb1b549SSpandan Das// file eventually, but not until Bazel gives us enough toolchain information 19*9bb1b549SSpandan Das// to compile ObjC files. 20*9bb1b549SSpandan Das 21*9bb1b549SSpandan Daspackage main 22*9bb1b549SSpandan Das 23*9bb1b549SSpandan Dasimport ( 24*9bb1b549SSpandan Das "bytes" 25*9bb1b549SSpandan Das "fmt" 26*9bb1b549SSpandan Das "io/ioutil" 27*9bb1b549SSpandan Das "os" 28*9bb1b549SSpandan Das "path/filepath" 29*9bb1b549SSpandan Das "strings" 30*9bb1b549SSpandan Das) 31*9bb1b549SSpandan Das 32*9bb1b549SSpandan Das// cgo2 processes a set of mixed source files with cgo. 33*9bb1b549SSpandan Dasfunc cgo2(goenv *env, goSrcs, cgoSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, packagePath, packageName string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags, ldFlags []string, cgoExportHPath string) (srcDir string, allGoSrcs, cObjs []string, err error) { 34*9bb1b549SSpandan Das // Report an error if the C/C++ toolchain wasn't configured. 35*9bb1b549SSpandan Das if cc == "" { 36*9bb1b549SSpandan Das err := cgoError(cgoSrcs[:]) 37*9bb1b549SSpandan Das err = append(err, cSrcs...) 38*9bb1b549SSpandan Das err = append(err, cxxSrcs...) 39*9bb1b549SSpandan Das err = append(err, objcSrcs...) 40*9bb1b549SSpandan Das err = append(err, objcxxSrcs...) 41*9bb1b549SSpandan Das err = append(err, sSrcs...) 42*9bb1b549SSpandan Das return "", nil, nil, err 43*9bb1b549SSpandan Das } 44*9bb1b549SSpandan Das 45*9bb1b549SSpandan Das // If we only have C/C++ sources without cgo, just compile and pack them 46*9bb1b549SSpandan Das // without generating code. The Go command forbids this, but we've 47*9bb1b549SSpandan Das // historically allowed it. 48*9bb1b549SSpandan Das // TODO(jayconrod): this doesn't write CGO_LDFLAGS into the archive. We 49*9bb1b549SSpandan Das // might miss dependencies like -lstdc++ if they aren't referenced in 50*9bb1b549SSpandan Das // some other way. 51*9bb1b549SSpandan Das if len(cgoSrcs) == 0 { 52*9bb1b549SSpandan Das cObjs, err = compileCSources(goenv, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs, cc, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags) 53*9bb1b549SSpandan Das return ".", nil, cObjs, err 54*9bb1b549SSpandan Das } 55*9bb1b549SSpandan Das 56*9bb1b549SSpandan Das workDir, cleanup, err := goenv.workDir() 57*9bb1b549SSpandan Das if err != nil { 58*9bb1b549SSpandan Das return "", nil, nil, err 59*9bb1b549SSpandan Das } 60*9bb1b549SSpandan Das defer cleanup() 61*9bb1b549SSpandan Das 62*9bb1b549SSpandan Das // cgo2 will gather sources into a single temporary directory, since nogo 63*9bb1b549SSpandan Das // scanners might want to include or exclude these sources we need to ensure 64*9bb1b549SSpandan Das // that a fragment of the path is stable and human friendly enough to be 65*9bb1b549SSpandan Das // referenced in nogo configuration. 66*9bb1b549SSpandan Das workDir = filepath.Join(workDir, "cgo", packagePath) 67*9bb1b549SSpandan Das if err := os.MkdirAll(workDir, 0700); err != nil { 68*9bb1b549SSpandan Das return "", nil, nil, err 69*9bb1b549SSpandan Das } 70*9bb1b549SSpandan Das 71*9bb1b549SSpandan Das // Filter out -lstdc++ and -lc++ from ldflags if we don't have C++ sources, 72*9bb1b549SSpandan Das // and set CGO_LDFLAGS. These flags get written as special comments into cgo 73*9bb1b549SSpandan Das // generated sources. The compiler encodes those flags in the compiled .a 74*9bb1b549SSpandan Das // file, and the linker passes them on to the external linker. 75*9bb1b549SSpandan Das haveCxx := len(cxxSrcs)+len(objcxxSrcs) > 0 76*9bb1b549SSpandan Das if !haveCxx { 77*9bb1b549SSpandan Das for _, f := range ldFlags { 78*9bb1b549SSpandan Das if strings.HasSuffix(f, ".a") { 79*9bb1b549SSpandan Das // These flags come from cdeps options. Assume C++. 80*9bb1b549SSpandan Das haveCxx = true 81*9bb1b549SSpandan Das break 82*9bb1b549SSpandan Das } 83*9bb1b549SSpandan Das } 84*9bb1b549SSpandan Das } 85*9bb1b549SSpandan Das var combinedLdFlags []string 86*9bb1b549SSpandan Das if haveCxx { 87*9bb1b549SSpandan Das combinedLdFlags = append(combinedLdFlags, ldFlags...) 88*9bb1b549SSpandan Das } else { 89*9bb1b549SSpandan Das for _, f := range ldFlags { 90*9bb1b549SSpandan Das if f != "-lc++" && f != "-lstdc++" { 91*9bb1b549SSpandan Das combinedLdFlags = append(combinedLdFlags, f) 92*9bb1b549SSpandan Das } 93*9bb1b549SSpandan Das } 94*9bb1b549SSpandan Das } 95*9bb1b549SSpandan Das combinedLdFlags = append(combinedLdFlags, defaultLdFlags()...) 96*9bb1b549SSpandan Das os.Setenv("CGO_LDFLAGS", strings.Join(combinedLdFlags, " ")) 97*9bb1b549SSpandan Das 98*9bb1b549SSpandan Das // If cgo sources are in different directories, gather them into a temporary 99*9bb1b549SSpandan Das // directory so we can use -srcdir. 100*9bb1b549SSpandan Das srcDir = filepath.Dir(cgoSrcs[0]) 101*9bb1b549SSpandan Das srcsInSingleDir := true 102*9bb1b549SSpandan Das for _, src := range cgoSrcs[1:] { 103*9bb1b549SSpandan Das if filepath.Dir(src) != srcDir { 104*9bb1b549SSpandan Das srcsInSingleDir = false 105*9bb1b549SSpandan Das break 106*9bb1b549SSpandan Das } 107*9bb1b549SSpandan Das } 108*9bb1b549SSpandan Das 109*9bb1b549SSpandan Das if srcsInSingleDir { 110*9bb1b549SSpandan Das for i := range cgoSrcs { 111*9bb1b549SSpandan Das cgoSrcs[i] = filepath.Base(cgoSrcs[i]) 112*9bb1b549SSpandan Das } 113*9bb1b549SSpandan Das } else { 114*9bb1b549SSpandan Das srcDir = filepath.Join(workDir, "cgosrcs") 115*9bb1b549SSpandan Das if err := os.Mkdir(srcDir, 0777); err != nil { 116*9bb1b549SSpandan Das return "", nil, nil, err 117*9bb1b549SSpandan Das } 118*9bb1b549SSpandan Das copiedSrcs, err := gatherSrcs(srcDir, cgoSrcs) 119*9bb1b549SSpandan Das if err != nil { 120*9bb1b549SSpandan Das return "", nil, nil, err 121*9bb1b549SSpandan Das } 122*9bb1b549SSpandan Das cgoSrcs = copiedSrcs 123*9bb1b549SSpandan Das } 124*9bb1b549SSpandan Das 125*9bb1b549SSpandan Das // Generate Go and C code. 126*9bb1b549SSpandan Das hdrDirs := map[string]bool{} 127*9bb1b549SSpandan Das var hdrIncludes []string 128*9bb1b549SSpandan Das for _, hdr := range hSrcs { 129*9bb1b549SSpandan Das hdrDir := filepath.Dir(hdr) 130*9bb1b549SSpandan Das if !hdrDirs[hdrDir] { 131*9bb1b549SSpandan Das hdrDirs[hdrDir] = true 132*9bb1b549SSpandan Das hdrIncludes = append(hdrIncludes, "-iquote", hdrDir) 133*9bb1b549SSpandan Das } 134*9bb1b549SSpandan Das } 135*9bb1b549SSpandan Das hdrIncludes = append(hdrIncludes, "-iquote", workDir) // for _cgo_export.h 136*9bb1b549SSpandan Das 137*9bb1b549SSpandan Das execRoot, err := bazelExecRoot() 138*9bb1b549SSpandan Das if err != nil { 139*9bb1b549SSpandan Das return "", nil, nil, err 140*9bb1b549SSpandan Das } 141*9bb1b549SSpandan Das // Trim the execroot from the //line comments emitted by cgo. 142*9bb1b549SSpandan Das args := goenv.goTool("cgo", "-srcdir", srcDir, "-objdir", workDir, "-trimpath", execRoot) 143*9bb1b549SSpandan Das if packagePath != "" { 144*9bb1b549SSpandan Das args = append(args, "-importpath", packagePath) 145*9bb1b549SSpandan Das } 146*9bb1b549SSpandan Das args = append(args, "--") 147*9bb1b549SSpandan Das args = append(args, cppFlags...) 148*9bb1b549SSpandan Das args = append(args, hdrIncludes...) 149*9bb1b549SSpandan Das args = append(args, cFlags...) 150*9bb1b549SSpandan Das args = append(args, cgoSrcs...) 151*9bb1b549SSpandan Das if err := goenv.runCommand(args); err != nil { 152*9bb1b549SSpandan Das return "", nil, nil, err 153*9bb1b549SSpandan Das } 154*9bb1b549SSpandan Das 155*9bb1b549SSpandan Das if cgoExportHPath != "" { 156*9bb1b549SSpandan Das if err := copyFile(filepath.Join(workDir, "_cgo_export.h"), cgoExportHPath); err != nil { 157*9bb1b549SSpandan Das return "", nil, nil, err 158*9bb1b549SSpandan Das } 159*9bb1b549SSpandan Das } 160*9bb1b549SSpandan Das genGoSrcs := make([]string, 1+len(cgoSrcs)) 161*9bb1b549SSpandan Das genGoSrcs[0] = filepath.Join(workDir, "_cgo_gotypes.go") 162*9bb1b549SSpandan Das genCSrcs := make([]string, 1+len(cgoSrcs)) 163*9bb1b549SSpandan Das genCSrcs[0] = filepath.Join(workDir, "_cgo_export.c") 164*9bb1b549SSpandan Das for i, src := range cgoSrcs { 165*9bb1b549SSpandan Das stem := strings.TrimSuffix(filepath.Base(src), ".go") 166*9bb1b549SSpandan Das genGoSrcs[i+1] = filepath.Join(workDir, stem+".cgo1.go") 167*9bb1b549SSpandan Das genCSrcs[i+1] = filepath.Join(workDir, stem+".cgo2.c") 168*9bb1b549SSpandan Das } 169*9bb1b549SSpandan Das cgoMainC := filepath.Join(workDir, "_cgo_main.c") 170*9bb1b549SSpandan Das 171*9bb1b549SSpandan Das // Compile C, C++, Objective-C/C++, and assembly code. 172*9bb1b549SSpandan Das defaultCFlags := defaultCFlags(workDir) 173*9bb1b549SSpandan Das combinedCFlags := combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags) 174*9bb1b549SSpandan Das for _, lang := range []struct{ srcs, flags []string }{ 175*9bb1b549SSpandan Das {genCSrcs, combinedCFlags}, 176*9bb1b549SSpandan Das {cSrcs, combinedCFlags}, 177*9bb1b549SSpandan Das {cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)}, 178*9bb1b549SSpandan Das {objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)}, 179*9bb1b549SSpandan Das {objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)}, 180*9bb1b549SSpandan Das {sSrcs, nil}, 181*9bb1b549SSpandan Das } { 182*9bb1b549SSpandan Das for _, src := range lang.srcs { 183*9bb1b549SSpandan Das obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs))) 184*9bb1b549SSpandan Das cObjs = append(cObjs, obj) 185*9bb1b549SSpandan Das if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil { 186*9bb1b549SSpandan Das return "", nil, nil, err 187*9bb1b549SSpandan Das } 188*9bb1b549SSpandan Das } 189*9bb1b549SSpandan Das } 190*9bb1b549SSpandan Das 191*9bb1b549SSpandan Das mainObj := filepath.Join(workDir, "_cgo_main.o") 192*9bb1b549SSpandan Das if err := cCompile(goenv, cgoMainC, cc, combinedCFlags, mainObj); err != nil { 193*9bb1b549SSpandan Das return "", nil, nil, err 194*9bb1b549SSpandan Das } 195*9bb1b549SSpandan Das 196*9bb1b549SSpandan Das // Link cgo binary and use the symbols to generate _cgo_import.go. 197*9bb1b549SSpandan Das mainBin := filepath.Join(workDir, "_cgo_.o") // .o is a lie; it's an executable 198*9bb1b549SSpandan Das args = append([]string{cc, "-o", mainBin, mainObj}, cObjs...) 199*9bb1b549SSpandan Das args = append(args, combinedLdFlags...) 200*9bb1b549SSpandan Das var originalErrBuf bytes.Buffer 201*9bb1b549SSpandan Das if err := goenv.runCommandToFile(os.Stdout, &originalErrBuf, args); err != nil { 202*9bb1b549SSpandan Das // If linking the binary for cgo fails, this is usually because the 203*9bb1b549SSpandan Das // object files reference external symbols that can't be resolved yet. 204*9bb1b549SSpandan Das // Since the binary is only produced to have its symbols read by the cgo 205*9bb1b549SSpandan Das // command, there is no harm in trying to build it allowing unresolved 206*9bb1b549SSpandan Das // symbols - the real link that happens at the end will fail if they 207*9bb1b549SSpandan Das // rightfully can't be resolved. 208*9bb1b549SSpandan Das var allowUnresolvedSymbolsLdFlag string 209*9bb1b549SSpandan Das switch os.Getenv("GOOS") { 210*9bb1b549SSpandan Das case "windows": 211*9bb1b549SSpandan Das // MinGW's linker doesn't seem to support --unresolved-symbols 212*9bb1b549SSpandan Das // and MSVC isn't supported at all. 213*9bb1b549SSpandan Das return "", nil, nil, err 214*9bb1b549SSpandan Das case "darwin", "ios": 215*9bb1b549SSpandan Das allowUnresolvedSymbolsLdFlag = "-Wl,-undefined,dynamic_lookup" 216*9bb1b549SSpandan Das default: 217*9bb1b549SSpandan Das allowUnresolvedSymbolsLdFlag = "-Wl,--unresolved-symbols=ignore-all" 218*9bb1b549SSpandan Das } 219*9bb1b549SSpandan Das // Print and return the original error if we can't link the binary with 220*9bb1b549SSpandan Das // the additional linker flags as they may simply be incorrect for the 221*9bb1b549SSpandan Das // particular compiler/linker pair and would obscure the true reason for 222*9bb1b549SSpandan Das // the failure of the original command. 223*9bb1b549SSpandan Das if err2 := goenv.runCommandToFile( 224*9bb1b549SSpandan Das os.Stdout, 225*9bb1b549SSpandan Das ioutil.Discard, 226*9bb1b549SSpandan Das append(args, allowUnresolvedSymbolsLdFlag), 227*9bb1b549SSpandan Das ); err2 != nil { 228*9bb1b549SSpandan Das os.Stderr.Write(relativizePaths(originalErrBuf.Bytes())) 229*9bb1b549SSpandan Das return "", nil, nil, err 230*9bb1b549SSpandan Das } 231*9bb1b549SSpandan Das // Do not print the original error - rerunning the command with the 232*9bb1b549SSpandan Das // additional linker flag fixed it. 233*9bb1b549SSpandan Das } 234*9bb1b549SSpandan Das 235*9bb1b549SSpandan Das cgoImportsGo := filepath.Join(workDir, "_cgo_imports.go") 236*9bb1b549SSpandan Das args = goenv.goTool("cgo", "-dynpackage", packageName, "-dynimport", mainBin, "-dynout", cgoImportsGo) 237*9bb1b549SSpandan Das if err := goenv.runCommand(args); err != nil { 238*9bb1b549SSpandan Das return "", nil, nil, err 239*9bb1b549SSpandan Das } 240*9bb1b549SSpandan Das genGoSrcs = append(genGoSrcs, cgoImportsGo) 241*9bb1b549SSpandan Das 242*9bb1b549SSpandan Das // Copy regular Go source files into the work directory so that we can 243*9bb1b549SSpandan Das // use -trimpath=workDir. 244*9bb1b549SSpandan Das goBases, err := gatherSrcs(workDir, goSrcs) 245*9bb1b549SSpandan Das if err != nil { 246*9bb1b549SSpandan Das return "", nil, nil, err 247*9bb1b549SSpandan Das } 248*9bb1b549SSpandan Das 249*9bb1b549SSpandan Das allGoSrcs = make([]string, len(goSrcs)+len(genGoSrcs)) 250*9bb1b549SSpandan Das for i := range goSrcs { 251*9bb1b549SSpandan Das allGoSrcs[i] = filepath.Join(workDir, goBases[i]) 252*9bb1b549SSpandan Das } 253*9bb1b549SSpandan Das copy(allGoSrcs[len(goSrcs):], genGoSrcs) 254*9bb1b549SSpandan Das return workDir, allGoSrcs, cObjs, nil 255*9bb1b549SSpandan Das} 256*9bb1b549SSpandan Das 257*9bb1b549SSpandan Das// compileCSources compiles a list of C, C++, Objective-C, Objective-C++, 258*9bb1b549SSpandan Das// and assembly sources into .o files to be packed into the archive. 259*9bb1b549SSpandan Das// It does not run cgo. This is used for packages with "cgo = True" but 260*9bb1b549SSpandan Das// without any .go files that import "C". The Go command forbids this, 261*9bb1b549SSpandan Das// but we have historically allowed it. 262*9bb1b549SSpandan Dasfunc compileCSources(goenv *env, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []string, cc string, cppFlags, cFlags, cxxFlags, objcFlags, objcxxFlags []string) (cObjs []string, err error) { 263*9bb1b549SSpandan Das workDir, cleanup, err := goenv.workDir() 264*9bb1b549SSpandan Das if err != nil { 265*9bb1b549SSpandan Das return nil, err 266*9bb1b549SSpandan Das } 267*9bb1b549SSpandan Das defer cleanup() 268*9bb1b549SSpandan Das 269*9bb1b549SSpandan Das hdrDirs := map[string]bool{} 270*9bb1b549SSpandan Das var hdrIncludes []string 271*9bb1b549SSpandan Das for _, hdr := range hSrcs { 272*9bb1b549SSpandan Das hdrDir := filepath.Dir(hdr) 273*9bb1b549SSpandan Das if !hdrDirs[hdrDir] { 274*9bb1b549SSpandan Das hdrDirs[hdrDir] = true 275*9bb1b549SSpandan Das hdrIncludes = append(hdrIncludes, "-iquote", hdrDir) 276*9bb1b549SSpandan Das } 277*9bb1b549SSpandan Das } 278*9bb1b549SSpandan Das 279*9bb1b549SSpandan Das defaultCFlags := defaultCFlags(workDir) 280*9bb1b549SSpandan Das for _, lang := range []struct{ srcs, flags []string }{ 281*9bb1b549SSpandan Das {cSrcs, combineFlags(cppFlags, hdrIncludes, cFlags, defaultCFlags)}, 282*9bb1b549SSpandan Das {cxxSrcs, combineFlags(cppFlags, hdrIncludes, cxxFlags, defaultCFlags)}, 283*9bb1b549SSpandan Das {objcSrcs, combineFlags(cppFlags, hdrIncludes, objcFlags, defaultCFlags)}, 284*9bb1b549SSpandan Das {objcxxSrcs, combineFlags(cppFlags, hdrIncludes, objcxxFlags, defaultCFlags)}, 285*9bb1b549SSpandan Das {sSrcs, nil}, 286*9bb1b549SSpandan Das } { 287*9bb1b549SSpandan Das for _, src := range lang.srcs { 288*9bb1b549SSpandan Das obj := filepath.Join(workDir, fmt.Sprintf("_x%d.o", len(cObjs))) 289*9bb1b549SSpandan Das cObjs = append(cObjs, obj) 290*9bb1b549SSpandan Das if err := cCompile(goenv, src, cc, lang.flags, obj); err != nil { 291*9bb1b549SSpandan Das return nil, err 292*9bb1b549SSpandan Das } 293*9bb1b549SSpandan Das } 294*9bb1b549SSpandan Das } 295*9bb1b549SSpandan Das return cObjs, nil 296*9bb1b549SSpandan Das} 297*9bb1b549SSpandan Das 298*9bb1b549SSpandan Dasfunc combineFlags(lists ...[]string) []string { 299*9bb1b549SSpandan Das n := 0 300*9bb1b549SSpandan Das for _, list := range lists { 301*9bb1b549SSpandan Das n += len(list) 302*9bb1b549SSpandan Das } 303*9bb1b549SSpandan Das flags := make([]string, 0, n) 304*9bb1b549SSpandan Das for _, list := range lists { 305*9bb1b549SSpandan Das flags = append(flags, list...) 306*9bb1b549SSpandan Das } 307*9bb1b549SSpandan Das return flags 308*9bb1b549SSpandan Das} 309*9bb1b549SSpandan Das 310*9bb1b549SSpandan Dasfunc cCompile(goenv *env, src, cc string, flags []string, out string) error { 311*9bb1b549SSpandan Das args := []string{cc} 312*9bb1b549SSpandan Das args = append(args, flags...) 313*9bb1b549SSpandan Das args = append(args, "-c", src, "-o", out) 314*9bb1b549SSpandan Das return goenv.runCommand(args) 315*9bb1b549SSpandan Das} 316*9bb1b549SSpandan Das 317*9bb1b549SSpandan Dasfunc defaultCFlags(workDir string) []string { 318*9bb1b549SSpandan Das flags := []string{ 319*9bb1b549SSpandan Das "-fdebug-prefix-map=" + abs(".") + "=.", 320*9bb1b549SSpandan Das "-fdebug-prefix-map=" + workDir + "=.", 321*9bb1b549SSpandan Das } 322*9bb1b549SSpandan Das goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH") 323*9bb1b549SSpandan Das switch { 324*9bb1b549SSpandan Das case goos == "darwin" || goos == "ios": 325*9bb1b549SSpandan Das return flags 326*9bb1b549SSpandan Das case goos == "windows" && goarch == "amd64": 327*9bb1b549SSpandan Das return append(flags, "-mthreads") 328*9bb1b549SSpandan Das default: 329*9bb1b549SSpandan Das return append(flags, "-pthread") 330*9bb1b549SSpandan Das } 331*9bb1b549SSpandan Das} 332*9bb1b549SSpandan Das 333*9bb1b549SSpandan Dasfunc defaultLdFlags() []string { 334*9bb1b549SSpandan Das goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH") 335*9bb1b549SSpandan Das switch { 336*9bb1b549SSpandan Das case goos == "android": 337*9bb1b549SSpandan Das return []string{"-llog", "-ldl"} 338*9bb1b549SSpandan Das case goos == "darwin" || goos == "ios": 339*9bb1b549SSpandan Das return nil 340*9bb1b549SSpandan Das case goos == "windows" && goarch == "amd64": 341*9bb1b549SSpandan Das return []string{"-mthreads"} 342*9bb1b549SSpandan Das default: 343*9bb1b549SSpandan Das return []string{"-pthread"} 344*9bb1b549SSpandan Das } 345*9bb1b549SSpandan Das} 346*9bb1b549SSpandan Das 347*9bb1b549SSpandan Das// gatherSrcs copies or links files listed in srcs into dir. This is needed 348*9bb1b549SSpandan Das// to effectively use -trimpath with generated sources. It's also needed by cgo. 349*9bb1b549SSpandan Das// 350*9bb1b549SSpandan Das// gatherSrcs returns the basenames of copied files in the directory. 351*9bb1b549SSpandan Dasfunc gatherSrcs(dir string, srcs []string) ([]string, error) { 352*9bb1b549SSpandan Das copiedBases := make([]string, len(srcs)) 353*9bb1b549SSpandan Das for i, src := range srcs { 354*9bb1b549SSpandan Das base := filepath.Base(src) 355*9bb1b549SSpandan Das ext := filepath.Ext(base) 356*9bb1b549SSpandan Das stem := base[:len(base)-len(ext)] 357*9bb1b549SSpandan Das var err error 358*9bb1b549SSpandan Das for j := 1; j < 10000; j++ { 359*9bb1b549SSpandan Das if err = copyOrLinkFile(src, filepath.Join(dir, base)); err == nil { 360*9bb1b549SSpandan Das break 361*9bb1b549SSpandan Das } else if !os.IsExist(err) { 362*9bb1b549SSpandan Das return nil, err 363*9bb1b549SSpandan Das } else { 364*9bb1b549SSpandan Das base = fmt.Sprintf("%s_%d%s", stem, j, ext) 365*9bb1b549SSpandan Das } 366*9bb1b549SSpandan Das } 367*9bb1b549SSpandan Das if err != nil { 368*9bb1b549SSpandan Das return nil, fmt.Errorf("could not find unique name for file %s", src) 369*9bb1b549SSpandan Das } 370*9bb1b549SSpandan Das copiedBases[i] = base 371*9bb1b549SSpandan Das } 372*9bb1b549SSpandan Das return copiedBases, nil 373*9bb1b549SSpandan Das} 374*9bb1b549SSpandan Das 375*9bb1b549SSpandan Dasfunc bazelExecRoot() (string, error) { 376*9bb1b549SSpandan Das // Bazel executes the builder with a working directory of the form 377*9bb1b549SSpandan Das // .../execroot/<workspace name>. By stripping the last segment, we obtain a 378*9bb1b549SSpandan Das // prefix of all possible source files, even when contained in external 379*9bb1b549SSpandan Das // repositories. 380*9bb1b549SSpandan Das cwd, err := os.Getwd() 381*9bb1b549SSpandan Das if err != nil { 382*9bb1b549SSpandan Das return "", err 383*9bb1b549SSpandan Das } 384*9bb1b549SSpandan Das return filepath.Dir(cwd), nil 385*9bb1b549SSpandan Das} 386*9bb1b549SSpandan Das 387*9bb1b549SSpandan Dastype cgoError []string 388*9bb1b549SSpandan Das 389*9bb1b549SSpandan Dasfunc (e cgoError) Error() string { 390*9bb1b549SSpandan Das b := &bytes.Buffer{} 391*9bb1b549SSpandan Das fmt.Fprint(b, "CC is not set and files need to be processed with cgo:\n") 392*9bb1b549SSpandan Das for _, f := range e { 393*9bb1b549SSpandan Das fmt.Fprintf(b, "\t%s\n", f) 394*9bb1b549SSpandan Das } 395*9bb1b549SSpandan Das fmt.Fprintf(b, "Ensure that 'cgo = True' is set and the C/C++ toolchain is configured.") 396*9bb1b549SSpandan Das return b.String() 397*9bb1b549SSpandan Das} 398