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

View as plain text