|  | // Copyright 2013 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 cgo handles cgo preprocessing of files containing `import "C"`. | 
|  | // | 
|  | // DESIGN | 
|  | // | 
|  | // The approach taken is to run the cgo processor on the package's | 
|  | // CgoFiles and parse the output, faking the filenames of the | 
|  | // resulting ASTs so that the synthetic file containing the C types is | 
|  | // called "C" (e.g. "~/go/src/net/C") and the preprocessed files | 
|  | // have their original names (e.g. "~/go/src/net/cgo_unix.go"), | 
|  | // not the names of the actual temporary files. | 
|  | // | 
|  | // The advantage of this approach is its fidelity to 'go build'.  The | 
|  | // downside is that the token.Position.Offset for each AST node is | 
|  | // incorrect, being an offset within the temporary file.  Line numbers | 
|  | // should still be correct because of the //line comments. | 
|  | // | 
|  | // The logic of this file is mostly plundered from the 'go build' | 
|  | // tool, which also invokes the cgo preprocessor. | 
|  | // | 
|  | // | 
|  | // REJECTED ALTERNATIVE | 
|  | // | 
|  | // An alternative approach that we explored is to extend go/types' | 
|  | // Importer mechanism to provide the identity of the importing package | 
|  | // so that each time `import "C"` appears it resolves to a different | 
|  | // synthetic package containing just the objects needed in that case. | 
|  | // The loader would invoke cgo but parse only the cgo_types.go file | 
|  | // defining the package-level objects, discarding the other files | 
|  | // resulting from preprocessing. | 
|  | // | 
|  | // The benefit of this approach would have been that source-level | 
|  | // syntax information would correspond exactly to the original cgo | 
|  | // file, with no preprocessing involved, making source tools like | 
|  | // godoc, guru, and eg happy.  However, the approach was rejected | 
|  | // due to the additional complexity it would impose on go/types.  (It | 
|  | // made for a beautiful demo, though.) | 
|  | // | 
|  | // cgo files, despite their *.go extension, are not legal Go source | 
|  | // files per the specification since they may refer to unexported | 
|  | // members of package "C" such as C.int.  Also, a function such as | 
|  | // C.getpwent has in effect two types, one matching its C type and one | 
|  | // which additionally returns (errno C.int).  The cgo preprocessor | 
|  | // uses name mangling to distinguish these two functions in the | 
|  | // processed code, but go/types would need to duplicate this logic in | 
|  | // its handling of function calls, analogous to the treatment of map | 
|  | // lookups in which y=m[k] and y,ok=m[k] are both legal. | 
|  |  | 
|  | package cgo | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "go/ast" | 
|  | "go/build" | 
|  | "go/parser" | 
|  | "go/token" | 
|  | "io/ioutil" | 
|  | "log" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "regexp" | 
|  | "strings" | 
|  |  | 
|  | exec "golang.org/x/sys/execabs" | 
|  | ) | 
|  |  | 
|  | // ProcessFiles invokes the cgo preprocessor on bp.CgoFiles, parses | 
|  | // the output and returns the resulting ASTs. | 
|  | func ProcessFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) { | 
|  | tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C") | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | defer os.RemoveAll(tmpdir) | 
|  |  | 
|  | pkgdir := bp.Dir | 
|  | if DisplayPath != nil { | 
|  | pkgdir = DisplayPath(pkgdir) | 
|  | } | 
|  |  | 
|  | cgoFiles, cgoDisplayFiles, err := Run(bp, pkgdir, tmpdir, false) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | var files []*ast.File | 
|  | for i := range cgoFiles { | 
|  | rd, err := os.Open(cgoFiles[i]) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | display := filepath.Join(bp.Dir, cgoDisplayFiles[i]) | 
|  | f, err := parser.ParseFile(fset, display, rd, mode) | 
|  | rd.Close() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | files = append(files, f) | 
|  | } | 
|  | return files, nil | 
|  | } | 
|  |  | 
|  | var cgoRe = regexp.MustCompile(`[/\\:]`) | 
|  |  | 
|  | // Run invokes the cgo preprocessor on bp.CgoFiles and returns two | 
|  | // lists of files: the resulting processed files (in temporary | 
|  | // directory tmpdir) and the corresponding names of the unprocessed files. | 
|  | // | 
|  | // Run is adapted from (*builder).cgo in | 
|  | // $GOROOT/src/cmd/go/build.go, but these features are unsupported: | 
|  | // Objective C, CGOPKGPATH, CGO_FLAGS. | 
|  | // | 
|  | // If useabs is set to true, absolute paths of the bp.CgoFiles will be passed in | 
|  | // to the cgo preprocessor. This in turn will set the // line comments | 
|  | // referring to those files to use absolute paths. This is needed for | 
|  | // go/packages using the legacy go list support so it is able to find | 
|  | // the original files. | 
|  | func Run(bp *build.Package, pkgdir, tmpdir string, useabs bool) (files, displayFiles []string, err error) { | 
|  | cgoCPPFLAGS, _, _, _ := cflags(bp, true) | 
|  | _, cgoexeCFLAGS, _, _ := cflags(bp, false) | 
|  |  | 
|  | if len(bp.CgoPkgConfig) > 0 { | 
|  | pcCFLAGS, err := pkgConfigFlags(bp) | 
|  | if err != nil { | 
|  | return nil, nil, err | 
|  | } | 
|  | cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) | 
|  | } | 
|  |  | 
|  | // Allows including _cgo_export.h from .[ch] files in the package. | 
|  | cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir) | 
|  |  | 
|  | // _cgo_gotypes.go (displayed "C") contains the type definitions. | 
|  | files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go")) | 
|  | displayFiles = append(displayFiles, "C") | 
|  | for _, fn := range bp.CgoFiles { | 
|  | // "foo.cgo1.go" (displayed "foo.go") is the processed Go source. | 
|  | f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_") | 
|  | files = append(files, filepath.Join(tmpdir, f+"cgo1.go")) | 
|  | displayFiles = append(displayFiles, fn) | 
|  | } | 
|  |  | 
|  | var cgoflags []string | 
|  | if bp.Goroot && bp.ImportPath == "runtime/cgo" { | 
|  | cgoflags = append(cgoflags, "-import_runtime_cgo=false") | 
|  | } | 
|  | if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" { | 
|  | cgoflags = append(cgoflags, "-import_syscall=false") | 
|  | } | 
|  |  | 
|  | var cgoFiles []string = bp.CgoFiles | 
|  | if useabs { | 
|  | cgoFiles = make([]string, len(bp.CgoFiles)) | 
|  | for i := range cgoFiles { | 
|  | cgoFiles[i] = filepath.Join(pkgdir, bp.CgoFiles[i]) | 
|  | } | 
|  | } | 
|  |  | 
|  | args := stringList( | 
|  | "go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--", | 
|  | cgoCPPFLAGS, cgoexeCFLAGS, cgoFiles, | 
|  | ) | 
|  | if false { | 
|  | log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir) | 
|  | } | 
|  | cmd := exec.Command(args[0], args[1:]...) | 
|  | cmd.Dir = pkgdir | 
|  | cmd.Env = append(os.Environ(), "PWD="+pkgdir) | 
|  | cmd.Stdout = os.Stderr | 
|  | cmd.Stderr = os.Stderr | 
|  | if err := cmd.Run(); err != nil { | 
|  | return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err) | 
|  | } | 
|  |  | 
|  | return files, displayFiles, nil | 
|  | } | 
|  |  | 
|  | // -- unmodified from 'go build' --------------------------------------- | 
|  |  | 
|  | // Return the flags to use when invoking the C or C++ compilers, or cgo. | 
|  | func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) { | 
|  | var defaults string | 
|  | if def { | 
|  | defaults = "-g -O2" | 
|  | } | 
|  |  | 
|  | cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS) | 
|  | cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS) | 
|  | cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS) | 
|  | ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS) | 
|  | return | 
|  | } | 
|  |  | 
|  | // envList returns the value of the given environment variable broken | 
|  | // into fields, using the default value when the variable is empty. | 
|  | func envList(key, def string) []string { | 
|  | v := os.Getenv(key) | 
|  | if v == "" { | 
|  | v = def | 
|  | } | 
|  | return strings.Fields(v) | 
|  | } | 
|  |  | 
|  | // stringList's arguments should be a sequence of string or []string values. | 
|  | // stringList flattens them into a single []string. | 
|  | func stringList(args ...interface{}) []string { | 
|  | var x []string | 
|  | for _, arg := range args { | 
|  | switch arg := arg.(type) { | 
|  | case []string: | 
|  | x = append(x, arg...) | 
|  | case string: | 
|  | x = append(x, arg) | 
|  | default: | 
|  | panic("stringList: invalid argument") | 
|  | } | 
|  | } | 
|  | return x | 
|  | } |