Source file src/cmd/link/cgo_test.go

     1  // Copyright 2021 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  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"testing"
    15  )
    16  
    17  // Issues 43830, 46295
    18  func TestCGOLTO(t *testing.T) {
    19  	testenv.MustHaveCGO(t)
    20  	testenv.MustHaveGoBuild(t)
    21  
    22  	t.Parallel()
    23  
    24  	for _, cc := range []string{"gcc", "clang"} {
    25  		for test := 0; test < 2; test++ {
    26  			t.Run(fmt.Sprintf("%s-%d", cc, test), func(t *testing.T) {
    27  				testCGOLTO(t, cc, test)
    28  			})
    29  		}
    30  	}
    31  }
    32  
    33  const test1_main = `
    34  package main
    35  
    36  /*
    37  extern int myadd(int, int);
    38  int c_add(int a, int b) {
    39  	return myadd(a, b);
    40  }
    41  */
    42  import "C"
    43  
    44  func main() {
    45  	println(C.c_add(1, 2))
    46  }
    47  `
    48  
    49  const test1_add = `
    50  package main
    51  
    52  import "C"
    53  
    54  /* test */
    55  
    56  //export myadd
    57  func myadd(a C.int, b C.int) C.int {
    58  	return a + b
    59  }
    60  `
    61  
    62  const test2_main = `
    63  package main
    64  
    65  import "fmt"
    66  
    67  /*
    68  #include <stdio.h>
    69  
    70  void hello(void) {
    71    printf("hello\n");
    72  }
    73  */
    74  import "C"
    75  
    76  func main() {
    77  	hello := C.hello
    78  	fmt.Printf("%v\n", hello)
    79  }
    80  `
    81  
    82  func testCGOLTO(t *testing.T, cc string, test int) {
    83  	t.Parallel()
    84  
    85  	if _, err := exec.LookPath(cc); err != nil {
    86  		t.Skipf("no %s compiler", cc)
    87  	}
    88  
    89  	dir := t.TempDir()
    90  
    91  	writeTempFile := func(name, contents string) {
    92  		if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0644); err != nil {
    93  			t.Fatal(err)
    94  		}
    95  	}
    96  
    97  	writeTempFile("go.mod", "module cgolto\n")
    98  
    99  	switch test {
   100  	case 0:
   101  		writeTempFile("main.go", test1_main)
   102  		writeTempFile("add.go", test1_add)
   103  	case 1:
   104  		writeTempFile("main.go", test2_main)
   105  	default:
   106  		t.Fatalf("bad case %d", test)
   107  	}
   108  
   109  	cmd := exec.Command(testenv.GoToolPath(t), "build")
   110  	cmd.Dir = dir
   111  	cmd.Env = append(os.Environ(),
   112  		"CC="+cc,
   113  		"CGO_CFLAGS=-flto",
   114  	)
   115  
   116  	t.Log("go build")
   117  	out, err := cmd.CombinedOutput()
   118  	t.Logf("%s", out)
   119  
   120  	if err != nil {
   121  		t.Logf("go build failed: %v", err)
   122  
   123  		// Error messages we've seen indicating that LTO is not supported.
   124  		// These errors come from GCC or clang, not Go.
   125  		var noLTO = []string{
   126  			`unrecognized command line option "-flto"`,
   127  			"unable to pass LLVM bit-code files to linker",
   128  			"file not recognized: File format not recognized",
   129  			"LTO support has not been enabled",
   130  			"linker command failed with exit code",
   131  			"gcc: can't load library",
   132  		}
   133  		for _, msg := range noLTO {
   134  			if bytes.Contains(out, []byte(msg)) {
   135  				t.Skipf("C compiler %v does not support LTO", cc)
   136  			}
   137  		}
   138  
   139  		t.Error("failed")
   140  	}
   141  }
   142  

View as plain text