Browse Source

cgo: add support for CFLAGS in .c files

This patch adds support for passing CFLAGS added in #cgo lines of the
CGo preprocessing phase to the compiler when compiling C files inside
packages. This is expected and convenient but didn't work before.
pull/1774/head
Ayke van Laethem 4 years ago
committed by Ron Evans
parent
commit
1bed192de0
  1. 5
      builder/build.go
  2. 4
      builder/cc.go
  3. 31
      cgo/cgo.go
  4. 2
      cgo/cgo_test.go
  5. 12
      loader/loader.go
  6. 2
      testdata/cgo/main.c
  7. 4
      testdata/cgo/main.go
  8. 2
      testdata/cgo/main.h
  9. 1
      testdata/cgo/out.txt

5
builder/build.go

@ -430,7 +430,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
job := &compileJob{ job := &compileJob{
description: "compile extra file " + path, description: "compile extra file " + path,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, config) result, err := compileAndCacheCFile(abspath, dir, config.CFlags(), config)
job.result = result job.result = result
return err return err
}, },
@ -443,12 +443,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// TODO: do this as part of building the package to be able to link the // TODO: do this as part of building the package to be able to link the
// bitcode files together. // bitcode files together.
for _, pkg := range lprogram.Sorted() { for _, pkg := range lprogram.Sorted() {
pkg := pkg
for _, filename := range pkg.CFiles { for _, filename := range pkg.CFiles {
abspath := filepath.Join(pkg.Dir, filename) abspath := filepath.Join(pkg.Dir, filename)
job := &compileJob{ job := &compileJob{
description: "compile CGo file " + abspath, description: "compile CGo file " + abspath,
run: func(job *compileJob) error { run: func(job *compileJob) error {
result, err := compileAndCacheCFile(abspath, dir, config) result, err := compileAndCacheCFile(abspath, dir, pkg.CFlags, config)
job.result = result job.result = result
return err return err
}, },

4
builder/cc.go

@ -57,7 +57,7 @@ import (
// depfile but without invalidating its name. For this reason, the depfile is // depfile but without invalidating its name. For this reason, the depfile is
// written on each new compilation (even when it seems unnecessary). However, it // written on each new compilation (even when it seems unnecessary). However, it
// could in rare cases lead to a stale file fetched from the cache. // could in rare cases lead to a stale file fetched from the cache.
func compileAndCacheCFile(abspath, tmpdir string, config *compileopts.Config) (string, error) { func compileAndCacheCFile(abspath, tmpdir string, cflags []string, config *compileopts.Config) (string, error) {
// Hash input file. // Hash input file.
fileHash, err := hashFile(abspath) fileHash, err := hashFile(abspath)
if err != nil { if err != nil {
@ -121,7 +121,7 @@ func compileAndCacheCFile(abspath, tmpdir string, config *compileopts.Config) (s
return "", err return "", err
} }
depTmpFile.Close() depTmpFile.Close()
flags := config.CFlags() flags := append([]string{}, cflags...) // copy cflags
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath) flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
if config.Options.PrintCommands { if config.Options.PrintCommands {

31
cgo/cgo.go

@ -41,7 +41,8 @@ type cgoPackage struct {
elaboratedTypes map[string]*elaboratedTypeInfo elaboratedTypes map[string]*elaboratedTypeInfo
enums map[string]enumInfo enums map[string]enumInfo
anonStructNum int anonStructNum int
ldflags []string cflags []string // CFlags from #cgo lines
ldflags []string // LDFlags from #cgo lines
visitedFiles map[string][]byte visitedFiles map[string][]byte
} }
@ -157,10 +158,10 @@ typedef unsigned long long _Cgo_ulonglong;
// Process extracts `import "C"` statements from the AST, parses the comment // Process extracts `import "C"` statements from the AST, parses the comment
// with libclang, and modifies the AST to use this information. It returns a // with libclang, and modifies the AST to use this information. It returns a
// newly created *ast.File that should be added to the list of to-be-parsed // newly created *ast.File that should be added to the list of to-be-parsed
// files, the LDFLAGS for this package, and a map of file hashes of the accessed // files, the CFLAGS and LDFLAGS found in #cgo lines, and a map of file hashes
// C header files. If there is one or more error, it returns these in the // of the accessed C header files. If there is one or more error, it returns
// []error slice but still modifies the AST. // these in the []error slice but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, map[string][]byte, []error) { func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []string, map[string][]byte, []error) {
p := &cgoPackage{ p := &cgoPackage{
dir: dir, dir: dir,
fset: fset, fset: fset,
@ -175,11 +176,6 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
visitedFiles: map[string][]byte{}, visitedFiles: map[string][]byte{},
} }
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
// Note that it is only disabled for memcpy (etc) calls made from Go, which
// have better alternatives anyway.
cflags = append(cflags, "-D_FORTIFY_SOURCE=0")
// Add a new location for the following file. // Add a new location for the following file.
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0) generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
generatedTokenPos.SetLines([]int{0}) generatedTokenPos.SetLines([]int{0})
@ -188,7 +184,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Find the absolute path for this package. // Find the absolute path for this package.
packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name()) packagePath, err := filepath.Abs(fset.File(files[0].Pos()).Name())
if err != nil { if err != nil {
return nil, nil, nil, []error{ return nil, nil, nil, nil, []error{
scanner.Error{ scanner.Error{
Pos: fset.Position(files[0].Pos()), Pos: fset.Position(files[0].Pos()),
Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error Msg: "cgo: cannot find absolute path: " + err.Error(), // TODO: wrap this error
@ -363,7 +359,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
continue continue
} }
makePathsAbsolute(flags, packagePath) makePathsAbsolute(flags, packagePath)
cflags = append(cflags, flags...) p.cflags = append(p.cflags, flags...)
case "LDFLAGS": case "LDFLAGS":
flags, err := shlex.Split(value) flags, err := shlex.Split(value)
if err != nil { if err != nil {
@ -386,6 +382,13 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
} }
} }
// Define CFlags that will be used while parsing the package.
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
// Note that it is only disabled for memcpy (etc) calls made from Go, which
// have better alternatives anyway.
cflagsForCGo := append([]string{"-D_FORTIFY_SOURCE=0"}, cflags...)
cflagsForCGo = append(cflagsForCGo, p.cflags...)
// Process all CGo imports. // Process all CGo imports.
for _, genDecl := range statements { for _, genDecl := range statements {
cgoComment := genDecl.Doc.Text() cgoComment := genDecl.Doc.Text()
@ -395,7 +398,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
pos = genDecl.Doc.Pos() pos = genDecl.Doc.Pos()
} }
position := fset.PositionFor(pos, true) position := fset.PositionFor(pos, true)
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line) p.parseFragment(cgoComment+cgoTypes, cflagsForCGo, position.Filename, position.Line)
} }
// Declare functions found by libclang. // Declare functions found by libclang.
@ -430,7 +433,7 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
// Print the newly generated in-memory AST, for debugging. // Print the newly generated in-memory AST, for debugging.
//ast.Print(fset, p.generated) //ast.Print(fset, p.generated)
return p.generated, p.ldflags, p.visitedFiles, p.errors return p.generated, p.cflags, p.ldflags, p.visitedFiles, p.errors
} }
// makePathsAbsolute converts some common path compiler flags (-I, -L) from // makePathsAbsolute converts some common path compiler flags (-I, -L) from

2
cgo/cgo_test.go

@ -65,7 +65,7 @@ func TestCGo(t *testing.T) {
} }
// Process the AST with CGo. // Process the AST with CGo.
cgoAST, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags) cgoAST, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", fset, cflags)
// Check the AST for type errors. // Check the AST for type errors.
var typecheckErrors []error var typecheckErrors []error

12
loader/loader.go

@ -382,14 +382,14 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
// Do CGo processing. // Do CGo processing.
if len(p.CgoFiles) != 0 { if len(p.CgoFiles) != 0 {
var cflags []string var initialCFlags []string
cflags = append(cflags, p.program.config.CFlags()...) initialCFlags = append(initialCFlags, p.program.config.CFlags()...)
cflags = append(cflags, "-I"+p.Dir) initialCFlags = append(initialCFlags, "-I"+p.Dir)
if p.program.clangHeaders != "" { if p.program.clangHeaders != "" {
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders) initialCFlags = append(initialCFlags, "-Xclang", "-internal-isystem", "-Xclang", p.program.clangHeaders)
} }
p.CFlags = cflags generated, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, initialCFlags)
generated, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.program.fset, cflags) p.CFlags = append(initialCFlags, cflags...)
for path, hash := range accessedFiles { for path, hash := range accessedFiles {
p.FileHashes[path] = hash p.FileHashes[path] = hash
} }

2
testdata/cgo/main.c

@ -20,6 +20,8 @@ int globalUnionSize = sizeof(globalUnion);
option_t globalOption = optionG; option_t globalOption = optionG;
bitfield_t globalBitfield = {244, 15, 1, 2, 47, 5}; bitfield_t globalBitfield = {244, 15, 1, 2, 47, 5};
int cflagsConstant = SOME_CONSTANT;
int smallEnumWidth = sizeof(option2_t); int smallEnumWidth = sizeof(option2_t);
int fortytwo() { int fortytwo() {

4
testdata/cgo/main.go

@ -5,6 +5,7 @@ int fortytwo(void);
#include "main.h" #include "main.h"
int mul(int, int); int mul(int, int);
#include <string.h> #include <string.h>
#cgo CFLAGS: -DSOME_CONSTANT=17
*/ */
import "C" import "C"
@ -118,6 +119,9 @@ func main() {
// Check that enums are considered the same width in C and CGo. // Check that enums are considered the same width in C and CGo.
println("enum width matches:", unsafe.Sizeof(C.option2_t(0)) == uintptr(C.smallEnumWidth)) println("enum width matches:", unsafe.Sizeof(C.option2_t(0)) == uintptr(C.smallEnumWidth))
// Check whether CFLAGS are correctly passed on to compiled C files.
println("CFLAGS value:", C.cflagsConstant)
// libc: test whether C functions work at all. // libc: test whether C functions work at all.
buf1 := []byte("foobar\x00") buf1 := []byte("foobar\x00")
buf2 := make([]byte, len(buf1)) buf2 := make([]byte, len(buf1))

2
testdata/cgo/main.h

@ -139,6 +139,8 @@ extern bitfield_t globalBitfield;
extern int smallEnumWidth; extern int smallEnumWidth;
extern int cflagsConstant;
// test duplicate definitions // test duplicate definitions
int add(int a, int b); int add(int a, int b);
extern int global; extern int global;

1
testdata/cgo/out.txt

@ -58,4 +58,5 @@ option G: 12
option 2A: 20 option 2A: 20
option 3A: 21 option 3A: 21
enum width matches: true enum width matches: true
CFLAGS value: 17
copied string: foobar copied string: foobar

Loading…
Cancel
Save