summaryrefslogtreecommitdiffstats
path: root/src/cmd/cgo/util.go
blob: 00d931b98a0c1e8416d5bb14334946dbd9a56304 (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
// Copyright 2009 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"
	"go/token"
	exec "internal/execabs"
	"io/ioutil"
	"os"
)

// run runs the command argv, feeding in stdin on standard input.
// It returns the output to standard output and standard error.
// ok indicates whether the command exited successfully.
func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
	if i := find(argv, "-xc"); i >= 0 && argv[len(argv)-1] == "-" {
		// Some compilers have trouble with standard input.
		// Others have trouble with -xc.
		// Avoid both problems by writing a file with a .c extension.
		f, err := ioutil.TempFile("", "cgo-gcc-input-")
		if err != nil {
			fatalf("%s", err)
		}
		name := f.Name()
		f.Close()
		if err := ioutil.WriteFile(name+".c", stdin, 0666); err != nil {
			os.Remove(name)
			fatalf("%s", err)
		}
		defer os.Remove(name)
		defer os.Remove(name + ".c")

		// Build new argument list without -xc and trailing -.
		new := append(argv[:i:i], argv[i+1:len(argv)-1]...)

		// Since we are going to write the file to a temporary directory,
		// we will need to add -I . explicitly to the command line:
		// any #include "foo" before would have looked in the current
		// directory as the directory "holding" standard input, but now
		// the temporary directory holds the input.
		// We've also run into compilers that reject "-I." but allow "-I", ".",
		// so be sure to use two arguments.
		// This matters mainly for people invoking cgo -godefs by hand.
		new = append(new, "-I", ".")

		// Finish argument list with path to C file.
		new = append(new, name+".c")

		argv = new
		stdin = nil
	}

	p := exec.Command(argv[0], argv[1:]...)
	p.Stdin = bytes.NewReader(stdin)
	var bout, berr bytes.Buffer
	p.Stdout = &bout
	p.Stderr = &berr
	// Disable escape codes in clang error messages.
	p.Env = append(os.Environ(), "TERM=dumb")
	err := p.Run()
	if _, ok := err.(*exec.ExitError); err != nil && !ok {
		fatalf("exec %s: %s", argv[0], err)
	}
	ok = p.ProcessState.Success()
	stdout, stderr = bout.Bytes(), berr.Bytes()
	return
}

func find(argv []string, target string) int {
	for i, arg := range argv {
		if arg == target {
			return i
		}
	}
	return -1
}

func lineno(pos token.Pos) string {
	return fset.Position(pos).String()
}

// Die with an error message.
func fatalf(msg string, args ...interface{}) {
	// If we've already printed other errors, they might have
	// caused the fatal condition. Assume they're enough.
	if nerrors == 0 {
		fmt.Fprintf(os.Stderr, "cgo: "+msg+"\n", args...)
	}
	os.Exit(2)
}

var nerrors int

func error_(pos token.Pos, msg string, args ...interface{}) {
	nerrors++
	if pos.IsValid() {
		fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String())
	} else {
		fmt.Fprintf(os.Stderr, "cgo: ")
	}
	fmt.Fprintf(os.Stderr, msg, args...)
	fmt.Fprintf(os.Stderr, "\n")
}

// isName reports whether s is a valid C identifier
func isName(s string) bool {
	for i, v := range s {
		if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') {
			return false
		}
		if i == 0 && '0' <= v && v <= '9' {
			return false
		}
	}
	return s != ""
}

func creat(name string) *os.File {
	f, err := os.Create(name)
	if err != nil {
		fatalf("%s", err)
	}
	return f
}

func slashToUnderscore(c rune) rune {
	if c == '/' || c == '\\' || c == ':' {
		c = '_'
	}
	return c
}