1// Copyright 2009 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	"fmt"
10	"go/token"
11	"os"
12	"os/exec"
13)
14
15// run runs the command argv, feeding in stdin on standard input.
16// It returns the output to standard output and standard error.
17// ok indicates whether the command exited successfully.
18func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
19	if i := find(argv, "-xc"); i >= 0 && argv[len(argv)-1] == "-" {
20		// Some compilers have trouble with standard input.
21		// Others have trouble with -xc.
22		// Avoid both problems by writing a file with a .c extension.
23		f, err := os.CreateTemp("", "cgo-gcc-input-")
24		if err != nil {
25			fatalf("%s", err)
26		}
27		name := f.Name()
28		f.Close()
29		if err := os.WriteFile(name+".c", stdin, 0666); err != nil {
30			os.Remove(name)
31			fatalf("%s", err)
32		}
33		defer os.Remove(name)
34		defer os.Remove(name + ".c")
35
36		// Build new argument list without -xc and trailing -.
37		new := append(argv[:i:i], argv[i+1:len(argv)-1]...)
38
39		// Since we are going to write the file to a temporary directory,
40		// we will need to add -I . explicitly to the command line:
41		// any #include "foo" before would have looked in the current
42		// directory as the directory "holding" standard input, but now
43		// the temporary directory holds the input.
44		// We've also run into compilers that reject "-I." but allow "-I", ".",
45		// so be sure to use two arguments.
46		// This matters mainly for people invoking cgo -godefs by hand.
47		new = append(new, "-I", ".")
48
49		// Finish argument list with path to C file.
50		new = append(new, name+".c")
51
52		argv = new
53		stdin = nil
54	}
55
56	p := exec.Command(argv[0], argv[1:]...)
57	p.Stdin = bytes.NewReader(stdin)
58	var bout, berr bytes.Buffer
59	p.Stdout = &bout
60	p.Stderr = &berr
61	// Disable escape codes in clang error messages.
62	p.Env = append(os.Environ(), "TERM=dumb")
63	err := p.Run()
64	if _, ok := err.(*exec.ExitError); err != nil && !ok {
65		fatalf("exec %s: %s", argv[0], err)
66	}
67	ok = p.ProcessState.Success()
68	stdout, stderr = bout.Bytes(), berr.Bytes()
69	return
70}
71
72func find(argv []string, target string) int {
73	for i, arg := range argv {
74		if arg == target {
75			return i
76		}
77	}
78	return -1
79}
80
81func lineno(pos token.Pos) string {
82	return fset.Position(pos).String()
83}
84
85// Die with an error message.
86func fatalf(msg string, args ...interface{}) {
87	// If we've already printed other errors, they might have
88	// caused the fatal condition. Assume they're enough.
89	if nerrors == 0 {
90		fmt.Fprintf(os.Stderr, "cgo: "+msg+"\n", args...)
91	}
92	os.Exit(2)
93}
94
95var nerrors int
96
97func error_(pos token.Pos, msg string, args ...interface{}) {
98	nerrors++
99	if pos.IsValid() {
100		fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String())
101	} else {
102		fmt.Fprintf(os.Stderr, "cgo: ")
103	}
104	fmt.Fprintf(os.Stderr, msg, args...)
105	fmt.Fprintf(os.Stderr, "\n")
106}
107
108func creat(name string) *os.File {
109	f, err := os.Create(name)
110	if err != nil {
111		fatalf("%s", err)
112	}
113	return f
114}
115