...

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

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

     1  // Copyright 2017 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  // link combines the results of a compile step using "go tool link". It is invoked by the
    16  // Go rules as an action.
    17  package main
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"regexp"
    29  	"runtime"
    30  	"strings"
    31  )
    32  
    33  func link(args []string) error {
    34  	// Parse arguments.
    35  	args, _, err := expandParamsFiles(args)
    36  	if err != nil {
    37  		return err
    38  	}
    39  	builderArgs, toolArgs := splitArgs(args)
    40  	stamps := multiFlag{}
    41  	xdefs := multiFlag{}
    42  	archives := archiveMultiFlag{}
    43  	flags := flag.NewFlagSet("link", flag.ExitOnError)
    44  	goenv := envFlags(flags)
    45  	main := flags.String("main", "", "Path to the main archive.")
    46  	packagePath := flags.String("p", "", "Package path of the main archive.")
    47  	outFile := flags.String("o", "", "Path to output file.")
    48  	flags.Var(&archives, "arc", "Label, package path, and file name of a dependency, separated by '='")
    49  	packageList := flags.String("package_list", "", "The file containing the list of standard library packages")
    50  	buildmode := flags.String("buildmode", "", "Build mode used.")
    51  	flags.Var(&xdefs, "X", "A string variable to replace in the linked binary (repeated).")
    52  	flags.Var(&stamps, "stamp", "The name of a file with stamping values.")
    53  	conflictErrMsg := flags.String("conflict_err", "", "Error message about conflicts to report if there's a link error.")
    54  	if err := flags.Parse(builderArgs); err != nil {
    55  		return err
    56  	}
    57  	if err := goenv.checkFlags(); err != nil {
    58  		return err
    59  	}
    60  
    61  	if *conflictErrMsg != "" {
    62  		return errors.New(*conflictErrMsg)
    63  	}
    64  
    65  	// On Windows, take the absolute path of the output file and main file.
    66  	// This is needed on Windows because the relative path is frequently too long.
    67  	// os.Open on Windows converts absolute paths to some other path format with
    68  	// longer length limits. Absolute paths do not work on macOS for .dylib
    69  	// outputs because they get baked in as the "install path".
    70  	if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
    71  		*outFile = abs(*outFile)
    72  	}
    73  	*main = abs(*main)
    74  
    75  	// If we were given any stamp value files, read and parse them
    76  	stampMap := map[string]string{}
    77  	for _, stampfile := range stamps {
    78  		stampbuf, err := ioutil.ReadFile(stampfile)
    79  		if err != nil {
    80  			return fmt.Errorf("Failed reading stamp file %s: %v", stampfile, err)
    81  		}
    82  		scanner := bufio.NewScanner(bytes.NewReader(stampbuf))
    83  		for scanner.Scan() {
    84  			line := strings.SplitN(scanner.Text(), " ", 2)
    85  			switch len(line) {
    86  			case 0:
    87  				// Nothing to do here
    88  			case 1:
    89  				// Map to the empty string
    90  				stampMap[line[0]] = ""
    91  			case 2:
    92  				// Key and value
    93  				stampMap[line[0]] = line[1]
    94  			}
    95  		}
    96  	}
    97  
    98  	// Build an importcfg file.
    99  	importcfgName, err := buildImportcfgFileForLink(archives, *packageList, goenv.installSuffix, filepath.Dir(*outFile))
   100  	if err != nil {
   101  		return err
   102  	}
   103  	if !goenv.shouldPreserveWorkDir {
   104  		defer os.Remove(importcfgName)
   105  	}
   106  
   107  	// generate any additional link options we need
   108  	goargs := goenv.goTool("link")
   109  	goargs = append(goargs, "-importcfg", importcfgName)
   110  
   111  	parseXdef := func(xdef string) (pkg, name, value string, err error) {
   112  		eq := strings.IndexByte(xdef, '=')
   113  		if eq < 0 {
   114  			return "", "", "", fmt.Errorf("-X flag does not contain '=': %s", xdef)
   115  		}
   116  		dot := strings.LastIndexByte(xdef[:eq], '.')
   117  		if dot < 0 {
   118  			return "", "", "", fmt.Errorf("-X flag does not contain '.': %s", xdef)
   119  		}
   120  		pkg, name, value = xdef[:dot], xdef[dot+1:eq], xdef[eq+1:]
   121  		if pkg == *packagePath {
   122  			pkg = "main"
   123  		}
   124  		return pkg, name, value, nil
   125  	}
   126  	for _, xdef := range xdefs {
   127  		pkg, name, value, err := parseXdef(xdef)
   128  		if err != nil {
   129  			return err
   130  		}
   131  		var missingKey bool
   132  		value = regexp.MustCompile(`\{.+?\}`).ReplaceAllStringFunc(value, func(key string) string {
   133  			if value, ok := stampMap[key[1:len(key)-1]]; ok {
   134  				return value
   135  			}
   136  			missingKey = true
   137  			return key
   138  		})
   139  		if !missingKey {
   140  			goargs = append(goargs, "-X", fmt.Sprintf("%s.%s=%s", pkg, name, value))
   141  		}
   142  	}
   143  
   144  	if *buildmode != "" {
   145  		goargs = append(goargs, "-buildmode", *buildmode)
   146  	}
   147  	goargs = append(goargs, "-o", *outFile)
   148  
   149  	// add in the unprocess pass through options
   150  	goargs = append(goargs, toolArgs...)
   151  	goargs = append(goargs, *main)
   152  	if err := goenv.runCommand(goargs); err != nil {
   153  		return err
   154  	}
   155  
   156  	if *buildmode == "c-archive" {
   157  		if err := stripArMetadata(*outFile); err != nil {
   158  			return fmt.Errorf("error stripping archive metadata: %v", err)
   159  		}
   160  	}
   161  
   162  	return nil
   163  }
   164  

View as plain text