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
126 changes: 16 additions & 110 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ import (
"github.com/goplus/llgo/internal/mockable"
"github.com/goplus/llgo/internal/packages"
"github.com/goplus/llgo/internal/typepatch"
llvmTarget "github.com/goplus/llgo/internal/xtool/llvm"
"github.com/goplus/llgo/ssa/abi"
"github.com/goplus/llgo/xtool/clang"
xenv "github.com/goplus/llgo/xtool/env"
"github.com/goplus/llgo/xtool/env/llvm"

Expand Down Expand Up @@ -247,7 +247,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows

output := conf.OutFile != ""
export, err := crosscompile.UseCrossCompileSDK(conf.Goos, conf.Goarch, IsWasiThreadsEnabled())
export, err := crosscompile.Use(conf.Goos, conf.Goarch, IsWasiThreadsEnabled(), IsRpathChangeEnabled())
check(err)
ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool), conf, export}
pkgs, err := buildAllPkgs(ctx, initial, verbose)
Expand Down Expand Up @@ -334,6 +334,15 @@ type context struct {
crossCompile crosscompile.Export
}

func (c *context) compiler() *clang.Cmd {
cmd := c.env.Clang()
if c.crossCompile.CC != "" {
cmd = clang.New(c.crossCompile.CC)
}
cmd.Verbose = c.buildConf.Verbose
return cmd
}

func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) {
pkgs, errPkgs := allPkgs(ctx, initial, verbose)
for _, errPkg := range errPkgs {
Expand Down Expand Up @@ -404,7 +413,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
ctx.nLibdir++
}
}
if err := ctx.env.Clang().CheckLinkArgs(pkgLinkArgs); err != nil {
if err := ctx.compiler().CheckLinkArgs(pkgLinkArgs, isWasmTarget(ctx.buildConf.Goos)); err != nil {
panic(fmt.Sprintf("test link args '%s' failed\n\texpanded to: %v\n\tresolved to: %v\n\terror: %v", param, expdArgs, pkgLinkArgs, err))
}
aPkg.LinkArgs = append(aPkg.LinkArgs, pkgLinkArgs...)
Expand Down Expand Up @@ -434,7 +443,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, conf *Co
} else {
app = filepath.Join(conf.BinPath, name+conf.AppExt)
}
} else if !strings.HasSuffix(app, conf.AppExt) {
} else if filepath.Ext(app) == "" {
app += conf.AppExt
}

Expand Down Expand Up @@ -550,123 +559,23 @@ func compileAndLinkLLFiles(ctx *context, app string, llFiles, linkArgs []string,
buildArgs = append(buildArgs, linkArgs...)

// Add common linker arguments based on target OS and architecture
targetTriple := llvmTarget.GetTargetTriple(ctx.buildConf.Goos, ctx.buildConf.Goarch)
buildArgs = append(buildArgs, buildLdflags(ctx.buildConf.Goos, ctx.buildConf.Goarch, targetTriple)...)

if IsDbgSymsEnabled() {
buildArgs = append(buildArgs, "-gdwarf-4")
}

buildArgs = append(buildArgs, ctx.crossCompile.CCFLAGS...)
buildArgs = append(buildArgs, ctx.crossCompile.LDFLAGS...)
buildArgs = append(buildArgs, ctx.crossCompile.EXTRAFLAGS...)
buildArgs = append(buildArgs, llFiles...)
if verbose {
buildArgs = append(buildArgs, "-v")
}

cmd := ctx.env.Clang()
cmd := ctx.compiler()
cmd.Verbose = verbose
return cmd.Link(buildArgs...)
}

func buildCflags(goos, goarch, targetTriple string) []string {
args := []string{}
if goarch == "wasm" {
args = append(args, "-target", targetTriple)
}
return args
}

