github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/internal/goobj/goobj_test.go (about)

     1  // Copyright 2017 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 goobj
     6  
     7  import (
     8  	"debug/elf"
     9  	"debug/macho"
    10  	"debug/pe"
    11  	"fmt"
    12  	"github.com/gagliardetto/golang-go/not-internal/testenv"
    13  	"github.com/gagliardetto/golang-go/not-internal/xcoff"
    14  	"io"
    15  	"io/ioutil"
    16  	"os"
    17  	"os/exec"
    18  	"path/filepath"
    19  	"runtime"
    20  	"testing"
    21  )
    22  
    23  var (
    24  	buildDir   string
    25  	go1obj     string
    26  	go2obj     string
    27  	goarchive  string
    28  	cgoarchive string
    29  )
    30  
    31  func TestMain(m *testing.M) {
    32  	if !testenv.HasGoBuild() {
    33  		return
    34  	}
    35  
    36  	if err := buildGoobj(); err != nil {
    37  		fmt.Println(err)
    38  		os.RemoveAll(buildDir)
    39  		os.Exit(1)
    40  	}
    41  
    42  	exit := m.Run()
    43  
    44  	os.RemoveAll(buildDir)
    45  	os.Exit(exit)
    46  }
    47  
    48  func copyDir(dst, src string) error {
    49  	err := os.MkdirAll(dst, 0777)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	fis, err := ioutil.ReadDir(src)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	for _, fi := range fis {
    58  		err = copyFile(filepath.Join(dst, fi.Name()), filepath.Join(src, fi.Name()))
    59  		if err != nil {
    60  			return err
    61  		}
    62  	}
    63  	return nil
    64  }
    65  
    66  func copyFile(dst, src string) (err error) {
    67  	var s, d *os.File
    68  	s, err = os.Open(src)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer s.Close()
    73  	d, err = os.Create(dst)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	defer func() {
    78  		e := d.Close()
    79  		if err == nil {
    80  			err = e
    81  		}
    82  	}()
    83  	_, err = io.Copy(d, s)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	return nil
    88  }
    89  
    90  func buildGoobj() error {
    91  	var err error
    92  
    93  	buildDir, err = ioutil.TempDir("", "TestGoobj")
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	go1obj = filepath.Join(buildDir, "go1.o")
    99  	go2obj = filepath.Join(buildDir, "go2.o")
   100  	goarchive = filepath.Join(buildDir, "go.a")
   101  
   102  	gotool, err := testenv.GoTool()
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	go1src := filepath.Join("testdata", "go1.go")
   108  	go2src := filepath.Join("testdata", "go2.go")
   109  
   110  	out, err := exec.Command(gotool, "tool", "compile", "-o", go1obj, go1src).CombinedOutput()
   111  	if err != nil {
   112  		return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out)
   113  	}
   114  	out, err = exec.Command(gotool, "tool", "compile", "-o", go2obj, go2src).CombinedOutput()
   115  	if err != nil {
   116  		return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out)
   117  	}
   118  	out, err = exec.Command(gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput()
   119  	if err != nil {
   120  		return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out)
   121  	}
   122  
   123  	if testenv.HasCGO() {
   124  		gopath := filepath.Join(buildDir, "gopath")
   125  		err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo"))
   126  		if err == nil {
   127  			err = ioutil.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666)
   128  		}
   129  		if err != nil {
   130  			return err
   131  		}
   132  		cmd := exec.Command(gotool, "install", "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo")
   133  		cmd.Dir = filepath.Join(gopath, "src", "mycgo")
   134  		cmd.Env = append(os.Environ(), "GOPATH="+gopath)
   135  		out, err = cmd.CombinedOutput()
   136  		if err != nil {
   137  			return fmt.Errorf("go install mycgo: %v\n%s", err, out)
   138  		}
   139  		pat := filepath.Join(gopath, "pkg", "*", "mycgo.a")
   140  		ms, err := filepath.Glob(pat)
   141  		if err != nil {
   142  			return err
   143  		}
   144  		if len(ms) == 0 {
   145  			return fmt.Errorf("cannot found paths for pattern %s", pat)
   146  		}
   147  		cgoarchive = ms[0]
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func TestParseGoobj(t *testing.T) {
   154  	path := go1obj
   155  
   156  	f, err := os.Open(path)
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  	defer f.Close()
   161  
   162  	p, err := Parse(f, "mypkg")
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	if p.Arch != runtime.GOARCH {
   167  		t.Errorf("%s: got %v, want %v", path, p.Arch, runtime.GOARCH)
   168  	}
   169  	var found bool
   170  	for _, s := range p.Syms {
   171  		if s.Name == "mypkg.go1" {
   172  			found = true
   173  			break
   174  		}
   175  	}
   176  	if !found {
   177  		t.Errorf(`%s: symbol "mypkg.go1" not found`, path)
   178  	}
   179  }
   180  
   181  func TestParseArchive(t *testing.T) {
   182  	path := goarchive
   183  
   184  	f, err := os.Open(path)
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  	defer f.Close()
   189  
   190  	p, err := Parse(f, "mypkg")
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	if p.Arch != runtime.GOARCH {
   195  		t.Errorf("%s: got %v, want %v", path, p.Arch, runtime.GOARCH)
   196  	}
   197  	var found1 bool
   198  	var found2 bool
   199  	for _, s := range p.Syms {
   200  		if s.Name == "mypkg.go1" {
   201  			found1 = true
   202  		}
   203  		if s.Name == "mypkg.go2" {
   204  			found2 = true
   205  		}
   206  	}
   207  	if !found1 {
   208  		t.Errorf(`%s: symbol "mypkg.go1" not found`, path)
   209  	}
   210  	if !found2 {
   211  		t.Errorf(`%s: symbol "mypkg.go2" not found`, path)
   212  	}
   213  }
   214  
   215  func TestParseCGOArchive(t *testing.T) {
   216  	testenv.MustHaveCGO(t)
   217  
   218  	path := cgoarchive
   219  
   220  	f, err := os.Open(path)
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	defer f.Close()
   225  
   226  	p, err := Parse(f, "mycgo")
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	if p.Arch != runtime.GOARCH {
   231  		t.Errorf("%s: got %v, want %v", path, p.Arch, runtime.GOARCH)
   232  	}
   233  	var found1 bool
   234  	var found2 bool
   235  	for _, s := range p.Syms {
   236  		if s.Name == "mycgo.go1" {
   237  			found1 = true
   238  		}
   239  		if s.Name == "mycgo.go2" {
   240  			found2 = true
   241  		}
   242  	}
   243  	if !found1 {
   244  		t.Errorf(`%s: symbol "mycgo.go1" not found`, path)
   245  	}
   246  	if !found2 {
   247  		t.Errorf(`%s: symbol "mycgo.go2" not found`, path)
   248  	}
   249  
   250  	c1 := "c1"
   251  	c2 := "c2"
   252  
   253  	found1 = false
   254  	found2 = false
   255  
   256  	switch runtime.GOOS {
   257  	case "darwin":
   258  		c1 = "_" + c1
   259  		c2 = "_" + c2
   260  		for _, obj := range p.Native {
   261  			mf, err := macho.NewFile(obj)
   262  			if err != nil {
   263  				t.Fatal(err)
   264  			}
   265  			if mf.Symtab == nil {
   266  				continue
   267  			}
   268  			for _, s := range mf.Symtab.Syms {
   269  				switch s.Name {
   270  				case c1:
   271  					found1 = true
   272  				case c2:
   273  					found2 = true
   274  				}
   275  			}
   276  		}
   277  	case "windows":
   278  		if runtime.GOARCH == "386" {
   279  			c1 = "_" + c1
   280  			c2 = "_" + c2
   281  		}
   282  		for _, obj := range p.Native {
   283  			pf, err := pe.NewFile(obj)
   284  			if err != nil {
   285  				t.Fatal(err)
   286  			}
   287  			for _, s := range pf.Symbols {
   288  				switch s.Name {
   289  				case c1:
   290  					found1 = true
   291  				case c2:
   292  					found2 = true
   293  				}
   294  			}
   295  		}
   296  	case "aix":
   297  		c1 = "." + c1
   298  		c2 = "." + c2
   299  		for _, obj := range p.Native {
   300  			xf, err := xcoff.NewFile(obj)
   301  			if err != nil {
   302  				t.Fatal(err)
   303  			}
   304  			for _, s := range xf.Symbols {
   305  				switch s.Name {
   306  				case c1:
   307  					found1 = true
   308  				case c2:
   309  					found2 = true
   310  				}
   311  			}
   312  		}
   313  
   314  	default:
   315  		for _, obj := range p.Native {
   316  			ef, err := elf.NewFile(obj)
   317  			if err != nil {
   318  				t.Fatal(err)
   319  			}
   320  			syms, err := ef.Symbols()
   321  			if err != nil {
   322  				t.Fatal(err)
   323  			}
   324  			for _, s := range syms {
   325  				switch s.Name {
   326  				case c1:
   327  					found1 = true
   328  				case c2:
   329  					found2 = true
   330  				}
   331  			}
   332  		}
   333  	}
   334  
   335  	if !found1 {
   336  		t.Errorf(`%s: symbol %q not found`, path, c1)
   337  	}
   338  	if !found2 {
   339  		t.Errorf(`%s: symbol %q not found`, path, c2)
   340  	}
   341  }