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