Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions chore/gentests/gentests.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import (
"github.com/goplus/llgo/internal/littest"
"github.com/goplus/llgo/internal/llgen"
"github.com/goplus/llgo/internal/lto"
"github.com/goplus/llgo/xtool/env/llvm"
"github.com/goplus/mod"
)

func main() {
llvm.SetupPath()
dir, _, err := mod.FindGoMod(".")
check(err)

Expand Down
2 changes: 2 additions & 0 deletions chore/litgen/litgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import (
"path/filepath"

"github.com/goplus/llgo/internal/littest"
"github.com/goplus/llgo/xtool/env/llvm"
)

func main() {
llvm.SetupPath()
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s <file-or-dir> [<file-or-dir>...]\n", filepath.Base(os.Args[0]))
flag.PrintDefaults()
Expand Down
2 changes: 2 additions & 0 deletions chore/llgen/llgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/llgen"
"github.com/goplus/llgo/xtool/env/llvm"
)

var (
Expand All @@ -31,6 +32,7 @@ var (
)

func main() {
llvm.SetupPath()
flag.Parse()
if len(flag.Args()) != 1 {
fmt.Fprintln(os.Stderr, "Usage: llgen [flags] <pkg>")
Expand Down
10 changes: 10 additions & 0 deletions cmd/llgo/llvm_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import "github.com/goplus/llgo/xtool/env/llvm"

// LLVM is part of the llgo process environment. Prepare PATH before command
// dispatch so build requests and their workers only need an environment
// snapshot; they do not carry or reselect an LLVM installation.
func init() {
llvm.SetupPath()
}
160 changes: 111 additions & 49 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import (
"github.com/goplus/llgo/internal/typepatch"
"github.com/goplus/llgo/ssa/abi"
xenv "github.com/goplus/llgo/xtool/env"
"github.com/goplus/llgo/xtool/env/llvm"
gllvm "github.com/xgo-dev/llvm"

llruntime "github.com/goplus/llgo/runtime"
Expand Down Expand Up @@ -213,6 +212,78 @@ type Config struct {

type Rewrites map[string]string

// clone returns an independent copy of c for use by a single build. Do
// resolves defaults and target-specific values on this copy so callers can
// safely reuse their input configuration after Do returns.
func (c *Config) clone() *Config {
if c == nil {
return nil
}
cloned := *c
cloned.RunArgs = slices.Clone(c.RunArgs)
cloned.GoBuildFlags = slices.Clone(c.GoBuildFlags)
cloned.Overlay = cloneOverlay(c.Overlay)
if c.GlobalRewrites != nil {
cloned.GlobalRewrites = make(map[string]Rewrites, len(c.GlobalRewrites))
for pkgPath, rewrites := range c.GlobalRewrites {
if rewrites == nil {
cloned.GlobalRewrites[pkgPath] = nil
continue
}
copied := make(Rewrites, len(rewrites))
for name, value := range rewrites {
copied[name] = value
}
cloned.GlobalRewrites[pkgPath] = copied
}
}
return &cloned
}

// resolveBuildConfig validates and fills build-local defaults without
// modifying the caller's Config. Target-derived GOOS/GOARCH values are
// resolved later, after crosscompile.Use has selected the toolchain.
func resolveBuildConfig(input *Config) (*Config, error) {
if input == nil {
return nil, errors.New("build config must not be nil")
}
conf := input.clone()
if conf.Goos == "" {
conf.Goos = runtime.GOOS
}
if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH
}
if conf.AppExt == "" {
conf.AppExt = defaultAppExt(conf)
}
if conf.BuildMode == "" {
conf.BuildMode = BuildModeExe
}
if conf.BuildMode != BuildModeExe {
conf.DeadcodeDrop = false
}
conf.PCLNMode = effectivePCLNMode(conf)
conf.PCLNModeSet = true
if conf.SizeReport && conf.SizeFormat == "" {
conf.SizeFormat = "text"
}
if conf.SizeReport && conf.SizeLevel == "" {
conf.SizeLevel = "module"
}
if err := validatePCLNMode(conf); err != nil {
return nil, err
}
if err := ensureSizeReporting(conf); err != nil {
return nil, err
}
if err := conf.LinkOptions.validate(); err != nil {
return nil, err
}
conf.OptLevel = effectiveOptLevel(conf)
return conf, nil
}

func NewDefaultConf(mode Mode) *Config {
bin := os.Getenv("GOBIN")
if bin == "" {
Expand All @@ -222,9 +293,6 @@ func NewDefaultConf(mode Mode) *Config {
}
bin = filepath.Join(gopath, "bin")
}
if err := os.MkdirAll(bin, 0755); err != nil {
panic(fmt.Errorf("cannot create bin directory: %v", err))
}
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
if goos == "" {
goos = runtime.GOOS
Expand Down Expand Up @@ -292,39 +360,25 @@ const (
)

func Do(args []string, conf *Config) ([]Package, error) {
if conf.Goos == "" {
conf.Goos = runtime.GOOS
}
if conf.Goarch == "" {
conf.Goarch = runtime.GOARCH
}
if conf.AppExt == "" {
conf.AppExt = defaultAppExt(conf)
}
if conf.BuildMode == "" {
conf.BuildMode = BuildModeExe
}
if conf.BuildMode != BuildModeExe {
conf.DeadcodeDrop = false
}
conf.PCLNMode = effectivePCLNMode(conf)
conf.PCLNModeSet = true
if conf.SizeReport && conf.SizeFormat == "" {
conf.SizeFormat = "text"
}
if conf.SizeReport && conf.SizeLevel == "" {
conf.SizeLevel = "module"
}
if err := validatePCLNMode(conf); err != nil {
return nil, err
}
if err := ensureSizeReporting(conf); err != nil {
return nil, err
return Build(Invocation{Args: args, Config: conf})
}

// Build executes one build invocation.
func Build(inv Invocation) ([]Package, error) {
dir := inv.Dir
if dir == "" {
var err error
dir, err = os.Getwd()
if err != nil {
return nil, err
}
}
if err := conf.LinkOptions.validate(); err != nil {
environ := os.Environ()
commands := commandEnv{dir: dir, environ: environ}
conf, err := resolveBuildConfig(inv.Config)
if err != nil {
return nil, err
}
conf.OptLevel = effectiveOptLevel(conf)
// Handle crosscompile configuration first to set correct GOOS/GOARCH
forceEspClang := conf.ForceEspClang || conf.Target != ""
export, err := crosscompile.Use(conf.Goos, conf.Goarch, conf.Target, IsWasiThreadsEnabled(), forceEspClang, conf.OptLevel, conf.ltoMode(), conf.goGlobalDCEEnabled())
Expand All @@ -348,7 +402,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
}

verbose := conf.Verbose
patterns := args
patterns := slices.Clone(inv.Args)
tags := defaultBuildTags(conf.Goarch, conf.Target)
if conf.PCLNMode == PCLNExternal {
// Select the optional runtime loader as part of the normal package
Expand All @@ -370,9 +424,10 @@ func Do(args []string, conf *Config) ([]Package, error) {
cfg := &packages.Config{
Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile,
BuildFlags: goBuildFlags,
Dir: dir,
Fset: token.NewFileSet(),
Tests: conf.Mode == ModeTest,
Env: append(slices.Clone(os.Environ()), "GOOS="+conf.Goos, "GOARCH="+conf.Goarch),
Env: withEnv(environ, "GOOS="+conf.Goos, "GOARCH="+conf.Goarch),
}
if conf.Mode == ModeTest {
cfg.Mode |= packages.NeedForTest
Expand Down Expand Up @@ -546,11 +601,8 @@ func Do(args []string, conf *Config) ([]Package, error) {
patches := make(cl.Patches, len(altPkgPaths))
altSSAPkgs(progSSA, patches, altPkgs[1:], conf, verbose)

env := llvm.New("")
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows

output := conf.OutFile != ""
ctx := &context{env: env, conf: cfg, progSSA: progSSA, prog: prog, dedup: dedup,
ctx := &context{conf: cfg, progSSA: progSSA, prog: prog, dedup: dedup,
patches: patches, callerTracking: cl.NewCallerTracking(),
built: make(map[string]none), initial: initial, mode: mode,
fingerprinting: make(map[string]bool),
Expand All @@ -560,6 +612,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
passOpt: passOpt,
buildConf: conf,
crossCompile: export,
commands: commands,
cTransformer: cabi.NewTransformer(prog, export.LLVMTarget, export.TargetABI, conf.AbiMode, cabiOptimize),
}
defer ctx.closePackageMetas()
Expand Down Expand Up @@ -601,6 +654,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
if err != nil {
return nil, err
}
resolveOutputs(ctx.commands.dir, outFmts)

// Link main package using the output path from buildOutFmts
err = linkMainPkg(ctx, pkg, allPkgs, outFmts.Out, verbose)
Expand Down Expand Up @@ -656,7 +710,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
if conf.Target == "" {
err = runNative(ctx, outFmts.Out, pkg.Dir, pkg.PkgPath, conf, mode)
} else if conf.Emulator {
err = runInEmulator(ctx.crossCompile.Emulator, envMap, pkg.Dir, pkg.PkgPath, conf, mode, verbose)
err = runInEmulator(ctx.commands, ctx.crossCompile.Emulator, envMap, pkg.Dir, pkg.PkgPath, conf, mode, verbose)
} else {
err = flash.FlashDevice(ctx.crossCompile.Device, envMap, ctx.buildConf.Port, verbose)
if err != nil {
Expand Down Expand Up @@ -798,7 +852,6 @@ const (
)

type context struct {
env *llvm.Env
conf *packages.Config
progSSA *ssa.Program
prog llssa.Program
Expand All @@ -817,6 +870,7 @@ type context struct {

buildConf *Config
crossCompile crosscompile.Export
commands commandEnv

cTransformer *cabi.Transformer

Expand Down Expand Up @@ -860,6 +914,8 @@ func (c *context) compiler() *clang.Cmd {
c.crossCompile.Linker,
)
cmd := clang.NewCompiler(config)
cmd.Dir = c.commands.dir
cmd.Env = slices.Clone(c.commands.environ)
cmd.Verbose = c.shouldPrintCommands(false)
return cmd
}
Expand All @@ -873,6 +929,8 @@ func (c *context) linker() *clang.Cmd {
c.crossCompile.Linker,
)
cmd := clang.NewLinker(config)
cmd.Dir = c.commands.dir
cmd.Env = slices.Clone(c.commands.environ)
cmd.Verbose = c.shouldPrintCommands(false)
return cmd
}
Expand Down Expand Up @@ -1394,6 +1452,11 @@ func isRuntimePkg(pkgPath string) bool {

func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose bool) error {
printCmds := ctx.shouldPrintCommands(verbose)
if dir := filepath.Dir(app); dir != "." {
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("create output directory %s: %w", dir, err)
}
}
// Handle c-archive mode differently - use ar tool instead of linker
if ctx.buildConf.BuildMode == BuildModeCArchive {
return ctx.createMergedArchiveFile(app, objFiles, printCmds)
Expand Down Expand Up @@ -1597,7 +1660,7 @@ func (c *context) createMergedArchiveFile(archivePath string, inputs []string, v
if err != nil {
return err
}
cmd := exec.Command(arCmd, "-M")
cmd := c.commands.configure(exec.Command(arCmd, "-M"))
cmd.Stdin = strings.NewReader(script.String())
printCmds := c.shouldPrintCommands(len(verbose) > 0 && verbose[0])
if printCmds {
Expand Down Expand Up @@ -1636,7 +1699,7 @@ func (c *context) createArchiveFile(archivePath string, objFiles []string, verbo

args := append([]string{"rcs", tmpName}, objFiles...)
arCmd := c.archiver()
cmd := exec.Command(arCmd, args...)
cmd := c.commands.configure(exec.Command(arCmd, args...))
printCmds := c.shouldPrintCommands(len(verbose) > 0 && verbose[0])
if printCmds {
fmt.Fprintf(os.Stderr, "%s %s\n", filepath.Base(arCmd), strings.Join(args, " "))
Expand Down Expand Up @@ -1843,7 +1906,7 @@ func dumpLLVMIRIfNeeded(ctx *context, pkgPath string, exportFile string, data st
return err
}
if ctx.buildConf.CheckLLFiles {
if msg, err := llcCheck(ctx.env, f.Name()); err != nil {
if msg, err := llcCheck(ctx.commands, f.Name()); err != nil {
fmt.Fprintf(os.Stderr, "==> llc %v: %v\n%v\n", pkgPath, f.Name(), msg)
}
}
Expand Down Expand Up @@ -1927,7 +1990,7 @@ func exportObjectWithClang(ctx *context, pkgPath string, exportFile string, data
return exportFile, err
}
if ctx.buildConf.CheckLLFiles {
if msg, err := llcCheck(ctx.env, f.Name()); err != nil {
if msg, err := llcCheck(ctx.commands, f.Name()); err != nil {
fmt.Fprintf(os.Stderr, "==> llc %v: %v\n%v\n", pkgPath, f.Name(), msg)
}
}
Expand Down Expand Up @@ -1955,9 +2018,8 @@ func exportObjectWithClang(ctx *context, pkgPath string, exportFile string, data
return objFile.Name(), cmd.Compile(args...)
}

func llcCheck(env *llvm.Env, exportFile string) (msg string, err error) {
bin := filepath.Join(env.BinDir(), "llc")
cmd := exec.Command(bin, "-filetype=null", exportFile)
func llcCheck(commands commandEnv, exportFile string) (msg string, err error) {
cmd := commands.configure(exec.Command("llc", "-filetype=null", exportFile))
var buf bytes.Buffer
cmd.Stderr = &buf
if err = cmd.Run(); err != nil {
Expand Down
Loading
Loading