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