-
Notifications
You must be signed in to change notification settings - Fork 0
/
linker.go
116 lines (103 loc) · 2.49 KB
/
linker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package linker
import (
"bytes"
"context"
"fmt"
gobuild "go/build"
"io/ioutil"
"os"
"path"
"sync"
"github.com/gophertest/build"
"github.com/hpidcock/gophertest/dag"
"github.com/pkg/errors"
)
type Logger interface {
Infof(format string, args ...interface{})
}
type Linker struct {
Logger Logger
BuildCtx gobuild.Context
Tools build.Tools
WorkDir string
OutFile string
packageMapMutex sync.Mutex
packageMap map[string]string
}
func (l *Linker) Visit(ctx context.Context, node *dag.Node) error {
if node.ImportPath != "main" {
if len(node.Deps) == 0 {
return nil
}
if node.Intrinsic {
return nil
}
if node.Shlib == "" {
return fmt.Errorf("missing shlib for %q", node.ImportPath)
}
if _, err := os.Stat(node.Shlib); os.IsNotExist(err) {
return fmt.Errorf("missing shlib for %q", node.ImportPath)
} else if err != nil {
return errors.WithStack(err)
}
l.packageMapMutex.Lock()
defer l.packageMapMutex.Unlock()
if l.packageMap == nil {
l.packageMap = make(map[string]string)
}
if _, ok := l.packageMap[node.ImportPath]; ok {
return fmt.Errorf("package map already contains import %q", node.ImportPath)
}
l.packageMap[node.ImportPath] = node.Shlib
return nil
}
if len(node.Deps) > 0 {
return fmt.Errorf("main has dependants")
}
l.packageMapMutex.Lock()
defer l.packageMapMutex.Unlock()
if l.packageMap == nil {
l.packageMap = make(map[string]string)
}
exeDir := path.Join(l.WorkDir, "exe")
err := os.Mkdir(exeDir, 0777)
if err != nil {
return errors.WithStack(err)
}
importConfigFile := path.Join(exeDir, "importcfg.link")
err = ioutil.WriteFile(importConfigFile, l.importConfigLink(), 0666)
if err != nil {
return errors.WithStack(err)
}
out := &bytes.Buffer{}
args := build.LinkArgs{
WorkingDirectory: exeDir,
Stdout: out,
Stderr: out,
BuildMode: "exe",
ExternalLinker: "gcc",
ImportConfigFile: importConfigFile,
OutputFile: l.OutFile,
Files: []string{node.Shlib},
StringDefines: []string{
"runtime/internal/sys.DefaultGoroot=" + l.BuildCtx.GOROOT,
},
}
err = l.Tools.Link(args)
if err != nil {
fmt.Fprint(os.Stderr, out)
return errors.WithStack(err)
}
return nil
}
func (l *Linker) importConfigLink() []byte {
cfg := &bytes.Buffer{}
fmt.Fprintf(cfg, "# import config\n")
for importPath, shLib := range l.packageMap {
if importPath == "main" {
continue
}
fmt.Fprintf(cfg, "packagefile %s=%s\n", importPath, shLib)
}
return cfg.Bytes()
}