1 package main
2
3 import (
4 "bufio"
5 "bytes"
6 "errors"
7 "fmt"
8 "io"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "strings"
13 )
14
15 type compileArgs struct {
16
17 cc string
18 cFlags []string
19
20 dir string
21
22 source string
23
24 dest string
25
26 target string
27
28 dep io.Writer
29 }
30
31 func compile(args compileArgs) error {
32
33 overrideFlags := []string{
34
35
36 "-O2",
37
38
39
40 "-mcpu=v1",
41 }
42
43 cmd := exec.Command(args.cc, append(overrideFlags, args.cFlags...)...)
44 cmd.Stderr = os.Stderr
45
46 inputDir := filepath.Dir(args.source)
47 relInputDir, err := filepath.Rel(args.dir, inputDir)
48 if err != nil {
49 return err
50 }
51
52 target := args.target
53 if target == "" {
54 target = "bpf"
55 }
56
57
58 cmd.Args = append(cmd.Args,
59 "-target", target,
60 "-c", args.source,
61 "-o", args.dest,
62
63 "-fno-ident",
64
65 "-fdebug-prefix-map="+inputDir+"="+relInputDir,
66 "-fdebug-compilation-dir", ".",
67
68 "-g",
69 fmt.Sprintf("-D__BPF_TARGET_MISSING=%q", "GCC error \"The eBPF is using target specific macros, please provide -target\""),
70 )
71 cmd.Dir = args.dir
72
73 var depRd, depWr *os.File
74 if args.dep != nil {
75 depRd, depWr, err = os.Pipe()
76 if err != nil {
77 return err
78 }
79 defer depRd.Close()
80 defer depWr.Close()
81
82
83 cmd.ExtraFiles = append(cmd.ExtraFiles, depWr)
84 cmd.Args = append(cmd.Args,
85
86 "-MD",
87
88
89 "-MP",
90
91 "-MF/dev/fd/3",
92 )
93 }
94
95 if err := cmd.Start(); err != nil {
96 return fmt.Errorf("can't execute %s: %s", args.cc, err)
97 }
98
99 if depRd != nil {
100
101
102 depWr.Close()
103 if _, err := io.Copy(args.dep, depRd); err != nil {
104 return fmt.Errorf("error writing depfile: %w", err)
105 }
106 }
107
108 if err := cmd.Wait(); err != nil {
109 return fmt.Errorf("%s: %s", args.cc, err)
110 }
111
112 return nil
113 }
114
115 func adjustDependencies(baseDir string, deps []dependency) ([]byte, error) {
116 var buf bytes.Buffer
117 for _, dep := range deps {
118 relativeFile, err := filepath.Rel(baseDir, dep.file)
119 if err != nil {
120 return nil, err
121 }
122
123 if len(dep.prerequisites) == 0 {
124 _, err := fmt.Fprintf(&buf, "%s:\n\n", relativeFile)
125 if err != nil {
126 return nil, err
127 }
128 continue
129 }
130
131 var prereqs []string
132 for _, prereq := range dep.prerequisites {
133 relativePrereq, err := filepath.Rel(baseDir, prereq)
134 if err != nil {
135 return nil, err
136 }
137
138 prereqs = append(prereqs, relativePrereq)
139 }
140
141 _, err = fmt.Fprintf(&buf, "%s: \\\n %s\n\n", relativeFile, strings.Join(prereqs, " \\\n "))
142 if err != nil {
143 return nil, err
144 }
145 }
146 return buf.Bytes(), nil
147 }
148
149 type dependency struct {
150 file string
151 prerequisites []string
152 }
153
154 func parseDependencies(baseDir string, in io.Reader) ([]dependency, error) {
155 abs := func(path string) string {
156 if filepath.IsAbs(path) {
157 return path
158 }
159 return filepath.Join(baseDir, path)
160 }
161
162 scanner := bufio.NewScanner(in)
163 var line strings.Builder
164 var deps []dependency
165 for scanner.Scan() {
166 buf := scanner.Bytes()
167 if line.Len()+len(buf) > 1024*1024 {
168 return nil, errors.New("line too long")
169 }
170
171 if bytes.HasSuffix(buf, []byte{'\\'}) {
172 line.Write(buf[:len(buf)-1])
173 continue
174 }
175
176 line.Write(buf)
177 if line.Len() == 0 {
178
179 continue
180 }
181
182 parts := strings.SplitN(line.String(), ":", 2)
183 if len(parts) < 2 {
184 return nil, fmt.Errorf("invalid line without ':'")
185 }
186
187
188
189 var prereqs []string
190 for _, prereq := range strings.Fields(parts[1]) {
191 prereqs = append(prereqs, abs(prereq))
192 }
193
194 deps = append(deps, dependency{
195 abs(string(parts[0])),
196 prereqs,
197 })
198 line.Reset()
199 }
200 if err := scanner.Err(); err != nil {
201 return nil, err
202 }
203
204
205 if len(deps) == 0 {
206 return nil, fmt.Errorf("empty dependency file")
207 }
208 return deps, nil
209 }
210
211
212 func strip(exe, file string) error {
213 cmd := exec.Command(exe, "-g", file)
214 cmd.Stderr = os.Stderr
215 if err := cmd.Run(); err != nil {
216 return fmt.Errorf("%s: %s", exe, err)
217 }
218 return nil
219 }
220
View as plain text