1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
66
67
68
69
70 if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
71 *outFile = abs(*outFile)
72 }
73 *main = abs(*main)
74
75
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
88 case 1:
89
90 stampMap[line[0]] = ""
91 case 2:
92
93 stampMap[line[0]] = line[1]
94 }
95 }
96 }
97
98
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
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
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