// buildLdflags builds the common linker arguments based on target OS and architecture
func buildLdflags(goos, goarch, targetTriple string) []string {
args := []string{
"-target", targetTriple,
"-Wno-override-module",
"-Wl,--error-limit=0",
}
if goos == runtime.GOOS {
// Non-cross-compile
args = append(args,
"-fuse-ld=lld",
"-Wno-override-module",
)
}

switch goos {
case "darwin": // ld64.lld (macOS)
if IsRpathChangeEnabled() {
args = append(
args,
"-rpath", "@loader_path",
"-rpath", "@loader_path/../lib",
)
}
args = append(
args,
"-Xlinker", "-dead_strip",
)
case "windows": // lld-link (Windows)
// TODO(xsw): Add options for Windows.
case "wasi", "wasip1", "js": // wasm-ld (WebAssembly)
args = append(
args,
// "-fdata-sections",
// "-ffunction-sections",
// "-nostdlib",
// "-Wl,--no-entry",
"-Wl,--export-all",
"-Wl,--allow-undefined",
"-Wl,--import-memory,", // unknown import: `env::memory` has not been defined
"-Wl,--export-memory",
"-Wl,--initial-memory=67108864", // 64MB
"-mbulk-memory",
"-mmultimemory",
"-z", "stack-size=10485760", // 10MB
"-Wl,--export=malloc", "-Wl,--export=free",
"-lc",
"-lcrypt",
"-lm",
"-lrt",
"-lutil",
// "-lxnet",
// "-lresolv",
"-lsetjmp",
"-lwasi-emulated-mman",
"-lwasi-emulated-getpid",
"-lwasi-emulated-process-clocks",
"-lwasi-emulated-signal",
"-fwasm-exceptions",
"-mllvm", "-wasm-enable-sjlj",
// "-mllvm", "-wasm-enable-eh", // unreachable error if enabled
// "-mllvm", "-wasm-disable-explicit-locals", // WASM module load failed: type mismatch: expect data but stack was empty if enabled
)
if IsWasiThreadsEnabled() {
args = append(
args,
"-lwasi-emulated-pthread",
"-lpthread",
"-pthread", // global is immutable if -pthread is not specified
// "-matomics", // undefined symbol: __atomic_load
)
}
default: // ld.lld (Unix)
args = append(
args,
// "-rpath", "$ORIGIN",
// "-rpath", "$ORIGIN/../lib",
"-fdata-sections",
"-ffunction-sections",
"-Xlinker",
"--gc-sections",
"-lm",
"-latomic",
"-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions.
)
}

return args
}

