diff --git a/internal/build/build.go b/internal/build/build.go index 5e161979..32345acd 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -34,6 +34,8 @@ import ( "github.com/goplus/llgo/cl" "github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/xtool/clang" + clangCheck "github.com/goplus/llgo/xtool/clang/check" + "github.com/goplus/llgo/xtool/env" llssa "github.com/goplus/llgo/ssa" ) @@ -207,11 +209,33 @@ func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, ve pkg.ExportFile = "" } if kind == cl.PkgLinkExtern { // need to be linked with external library - linkFile := os.ExpandEnv(strings.TrimSpace(param)) - dir, lib := filepath.Split(linkFile) - command := " -l " + lib - if dir != "" { - command += " -L " + dir[:len(dir)-1] + // format: ';' separated alternative link methods. e.g. + // link: $LLGO_LIB_PYTHON; $(pkg-config --libs python3-embed); -lpython3 + expd := "" + altParts := strings.Split(param, ";") + for _, param := range altParts { + expd = strings.TrimSpace(env.ExpandEnv(strings.TrimSpace(param))) + if len(expd) > 0 { + break + } + } + if expd == "" { + panic(fmt.Sprintf("'%s' cannot locate the external library", param)) + } + + command := "" + if expd[0] == '-' { + command += " " + expd + } else { + linkFile := expd + dir, lib := filepath.Split(linkFile) + command = " -l " + lib + if dir != "" { + command += " -L " + dir[:len(dir)-1] + } + } + if err := clangCheck.CheckLinkArgs(command); err != nil { + panic(fmt.Sprintf("test link args '%s' failed\n\texpanded to: %s\n\tresolved to: %v\n\terror: %v", param, expd, command, err)) } if isSingleLinkFile(pkg.ExportFile) { pkg.ExportFile = command + " " + pkg.ExportFile diff --git a/py/python.go b/py/python.go index 4b9231e6..78330505 100644 --- a/py/python.go +++ b/py/python.go @@ -23,7 +23,7 @@ import ( ) const ( - LLGoPackage = "link: $LLGO_LIB_PYTHON" + LLGoPackage = "link: $LLGO_LIB_PYTHON; $(pkg-config --libs python3-embed)" ) // ----------------------------------------------------------------------------- diff --git a/xtool/clang/check/check.go b/xtool/clang/check/check.go new file mode 100644 index 00000000..a740a519 --- /dev/null +++ b/xtool/clang/check/check.go @@ -0,0 +1,26 @@ +package check + +import ( + "errors" + "os/exec" + "runtime" + "strings" +) + +func CheckLinkArgs(args string) error { + cmdArgs := strings.Split(args, " ") + cmd := exec.Command("clang") + nul := "/dev/null" + if runtime.GOOS == "windows" { + nul = "NUL" + } + cmd.Args = append(cmd.Args, cmdArgs...) + cmd.Args = append(cmd.Args, "-x", "c", "-o", nul, "-") + src := "int main() {return 0;}" + srcIn := strings.NewReader(src) + cmd.Stdin = srcIn + if out, err := cmd.CombinedOutput(); err != nil { + return errors.New(string(out)) + } + return nil +} diff --git a/xtool/env/env.go b/xtool/env/env.go new file mode 100644 index 00000000..803bdd3e --- /dev/null +++ b/xtool/env/env.go @@ -0,0 +1,32 @@ +package env + +import ( + "os" + "os/exec" + "regexp" + "runtime" + "strings" +) + +func ExpandEnv(s string) string { + return expandEnvWithCmd(s) +} + +func expandEnvWithCmd(s string) string { + re := regexp.MustCompile(`\$\(([^)]+)\)`) + expanded := re.ReplaceAllStringFunc(s, func(m string) string { + cmd := re.FindStringSubmatch(m)[1] + var out []byte + var err error + if runtime.GOOS == "windows" { + out, err = exec.Command("cmd", "/C", cmd).Output() + } else { + out, err = exec.Command("sh", "-c", cmd).Output() + } + if err != nil { + return "" + } + return strings.TrimSpace(string(out)) + }) + return os.Expand(expanded, os.Getenv) +}