mirror of
https://github.com/goplus/llgo.git
synced 2025-10-26 01:00:37 +08:00
- Add ESP Clang download and extraction for cross-compilation - Support multiple platforms: darwin/amd64, darwin/arm64, linux/amd64, linux/arm64, windows/amd64 - Integrate ESP Clang with target-based configuration system - Add ClangRoot and ClangBinPath fields to Export struct - Support .tar.xz extraction for ESP Clang packages - Prioritize LLGoROOT clang installation over cached downloads - Update build system to use custom clang for embedded platforms 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
336 lines
8.0 KiB
Go
336 lines
8.0 KiB
Go
//go:build !llgo
|
|
// +build !llgo
|
|
|
|
package crosscompile
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
const (
|
|
sysrootPrefix = "--sysroot="
|
|
resourceDirPrefix = "-resource-dir="
|
|
includePrefix = "-I"
|
|
libPrefix = "-L"
|
|
)
|
|
|
|
func TestUseCrossCompileSDK(t *testing.T) {
|
|
// Skip long-running tests unless explicitly enabled
|
|
if testing.Short() {
|
|
t.Skip("Skipping test in short mode")
|
|
}
|
|
|
|
// Test cases
|
|
testCases := []struct {
|
|
name string
|
|
goos string
|
|
goarch string
|
|
expectSDK bool
|
|
expectCCFlags bool
|
|
expectCFlags bool
|
|
expectLDFlags bool
|
|
}{
|
|
{
|
|
name: "Same Platform",
|
|
goos: runtime.GOOS,
|
|
goarch: runtime.GOARCH,
|
|
expectSDK: false,
|
|
expectCCFlags: false,
|
|
expectCFlags: false,
|
|
expectLDFlags: false,
|
|
},
|
|
{
|
|
name: "WASM Target",
|
|
goos: "wasip1",
|
|
goarch: "wasm",
|
|
expectSDK: true,
|
|
expectCCFlags: true,
|
|
expectCFlags: true,
|
|
expectLDFlags: true,
|
|
},
|
|
{
|
|
name: "Unsupported Target",
|
|
goos: "windows",
|
|
goarch: "amd64",
|
|
expectSDK: false,
|
|
expectCCFlags: false,
|
|
expectCFlags: false,
|
|
expectLDFlags: false,
|
|
},
|
|
}
|
|
|
|
// Create a temporary directory for the cache
|
|
tempDir, err := os.MkdirTemp("", "crosscompile_test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Set environment variable for cache directory
|
|
oldEnv := os.Getenv("LLGO_CACHE_DIR")
|
|
os.Setenv("LLGO_CACHE_DIR", tempDir)
|
|
defer os.Setenv("LLGO_CACHE_DIR", oldEnv)
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
export, err := use(tc.goos, tc.goarch, false)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
t.Logf("export: %+v", export)
|
|
|
|
if tc.expectSDK {
|
|
// Check if flags are set correctly
|
|
if tc.expectCCFlags && len(export.CCFLAGS) == 0 {
|
|
t.Error("Expected CCFLAGS to be set, but they are empty")
|
|
}
|
|
|
|
if tc.expectCFlags && len(export.CFLAGS) == 0 {
|
|
t.Error("Expected CFLAGS to be set, but they are empty")
|
|
}
|
|
|
|
if tc.expectLDFlags && len(export.LDFLAGS) == 0 {
|
|
t.Error("Expected LDFLAGS to be set, but they are empty")
|
|
}
|
|
|
|
// Check for specific flags
|
|
if tc.expectCCFlags {
|
|
hasSysroot := false
|
|
hasResourceDir := false
|
|
|
|
for _, flag := range export.CCFLAGS {
|
|
if len(flag) >= len(sysrootPrefix) && flag[:len(sysrootPrefix)] == sysrootPrefix {
|
|
hasSysroot = true
|
|
}
|
|
if len(flag) >= len(resourceDirPrefix) && flag[:len(resourceDirPrefix)] == resourceDirPrefix {
|
|
hasResourceDir = true
|
|
}
|
|
}
|
|
|
|
if !hasSysroot {
|
|
t.Error("Missing --sysroot flag in CCFLAGS")
|
|
}
|
|
if !hasResourceDir {
|
|
t.Error("Missing -resource-dir flag in CCFLAGS")
|
|
}
|
|
}
|
|
|
|
if tc.expectCFlags {
|
|
hasInclude := false
|
|
|
|
for _, flag := range export.CFLAGS {
|
|
if len(flag) >= len(includePrefix) && flag[:len(includePrefix)] == includePrefix {
|
|
hasInclude = true
|
|
}
|
|
}
|
|
|
|
if !hasInclude {
|
|
t.Error("Missing -I flag in CFLAGS")
|
|
}
|
|
}
|
|
|
|
if tc.expectLDFlags {
|
|
hasLib := false
|
|
|
|
for _, flag := range export.LDFLAGS {
|
|
if len(flag) >= len(libPrefix) && flag[:len(libPrefix)] == libPrefix {
|
|
hasLib = true
|
|
}
|
|
}
|
|
|
|
if !hasLib {
|
|
t.Error("Missing -L flag in LDFLAGS")
|
|
}
|
|
}
|
|
} else {
|
|
if /*len(export.CCFLAGS) != 0 ||*/ len(export.CFLAGS) != 0 {
|
|
t.Errorf("Expected empty export, got CCFLAGS=%v, CFLAGS=%v, LDFLAGS=%v",
|
|
export.CCFLAGS, export.CFLAGS, export.LDFLAGS)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUseTarget(t *testing.T) {
|
|
// Test cases for target-based configuration
|
|
testCases := []struct {
|
|
name string
|
|
targetName string
|
|
expectError bool
|
|
expectLLVM string
|
|
expectCPU string
|
|
}{
|
|
{
|
|
name: "WASI Target",
|
|
targetName: "wasi",
|
|
expectError: false,
|
|
expectLLVM: "",
|
|
expectCPU: "generic",
|
|
},
|
|
{
|
|
name: "RP2040 Target",
|
|
targetName: "rp2040",
|
|
expectError: false,
|
|
expectLLVM: "thumbv6m-unknown-unknown-eabi",
|
|
expectCPU: "cortex-m0plus",
|
|
},
|
|
{
|
|
name: "Cortex-M Target",
|
|
targetName: "cortex-m",
|
|
expectError: false,
|
|
expectLLVM: "",
|
|
expectCPU: "",
|
|
},
|
|
{
|
|
name: "Arduino Target (with filtered flags)",
|
|
targetName: "arduino",
|
|
expectError: false,
|
|
expectLLVM: "avr",
|
|
expectCPU: "atmega328p",
|
|
},
|
|
{
|
|
name: "Nonexistent Target",
|
|
targetName: "nonexistent-target",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
export, err := useTarget(tc.targetName)
|
|
|
|
if tc.expectError {
|
|
if err == nil {
|
|
t.Errorf("Expected error for target %s, but got none", tc.targetName)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error for target %s: %v", tc.targetName, err)
|
|
}
|
|
|
|
// Check if LLVM target is in CCFLAGS
|
|
if tc.expectLLVM != "" {
|
|
found := false
|
|
expectedFlag := "--target=" + tc.expectLLVM
|
|
for _, flag := range export.CCFLAGS {
|
|
if flag == expectedFlag {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("Expected LLVM target %s in CCFLAGS, got %v", expectedFlag, export.CCFLAGS)
|
|
}
|
|
}
|
|
|
|
// Check if CPU is in CCFLAGS
|
|
if tc.expectCPU != "" {
|
|
found := false
|
|
expectedFlags := []string{"-mmcu=" + tc.expectCPU, "-mcpu=" + tc.expectCPU}
|
|
for _, flag := range export.CCFLAGS {
|
|
for _, expectedFlag := range expectedFlags {
|
|
if flag == expectedFlag {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("Expected CPU %s in CCFLAGS, got %v", tc.expectCPU, export.CCFLAGS)
|
|
}
|
|
}
|
|
|
|
t.Logf("Target %s: BuildTags=%v, CFlags=%v, CCFlags=%v, LDFlags=%v",
|
|
tc.targetName, export.BuildTags, export.CFLAGS, export.CCFLAGS, export.LDFLAGS)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUseWithTarget(t *testing.T) {
|
|
// Test target-based configuration takes precedence
|
|
export, err := Use("linux", "amd64", false, "wasi")
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// Check if LLVM target is in CCFLAGS
|
|
found := slices.Contains(export.CCFLAGS, "-mcpu=generic")
|
|
if !found {
|
|
t.Errorf("Expected CPU generic in CCFLAGS, got %v", export.CCFLAGS)
|
|
}
|
|
|
|
// Test fallback to goos/goarch when no target specified
|
|
export, err = Use(runtime.GOOS, runtime.GOARCH, false, "")
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %v", err)
|
|
}
|
|
|
|
// Should use native configuration (only check for macOS since that's where tests run)
|
|
if runtime.GOOS == "darwin" && len(export.LDFLAGS) == 0 {
|
|
t.Error("Expected LDFLAGS to be set for native build")
|
|
}
|
|
}
|
|
|
|
func TestFilterCompatibleLDFlags(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
input []string
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "Empty flags",
|
|
input: []string{},
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "Compatible flags only",
|
|
input: []string{"-lm", "-lpthread"},
|
|
expected: []string{"-lm", "-lpthread"},
|
|
},
|
|
{
|
|
name: "Incompatible flags filtered",
|
|
input: []string{"--gc-sections", "-lm", "--emit-relocs", "-lpthread"},
|
|
expected: []string{"--gc-sections", "-lm", "--emit-relocs", "-lpthread"},
|
|
},
|
|
{
|
|
name: "Defsym flags filtered",
|
|
input: []string{"--defsym=_stack_size=512", "-lm", "--defsym=_bootloader_size=512"},
|
|
expected: []string{"-lm"},
|
|
},
|
|
{
|
|
name: "Linker script flags filtered",
|
|
input: []string{"-T", "script.ld", "-lm"},
|
|
expected: []string{"-lm"},
|
|
},
|
|
{
|
|
name: "Mixed compatible and incompatible",
|
|
input: []string{"-lm", "--gc-sections", "--defsym=test=1", "-lpthread", "--no-demangle"},
|
|
expected: []string{"-lm", "--gc-sections", "-lpthread", "--no-demangle"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := filterCompatibleLDFlags(tc.input)
|
|
|
|
if len(result) != len(tc.expected) {
|
|
t.Errorf("Expected %d flags, got %d: %v", len(tc.expected), len(result), result)
|
|
return
|
|
}
|
|
|
|
for i, expected := range tc.expected {
|
|
if result[i] != expected {
|
|
t.Errorf("Expected flag[%d] = %s, got %s", i, expected, result[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|