func isWasmTarget(goos string) bool {
return slices.Contains([]string{"wasi", "js", "wasip1"}, goos)
}
Expand Down Expand Up @@ -1042,16 +951,13 @@ func clFiles(ctx *context, files string, pkg *packages.Package, procFile func(li

func clFile(ctx *context, args []string, cFile, expFile string, procFile func(linkFile string), verbose bool) {
llFile := expFile + filepath.Base(cFile) + ".ll"
targetTriple := llvmTarget.GetTargetTriple(ctx.buildConf.Goos, ctx.buildConf.Goarch)
cflags := buildCflags(ctx.buildConf.Goos, ctx.buildConf.Goarch, targetTriple)
args = append(cflags, args...)
args = append(args, "-emit-llvm", "-S", "-o", llFile, "-c", cFile)
args = append(args, ctx.crossCompile.CCFLAGS...)
args = append(args, ctx.crossCompile.CFLAGS...)
if verbose {
fmt.Fprintln(os.Stderr, "clang", args)
}
cmd := ctx.env.Clang()
cmd := ctx.compiler()
err := cmd.Compile(args...)
check(err)
procFile(llFile)
Expand Down
140 changes: 133 additions & 7 deletions internal/crosscompile/cosscompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
)

type Export struct {
CCFLAGS []string
CFLAGS []string
LDFLAGS []string
CC string // Compiler to use
CCFLAGS []string
CFLAGS []string
LDFLAGS []string
EXTRAFLAGS []string
}

const wasiSdkUrl = "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-macos.tar.gz"
Expand All @@ -23,44 +25,168 @@
return filepath.Join(env.LLGoCacheDir(), "crosscompile")
}

func UseCrossCompileSDK(goos, goarch string, wasiThreads bool) (export Export, err error) {
func Use(goos, goarch string, wasiThreads, changeRpath bool) (export Export, err error) {
targetTriple := llvm.GetTargetTriple(goos, goarch)

if runtime.GOOS == goos && runtime.GOARCH == goarch {
// not cross compile
// Set up basic flags for non-cross-compile
export.LDFLAGS = []string{
"-target", targetTriple,
"-Wno-override-module",
"-Wl,--error-limit=0",
"-fuse-ld=lld",
"-Wno-override-module",
}

// Add OS-specific flags
switch goos {
case "darwin": // ld64.lld (macOS)
if changeRpath {
export.LDFLAGS = append(
export.LDFLAGS,
"-rpath", "@loader_path",
"-rpath", "@loader_path/../lib",
)
}

Check warning on line 51 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L46-L51

Added lines #L46 - L51 were not covered by tests
export.LDFLAGS = append(
export.LDFLAGS,
"-Xlinker", "-dead_strip",
)
case "windows": // lld-link (Windows)

Check warning on line 56 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L56

Added line #L56 was not covered by tests
// TODO(lijie): Add options for Windows.
default: // ld.lld (Unix)
export.LDFLAGS = append(
export.LDFLAGS,
"-fdata-sections",
"-ffunction-sections",
"-Xlinker",
"--gc-sections",
"-lm",
"-latomic",
"-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions.
)

Check warning on line 68 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L58-L68

Added lines #L58 - L68 were not covered by tests
}
return
}
if goarch != "wasm" {
return
}
if goarch == "wasm" {

// Configure based on GOOS
switch goos {
case "wasip1":
sdkDir := filepath.Join(cacheDir(), llvm.GetTargetTriple(goos, goarch))
if _, err = os.Stat(sdkDir); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return
}

if err = downloadAndExtract(wasiSdkUrl, sdkDir); err != nil {
return
}
}
// WASI-SDK configuration
triple := "wasm32-wasip1"
if wasiThreads {
triple = "wasm32-wasip1-threads"
}
// Set up flags for the SDK

// Set up flags for the WASI-SDK
wasiSdkRoot := filepath.Join(sdkDir, "wasi-sdk-25.0-x86_64-macos")
sysrootDir := filepath.Join(wasiSdkRoot, "share", "wasi-sysroot")
libclangDir := filepath.Join(wasiSdkRoot, "lib", "clang", "19")
includeDir := filepath.Join(sysrootDir, "include", triple)
libDir := filepath.Join(sysrootDir, "lib", triple)

// Use system clang and sysroot of wasi-sdk
// Add compiler flags
export.CCFLAGS = []string{
"-target", targetTriple,
"--sysroot=" + sysrootDir,
"-resource-dir=" + libclangDir,
}
export.CFLAGS = []string{
"-I" + includeDir,
}
// Add WebAssembly linker flags
export.LDFLAGS = []string{
"-target", targetTriple,
"-Wno-override-module",
"-Wl,--error-limit=0",
"-L" + libDir,
"-Wl,--allow-undefined",
"-Wl,--import-memory,", // unknown import: `env::memory` has not been defined
"-Wl,--export-memory",
"-Wl,--initial-memory=67108864", // 64MB
"-mbulk-memory",
"-mmultimemory",
"-z", "stack-size=10485760", // 10MB
"-Wl,--export=malloc", "-Wl,--export=free",
"-lc",
"-lcrypt",
"-lm",
"-lrt",
"-lutil",
"-lsetjmp",
"-lwasi-emulated-mman",
"-lwasi-emulated-getpid",
"-lwasi-emulated-process-clocks",
"-lwasi-emulated-signal",
"-fwasm-exceptions",
"-mllvm", "-wasm-enable-sjlj",
}
// Add thread support if enabled
if wasiThreads {
export.LDFLAGS = append(
export.LDFLAGS,
"-lwasi-emulated-pthread",
"-lpthread",
"-pthread", // global is immutable if -pthread is not specified
)
}

Check warning on line 147 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L141-L147

Added lines #L141 - L147 were not covered by tests

case "js":
targetTriple := "wasm32-unknown-emscripten"
// Emscripten configuration using system installation
// Specify emcc as the compiler
export.CC = "emcc"
// Add compiler flags
export.CCFLAGS = []string{
"-target", targetTriple,
}
export.CFLAGS = []string{}
// Add WebAssembly linker flags for Emscripten
export.LDFLAGS = []string{
"-target", targetTriple,
"-Wno-override-module",
"-Wl,--error-limit=0",
"-s", "ALLOW_MEMORY_GROWTH=1",
"-Wl,--allow-undefined",
// "-Wl,--import-memory,",
// "-Wl,--export-memory",
// "-Wl,--initial-memory=67108864", // 64MB
// "-mbulk-memory",
// "-mmultimemory",
// "-z", "stack-size=10485760", // 10MB
// "-Wl,--export=malloc", "-Wl,--export=free",

Check warning on line 172 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L149-L172

Added lines #L149 - L172 were not covered by tests
}
export.EXTRAFLAGS = []string{
"-sENVIRONMENT=web",
"-DPLATFORM_WEB",
"-sEXPORT_KEEPALIVE=1",
"-sEXPORT_ES6=1",
"-sALLOW_MEMORY_GROWTH=1",
"-sEXPORTED_RUNTIME_METHODS=cwrap,allocateUTF8,stringToUTF8,UTF8ToString,FS,setValue,getValue",
"-sWASM=1",
"-sEXPORT_ALL=1",
"-sASYNCIFY=1",
"-sSTACK_SIZE=5242880", // 50MB
}

Check warning on line 185 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L174-L185

Added lines #L174 - L185 were not covered by tests

default:
err = errors.New("unsupported GOOS for WebAssembly: " + goos)

Check warning on line 188 in internal/crosscompile/cosscompile.go

View check run for this annotation

Codecov / codecov/patch

internal/crosscompile/cosscompile.go#L187-L188

Added lines #L187 - L188 were not covered by tests
return
}
// TODO(lijie): supports other platforms
return
}
Loading
Loading