Browse Source

cgo: Add LDFlags support

pull/1126/head
Lucas Teske 5 years ago
committed by Ayke
parent
commit
726d735ad3
  1. 6
      builder/build.go
  2. 20
      cgo/cgo.go
  3. 2
      cgo/cgo_test.go
  4. 7
      cgo/testdata/flags.go
  5. 1
      cgo/testdata/flags.out.go
  6. 16
      compiler/compiler.go
  7. 4
      loader/loader.go
  8. 8
      src/testing/testing.go

6
builder/build.go

@ -32,7 +32,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
if err != nil { if err != nil {
return err return err
} }
mod, extraFiles, errs := compiler.Compile(pkgName, machine, config) mod, extraFiles, extraLDFlags, errs := compiler.Compile(pkgName, machine, config)
if errs != nil { if errs != nil {
return newMultiError(errs) return newMultiError(errs)
} }
@ -187,6 +187,10 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
ldflags = append(ldflags, outpath) ldflags = append(ldflags, outpath)
} }
if len(extraLDFlags) > 0 {
ldflags = append(ldflags, extraLDFlags...)
}
// Link the object files together. // Link the object files together.
err = link(config.Target.Linker, ldflags...) err = link(config.Target.Linker, ldflags...)
if err != nil { if err != nil {

20
cgo/cgo.go

@ -41,6 +41,7 @@ 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
} }
// constantInfo stores some information about a CGo constant found by libclang // constantInfo stores some information about a CGo constant found by libclang
@ -156,7 +157,7 @@ typedef unsigned long long _Cgo_ulonglong;
// 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. If there is one or more error, it returns these in the []error slice // files. If there is one or more error, it returns these in the []error slice
// but still modifies the AST. // but still modifies the AST.
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []error) { func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []string, []error) {
p := &cgoPackage{ p := &cgoPackage{
dir: dir, dir: dir,
fset: fset, fset: fset,
@ -183,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, []error{ return 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
@ -359,6 +360,19 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
} }
makePathsAbsolute(flags, packagePath) makePathsAbsolute(flags, packagePath)
cflags = append(cflags, flags...) cflags = append(cflags, flags...)
case "LDFLAGS":
flags, err := shlex.Split(value)
if err != nil {
// TODO: find the exact location where the error happened.
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], "failed to parse flags in #cgo line: "+err.Error())
continue
}
if err := checkLinkerFlags(name, flags); err != nil {
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+colon+1], err.Error())
continue
}
makePathsAbsolute(flags, packagePath)
p.ldflags = append(p.ldflags, flags...)
default: default:
startPos := strings.LastIndex(line[4:colon], name) + 4 startPos := strings.LastIndex(line[4:colon], name) + 4
p.addErrorAfter(comment.Slash, comment.Text[:lineStart+startPos], "invalid #cgo line: "+name) p.addErrorAfter(comment.Slash, comment.Text[:lineStart+startPos], "invalid #cgo line: "+name)
@ -412,7 +426,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.errors return p.generated, p.ldflags, 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

@ -50,7 +50,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

7
cgo/testdata/flags.go

@ -21,6 +21,13 @@ package main
#if defined(NOTDEFINED) #if defined(NOTDEFINED)
#warning flag must not be defined #warning flag must not be defined
#endif #endif
// Check Compiler flags
#cgo LDFLAGS: -lc
// This flag is not valid ldflags
#cgo LDFLAGS: -does-not-exists
*/ */
import "C" import "C"

1
cgo/testdata/flags.out.go

@ -1,6 +1,7 @@
// CGo errors: // CGo errors:
// testdata/flags.go:5:7: invalid #cgo line: NOFLAGS // testdata/flags.go:5:7: invalid #cgo line: NOFLAGS
// testdata/flags.go:8:13: invalid flag: -fdoes-not-exist // testdata/flags.go:8:13: invalid flag: -fdoes-not-exist
// testdata/flags.go:29:14: invalid flag: -does-not-exists
package main package main

16
compiler/compiler.go

