mirror of
https://github.com/goplus/llgo.git
synced 2025-09-26 19:51:21 +08:00
652 lines
19 KiB
Go
652 lines
19 KiB
Go
package crosscompile
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/goplus/llgo/internal/crosscompile/compile"
|
|
"github.com/goplus/llgo/internal/env"
|
|
"github.com/goplus/llgo/internal/flash"
|
|
"github.com/goplus/llgo/internal/targets"
|
|
"github.com/goplus/llgo/internal/xtool/llvm"
|
|
envllvm "github.com/goplus/llgo/xtool/env/llvm"
|
|
)
|
|
|
|
type Export struct {
|
|
CC string // Compiler to use
|
|
CCFLAGS []string
|
|
CFLAGS []string
|
|
LDFLAGS []string
|
|
|
|
// Additional fields from target configuration
|
|
BuildTags []string
|
|
GOOS string
|
|
GOARCH string
|
|
Libc string
|
|
Linker string // Linker to use (e.g., "ld.lld", "avr-ld")
|
|
ExtraFiles []string // Extra files to compile and link (e.g., .s, .c files)
|
|
ClangRoot string // Root directory of custom clang installation
|
|
ClangBinPath string // Path to clang binary directory
|
|
|
|
BinaryFormat string // Binary format (e.g., "elf", "esp", "uf2")
|
|
FormatDetail string // For uf2, it's uf2FamilyID
|
|
Emulator string // Emulator command template (e.g., "qemu-system-arm -M {} -kernel {}")
|
|
|
|
// Flashing/Debugging configuration
|
|
Device flash.Device // Device configuration for flashing/debugging
|
|
}
|
|
|
|
// URLs and configuration that can be overridden for testing
|
|
var (
|
|
wasiSdkUrl = "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-macos.tar.gz"
|
|
wasiMacosSubdir = "wasi-sdk-25.0-x86_64-macos"
|
|
)
|
|
|
|
var (
|
|
espClangBaseUrl = "https://github.com/goplus/espressif-llvm-project-prebuilt/releases/download/19.1.2_20250905-3"
|
|
espClangVersion = "19.1.2_20250905-3"
|
|
)
|
|
|
|
// cacheRoot can be overridden for testing
|
|
var cacheRoot = env.LLGoCacheDir
|
|
|
|
func cacheDir() string {
|
|
return filepath.Join(cacheRoot(), "crosscompile")
|
|
}
|
|
|
|
// buildEnvMap creates a map of template variables for the current context
|
|
func buildEnvMap(llgoRoot string) map[string]string {
|
|
envs := make(map[string]string)
|
|
|
|
// Basic paths
|
|
envs["root"] = llgoRoot
|
|
envs["tmpDir"] = os.TempDir()
|
|
|
|
// These will typically be set by calling code when actual values are known
|
|
// envs["port"] = "" // Serial port (e.g., "/dev/ttyUSB0", "COM3")
|
|
// envs["hex"] = "" // Path to hex file
|
|
// envs["bin"] = "" // Path to binary file
|
|
// envs["img"] = "" // Path to image file
|
|
// envs["zip"] = "" // Path to zip file
|
|
|
|
return envs
|
|
}
|
|
|
|
// getCanonicalArchName returns the canonical architecture name for a target triple
|
|
func getCanonicalArchName(triple string) string {
|
|
arch := strings.Split(triple, "-")[0]
|
|
if arch == "arm64" {
|
|
return "aarch64"
|
|
}
|
|
if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") {
|
|
return "arm"
|
|
}
|
|
if arch == "mipsel" {
|
|
return "mips"
|
|
}
|
|
return arch
|
|
}
|
|
|
|
// getMacOSSysroot returns the macOS SDK path using xcrun
|
|
func getMacOSSysroot() (string, error) {
|
|
cmd := exec.Command("xcrun", "--sdk", "macosx", "--show-sdk-path")
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(string(output)), nil
|
|
}
|
|
|
|
// getESPClangRoot returns the ESP Clang root directory, checking LLGoROOT first,
|
|
// then downloading if needed and platform is supported
|
|
func getESPClangRoot(forceEspClang bool) (clangRoot string, err error) {
|
|
llgoRoot := env.LLGoROOT()
|
|
|
|
// First check if clang exists in LLGoROOT
|
|
espClangRoot := filepath.Join(llgoRoot, envllvm.CrosscompileClangPath)
|
|
if _, err = os.Stat(espClangRoot); err == nil {
|
|
clangRoot = espClangRoot
|
|
return
|
|
}
|
|
|
|
if !forceEspClang {
|
|
return "", nil
|
|
}
|
|
|
|
// Try to download ESP Clang if platform is supported
|
|
platformSuffix := getESPClangPlatform(runtime.GOOS, runtime.GOARCH)
|
|
if platformSuffix != "" {
|
|
cacheClangDir := filepath.Join(cacheRoot(), "crosscompile", "esp-clang-"+espClangVersion)
|
|
if _, err = os.Stat(cacheClangDir); err != nil {
|
|
if !errors.Is(err, fs.ErrNotExist) {
|
|
return
|
|
}
|
|
fmt.Fprintln(os.Stderr, "ESP Clang not found in LLGO_ROOT or cache, will download.")
|
|
if err = checkDownloadAndExtractESPClang(platformSuffix, cacheClangDir); err != nil {
|
|
return
|
|
}
|
|
}
|
|
clangRoot = cacheClangDir
|
|
return
|
|
}
|
|
|
|
err = fmt.Errorf("ESP Clang not found in LLGoROOT and platform %s/%s is not supported for download", runtime.GOOS, runtime.GOARCH)
|
|
return
|
|
}
|
|
|
|
// getESPClangPlatform returns the platform suffix for ESP Clang downloads
|
|
func getESPClangPlatform(goos, goarch string) string {
|
|
switch goos {
|
|
case "darwin":
|
|
switch goarch {
|
|
case "amd64":
|
|
return "x86_64-apple-darwin"
|
|
case "arm64":
|
|
return "aarch64-apple-darwin"
|
|
}
|
|
case "linux":
|
|
switch goarch {
|
|
case "amd64":
|
|
return "x86_64-linux-gnu"
|
|
case "arm64":
|
|
return "aarch64-linux-gnu"
|
|
case "arm":
|
|
return "arm-linux-gnueabihf"
|
|
}
|
|
case "windows":
|
|
switch goarch {
|
|
case "amd64":
|
|
return "x86_64-w64-mingw32"
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// ldFlagsFromFileName extracts the library name from a filename for use in linker flags
|
|
// For example, "libmath.a" becomes "math" for use with "-lmath"
|
|
func ldFlagsFromFileName(fileName string) string {
|
|
return strings.TrimPrefix(strings.TrimSuffix(fileName, ".a"), "lib")
|
|
}
|
|
|
|
// compileWithConfig compiles libraries according to the provided configuration
|
|
// and returns the necessary linker flags for linking against the compiled libraries
|
|
func compileWithConfig(
|
|
compileConfig compile.CompileConfig,
|
|
outputDir string, options compile.CompileOptions,
|
|
) (ldflags []string, err error) {
|
|
ldflags = append(ldflags, "-nostdlib", "-L"+outputDir)
|
|
|
|
for _, group := range compileConfig.Groups {
|
|
err = group.Compile(outputDir, options)
|
|
if err != nil {
|
|
break
|
|
}
|
|
if filepath.Ext(group.OutputFileName) == ".o" {
|
|
continue
|
|
}
|
|
ldflags = append(ldflags, "-l"+ldFlagsFromFileName(group.OutputFileName))
|
|
}
|
|
return
|
|
}
|
|
|
|
func use(goos, goarch string, wasiThreads, forceEspClang bool) (export Export, err error) {
|
|
targetTriple := llvm.GetTargetTriple(goos, goarch)
|
|
llgoRoot := env.LLGoROOT()
|
|
|
|
// Check for ESP Clang support for target-based builds
|
|
clangRoot, err := getESPClangRoot(forceEspClang)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Set ClangRoot and CC if clang is available
|
|
export.ClangRoot = clangRoot
|
|
if clangRoot != "" {
|
|
export.CC = filepath.Join(clangRoot, "bin", "clang++")
|
|
} else {
|
|
export.CC = "clang++"
|
|
}
|
|
|
|
if runtime.GOOS == goos && runtime.GOARCH == goarch {
|
|
// not cross compile
|
|
// Set up basic flags for non-cross-compile
|
|
export.LDFLAGS = []string{
|
|
"-target", targetTriple,
|
|
"-Qunused-arguments",
|
|
"-Wno-unused-command-line-argument",
|
|
"-Wl,--error-limit=0",
|
|
"-fuse-ld=lld",
|
|
}
|
|
if clangRoot != "" {
|
|
clangLib := filepath.Join(clangRoot, "lib")
|
|
clangInc := filepath.Join(clangRoot, "include")
|
|
export.CFLAGS = append(export.CFLAGS, "-I"+clangInc)
|
|
export.LDFLAGS = append(export.LDFLAGS, "-L"+clangLib)
|
|
// Add platform-specific rpath flags
|
|
switch goos {
|
|
case "darwin":
|
|
export.LDFLAGS = append(export.LDFLAGS, "-Wl,-rpath,"+clangLib)
|
|
case "linux":
|
|
export.LDFLAGS = append(export.LDFLAGS, "-Wl,-rpath,"+clangLib)
|
|
case "windows":
|
|
// Windows doesn't support rpath, DLLs should be in PATH or same directory
|
|
default:
|
|
// For other Unix-like systems, try the generic rpath
|
|
export.LDFLAGS = append(export.LDFLAGS, "-Wl,-rpath,"+clangLib)
|
|
}
|
|
}
|
|
export.CCFLAGS = []string{
|
|
"-Qunused-arguments",
|
|
"-Wno-unused-command-line-argument",
|
|
}
|
|
|
|
// Add sysroot for macOS only
|
|
if goos == "darwin" {
|
|
sysrootPath, sysrootErr := getMacOSSysroot()
|
|
if sysrootErr != nil {
|
|
err = fmt.Errorf("failed to get macOS SDK path: %w", sysrootErr)
|
|
return
|
|
}
|
|
export.CCFLAGS = append(export.CCFLAGS, []string{"--sysroot=" + sysrootPath}...)
|
|
export.LDFLAGS = append(export.LDFLAGS, []string{"--sysroot=" + sysrootPath}...)
|
|
}
|
|
|
|
// Add OS-specific flags
|
|
switch goos {
|
|
case "darwin": // ld64.lld (macOS)
|
|
export.LDFLAGS = append(
|
|
export.LDFLAGS,
|
|
"-Xlinker", "-dead_strip",
|
|
)
|
|
case "windows": // lld-link (Windows)
|
|
// TODO(lijie): Add options for Windows.
|
|
default: // ld.lld (Unix)
|
|
export.CCFLAGS = append(
|
|
export.CCFLAGS,
|
|
"-fdata-sections",
|
|
"-ffunction-sections",
|
|
)
|
|
export.LDFLAGS = append(
|
|
export.LDFLAGS,
|
|
"-fdata-sections",
|
|
"-ffunction-sections",
|
|
"-Xlinker",
|
|
"--gc-sections",
|
|
"-lm",
|
|
"-latomic",
|
|
// libpthread & libdl is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions.
|
|
"-lpthread",
|
|
"-ldl",
|
|
)
|
|
}
|
|
return
|
|
}
|
|
if goarch != "wasm" {
|
|
return
|
|
}
|
|
|
|
// Configure based on GOOS
|
|
switch goos {
|
|
case "wasip1":
|
|
// Set wasiSdkRoot path
|
|
wasiSdkRoot := filepath.Join(llgoRoot, "crosscompile", "wasi-libc")
|
|
|
|
// If not exists in LLGoROOT, download and use cached wasiSdkRoot
|
|
if _, err = os.Stat(wasiSdkRoot); err != nil {
|
|
sdkDir := filepath.Join(cacheDir(), llvm.GetTargetTriple(goos, goarch))
|
|
if wasiSdkRoot, err = checkDownloadAndExtractWasiSDK(sdkDir); err != nil {
|
|
return
|
|
}
|
|
}
|
|
// WASI-SDK configuration
|
|
triple := "wasm32-wasip1"
|
|
if wasiThreads {
|
|
triple = "wasm32-wasip1-threads"
|
|
}
|
|
|
|
// Set up flags for the WASI-SDK or wasi-libc
|
|
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,
|
|
"-matomics",
|
|
"-mbulk-memory",
|
|
}
|
|
export.CFLAGS = []string{
|
|
"-I" + includeDir,
|
|
"-Qunused-arguments",
|
|
"-Wno-unused-command-line-argument",
|
|
}
|
|
// Add WebAssembly linker flags
|
|
export.LDFLAGS = append(export.LDFLAGS, export.CCFLAGS...)
|
|
export.LDFLAGS = append(export.LDFLAGS, []string{
|
|
"-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.CCFLAGS = append(
|
|
export.CCFLAGS,
|
|
"-pthread",
|
|
)
|
|
export.LDFLAGS = append(export.LDFLAGS, export.CCFLAGS...)
|
|
export.LDFLAGS = append(
|
|
export.LDFLAGS,
|
|
"-lwasi-emulated-pthread",
|
|
"-lpthread",
|
|
)
|
|
}
|
|
|
|
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,
|
|
"-Qunused-arguments",
|
|
"-Wno-unused-command-line-argument",
|
|
}
|
|
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",
|
|
}
|
|
export.LDFLAGS = append(export.LDFLAGS, []string{
|
|
"-sENVIRONMENT=web,worker",
|
|
"-DPLATFORM_WEB",
|
|
"-sEXPORT_KEEPALIVE=1",
|
|
"-sEXPORT_ES6=1",
|
|
"-sALLOW_MEMORY_GROWTH=1",
|
|
"-sRESERVED_FUNCTION_POINTERS=1",
|
|
"-sEXPORTED_RUNTIME_METHODS=cwrap,allocateUTF8,stringToUTF8,UTF8ToString,FS,setValue,getValue",
|
|
"-sWASM=1",
|
|
"-sEXPORT_ALL=1",
|
|
"-sASYNCIFY=1",
|
|
"-sSTACK_SIZE=5242880", // 50MB
|
|
}...)
|
|
|
|
default:
|
|
err = errors.New("unsupported GOOS for WebAssembly: " + goos)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// UseTarget loads configuration from a target name (e.g., "rp2040", "wasi")
|
|
func UseTarget(targetName string) (export Export, err error) {
|
|
resolver := targets.NewDefaultResolver()
|
|
|
|
config, err := resolver.Resolve(targetName)
|
|
if err != nil {
|
|
return export, fmt.Errorf("failed to resolve target %s: %w", targetName, err)
|
|
}
|
|
|
|
target := config.LLVMTarget
|
|
if target == "" {
|
|
return export, fmt.Errorf("target '%s' does not have a valid LLVM target triple", targetName)
|
|
}
|
|
|
|
cpu := config.CPU
|
|
if cpu == "" {
|
|
return export, fmt.Errorf("target '%s' does not have a valid CPU configuration", targetName)
|
|
}
|
|
|
|
// Check for ESP Clang support for target-based builds
|
|
clangRoot, err := getESPClangRoot(true)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Set ClangRoot and CC if clang is available
|
|
export.ClangRoot = clangRoot
|
|
export.CC = filepath.Join(clangRoot, "bin", "clang++")
|
|
|
|
// Convert target config to Export - only export necessary fields
|
|
export.BuildTags = config.BuildTags
|
|
export.GOOS = config.GOOS
|
|
export.GOARCH = config.GOARCH
|
|
export.ExtraFiles = config.ExtraFiles
|
|
export.BinaryFormat = config.BinaryFormat
|
|
export.FormatDetail = config.FormatDetail()
|
|
export.Emulator = config.Emulator
|
|
|
|
// Set flashing/debugging configuration
|
|
export.Device = flash.Device{
|
|
Serial: config.Serial,
|
|
SerialPort: config.SerialPort,
|
|
Flash: flash.Flash{
|
|
Method: config.FlashMethod,
|
|
Command: config.FlashCommand,
|
|
Flash1200BpsReset: config.Flash1200BpsReset == "true",
|
|
},
|
|
MSD: flash.MSD{
|
|
VolumeName: config.MSDVolumeName,
|
|
FirmwareName: config.MSDFirmwareName,
|
|
},
|
|
OpenOCD: flash.OpenOCD{
|
|
Interface: config.OpenOCDInterface,
|
|
Transport: config.OpenOCDTransport,
|
|
Target: config.OpenOCDTarget,
|
|
},
|
|
}
|
|
|
|
// Build environment map for template variable expansion
|
|
envs := buildEnvMap(env.LLGoROOT())
|
|
|
|
// Convert LLVMTarget, CPU, Features to CCFLAGS/LDFLAGS
|
|
ldflags := []string{"-S"}
|
|
ccflags := []string{"-Oz"}
|
|
cflags := []string{"-Wno-override-module", "-Qunused-arguments", "-Wno-unused-command-line-argument"}
|
|
if config.LLVMTarget != "" {
|
|
cflags = append(cflags, "--target="+config.LLVMTarget)
|
|
ccflags = append(ccflags, "--target="+config.LLVMTarget)
|
|
}
|
|
// Expand template variables in cflags
|
|
expandedCFlags := env.ExpandEnvSlice(config.CFlags, envs)
|
|
cflags = append(cflags, expandedCFlags...)
|
|
|
|
// The following parameters are inspired by tinygo/builder/library.go
|
|
// Handle CPU configuration
|
|
if cpu != "" {
|
|
// X86 has deprecated the -mcpu flag, so we need to use -march instead.
|
|
// However, ARM has not done this.
|
|
if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") {
|
|
ccflags = append(ccflags, "-march="+cpu)
|
|
} else if strings.HasPrefix(target, "avr") {
|
|
ccflags = append(ccflags, "-mmcu="+cpu)
|
|
} else {
|
|
ccflags = append(ccflags, "-mcpu="+cpu)
|
|
}
|
|
|
|
// For ld.lld linker, also add CPU info to linker flags
|
|
if config.Linker == "ld.lld" {
|
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+cpu)
|
|
}
|
|
}
|
|
|
|
// Handle architecture-specific flags
|
|
canonicalArch := getCanonicalArchName(target)
|
|
switch canonicalArch {
|
|
case "arm":
|
|
if strings.Split(target, "-")[2] == "linux" {
|
|
ccflags = append(ccflags, "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
|
} else {
|
|
ccflags = append(ccflags, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables")
|
|
}
|
|
case "avr":
|
|
// AVR defaults to C float and double both being 32-bit. This deviates
|
|
// from what most code (and certainly compiler-rt) expects. So we need
|
|
// to force the compiler to use 64-bit floating point numbers for
|
|
// double.
|
|
ccflags = append(ccflags, "-mdouble=64")
|
|
case "riscv32":
|
|
ccflags = append(ccflags, "-march=rv32imac", "-fforce-enable-int128")
|
|
case "riscv64":
|
|
ccflags = append(ccflags, "-march=rv64gc")
|
|
case "mips":
|
|
ccflags = append(ccflags, "-fno-pic")
|
|
}
|
|
|
|
// Handle soft float
|
|
if strings.Contains(config.Features, "soft-float") || strings.Contains(strings.Join(config.CFlags, " "), "soft-float") {
|
|
// Use softfloat instead of floating point instructions. This is
|
|
// supported on many architectures.
|
|
ccflags = append(ccflags, "-msoft-float")
|
|
} else {
|
|
if strings.HasPrefix(target, "armv5") {
|
|
// On ARMv5 we need to explicitly enable hardware floating point
|
|
// instructions: Clang appears to assume the hardware doesn't have a
|
|
// FPU otherwise.
|
|
ccflags = append(ccflags, "-mfpu=vfpv2")
|
|
}
|
|
}
|
|
|
|
// Handle Features
|
|
if config.Features != "" {
|
|
// Only add -mllvm flags for non-WebAssembly linkers
|
|
if config.Linker == "ld.lld" {
|
|
ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features)
|
|
}
|
|
}
|
|
|
|
// Handle code generation configuration
|
|
if config.CodeModel != "" {
|
|
ccflags = append(ccflags, "-mcmodel="+config.CodeModel)
|
|
}
|
|
if config.TargetABI != "" {
|
|
ccflags = append(ccflags, "-mabi="+config.TargetABI)
|
|
}
|
|
if config.RelocationModel != "" {
|
|
switch config.RelocationModel {
|
|
case "pic":
|
|
ccflags = append(ccflags, "-fPIC")
|
|
case "static":
|
|
ccflags = append(ccflags, "-fno-pic")
|
|
}
|
|
}
|
|
|
|
// Handle Linker - keep it for external usage
|
|
if config.Linker != "" {
|
|
export.Linker = filepath.Join(clangRoot, "bin", config.Linker)
|
|
}
|
|
if config.LinkerScript != "" {
|
|
ldflags = append(ldflags, "-T", config.LinkerScript)
|
|
}
|
|
ldflags = append(ldflags, "-L", env.LLGoROOT()) // search targets/*.ld
|
|
|
|
var libcIncludeDir []string
|
|
|
|
if config.Libc != "" {
|
|
var outputDir string
|
|
var libcLDFlags []string
|
|
var compileConfig compile.CompileConfig
|
|
baseDir := filepath.Join(cacheRoot(), "crosscompile")
|
|
|
|
outputDir, compileConfig, err = getLibcCompileConfigByName(baseDir, config.Libc, config.LLVMTarget, config.CPU)
|
|
if err != nil {
|
|
return
|
|
}
|
|
libcLDFlags, err = compileWithConfig(compileConfig, outputDir, compile.CompileOptions{
|
|
CC: export.CC,
|
|
Linker: export.Linker,
|
|
CCFLAGS: ccflags,
|
|
LDFLAGS: ldflags,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
cflags = append(cflags, compileConfig.ExportCFlags...)
|
|
ldflags = append(ldflags, libcLDFlags...)
|
|
|
|
libcIncludeDir = compileConfig.ExportCFlags
|
|
export.Libc = config.Libc
|
|
}
|
|
|
|
if config.RTLib != "" {
|
|
var outputDir string
|
|
var rtLibLDFlags []string
|
|
var compileConfig compile.CompileConfig
|
|
baseDir := filepath.Join(cacheRoot(), "crosscompile")
|
|
|
|
outputDir, compileConfig, err = getRTCompileConfigByName(baseDir, config.RTLib, config.LLVMTarget)
|
|
if err != nil {
|
|
return
|
|
}
|
|
rtLibLDFlags, err = compileWithConfig(compileConfig, outputDir, compile.CompileOptions{
|
|
CC: export.CC,
|
|
Linker: export.Linker,
|
|
CCFLAGS: ccflags,
|
|
LDFLAGS: ldflags,
|
|
CFLAGS: libcIncludeDir,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
ldflags = append(ldflags, rtLibLDFlags...)
|
|
}
|
|
|
|
// Combine with config flags and expand template variables
|
|
export.CFLAGS = cflags
|
|
export.CCFLAGS = ccflags
|
|
expandedLDFlags := env.ExpandEnvSlice(config.LDFlags, envs)
|
|
export.LDFLAGS = append(ldflags, expandedLDFlags...)
|
|
|
|
return export, nil
|
|
}
|
|
|
|
// Use extends the original Use function to support target-based configuration
|
|
// If targetName is provided, it takes precedence over goos/goarch
|
|
func Use(goos, goarch, targetName string, wasiThreads, forceEspClang bool) (export Export, err error) {
|
|
if targetName != "" && !strings.HasPrefix(targetName, "wasm") && !strings.HasPrefix(targetName, "wasi") {
|
|
return UseTarget(targetName)
|
|
}
|
|
return use(goos, goarch, wasiThreads, forceEspClang)
|
|
}
|