diff --git a/compiler/compiler.go b/compiler/compiler.go index dd7e3d5e..27ee530b 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -41,6 +41,7 @@ type Config struct { PanicStrategy string // panic strategy ("abort" or "trap") CFlags []string // cflags to pass to cgo LDFlags []string // ldflags to pass to cgo + ClangHeaders string // Clang built-in header include path DumpSSA bool // dump Go SSA, for compiler debugging Debug bool // add debug symbols for gdb GOROOT string // GOROOT @@ -235,9 +236,10 @@ func (c *Compiler) Compile(mainPath string) []error { MaxAlign: int64(c.targetData.PrefTypeAlignment(c.i8ptrType)), }, }, - Dir: wd, - TINYGOROOT: c.TINYGOROOT, - CFlags: c.CFlags, + Dir: wd, + TINYGOROOT: c.TINYGOROOT, + CFlags: c.CFlags, + ClangHeaders: c.ClangHeaders, } if strings.HasSuffix(mainPath, ".go") { _, err = lprogram.ImportFile(mainPath) diff --git a/loader/loader.go b/loader/loader.go index 7b20a925..cfa81e99 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -26,6 +26,7 @@ type Program struct { Dir string // current working directory (for error reporting) TINYGOROOT string // root of the TinyGo installation or root of the source code CFlags []string + ClangHeaders string } // Package holds a loaded package, its imports, and its parsed files. @@ -306,15 +307,11 @@ func (p *Package) parseFiles() ([]*ast.File, error) { files = append(files, f) } if len(p.CgoFiles) != 0 { - clangIncludes := "" - if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) { - // Running from the source directory. - clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers") - } else { - // Running from the installation directory. - clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include") + cflags := append(p.CFlags, "-I"+p.Package.Dir) + if p.ClangHeaders != "" { + cflags = append(cflags, "-I"+p.ClangHeaders) } - generated, errs := cgo.Process(files, p.Program.Dir, p.fset, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes)) + generated, errs := cgo.Process(files, p.Program.Dir, p.fset, cflags) if errs != nil { fileErrs = append(fileErrs, errs...) } diff --git a/main.go b/main.go index ee4eca3c..4eaa359c 100644 --- a/main.go +++ b/main.go @@ -101,6 +101,7 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act PanicStrategy: config.panicStrategy, CFlags: cflags, LDFlags: ldflags, + ClangHeaders: getClangHeaderPath(root), Debug: config.debug, DumpSSA: config.dumpSSA, TINYGOROOT: root, diff --git a/target.go b/target.go index 418c18cc..d732e716 100644 --- a/target.go +++ b/target.go @@ -401,3 +401,51 @@ func getGorootVersion(goroot string) (major, minor int, err error) { } return } + +// getClangHeaderPath returns the path to the built-in Clang headers. It tries +// multiple locations, which should make it find the directory when installed in +// various ways. +func getClangHeaderPath(TINYGOROOT string) string { + // Check whether we're running from the source directory. + path := filepath.Join(TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers") + if _, err := os.Stat(path); !os.IsNotExist(err) { + return path + } + + // Check whether we're running from the installation directory. + path = filepath.Join(TINYGOROOT, "lib", "clang", "include") + if _, err := os.Stat(path); !os.IsNotExist(err) { + return path + } + + // It looks like we are built with a system-installed LLVM. Do a last + // attempt: try to use Clang headers relative to the clang binary. + for _, cmdName := range commands["clang"] { + binpath, err := exec.LookPath(cmdName) + if err == nil { + // This should be the command that will also be used by + // execCommand. To avoid inconsistencies, make sure we use the + // headers relative to this command. + binpath, err = filepath.EvalSymlinks(binpath) + if err != nil { + // Unexpected. + return "" + } + // Example executable: + // /usr/lib/llvm-8/bin/clang + // Example include path: + // /usr/lib/llvm-8/lib/clang/8.0.1/include/ + llvmRoot := filepath.Dir(filepath.Dir(binpath)) + clangVersionRoot := filepath.Join(llvmRoot, "lib", "clang") + dirnames, err := ioutil.ReadDir(clangVersionRoot) + if err != nil || len(dirnames) != 1 { + // Unexpected. + return "" + } + return filepath.Join(clangVersionRoot, dirnames[0].Name(), "include") + } + } + + // Could not find it. + return "" +}