@ -103,7 +103,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
// violation. Eventually, this Compile function should only compile a single // violation. Eventually, this Compile function should only compile a single
// package and not the whole program, and loading of the program (including CGo // package and not the whole program, and loading of the program (including CGo
// processing) should be moved outside the compiler package. // processing) should be moved outside the compiler package.
func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (llvm.Module, []string, []error) { func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Config) (mod llvm.Module, extrafiles []string, extraldflags []string, errors []error) {
c := &compilerContext{ c := &compilerContext{
Config: config, Config: config,
difiles: make(map[string]llvm.Metadata), difiles: make(map[string]llvm.Metadata),
@ -148,7 +148,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
return c.mod, nil, []error{err} return c.mod, nil, nil, []error{err}
} }
lprogram := &loader.Program{ lprogram := &loader.Program{
Build: &build.Context{ Build: &build.Context{
@ -211,14 +211,14 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
if strings.HasSuffix(pkgName, ".go") { if strings.HasSuffix(pkgName, ".go") {
_, err = lprogram.ImportFile(pkgName) _, err = lprogram.ImportFile(pkgName)
if err != nil { if err != nil {
return c.mod, nil, []error{err} return c.mod, nil, nil, []error{err}
} }
} else { } else {
_, err = lprogram.Import(pkgName, wd, token.Position{ _, err = lprogram.Import(pkgName, wd, token.Position{
Filename: "build command-line-arguments", Filename: "build command-line-arguments",
}) })
if err != nil { if err != nil {
return c.mod, nil, []error{err} return c.mod, nil, nil, []error{err}
} }
} }
@ -226,12 +226,12 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
Filename: "build default import", Filename: "build default import",
}) })
if err != nil { if err != nil {
return c.mod, nil, []error{err} return c.mod, nil, nil, []error{err}
} }
err = lprogram.Parse(c.TestConfig.CompileTestBinary) err = lprogram.Parse(c.TestConfig.CompileTestBinary)
if err != nil { if err != nil {
return c.mod, nil, []error{err} return c.mod, nil, nil, []error{err}
} }
c.ir = ir.NewProgram(lprogram, pkgName) c.ir = ir.NewProgram(lprogram, pkgName)
@ -239,7 +239,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
// Run a simple dead code elimination pass. // Run a simple dead code elimination pass.
err = c.ir.SimpleDCE() err = c.ir.SimpleDCE()
if err != nil { if err != nil {
return c.mod, nil, []error{err} return c.mod, nil, nil, []error{err}
} }
// Initialize debug information. // Initialize debug information.
@ -383,7 +383,7 @@ func Compile(pkgName string, machine llvm.TargetMachine, config *compileopts.Con
} }
} }
return c.mod, extraFiles, c.diagnostics return c.mod, extraFiles, lprogram.LDFlags, c.diagnostics
} }
// getLLVMRuntimeType obtains a named type from the runtime package and returns // getLLVMRuntimeType obtains a named type from the runtime package and returns

4
loader/loader.go

@ -31,6 +31,7 @@ type Program struct {
Dir string // current working directory (for error reporting) Dir string // current working directory (for error reporting)
TINYGOROOT string // root of the TinyGo installation or root of the source code TINYGOROOT string // root of the TinyGo installation or root of the source code
CFlags []string CFlags []string
LDFlags []string
ClangHeaders string ClangHeaders string
} }
@ -425,11 +426,12 @@ func (p *Package) parseFiles(includeTests bool) ([]*ast.File, error) {
if p.ClangHeaders != "" { if p.ClangHeaders != "" {
cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders) cflags = append(cflags, "-Xclang", "-internal-isystem", "-Xclang", p.ClangHeaders)
} }
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags) generated, ldflags, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags)
if errs != nil { if errs != nil {
fileErrs = append(fileErrs, errs...) fileErrs = append(fileErrs, errs...)
} }
files = append(files, generated) files = append(files, generated)
p.LDFlags = append(p.LDFlags, ldflags...)
} }
if len(fileErrs) != 0 { if len(fileErrs) != 0 {
return nil, Errors{p, fileErrs} return nil, Errors{p, fileErrs}

8
src/testing/testing.go

@ -20,10 +20,10 @@ import (
type common struct { type common struct {
output io.Writer output io.Writer
failed bool // Test or benchmark has failed. failed bool // Test or benchmark has failed.
skipped bool // Test of benchmark has been skipped. skipped bool // Test of benchmark has been skipped.
finished bool // Test function has completed. finished bool // Test function has completed.
name string // Name of test or benchmark. name string // Name of test or benchmark.
} }
// TB is the interface common to T and B. // TB is the interface common to T and B.

Loading…
Cancel
Save