Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/pack/pack.go

Documentation: cmd/pack

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"cmd/internal/archive"
     9  	"fmt"
    10  	"io"
    11  	"io/fs"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  )
    16  
    17  const usageMessage = `Usage: pack op file.a [name....]
    18  Where op is one of cprtx optionally followed by v for verbose output.
    19  For compatibility with old Go build environments the op string grc is
    20  accepted as a synonym for c.
    21  
    22  For more information, run
    23  	go doc cmd/pack`
    24  
    25  func usage() {
    26  	fmt.Fprintln(os.Stderr, usageMessage)
    27  	os.Exit(2)
    28  }
    29  
    30  func main() {
    31  	log.SetFlags(0)
    32  	log.SetPrefix("pack: ")
    33  	// need "pack op archive" at least.
    34  	if len(os.Args) < 3 {
    35  		log.Print("not enough arguments")
    36  		fmt.Fprintln(os.Stderr)
    37  		usage()
    38  	}
    39  	setOp(os.Args[1])
    40  	var ar *Archive
    41  	switch op {
    42  	case 'p':
    43  		ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
    44  		ar.scan(ar.printContents)
    45  	case 'r':
    46  		ar = openArchive(os.Args[2], os.O_RDWR|os.O_CREATE, os.Args[3:])
    47  		ar.addFiles()
    48  	case 'c':
    49  		ar = openArchive(os.Args[2], os.O_RDWR|os.O_TRUNC|os.O_CREATE, os.Args[3:])
    50  		ar.addPkgdef()
    51  		ar.addFiles()
    52  	case 't':
    53  		ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
    54  		ar.scan(ar.tableOfContents)
    55  	case 'x':
    56  		ar = openArchive(os.Args[2], os.O_RDONLY, os.Args[3:])
    57  		ar.scan(ar.extractContents)
    58  	default:
    59  		log.Printf("invalid operation %q", os.Args[1])
    60  		fmt.Fprintln(os.Stderr)
    61  		usage()
    62  	}
    63  	if len(ar.files) > 0 {
    64  		log.Fatalf("file %q not in archive", ar.files[0])
    65  	}
    66  }
    67  
    68  // The unusual ancestry means the arguments are not Go-standard.
    69  // These variables hold the decoded operation specified by the first argument.
    70  // op holds the operation we are doing (prtx).
    71  // verbose tells whether the 'v' option was specified.
    72  var (
    73  	op      rune
    74  	verbose bool
    75  )
    76  
    77  // setOp parses the operation string (first argument).
    78  func setOp(arg string) {
    79  	// Recognize 'go tool pack grc' because that was the
    80  	// formerly canonical way to build a new archive
    81  	// from a set of input files. Accepting it keeps old
    82  	// build systems working with both Go 1.2 and Go 1.3.
    83  	if arg == "grc" {
    84  		arg = "c"
    85  	}
    86  
    87  	for _, r := range arg {
    88  		switch r {
    89  		case 'c', 'p', 'r', 't', 'x':
    90  			if op != 0 {
    91  				// At most one can be set.
    92  				usage()
    93  			}
    94  			op = r
    95  		case 'v':
    96  			if verbose {
    97  				// Can be set only once.
    98  				usage()
    99  			}
   100  			verbose = true
   101  		default:
   102  			usage()
   103  		}
   104  	}
   105  }
   106  
   107  const (
   108  	arHeader = "!<arch>\n"
   109  )
   110  
   111  // An Archive represents an open archive file. It is always scanned sequentially
   112  // from start to end, without backing up.
   113  type Archive struct {
   114  	a        *archive.Archive
   115  	files    []string // Explicit list of files to be processed.
   116  	pad      int      // Padding bytes required at end of current archive file
   117  	matchAll bool     // match all files in archive
   118  }
   119  
   120  // archive opens (and if necessary creates) the named archive.
   121  func openArchive(name string, mode int, files []string) *Archive {
   122  	f, err := os.OpenFile(name, mode, 0666)
   123  	if err != nil {
   124  		log.Fatal(err)
   125  	}
   126  	var a *archive.Archive
   127  	if mode&os.O_TRUNC != 0 { // the c command
   128  		a, err = archive.New(f)
   129  	} else {
   130  		a, err = archive.Parse(f, verbose)
   131  		if err != nil && mode&os.O_CREATE != 0 { // the r command
   132  			a, err = archive.New(f)
   133  		}
   134  	}
   135  	if err != nil {
   136  		log.Fatal(err)
   137  	}
   138  	return &Archive{
   139  		a:        a,
   140  		files:    files,
   141  		matchAll: len(files) == 0,
   142  	}
   143  }
   144  
   145  // scan scans the archive and executes the specified action on each entry.
   146  func (ar *Archive) scan(action func(*archive.Entry)) {
   147  	for i := range ar.a.Entries {
   148  		e := &ar.a.Entries[i]
   149  		action(e)
   150  	}
   151  }
   152  
   153  // listEntry prints to standard output a line describing the entry.
   154  func listEntry(e *archive.Entry, verbose bool) {
   155  	if verbose {
   156  		fmt.Fprintf(stdout, "%s\n", e.String())
   157  	} else {
   158  		fmt.Fprintf(stdout, "%s\n", e.Name)
   159  	}
   160  }
   161  
   162  // output copies the entry to the specified writer.
   163  func (ar *Archive) output(e *archive.Entry, w io.Writer) {
   164  	r := io.NewSectionReader(ar.a.File(), e.Offset, e.Size)
   165  	n, err := io.Copy(w, r)
   166  	if err != nil {
   167  		log.Fatal(err)
   168  	}
   169  	if n != e.Size {
   170  		log.Fatal("short file")
   171  	}
   172  }
   173  
   174  // match reports whether the entry matches the argument list.
   175  // If it does, it also drops the file from the to-be-processed list.
   176  func (ar *Archive) match(e *archive.Entry) bool {
   177  	if ar.matchAll {
   178  		return true
   179  	}
   180  	for i, name := range ar.files {
   181  		if e.Name == name {
   182  			copy(ar.files[i:], ar.files[i+1:])
   183  			ar.files = ar.files[:len(ar.files)-1]
   184  			return true
   185  		}
   186  	}
   187  	return false
   188  }
   189  
   190  // addFiles adds files to the archive. The archive is known to be
   191  // sane and we are positioned at the end. No attempt is made
   192  // to check for existing files.
   193  func (ar *Archive) addFiles() {
   194  	if len(ar.files) == 0 {
   195  		usage()
   196  	}
   197  	for _, file := range ar.files {
   198  		if verbose {
   199  			fmt.Printf("%s\n", file)
   200  		}
   201  
   202  		f, err := os.Open(file)
   203  		if err != nil {
   204  			log.Fatal(err)
   205  		}
   206  		aro, err := archive.Parse(f, false)
   207  		if err != nil || !isGoCompilerObjFile(aro) {
   208  			f.Seek(0, io.SeekStart)
   209  			ar.addFile(f)
   210  			goto close
   211  		}
   212  
   213  		for _, e := range aro.Entries {
   214  			if e.Type != archive.EntryGoObj || e.Name != "_go_.o" {
   215  				continue
   216  			}
   217  			ar.a.AddEntry(archive.EntryGoObj, filepath.Base(file), 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
   218  		}
   219  	close:
   220  		f.Close()
   221  	}
   222  	ar.files = nil
   223  }
   224  
   225  // FileLike abstracts the few methods we need, so we can test without needing real files.
   226  type FileLike interface {
   227  	Name() string
   228  	Stat() (fs.FileInfo, error)
   229  	Read([]byte) (int, error)
   230  	Close() error
   231  }
   232  
   233  // addFile adds a single file to the archive
   234  func (ar *Archive) addFile(fd FileLike) {
   235  	// Format the entry.
   236  	// First, get its info.
   237  	info, err := fd.Stat()
   238  	if err != nil {
   239  		log.Fatal(err)
   240  	}
   241  	// mtime, uid, gid are all zero so repeated builds produce identical output.
   242  	mtime := int64(0)
   243  	uid := 0
   244  	gid := 0
   245  	ar.a.AddEntry(archive.EntryNativeObj, info.Name(), mtime, uid, gid, info.Mode(), info.Size(), fd)
   246  }
   247  
   248  // addPkgdef adds the __.PKGDEF file to the archive, copied
   249  // from the first Go object file on the file list, if any.
   250  // The archive is known to be empty.
   251  func (ar *Archive) addPkgdef() {
   252  	done := false
   253  	for _, file := range ar.files {
   254  		f, err := os.Open(file)
   255  		if err != nil {
   256  			log.Fatal(err)
   257  		}
   258  		aro, err := archive.Parse(f, false)
   259  		if err != nil || !isGoCompilerObjFile(aro) {
   260  			goto close
   261  		}
   262  
   263  		for _, e := range aro.Entries {
   264  			if e.Type != archive.EntryPkgDef {
   265  				continue
   266  			}
   267  			if verbose {
   268  				fmt.Printf("__.PKGDEF # %s\n", file)
   269  			}
   270  			ar.a.AddEntry(archive.EntryPkgDef, "__.PKGDEF", 0, 0, 0, 0644, e.Size, io.NewSectionReader(f, e.Offset, e.Size))
   271  			done = true
   272  		}
   273  	close:
   274  		f.Close()
   275  		if done {
   276  			break
   277  		}
   278  	}
   279  }
   280  
   281  // Finally, the actual commands. Each is an action.
   282  
   283  // can be modified for testing.
   284  var stdout io.Writer = os.Stdout
   285  
   286  // printContents implements the 'p' command.
   287  func (ar *Archive) printContents(e *archive.Entry) {
   288  	ar.extractContents1(e, stdout)
   289  }
   290  
   291  // tableOfContents implements the 't' command.
   292  func (ar *Archive) tableOfContents(e *archive.Entry) {
   293  	if ar.match(e) {
   294  		listEntry(e, verbose)
   295  	}
   296  }
   297  
   298  // extractContents implements the 'x' command.
   299  func (ar *Archive) extractContents(e *archive.Entry) {
   300  	ar.extractContents1(e, nil)
   301  }
   302  
   303  func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) {
   304  	if ar.match(e) {
   305  		if verbose {
   306  			listEntry(e, false)
   307  		}
   308  		if out == nil {
   309  			f, err := os.OpenFile(e.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444 /*e.Mode*/)
   310  			if err != nil {
   311  				log.Fatal(err)
   312  			}
   313  			defer f.Close()
   314  			out = f
   315  		}
   316  		ar.output(e, out)
   317  	}
   318  }
   319  
   320  // isGoCompilerObjFile reports whether file is an object file created
   321  // by the Go compiler, which is an archive file with exactly one entry
   322  // of __.PKGDEF, or _go_.o, or both entries.
   323  func isGoCompilerObjFile(a *archive.Archive) bool {
   324  	switch len(a.Entries) {
   325  	case 1:
   326  		return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") ||
   327  			(a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF")
   328  	case 2:
   329  		var foundPkgDef, foundGo bool
   330  		for _, e := range a.Entries {
   331  			if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
   332  				foundPkgDef = true
   333  			}
   334  			if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
   335  				foundGo = true
   336  			}
   337  		}
   338  		return foundPkgDef && foundGo
   339  	default:
   340  		return false
   341  	}
   342  }
   343  

View as plain text