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 5package main 6 7import ( 8 "bytes" 9 "internal/testenv" 10 "os" 11 "path/filepath" 12 "strconv" 13 "testing" 14) 15 16// Issues 43830, 46295 17func 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 46const test1_main = ` 47package main 48 49/* 50extern int myadd(int, int); 51int c_add(int a, int b) { 52 return myadd(a, b); 53} 54*/ 55import "C" 56 57func main() { 58 println(C.c_add(1, 2)) 59} 60` 61 62const test1_add = ` 63package main 64 65import "C" 66 67/* test */ 68 69//export myadd 70func myadd(a C.int, b C.int) C.int { 71 return a + b 72} 73` 74 75const test2_main = ` 76package main 77 78import "fmt" 79 80/* 81#include <stdio.h> 82 83void hello(void) { 84 printf("hello\n"); 85} 86*/ 87import "C" 88 89func main() { 90 hello := C.hello 91 fmt.Printf("%v\n", hello) 92} 93` 94 95func 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