]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/pack: treat compiler's -linkobj output as "compiler object"
authorCherry Zhang <cherryyz@google.com>
Mon, 21 Dec 2020 19:11:02 +0000 (14:11 -0500)
committerCherry Zhang <cherryyz@google.com>
Tue, 22 Dec 2020 15:50:18 +0000 (15:50 +0000)
Treat the compiler's -linkobj output as "compiler object, which
means "pack c" will "see through" the file and add individual
entry to the new archive, instead of the object as a whole.

This is somewhat peculiar. But Go 1.15's cmd/pack does this,
although seemingly accidental. We just do the same. FWIW, it
does make things more consistent with/without -linkobj flag.

Fixes #43271.

Change-Id: I6b2d99256db7ebf0fa430f85afa7464e334f6bcb
Reviewed-on: https://go-review.googlesource.com/c/go/+/279483
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/pack/pack.go
src/cmd/pack/pack_test.go

index 82546ea7dcbba68caa8dad7d77c52562e944a899..3dffabe5ec81fd9c7376b73bc21eca674fdac1a7 100644 (file)
@@ -315,20 +315,25 @@ func (ar *Archive) extractContents1(e *archive.Entry, out io.Writer) {
 }
 
 // isGoCompilerObjFile reports whether file is an object file created
-// by the Go compiler, which is an archive file with exactly two entries:
-// __.PKGDEF and _go_.o.
+// by the Go compiler, which is an archive file with exactly one entry
+// of __.PKGDEF, or _go_.o, or both entries.
 func isGoCompilerObjFile(a *archive.Archive) bool {
-       if len(a.Entries) != 2 {
-               return false
-       }
-       var foundPkgDef, foundGo bool
-       for _, e := range a.Entries {
-               if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
-                       foundPkgDef = true
-               }
-               if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
-                       foundGo = true
+       switch len(a.Entries) {
+       case 1:
+               return (a.Entries[0].Type == archive.EntryGoObj && a.Entries[0].Name == "_go_.o") ||
+                       (a.Entries[0].Type == archive.EntryPkgDef && a.Entries[0].Name == "__.PKGDEF")
+       case 2:
+               var foundPkgDef, foundGo bool
+               for _, e := range a.Entries {
+                       if e.Type == archive.EntryPkgDef && e.Name == "__.PKGDEF" {
+                               foundPkgDef = true
+                       }
+                       if e.Type == archive.EntryGoObj && e.Name == "_go_.o" {
+                               foundGo = true
+                       }
                }
+               return foundPkgDef && foundGo
+       default:
+               return false
        }
-       return foundPkgDef && foundGo
 }
index 218c7acda6ddf949eb3769c06ce1ee7b13f5eb41..16a51358009f685b7ac3574c1d0a462379e5456d 100644 (file)
@@ -302,6 +302,72 @@ func TestIssue21703(t *testing.T) {
        run(goBin, "tool", "compile", "-I", ".", "b.go")
 }
 
+// Test the "c" command can "see through" the archive generated by the compiler.
+// This is peculiar. (See issue )
+func TestCreateWithCompilerObj(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+
+       dir := tmpDir(t)
+       defer os.RemoveAll(dir)
+       src := filepath.Join(dir, "p.go")
+       prog := "package p; var X = 42\n"
+       err := os.WriteFile(src, []byte(prog), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       run := func(args ...string) string {
+               return doRun(t, dir, args...)
+       }
+
+       goBin := testenv.GoToolPath(t)
+       run(goBin, "build", "cmd/pack") // writes pack binary to dir
+       run(goBin, "tool", "compile", "-pack", "-o", "p.a", "p.go")
+       run("./pack", "c", "packed.a", "p.a")
+       fi, err := os.Stat(filepath.Join(dir, "p.a"))
+       if err != nil {
+               t.Fatalf("stat p.a failed: %v", err)
+       }
+       fi2, err := os.Stat(filepath.Join(dir, "packed.a"))
+       if err != nil {
+               t.Fatalf("stat packed.a failed: %v", err)
+       }
+       // For compiler-generated object file, the "c" command is
+       // expected to get (essentially) the same file back, instead
+       // of packing it into a new archive with a single entry.
+       if want, got := fi.Size(), fi2.Size(); want != got {
+               t.Errorf("packed file with different size: want %d, got %d", want, got)
+       }
+
+       // Test -linkobj flag as well.
+       run(goBin, "tool", "compile", "-linkobj", "p2.a", "-o", "p.x", "p.go")
+       run("./pack", "c", "packed2.a", "p2.a")
+       fi, err = os.Stat(filepath.Join(dir, "p2.a"))
+       if err != nil {
+               t.Fatalf("stat p2.a failed: %v", err)
+       }
+       fi2, err = os.Stat(filepath.Join(dir, "packed2.a"))
+       if err != nil {
+               t.Fatalf("stat packed2.a failed: %v", err)
+       }
+       if want, got := fi.Size(), fi2.Size(); want != got {
+               t.Errorf("packed file with different size: want %d, got %d", want, got)
+       }
+
+       run("./pack", "c", "packed3.a", "p.x")
+       fi, err = os.Stat(filepath.Join(dir, "p.x"))
+       if err != nil {
+               t.Fatalf("stat p.x failed: %v", err)
+       }
+       fi2, err = os.Stat(filepath.Join(dir, "packed3.a"))
+       if err != nil {
+               t.Fatalf("stat packed3.a failed: %v", err)
+       }
+       if want, got := fi.Size(), fi2.Size(); want != got {
+               t.Errorf("packed file with different size: want %d, got %d", want, got)
+       }
+}
+
 // doRun runs a program in a directory and returns the output.
 func doRun(t *testing.T, dir string, args ...string) string {
        cmd := exec.Command(args[0], args[1:]...)