summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/cgo_test.go
blob: 4393c3fa8019a60b75940cf71c857f571a98185a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"fmt"
	"internal/testenv"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
)

// Issues 43830, 46295
func TestCGOLTO(t *testing.T) {
	testenv.MustHaveCGO(t)
	testenv.MustHaveGoBuild(t)

	t.Parallel()

	for _, cc := range []string{"gcc", "clang"} {
		for test := 0; test < 2; test++ {
			t.Run(fmt.Sprintf("%s-%d", cc, test), func(t *testing.T) {
				testCGOLTO(t, cc, test)
			})
		}
	}
}

const test1_main = `
package main

/*
extern int myadd(int, int);
int c_add(int a, int b) {
	return myadd(a, b);
}
*/
import "C"

func main() {
	println(C.c_add(1, 2))
}
`

const test1_add = `
package main

import "C"

/* test */

//export myadd
func myadd(a C.int, b C.int) C.int {
	return a + b
}
`

const test2_main = `
package main

import "fmt"

/*
#include <stdio.h>

void hello(void) {
  printf("hello\n");
}
*/
import "C"

func main() {
	hello := C.hello
	fmt.Printf("%v\n", hello)
}
`

func testCGOLTO(t *testing.T, cc string, test int) {
	t.Parallel()

	if _, err := exec.LookPath(cc); err != nil {
		t.Skipf("no %s compiler", cc)
	}

	dir := t.TempDir()

	writeTempFile := func(name, contents string) {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(contents), 0644); err != nil {
			t.Fatal(err)
		}
	}

	writeTempFile("go.mod", "module cgolto\n")

	switch test {
	case 0:
		writeTempFile("main.go", test1_main)
		writeTempFile("add.go", test1_add)
	case 1:
		writeTempFile("main.go", test2_main)
	default:
		t.Fatalf("bad case %d", test)
	}

	cmd := testenv.Command(t, testenv.GoToolPath(t), "build")
	cmd.Dir = dir
	cmd.Env = append(os.Environ(),
		"CC="+cc,
		"CGO_CFLAGS=-flto",
	)

	t.Log("go build")
	out, err := cmd.CombinedOutput()
	t.Logf("%s", out)

	if err != nil {
		t.Logf("go build failed: %v", err)

		// Error messages we've seen indicating that LTO is not supported.
		// These errors come from GCC or clang, not Go.
		var noLTO = []string{
			`unrecognized command line option "-flto"`,
			"unable to pass LLVM bit-code files to linker",
			"file not recognized: File format not recognized",
			"LTO support has not been enabled",
			"linker command failed with exit code",
			"gcc: can't load library",
		}
		for _, msg := range noLTO {
			if bytes.Contains(out, []byte(msg)) {
				t.Skipf("C compiler %v does not support LTO", cc)
			}
		}

		t.Error("failed")
	}
}