Source file
src/cmd/pack/pack.go
1
2
3
4
5 package main
6
7 import (
8 "cmd/internal/archive"
9 "cmd/internal/objabi"
10 "cmd/internal/telemetry/counter"
11 "fmt"
12 "io"
13 "io/fs"
14 "log"
15 "os"
16 "path/filepath"
17 )
18
19 const usageMessage = `Usage: pack op file.a [name....]
20 Where op is one of cprtx optionally followed by v for verbose output.
21 For compatibility with old Go build environments the op string grc is
22 accepted as a synonym for c.
23
24 For more information, run
25 go doc cmd/pack`
26
27 func usage() {
28 fmt.Fprintln(os.Stderr, usageMessage)
29 os.Exit(2)
30 }
31
32 func main() {
33 log.SetFlags(0)
34 log.SetPrefix("pack: ")
35 counter.Open()
36 objabi.Flagparse(usage)
37
38 if len(os.Args) < 3 {
39 log.Print("not enough arguments")
40 fmt.Fprintln(os.Stderr)
41 usage()
42 }
43 setOp(os.Args[1])
44 counter.Inc("pack/invocations")
45 counter.Inc("pack/op:" + string(op))
46 var ar *Archive
47 switch op {
48 case 'p':
49 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
50 ar.scan(ar.printContents)
51 case 'r':
52 ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:])
53 ar.addFiles()
54 case 'c':
55 ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:])
56 ar.addPkgdef()
57 ar.addFiles()
58 case 't':
59 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
60 ar.scan(ar.tableOfContents)
61 case 'x':
62 ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
63 ar.scan(ar.extractContents)
64 default:
65 log.Printf("invalid operation %q", os.Args[1])
66 fmt.Fprintln(os.Stderr)
67 usage()
68 }
69 if len(ar.files) > 0 {
70 log.Fatalf("file %q not in archive", ar.files[0])
71 }
72 }
73
74
75
76
77
78 var (
79 op rune
80 verbose bool
81 )
82
83
84 func setOp(arg string) {
85
86
87
88
89 if arg == "grc" {
90 arg = "c"
91 }
92
93 for _, r := range arg {
94 switch r {
95 case 'c', 'p', 'r', 't', 'x':
96 if op != 0 {
97
98 usage()
99 }
100 op = r
101 case 'v':
102 if verbose {
103
104 usage()
105 }
106 verbose = true
107 default:
108 usage()
109 }
110 }
111 }
112
113 const (
114 arHeader = "!<arch>\n"
115 )
116
117
118
119 type Archive struct {
120 a *archive.Archive
121 files []string
122 pad int
123 matchAll bool
124 }
125
126
127 func openArchive(name string, mode int, files []string) *Archive {
128 f, err := os.OpenFile(name, mode, 0666)
129 if err != nil {
130 log.Fatal(err)
131 }
132 var a *archive.Archive
133 if mode&os.O_TRUNC != 0 {
134 a, err = archive.New(f)
135 } else {
136 a, err = archive.Parse(f, verbose)
137 if err != nil && mode&os.O_CREATE != 0 {
138 a, err = archive.New(f)
139 }
140 }
141 if err != nil {
142 log.Fatal(err)
143 }
144 return &Archive{
145 a: a,
146 files: files,
147 matchAll: len(files) == 0,
148 }
149 }
150
151
152 func (ar *Archive) scan(action func(*archive.Entry)) {
153 for i := range ar.a.Entries {
154 e := &ar.a.Entries[i]
155 action(e)
156 }
157 }
158
159
160 func listEntry(e *archive.Entry, verbose bool) {
161 if verbose {
162 fmt.Fprintf(stdout, "%s\n", e.String())
163 } else {
164 fmt.Fprintf(stdout, "%s\n", e.Name)
165 }
166 }
167
168
169 func (ar *Archive) output(e *archive.Entry, w io.Writer) {
170 r := io.NewSectionReader(ar.a.File(), e.Offset, e.Size)
171 n, err := io.Copy(w, r)
172 if err != nil {
173 log.Fatal(err)
174 }
175 if n != e.Size {
176 log.Fatal("short file")
177 }
178 }
179
180
181
182 func (ar *Archive) match(e *archive.Entry) bool {
183 if ar.matchAll {
184 return true
185 }
186 for i, name := range ar.files {
187 if e.Name == name {
188 copy(ar.files[i:], ar.files[i+1:])
189 ar.files = ar.files[:len(ar.files)-1]
190 return true
191 }
192 }
193 return false
194 }
195
196
197
198
199 func (ar *Archive) addFiles() {
200 if len(ar.files) == 0 {
201 usage()
202 }
203 for _, file := range ar.files {
204 if verbose {
205 fmt.Printf("%s\n", file)
206 }
207
208 f, err := os.Open(file)
209 if err != nil {
210 log.Fatal(err)
211 }
212 aro, err := archive.Parse(f, false)
213 if err != nil || !isGoCompilerObjFile(aro) {
214 f.Seek(0, io.SeekStart)
215 ar.addFile(f)
216 goto close
217 }
218
219 for _, e := range aro.Entries {
220 if e.Type != archive.EntryGoObj || e.Name != "_go_.o" {
221 continue
222 }
223 ar.a.AddEntry(archive.EntryGoObj, filepath.Base(file), 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
224 }
225 close:
226 f.Close()
227 }
228 ar.files = nil
229 }
230
231
232 type FileLike interface {
233 Name() string
234 Stat() (fs.FileInfo, error)
235 Read([]byte) (int, error)
236 Close() error
237 }
238
239
240 func (ar *Archive) addFile(fd FileLike) {
241
242
243 info, err := fd.Stat()
244 if err != nil {
245 log.Fatal(err)
246 }
247
248 mtime := int64(0)
249 uid := 0
250 gid := 0
251 ar.a.AddEntry(archive.EntryNativeObj, info.Name(), mtime, uid, gid, info.Mode(), info.Size(), fd)
252 }
253
254
255
256
257 func (ar *Archive) addPkgdef() {
258 done := false
259 for _, file := range ar.files {
260 f, err := os.Open(file)
261 if err != nil {
262 log.Fatal(err)
263 }
264 aro, err := archive.Parse(f, false)
265 if err != nil || !isGoCompilerObjFile(aro) {
266 goto close
267 }
268
269 for _, e := range aro.Entries {
270 if e.Type != archive.EntryPkgDef {
271 continue
272 }
273 if verbose {
274 fmt.Printf("__.PKGDEF # %s\n", file)
275 }
276 ar.a.AddEntry(archive.EntryPkgDef, "__.PKGDEF", 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
277 done = true
278 }
279 close:
280 f.Close()
281 if done {
282 break
283 }
284 }
285 }
286
287
288
289
290 var stdout io.Writer = os.Stdout
291
292
293 func (ar *Archive) printContents(e *archive.Entry) {
294 ar.extractContents1(e, stdout)
295 }
296
297
298 func (ar *Archive) tableOfContents(e *archive.Entry) {
299 if ar.match(e) {
300 listEntry(e, verbose)
301 }
302 }
303
304
305 func (ar *Archive) extractContents(e *archive.Entry) {
306 ar.extractContents1(e, nil)
307 }
308
309 func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) {
310 if ar.match(e) {
311 if verbose {
312 listEntry(e, false)
313 }
314 if out == nil {
315 f, err := os.OpenFile(e.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444 )
316 if err != nil {
317 log.Fatal(err)
318 }
319 defer f.Close()
320 out = f
321 }
322 ar.output(e, out)
323 }
324 }
325
326
327
328
329 func isGoCompilerObjFile(a *archive.Archive) bool {
330 switch len(a.Entries) {
331 case 1:
332 return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") ||
333 (a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF")
334 case 2:
335 var foundPkgDef, foundGo bool
336 for _, e := range a.Entries {
337 if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
338 foundPkgDef = true
339 }
340 if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
341 foundGo = true
342 }
343 }
344 return foundPkgDef && foundGo
345 default:
346 return false
347 }
348 }
349
View as plain text