...

Source file src/github.com/bazelbuild/rules_go/go/tools/builders/compilepkg.go

Documentation: github.com/bazelbuild/rules_go/go/tools/builders

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

View as plain text