Merge commit '6588f36123eababf6e24564b49e5af374285d2b5' into optional-esp-clang
# Conflicts: # internal/crosscompile/crosscompile.go # internal/crosscompile/crosscompile_test.go
This commit is contained in:
103
internal/crosscompile/compile/compile.go
Normal file
103
internal/crosscompile/compile/compile.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package compile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/clang"
|
||||
)
|
||||
|
||||
type CompileOptions struct {
|
||||
CC string // Compiler to use
|
||||
Linker string
|
||||
CCFLAGS []string
|
||||
CFLAGS []string
|
||||
LDFLAGS []string
|
||||
}
|
||||
|
||||
type CompileGroup struct {
|
||||
OutputFileName string
|
||||
Files []string // List of source files to compile
|
||||
CFlags []string // C compiler flags
|
||||
CCFlags []string
|
||||
LDFlags []string // Linker flags
|
||||
}
|
||||
|
||||
func (g CompileGroup) IsCompiled(outputDir string) bool {
|
||||
archive := filepath.Join(outputDir, filepath.Base(g.OutputFileName))
|
||||
_, err := os.Stat(archive)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (g CompileGroup) Compile(
|
||||
outputDir string, options CompileOptions,
|
||||
) (err error) {
|
||||
if g.IsCompiled(outputDir) {
|
||||
return
|
||||
}
|
||||
tmpCompileDir, err := os.MkdirTemp("", "compile-group*")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpCompileDir)
|
||||
|
||||
compileLDFlags := append(slices.Clone(options.LDFLAGS), g.LDFlags...)
|
||||
compileCCFlags := append(slices.Clone(options.CCFLAGS), g.CCFlags...)
|
||||
compileCFFlags := append(slices.Clone(options.CFLAGS), g.CFlags...)
|
||||
|
||||
cfg := clang.NewConfig(options.CC, compileCCFlags, compileCFFlags, compileLDFlags, options.Linker)
|
||||
|
||||
var objFiles []string
|
||||
|
||||
compiler := clang.NewCompiler(cfg)
|
||||
|
||||
compiler.Verbose = true
|
||||
|
||||
archive := filepath.Join(outputDir, filepath.Base(g.OutputFileName))
|
||||
fmt.Fprintf(os.Stderr, "Start to compile group %s to %s...\n", g.OutputFileName, archive)
|
||||
|
||||
for _, file := range g.Files {
|
||||
var tempObjFile *os.File
|
||||
tempObjFile, err = os.CreateTemp(tmpCompileDir, fmt.Sprintf("%s*.o", strings.ReplaceAll(file, string(os.PathSeparator), "-")))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lang := "c"
|
||||
if filepath.Ext(file) == ".S" {
|
||||
lang = "assembler-with-cpp"
|
||||
}
|
||||
err = compiler.Compile("-o", tempObjFile.Name(), "-x", lang, "-c", file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
objFiles = append(objFiles, tempObjFile.Name())
|
||||
}
|
||||
|
||||
args := []string{"rcs", archive}
|
||||
args = append(args, objFiles...)
|
||||
|
||||
ccDir := filepath.Dir(options.CC)
|
||||
llvmAr := filepath.Join(ccDir, "llvm-ar")
|
||||
|
||||
cmd := exec.Command(llvmAr, args...)
|
||||
// TODO(MeteorsLiu): support verbose
|
||||
// cmd.Stdout = os.Stdout
|
||||
// cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
return
|
||||
}
|
||||
|
||||
// CompileConfig represents compilation configuration
|
||||
type CompileConfig struct {
|
||||
Url string
|
||||
Name string // compile name (e.g., "picolibc", "musl", "glibc")
|
||||
Groups []CompileGroup
|
||||
ArchiveSrcDir string
|
||||
LibcCFlags []string
|
||||
}
|
||||
209
internal/crosscompile/compile/compile_test.go
Normal file
209
internal/crosscompile/compile/compile_test.go
Normal file
@@ -0,0 +1,209 @@
|
||||
//go:build !llgo
|
||||
|
||||
package compile
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/llgo/xtool/nm"
|
||||
)
|
||||
|
||||
func TestIsCompile(t *testing.T) {
|
||||
t.Run("IsCompile Not Exists", func(t *testing.T) {
|
||||
cfg := CompileConfig{
|
||||
Groups: []CompileGroup{
|
||||
{
|
||||
OutputFileName: "fakefile1.a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if cfg.Groups[0].IsCompiled(".") {
|
||||
t.Errorf("unexpected result: should false")
|
||||
}
|
||||
})
|
||||
t.Run("IsCompile Exists", func(t *testing.T) {
|
||||
tmpFile, err := os.CreateTemp("", "test*.a")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
cfg := CompileConfig{
|
||||
Groups: []CompileGroup{
|
||||
{
|
||||
OutputFileName: tmpFile.Name(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !cfg.Groups[0].IsCompiled(filepath.Dir(tmpFile.Name())) {
|
||||
t.Errorf("unexpected result: should true")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompile(t *testing.T) {
|
||||
t.Run("Skip compile", func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "test-compile*")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test*.a")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
group := CompileGroup{
|
||||
OutputFileName: tmpFile.Name(),
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected result: should nil: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TmpDir Fail", func(t *testing.T) {
|
||||
tmpDir := filepath.Join(t.TempDir(), "test-compile")
|
||||
os.RemoveAll(tmpDir)
|
||||
|
||||
err := os.Mkdir(tmpDir, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
os.Setenv("TMPDIR", tmpDir)
|
||||
defer os.Unsetenv("TMPDIR")
|
||||
|
||||
group := CompileGroup{
|
||||
OutputFileName: "nop.a",
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("unexpected result: should not nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Compile", func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "test-compile*")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test*.c")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = tmpFile.Write([]byte(`#include <math.h>
|
||||
void Foo() {
|
||||
double x = 2.0;
|
||||
double y = sqrt(x);
|
||||
(void) y ;
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
group := CompileGroup{
|
||||
OutputFileName: "nop.a",
|
||||
Files: []string{tmpFile.Name()},
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
CCFLAGS: []string{"-nostdinc"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("unexpected result: should not nil")
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected result: should not nil")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(tmpDir, "nop.a")); os.IsNotExist(err) {
|
||||
t.Error("unexpected result: compiled nop.a not found")
|
||||
return
|
||||
}
|
||||
|
||||
items, err := nm.New("").List(filepath.Join(tmpDir, "nop.a"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
want := "Foo"
|
||||
found := false
|
||||
loop:
|
||||
for _, item := range items {
|
||||
for _, sym := range item.Symbols {
|
||||
if strings.Contains(sym.Name, want) {
|
||||
found = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("cannot find symbol Foo")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Compile Asm", func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "test-compile*")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test*.S")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
_, err = tmpFile.Write([]byte(`
|
||||
.text
|
||||
.globl _test
|
||||
`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
group := CompileGroup{
|
||||
OutputFileName: "nop.a",
|
||||
Files: []string{tmpFile.Name()},
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
CCFLAGS: []string{"--target=x86_64-linux-gnu"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected result: should nil %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
630
internal/crosscompile/compile/libc/libc_test.go
Normal file
630
internal/crosscompile/compile/libc/libc_test.go
Normal file
@@ -0,0 +1,630 @@
|
||||
package libc
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetPicolibcConfig(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "test-target"
|
||||
|
||||
config := GetPicolibcConfig(baseDir, target)
|
||||
|
||||
if config.Name != "picolibc" {
|
||||
t.Errorf("Expected Name 'picolibc', got '%s'", config.Name)
|
||||
}
|
||||
if config.ArchiveSrcDir != "picolibc-main" {
|
||||
t.Errorf("Expected ArchiveSrcDir 'picolibc-main', got '%s'", config.ArchiveSrcDir)
|
||||
}
|
||||
|
||||
// Test LibcCFlags
|
||||
if len(config.LibcCFlags) != 2 {
|
||||
t.Errorf("Expected 2 LibcCFlags, got %d", len(config.LibcCFlags))
|
||||
} else {
|
||||
expected := "-I" + baseDir
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
|
||||
expected = "-isystem" + filepath.Join(baseDir, "newlib", "libc", "include")
|
||||
if config.LibcCFlags[1] != expected {
|
||||
t.Errorf("Expected LibcCFlags[1] to be '%s', got '%s'", expected, config.LibcCFlags[1])
|
||||
}
|
||||
}
|
||||
|
||||
// Test Groups configuration
|
||||
if len(config.Groups) != 1 {
|
||||
t.Errorf("Expected 1 group, got %d", len(config.Groups))
|
||||
} else {
|
||||
group := config.Groups[0]
|
||||
|
||||
// Test output file name
|
||||
expectedOutput := "libc-" + target + ".a"
|
||||
if group.OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, group.OutputFileName)
|
||||
}
|
||||
|
||||
// Test files list
|
||||
if len(group.Files) == 0 {
|
||||
t.Error("Expected non-empty files list")
|
||||
} else {
|
||||
// Check a few sample files
|
||||
sampleFiles := []string{
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-malloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "printf.c"),
|
||||
}
|
||||
|
||||
for _, sample := range sampleFiles {
|
||||
found := false
|
||||
for _, file := range group.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group files", sample)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test CFlags
|
||||
expectedCFlags := []string{
|
||||
"-D_COMPILING_NEWLIB",
|
||||
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||
"-DTINY_STDIO",
|
||||
"-DPOSIX_IO",
|
||||
"-DFORMAT_DEFAULT_INTEGER",
|
||||
"-D_IEEE_LIBM",
|
||||
"-D__OBSOLETE_MATH_FLOAT=1",
|
||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||
"-D_WANT_IO_C99_FORMATS",
|
||||
"-nostdlib",
|
||||
"-I" + baseDir,
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libm", "common"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "locale"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "tinystdio"),
|
||||
}
|
||||
|
||||
if len(group.CFlags) != len(expectedCFlags) {
|
||||
t.Errorf("Expected %d CFlags, got %d", len(expectedCFlags), len(group.CFlags))
|
||||
} else {
|
||||
for i, expected := range expectedCFlags {
|
||||
if group.CFlags[i] != expected {
|
||||
t.Errorf("CFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, group.CFlags[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test LDFlags and CCFlags
|
||||
if len(group.LDFlags) == 0 {
|
||||
t.Error("Expected non-empty LDFlags")
|
||||
}
|
||||
if len(group.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPicolibcConfig_EdgeCases(t *testing.T) {
|
||||
t.Run("EmptyBaseDir", func(t *testing.T) {
|
||||
config := GetPicolibcConfig("", "test-target")
|
||||
|
||||
// Check that paths are constructed correctly even with empty baseDir
|
||||
expected := "-I"
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
|
||||
expected = "-isystem" + filepath.Join("", "newlib", "libc", "include")
|
||||
if config.LibcCFlags[1] != expected {
|
||||
t.Errorf("Expected LibcCFlags[1] to be '%s', got '%s'", expected, config.LibcCFlags[1])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyTarget", func(t *testing.T) {
|
||||
config := GetPicolibcConfig("/test/base", "")
|
||||
|
||||
// Check output file name formatting
|
||||
expectedOutput := "libc-.a"
|
||||
if config.Groups[0].OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetNewlibESP32ConfigRISCV(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "riscv32-unknown-elf"
|
||||
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
|
||||
// Test basic configuration
|
||||
if config.Url != _newlibUrl {
|
||||
t.Errorf("Expected URL '%s', got '%s'", _newlibUrl, config.Url)
|
||||
}
|
||||
if config.Name != "newlib-esp32" {
|
||||
t.Errorf("Expected Name 'newlib-esp32', got '%s'", config.Name)
|
||||
}
|
||||
if config.ArchiveSrcDir != _archiveInternalSrcDir {
|
||||
t.Errorf("Expected ArchiveSrcDir '%s', got '%s'", _archiveInternalSrcDir, config.ArchiveSrcDir)
|
||||
}
|
||||
|
||||
// Test LibcCFlags
|
||||
libcDir := filepath.Join(baseDir, "newlib", "libc")
|
||||
expectedCFlags := []string{
|
||||
"-isystem" + filepath.Join(libcDir, "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib"),
|
||||
"-I" + libcDir,
|
||||
}
|
||||
if len(config.LibcCFlags) != len(expectedCFlags) {
|
||||
t.Errorf("Expected %d LibcCFlags, got %d", len(expectedCFlags), len(config.LibcCFlags))
|
||||
} else {
|
||||
for i, expected := range expectedCFlags {
|
||||
if config.LibcCFlags[i] != expected {
|
||||
t.Errorf("LibcCFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, config.LibcCFlags[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test Groups configuration
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 3 groups, got %d", len(config.Groups))
|
||||
} else {
|
||||
// Group 0: libcrt0
|
||||
group0 := config.Groups[0]
|
||||
expectedOutput0 := "libcrt0-" + target + ".a"
|
||||
if group0.OutputFileName != expectedOutput0 {
|
||||
t.Errorf("Group0 OutputFileName expected '%s', got '%s'", expectedOutput0, group0.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group0
|
||||
sampleFiles0 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "riscv", "esp", "esp_board.c"),
|
||||
filepath.Join(baseDir, "libgloss", "riscv", "esp", "crt1-board.S"),
|
||||
}
|
||||
for _, sample := range sampleFiles0 {
|
||||
found := false
|
||||
for _, file := range group0.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group0 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Group 1: libgloss
|
||||
group1 := config.Groups[1]
|
||||
expectedOutput1 := "libgloss-" + target + ".a"
|
||||
if group1.OutputFileName != expectedOutput1 {
|
||||
t.Errorf("Group1 OutputFileName expected '%s', got '%s'", expectedOutput1, group1.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group1
|
||||
sampleFiles1 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "libnosys", "close.c"),
|
||||
filepath.Join(baseDir, "libgloss", "libnosys", "sbrk.c"),
|
||||
}
|
||||
for _, sample := range sampleFiles1 {
|
||||
found := false
|
||||
for _, file := range group1.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group1 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Group 2: libc
|
||||
group2 := config.Groups[2]
|
||||
expectedOutput2 := "libc-" + target + ".a"
|
||||
if group2.OutputFileName != expectedOutput2 {
|
||||
t.Errorf("Group2 OutputFileName expected '%s', got '%s'", expectedOutput2, group2.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group2
|
||||
sampleFiles2 := []string{
|
||||
filepath.Join(libcDir, "string", "memcpy.c"),
|
||||
filepath.Join(libcDir, "stdlib", "malloc.c"),
|
||||
}
|
||||
for _, sample := range sampleFiles2 {
|
||||
found := false
|
||||
for _, file := range group2.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group2 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Test CFlags for group2
|
||||
expectedCFlagsGroup2 := []string{
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
"-DHAVE_NANOSLEEP",
|
||||
"-D__NO_SYSCALLS__",
|
||||
// ... (other expected flags)
|
||||
}
|
||||
for _, expectedFlag := range expectedCFlagsGroup2 {
|
||||
found := false
|
||||
for _, flag := range group2.CFlags {
|
||||
if flag == expectedFlag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected flag '%s' not found in group2 CFlags", expectedFlag)
|
||||
}
|
||||
}
|
||||
|
||||
// Test LDFlags and CCFlags
|
||||
if len(group0.LDFlags) == 0 {
|
||||
t.Error("Expected non-empty LDFlags in group0")
|
||||
}
|
||||
if len(group0.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags in group0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNewlibESP32ConfigXtensa(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "xtensa-esp32-elf"
|
||||
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
|
||||
// Test basic configuration
|
||||
if config.Url != _newlibUrl {
|
||||
t.Errorf("Expected URL '%s', got '%s'", _newlibUrl, config.Url)
|
||||
}
|
||||
if config.Name != "newlib-esp32" {
|
||||
t.Errorf("Expected Name 'newlib-esp32', got '%s'", config.Name)
|
||||
}
|
||||
if config.ArchiveSrcDir != _archiveInternalSrcDir {
|
||||
t.Errorf("Expected ArchiveSrcDir '%s', got '%s'", _archiveInternalSrcDir, config.ArchiveSrcDir)
|
||||
}
|
||||
|
||||
// Test LibcCFlags
|
||||
libcDir := filepath.Join(baseDir, "newlib", "libc")
|
||||
expectedCFlags := []string{
|
||||
"-I" + filepath.Join(libcDir, "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib"),
|
||||
"-I" + libcDir,
|
||||
}
|
||||
if len(config.LibcCFlags) != len(expectedCFlags) {
|
||||
t.Errorf("Expected %d LibcCFlags, got %d", len(expectedCFlags), len(config.LibcCFlags))
|
||||
} else {
|
||||
for i, expected := range expectedCFlags {
|
||||
if config.LibcCFlags[i] != expected {
|
||||
t.Errorf("LibcCFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, config.LibcCFlags[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test Groups configuration
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 2 groups, got %d", len(config.Groups))
|
||||
} else {
|
||||
// Group 0: libcrt0
|
||||
group0 := config.Groups[0]
|
||||
expectedOutput0 := "libcrt0-" + target + ".a"
|
||||
if group0.OutputFileName != expectedOutput0 {
|
||||
t.Errorf("Group0 OutputFileName expected '%s', got '%s'", expectedOutput0, group0.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group0
|
||||
sampleFiles0 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "xtensa", "clibrary_init.c"),
|
||||
filepath.Join(baseDir, "libgloss", "xtensa", "crt1-boards.S"),
|
||||
}
|
||||
for _, sample := range sampleFiles0 {
|
||||
found := false
|
||||
for _, file := range group0.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group0 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Group 1: libgloss + libc
|
||||
group1 := config.Groups[1]
|
||||
expectedOutput1 := "libgloss-" + target + ".a"
|
||||
if group1.OutputFileName != expectedOutput1 {
|
||||
t.Errorf("Group1 OutputFileName expected '%s', got '%s'", expectedOutput1, group1.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group1
|
||||
sampleFiles1 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "libnosys", "close.c"),
|
||||
}
|
||||
for _, sample := range sampleFiles1 {
|
||||
found := false
|
||||
for _, file := range group1.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group1 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Test CFlags for group1
|
||||
expectedCFlagsGroup1 := []string{
|
||||
"-D__NO_SYSCALLS__",
|
||||
"-D_NO_GETUT",
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
// ... (other expected flags)
|
||||
}
|
||||
for _, expectedFlag := range expectedCFlagsGroup1 {
|
||||
found := false
|
||||
for _, flag := range group1.CFlags {
|
||||
if flag == expectedFlag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected flag '%s' not found in group1 CFlags", expectedFlag)
|
||||
}
|
||||
}
|
||||
|
||||
// Test LDFlags and CCFlags
|
||||
if len(group0.LDFlags) == 0 {
|
||||
t.Error("Expected non-empty LDFlags in group0")
|
||||
}
|
||||
if len(group0.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags in group0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEdgeCases(t *testing.T) {
|
||||
t.Run("EmptyBaseDir_RISCV", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV("", "test-target")
|
||||
libcDir := filepath.Join("", "newlib", "libc")
|
||||
|
||||
// Check that paths are constructed correctly
|
||||
expected := "-isystem" + filepath.Join(libcDir, "include")
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyTarget_RISCV", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV("/test/base", "")
|
||||
|
||||
// Check output file name formatting
|
||||
expectedOutput := "libcrt0-.a"
|
||||
if config.Groups[0].OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyBaseDir_Xtensa", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa("", "test-target")
|
||||
libcDir := filepath.Join("", "newlib", "libc")
|
||||
|
||||
// Check that paths are constructed correctly
|
||||
expected := "-I" + filepath.Join(libcDir, "include")
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyTarget_Xtensa", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa("/test/base", "")
|
||||
|
||||
// Check output file name formatting
|
||||
expectedOutput := "libcrt0-.a"
|
||||
if config.Groups[0].OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGroupConfiguration(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "test-target"
|
||||
|
||||
t.Run("RISCV_GroupCount", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 3 groups for RISCV, got %d", len(config.Groups))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Xtensa_GroupCount", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 2 groups for Xtensa, got %d", len(config.Groups))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RISCV_GroupNames", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
expectedNames := []string{
|
||||
"libcrt0-" + target + ".a",
|
||||
"libgloss-" + target + ".a",
|
||||
"libc-" + target + ".a",
|
||||
}
|
||||
|
||||
for i, group := range config.Groups {
|
||||
if group.OutputFileName != expectedNames[i] {
|
||||
t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Xtensa_GroupNames", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
expectedNames := []string{
|
||||
"libcrt0-" + target + ".a",
|
||||
"libgloss-" + target + ".a",
|
||||
}
|
||||
|
||||
for i, group := range config.Groups {
|
||||
if i >= len(expectedNames) {
|
||||
return
|
||||
}
|
||||
if group.OutputFileName != expectedNames[i] {
|
||||
t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompilerFlags(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "test-target"
|
||||
|
||||
t.Run("RISCV_CFlags", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
group := config.Groups[2] // libc group
|
||||
|
||||
requiredFlags := []string{
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
"-D__NO_SYSCALLS__",
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
}
|
||||
|
||||
for _, flag := range requiredFlags {
|
||||
found := false
|
||||
for _, cflag := range group.CFlags {
|
||||
if cflag == flag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required flag '%s' not found in RISCV CFlags", flag)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Xtensa_CFlags", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
group := config.Groups[1] // libgloss+libc group
|
||||
|
||||
requiredFlags := []string{
|
||||
"-D__NO_SYSCALLS__",
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
}
|
||||
|
||||
for _, flag := range requiredFlags {
|
||||
found := false
|
||||
for _, cflag := range group.CFlags {
|
||||
if cflag == flag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required flag '%s' not found in Xtensa CFlags", flag)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CommonFlags", func(t *testing.T) {
|
||||
configRISCV := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
configXtensa := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
|
||||
// Test LDFlags
|
||||
expectedLDFlags := []string{
|
||||
"-nostdlib",
|
||||
"-ffunction-sections",
|
||||
"-fdata-sections",
|
||||
}
|
||||
|
||||
for _, group := range configRISCV.Groups {
|
||||
for _, expected := range expectedLDFlags {
|
||||
found := false
|
||||
for _, flag := range group.LDFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required LDFlag '%s' not found in RISCV group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range configXtensa.Groups {
|
||||
for _, expected := range expectedLDFlags {
|
||||
found := false
|
||||
for _, flag := range group.LDFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required LDFlag '%s' not found in Xtensa group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test CCFlags
|
||||
expectedCCFlags := []string{
|
||||
"-Oz",
|
||||
"-fno-builtin",
|
||||
"-ffreestanding",
|
||||
}
|
||||
|
||||
for _, group := range configRISCV.Groups {
|
||||
for _, expected := range expectedCCFlags {
|
||||
found := false
|
||||
for _, flag := range group.CCFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required CCFlag '%s' not found in RISCV group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range configXtensa.Groups {
|
||||
for _, expected := range expectedCCFlags {
|
||||
found := false
|
||||
for _, flag := range group.CCFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required CCFlag '%s' not found in Xtensa group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
2083
internal/crosscompile/compile/libc/newlibesp.go
Normal file
2083
internal/crosscompile/compile/libc/newlibesp.go
Normal file
File diff suppressed because it is too large
Load Diff
163
internal/crosscompile/compile/libc/picolibc.go
Normal file
163
internal/crosscompile/compile/libc/picolibc.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package libc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
)
|
||||
|
||||
// getPicolibcConfig returns configuration for picolibc
|
||||
func GetPicolibcConfig(baseDir, target string) *compile.CompileConfig {
|
||||
return &compile.CompileConfig{
|
||||
Url: "https://github.com/goplus/picolibc/archive/refs/heads/main.zip",
|
||||
Name: "picolibc",
|
||||
LibcCFlags: []string{
|
||||
"-I" + baseDir,
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
},
|
||||
|
||||
Groups: []compile.CompileGroup{
|
||||
{
|
||||
OutputFileName: fmt.Sprintf("libc-%s.a", target),
|
||||
Files: []string{
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bcopy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bzero.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "explicit_bzero.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "ffsl.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "ffsll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "fls.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "flsl.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "flsll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "gnu_basename.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "index.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memccpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memmem.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memmove.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "mempcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memrchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memset.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "rawmemchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "rindex.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "stpcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "stpncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcasestr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strchrnul.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcoll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcoll_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strerror_r.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlcat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlwr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strndup.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strnlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strnstr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strpbrk.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strrchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strsep.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strsignal.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strstr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strtok.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strtok_r.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strupr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strverscmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strxfrm.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strxfrm_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "swab.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "timingsafe_bcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "timingsafe_memcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strerror.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcpcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcpncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcschr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscoll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscoll_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsdup.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcslcat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcslcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcslen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsnlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcspbrk.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsrchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsstr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcstok.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcswidth.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsxfrm.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsxfrm_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcwidth.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemmove.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmempcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemset.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "xpg_strerror_r.c"),
|
||||
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-calloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-malloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-pvalloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-realloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-valloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "rand.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "srand.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-free.c"),
|
||||
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "printf.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "putchar.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "puts.c"),
|
||||
},
|
||||
CFlags: []string{
|
||||
"-D_COMPILING_NEWLIB",
|
||||
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||
"-DTINY_STDIO",
|
||||
"-DPOSIX_IO",
|
||||
"-DFORMAT_DEFAULT_INTEGER",
|
||||
"-D_IEEE_LIBM",
|
||||
"-D__OBSOLETE_MATH_FLOAT=1",
|
||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||
"-D_WANT_IO_C99_FORMATS",
|
||||
"-nostdlib",
|
||||
"-I" + baseDir,
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libm", "common"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "locale"),
|
||||
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "tinystdio"),
|
||||
},
|
||||
LDFlags: _libcLDFlags,
|
||||
CCFlags: _libcCCFlags,
|
||||
},
|
||||
},
|
||||
ArchiveSrcDir: "picolibc-main",
|
||||
}
|
||||
}
|
||||
288
internal/crosscompile/compile/rtlib/compiler_rt.go
Normal file
288
internal/crosscompile/compile/rtlib/compiler_rt.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package rtlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
)
|
||||
|
||||
func platformSpecifiedFiles(builtinsDir, target string) []string {
|
||||
switch {
|
||||
case strings.Contains(target, "riscv32"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "riscv", "mulsi3.S"),
|
||||
filepath.Join(builtinsDir, "riscv", "fp_mode.c"),
|
||||
filepath.Join(builtinsDir, "riscv", "save.S"),
|
||||
filepath.Join(builtinsDir, "riscv", "restore.S"),
|
||||
filepath.Join(builtinsDir, "atomic.c"),
|
||||
}
|
||||
case strings.Contains(target, "riscv64"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "addtf3.c"),
|
||||
filepath.Join(builtinsDir, "comparetf2.c"),
|
||||
filepath.Join(builtinsDir, "divtc3.c"),
|
||||
filepath.Join(builtinsDir, "divtf3.c"),
|
||||
filepath.Join(builtinsDir, "extenddftf2.c"),
|
||||
filepath.Join(builtinsDir, "extendhftf2.c"),
|
||||
filepath.Join(builtinsDir, "extendsftf2.c"),
|
||||
filepath.Join(builtinsDir, "fixtfdi.c"),
|
||||
filepath.Join(builtinsDir, "fixtfsi.c"),
|
||||
filepath.Join(builtinsDir, "fixtfti.c"),
|
||||
filepath.Join(builtinsDir, "fixunstfdi.c"),
|
||||
filepath.Join(builtinsDir, "fixunstfsi.c"),
|
||||
filepath.Join(builtinsDir, "fixunstfti.c"),
|
||||
filepath.Join(builtinsDir, "floatditf.c"),
|
||||
filepath.Join(builtinsDir, "floatsitf.c"),
|
||||
filepath.Join(builtinsDir, "floattitf.c"),
|
||||
filepath.Join(builtinsDir, "floatunditf.c"),
|
||||
filepath.Join(builtinsDir, "floatunsitf.c"),
|
||||
filepath.Join(builtinsDir, "floatuntitf.c"),
|
||||
filepath.Join(builtinsDir, "multc3.c"),
|
||||
filepath.Join(builtinsDir, "multf3.c"),
|
||||
filepath.Join(builtinsDir, "powitf2.c"),
|
||||
filepath.Join(builtinsDir, "subtf3.c"),
|
||||
filepath.Join(builtinsDir, "trunctfdf2.c"),
|
||||
filepath.Join(builtinsDir, "trunctfhf2.c"),
|
||||
filepath.Join(builtinsDir, "trunctfsf2.c"),
|
||||
filepath.Join(builtinsDir, "atomic.c"),
|
||||
}
|
||||
case strings.Contains(target, "arm"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cdcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cdcmpeq_check_nan.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cfcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cfcmpeq_check_nan.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_dcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_div0.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_drsub.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_fcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_frsub.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_idivmod.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_ldivmod.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memcpy.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memmove.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memset.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_uidivmod.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_uldivmod.S"),
|
||||
|
||||
// These two are not technically EABI builtins but are used by them and only
|
||||
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||
// other architectures.
|
||||
// Most importantly, they have a different calling convention on AVR so
|
||||
// should not be used on AVR.
|
||||
filepath.Join(builtinsDir, "divmodsi4.c"),
|
||||
filepath.Join(builtinsDir, "udivmodsi4.c"),
|
||||
}
|
||||
case strings.Contains(target, "avr"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "avr", "divmodhi4.S"),
|
||||
filepath.Join(builtinsDir, "avr", "divmodqi4.S"),
|
||||
filepath.Join(builtinsDir, "avr", "mulhi3.S"),
|
||||
filepath.Join(builtinsDir, "avr", "mulqi3.S"),
|
||||
filepath.Join(builtinsDir, "avr", "udivmodhi4.S"),
|
||||
filepath.Join(builtinsDir, "avr", "udivmodqi4.S"),
|
||||
}
|
||||
|
||||
case target == "xtensa":
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "xtensa", "ieee754_sqrtf.S"),
|
||||
filepath.Join(builtinsDir, "atomic.c"),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withPlatformSpecifiedFiles(baseDir, target string, files []string) []string {
|
||||
builtinsDir := filepath.Join(baseDir, "lib", "builtins")
|
||||
return append(files, platformSpecifiedFiles(builtinsDir, target)...)
|
||||
}
|
||||
|
||||
func GetCompilerRTConfig(baseDir, target string) *compile.CompileConfig {
|
||||
return &compile.CompileConfig{
|
||||
Url: "https://github.com/goplus/compiler-rt/archive/refs/tags/v0.1.0.tar.gz",
|
||||
ArchiveSrcDir: "compiler-rt-0.1.0",
|
||||
Groups: []compile.CompileGroup{
|
||||
{
|
||||
OutputFileName: fmt.Sprintf("libclang_builtins-%s.a", target),
|
||||
Files: withPlatformSpecifiedFiles(baseDir, target, []string{
|
||||
filepath.Join(baseDir, "lib", "builtins", "absvdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "absvsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "absvti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "adddf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addvdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addvsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addvti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "apple_versioning.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashldi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashlti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashrdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashrti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "bswapdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "bswapsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clzdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clzsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clzti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "cmpdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "cmpti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "comparedf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "comparesf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ctzdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ctzsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ctzti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divdc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divdf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divmoddi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divmodsi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divmodti4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divsc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendsfdf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendhfsf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ffsdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ffssi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ffsti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixdfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixdfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixdfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixsfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixsfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixsfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunsdfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunsdfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunsdfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunssfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunssfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunssfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatdidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatdisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatsidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatsisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floattidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floattisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatundidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatundisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunsidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunsisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatuntidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatuntisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fp_mode.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "int_util.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "lshrdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "lshrti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "moddi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "modsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "modti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muldc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muldf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muldi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulodi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulosi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muloti4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulsc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "multi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulvdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulvsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulvti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negdf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negsf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negvdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negvsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negvti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "os_version_check.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "paritydi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "paritysi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "parityti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "popcountdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "popcountsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "popcountti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "powidf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "powisf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subdf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subvdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subvsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subvti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trampoline_setup.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "truncdfhf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "truncdfsf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "truncsfhf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ucmpdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ucmpti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivmoddi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivmodsi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivmodti4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "umoddi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "umodsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "umodti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "gcc_personality_v0.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clear_cache.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addtf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "comparetf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divtc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divtf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extenddftf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendhftf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendsftf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixtfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixtfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixtfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunstfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunstfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunstfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatditf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatsitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floattitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunditf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunsitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatuntitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "multc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "multf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "powitf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subtf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trunctfdf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trunctfhf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trunctfsf2.c"),
|
||||
}),
|
||||
CFlags: []string{
|
||||
"-DNDEBUG",
|
||||
"-DVISIBILITY_HIDDEN",
|
||||
},
|
||||
CCFlags: []string{
|
||||
"-Oz",
|
||||
"-fno-ident",
|
||||
"-Wno-unused-parameter",
|
||||
"-fno-lto",
|
||||
"-Werror=array-bounds",
|
||||
"-Werror=uninitialized",
|
||||
"-Werror=shadow",
|
||||
"-Werror=empty-body",
|
||||
"-Werror=sizeof-pointer-memaccess",
|
||||
"-Werror=sizeof-array-argument",
|
||||
"-Werror=suspicious-memaccess",
|
||||
"-Werror=builtin-memcpy-chk-size",
|
||||
"-Werror=array-bounds-pointer-arithmetic",
|
||||
"-Werror=return-stack-address",
|
||||
"-Werror=sizeof-array-decay",
|
||||
"-Werror=format-insufficient-args",
|
||||
"-Wformat -std=c11",
|
||||
"-fno-builtin",
|
||||
"-fvisibility=hidden",
|
||||
"-fomit-frame-pointer",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
124
internal/crosscompile/compile/rtlib/rt_test.go
Normal file
124
internal/crosscompile/compile/rtlib/rt_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package rtlib
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPlatformSpecifiedFiles(t *testing.T) {
|
||||
tests := []struct {
|
||||
target string
|
||||
expected int // Number of expected files
|
||||
}{
|
||||
{"riscv32-unknown-elf", 5},
|
||||
{"riscv64-unknown-elf", 27},
|
||||
{"arm-none-eabi", 19},
|
||||
{"avr-unknown-elf", 6},
|
||||
{"xtensa", 2},
|
||||
{"x86_64-pc-windows", 0},
|
||||
}
|
||||
|
||||
builtinsDir := "/test/builtins"
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.target, func(t *testing.T) {
|
||||
result := platformSpecifiedFiles(builtinsDir, tt.target)
|
||||
if len(result) != tt.expected {
|
||||
t.Errorf("For target %s, expected %d files, got %d", tt.target, tt.expected, len(result))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPlatformSpecifiedFiles(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "riscv32-unknown-elf"
|
||||
inputFiles := []string{"file1.c", "file2.c"}
|
||||
|
||||
result := withPlatformSpecifiedFiles(baseDir, target, inputFiles)
|
||||
|
||||
// Should have input files + platform specific files
|
||||
if len(result) <= len(inputFiles) {
|
||||
t.Errorf("Expected more files than input, got %d", len(result))
|
||||
}
|
||||
|
||||
// Check that input files are preserved
|
||||
for _, inputFile := range inputFiles {
|
||||
found := false
|
||||
for _, resultFile := range result {
|
||||
if resultFile == inputFile {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Input file %s not found in result", inputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCompilerRTConfig(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "riscv32-unknown-elf"
|
||||
|
||||
config := GetCompilerRTConfig(baseDir, target)
|
||||
|
||||
// Test groups configuration
|
||||
if len(config.Groups) != 1 {
|
||||
t.Errorf("Expected 1 group, got %d", len(config.Groups))
|
||||
} else {
|
||||
group := config.Groups[0]
|
||||
expectedOutput := "libclang_builtins-" + target + ".a"
|
||||
if group.OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected output file %s, got %s", expectedOutput, group.OutputFileName)
|
||||
}
|
||||
|
||||
// Check that files list contains platform-specific files
|
||||
if len(group.Files) == 0 {
|
||||
t.Error("Expected non-empty files list")
|
||||
}
|
||||
|
||||
// Check that CFlags are set
|
||||
if len(group.CFlags) == 0 {
|
||||
t.Error("Expected non-empty CFlags")
|
||||
}
|
||||
|
||||
// Check that CCFlags are set
|
||||
if len(group.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCompilerRTConfig_DifferentTargets(t *testing.T) {
|
||||
targets := []string{
|
||||
"riscv32-unknown-elf",
|
||||
"riscv64-unknown-elf",
|
||||
"arm-none-eabi",
|
||||
"avr-unknown-elf",
|
||||
"xtensa",
|
||||
}
|
||||
|
||||
baseDir := "/test/base"
|
||||
for _, target := range targets {
|
||||
t.Run(target, func(t *testing.T) {
|
||||
config := GetCompilerRTConfig(baseDir, target)
|
||||
|
||||
// Basic validation
|
||||
if config.Url == "" {
|
||||
t.Error("URL should not be empty")
|
||||
}
|
||||
if config.ArchiveSrcDir == "" {
|
||||
t.Error("ArchiveSrcDir should not be empty")
|
||||
}
|
||||
if len(config.Groups) == 0 {
|
||||
t.Error("Should have at least one group")
|
||||
}
|
||||
|
||||
// Check output filename contains target
|
||||
group := config.Groups[0]
|
||||
if !strings.Contains(group.OutputFileName, target) {
|
||||
t.Errorf("Output filename %s should contain target %s", group.OutputFileName, target)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
"github.com/goplus/llgo/internal/env"
|
||||
"github.com/goplus/llgo/internal/targets"
|
||||
"github.com/goplus/llgo/internal/xtool/llvm"
|
||||
@@ -25,6 +26,7 @@ type Export struct {
|
||||
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
|
||||
@@ -219,6 +221,35 @@ func getESPClangPlatform(goos, goarch string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func ldFlagsFromFileName(fileName string) string {
|
||||
return strings.TrimPrefix(strings.TrimSuffix(fileName, ".a"), "lib")
|
||||
}
|
||||
|
||||
func getOrCompileWithConfig(
|
||||
compileConfig *compile.CompileConfig,
|
||||
outputDir string, options compile.CompileOptions,
|
||||
) (ldflags []string, err error) {
|
||||
if err = checkDownloadAndExtractLib(
|
||||
compileConfig.Url, outputDir,
|
||||
compileConfig.ArchiveSrcDir,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
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()
|
||||
@@ -583,6 +614,57 @@ func useTarget(targetName string) (export Export, err error) {
|
||||
}
|
||||
ldflags = append(ldflags, "-L", env.LLGoROOT()) // search targets/*.ld
|
||||
|
||||
var libcIncludeDir []string
|
||||
|
||||
if config.Libc != "" {
|
||||
var libcLDFlags []string
|
||||
var compileConfig *compile.CompileConfig
|
||||
baseDir := filepath.Join(cacheRoot(), "crosscompile")
|
||||
outputDir := filepath.Join(baseDir, config.Libc)
|
||||
|
||||
compileConfig, err = getLibcCompileConfigByName(baseDir, config.Libc, config.LLVMTarget, config.CPU)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
libcLDFlags, err = getOrCompileWithConfig(compileConfig, outputDir, compile.CompileOptions{
|
||||
CC: export.CC,
|
||||
Linker: export.Linker,
|
||||
CCFLAGS: ccflags,
|
||||
LDFLAGS: ldflags,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cflags = append(cflags, compileConfig.LibcCFlags...)
|
||||
ldflags = append(ldflags, libcLDFlags...)
|
||||
|
||||
libcIncludeDir = compileConfig.LibcCFlags
|
||||
export.Libc = config.Libc
|
||||
}
|
||||
|
||||
if config.RTLib != "" {
|
||||
var rtLibLDFlags []string
|
||||
var compileConfig *compile.CompileConfig
|
||||
baseDir := filepath.Join(cacheRoot(), "crosscompile")
|
||||
outputDir := filepath.Join(baseDir, config.RTLib)
|
||||
|
||||
compileConfig, err = getRTCompileConfigByName(baseDir, config.RTLib, config.LLVMTarget)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rtLibLDFlags, err = getOrCompileWithConfig(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
|
||||
@@ -595,7 +677,7 @@ func useTarget(targetName string) (export Export, err error) {
|
||||
// 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 != "" {
|
||||
if targetName != "" && !strings.HasPrefix(targetName, "wasm") && !strings.HasPrefix(targetName, "wasi") {
|
||||
return useTarget(targetName)
|
||||
}
|
||||
return use(goos, goarch, wasiThreads, forceEspClang)
|
||||
|
||||
@@ -176,13 +176,14 @@ func TestUseTarget(t *testing.T) {
|
||||
expectLLVM string
|
||||
expectCPU string
|
||||
}{
|
||||
{
|
||||
name: "WASI Target",
|
||||
targetName: "wasi",
|
||||
expectError: false,
|
||||
expectLLVM: "",
|
||||
expectCPU: "generic",
|
||||
},
|
||||
// FIXME(MeteorsLiu): wasi in useTarget
|
||||
// {
|
||||
// name: "WASI Target",
|
||||
// targetName: "wasi",
|
||||
// expectError: false,
|
||||
// expectLLVM: "",
|
||||
// expectCPU: "generic",
|
||||
// },
|
||||
{
|
||||
name: "RP2040 Target",
|
||||
targetName: "rp2040",
|
||||
@@ -279,13 +280,13 @@ func TestUseTarget(t *testing.T) {
|
||||
|
||||
func TestUseWithTarget(t *testing.T) {
|
||||
// Test target-based configuration takes precedence
|
||||
export, err := Use("linux", "amd64", "wasi", false, true)
|
||||
export, err := Use("linux", "amd64", "esp32", false, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Check if LLVM target is in CCFLAGS
|
||||
found := slices.Contains(export.CCFLAGS, "-mcpu=generic")
|
||||
found := slices.Contains(export.CCFLAGS, "-mcpu=esp32")
|
||||
if !found {
|
||||
t.Errorf("Expected CPU generic in CCFLAGS, got %v", export.CCFLAGS)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package crosscompile
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -78,6 +80,48 @@ func checkDownloadAndExtractESPClang(platformSuffix, dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDownloadAndExtractLib(url, dstDir, internalArchiveSrcDir string) error {
|
||||
// Check if already exists
|
||||
if _, err := os.Stat(dstDir); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create lock file path for the final destination
|
||||
lockPath := dstDir + ".lock"
|
||||
lockFile, err := acquireLock(lockPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire lock: %w", err)
|
||||
}
|
||||
defer releaseLock(lockFile)
|
||||
|
||||
// Double-check after acquiring lock
|
||||
if _, err := os.Stat(dstDir); err == nil {
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s not found in LLGO_ROOT or cache, will download and compile.\n", dstDir)
|
||||
|
||||
description := fmt.Sprintf("lib %s", path.Base(url))
|
||||
|
||||
// Use temporary extraction directory
|
||||
tempExtractDir := dstDir + ".extract"
|
||||
if err := downloadAndExtractArchive(url, tempExtractDir, description); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempExtractDir)
|
||||
|
||||
srcDir := tempExtractDir
|
||||
|
||||
if internalArchiveSrcDir != "" {
|
||||
srcDir = filepath.Join(tempExtractDir, internalArchiveSrcDir)
|
||||
}
|
||||
|
||||
if err := os.Rename(srcDir, dstDir); err != nil {
|
||||
return fmt.Errorf("failed to rename lib directory: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// acquireLock creates and locks a file to prevent concurrent operations
|
||||
func acquireLock(lockPath string) (*os.File, error) {
|
||||
// Ensure the parent directory exists
|
||||
@@ -140,10 +184,14 @@ func downloadAndExtractArchive(url, destDir, description string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract %s archive: %w", description, err)
|
||||
}
|
||||
} else if strings.HasSuffix(filename, ".zip") {
|
||||
err := extractZip(localFile, tempDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract %s archive: %w", description, err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unsupported archive format: %s", filename)
|
||||
}
|
||||
|
||||
// Rename temp directory to target directory
|
||||
if err := os.Rename(tempDir, destDir); err != nil {
|
||||
return fmt.Errorf("failed to rename directory: %w", err)
|
||||
@@ -223,3 +271,44 @@ func extractTarXz(tarXzFile, dest string) error {
|
||||
cmd := exec.Command("tar", "-xf", tarXzFile, "-C", dest)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func extractZip(zipFile, dest string) error {
|
||||
r, err := zip.OpenReader(zipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
decompress := func(file *zip.File) error {
|
||||
path := filepath.Join(dest, file.Name)
|
||||
|
||||
if file.FileInfo().IsDir() {
|
||||
return os.MkdirAll(path, 0700)
|
||||
}
|
||||
|
||||
fs, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
w, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(w, fs); err != nil {
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, file := range r.File {
|
||||
if err = decompress(file); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@ package crosscompile
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -287,14 +290,14 @@ func TestDownloadAndExtractArchive(t *testing.T) {
|
||||
|
||||
func TestDownloadAndExtractArchiveUnsupportedFormat(t *testing.T) {
|
||||
server := createTestServer(t, map[string]string{
|
||||
"test.zip": "fake zip content",
|
||||
"test.7z": "fake zip content",
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
destDir := filepath.Join(tempDir, "extracted")
|
||||
|
||||
err := downloadAndExtractArchive(server.URL+"/test.zip", destDir, "Test Archive")
|
||||
err := downloadAndExtractArchive(server.URL+"/test.7z", destDir, "Test Archive")
|
||||
if err == nil {
|
||||
t.Error("Expected error for unsupported format, got nil")
|
||||
}
|
||||
@@ -303,6 +306,162 @@ func TestDownloadAndExtractArchiveUnsupportedFormat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckDownloadAndExtractLib(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"lib-src/file1.c": "int func1() { return 1; }",
|
||||
"lib-src/file2.c": "int func2() { return 2; }",
|
||||
"lib-src/include/lib.h": "#define LIB_VERSION 1",
|
||||
}
|
||||
|
||||
archivePath := createTestTarGz(t, files)
|
||||
defer os.Remove(archivePath)
|
||||
|
||||
archiveContent, err := os.ReadFile(archivePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read test archive: %v", err)
|
||||
}
|
||||
|
||||
server := createTestServer(t, map[string]string{
|
||||
"test-lib.tar.gz": string(archiveContent),
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
destDir := filepath.Join(tempDir, "test-lib")
|
||||
|
||||
t.Run("LibAlreadyExists", func(t *testing.T) {
|
||||
if err := os.MkdirAll(destDir, 0755); err != nil {
|
||||
t.Fatalf("Failed to create existing lib dir: %v", err)
|
||||
}
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "")
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error when lib exists, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DownloadAndExtractWithoutInternalDir", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "lib-src")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download and extract lib: %v", err)
|
||||
}
|
||||
cmd := exec.Command("ls", destDir)
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Run()
|
||||
|
||||
for name, expectedContent := range files {
|
||||
relPath := strings.TrimPrefix(name, "lib-src/")
|
||||
filePath := filepath.Join(destDir, relPath)
|
||||
|
||||
fmt.Println(filePath, destDir)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read extracted file %s: %v", relPath, err)
|
||||
continue
|
||||
}
|
||||
if string(content) != expectedContent {
|
||||
t.Errorf("File %s: expected content %q, got %q", relPath, expectedContent, string(content))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DownloadAndExtractWithInternalDir", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "lib-src")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download and extract lib: %v", err)
|
||||
}
|
||||
|
||||
for name, expectedContent := range files {
|
||||
relPath := strings.TrimPrefix(name, "lib-src/")
|
||||
filePath := filepath.Join(destDir, relPath)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read extracted file %s: %v", relPath, err)
|
||||
continue
|
||||
}
|
||||
if string(content) != expectedContent {
|
||||
t.Errorf("File %s: expected content %q, got %q", relPath, expectedContent, string(content))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DownloadFailure", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/nonexistent.tar.gz", destDir, "")
|
||||
if err == nil {
|
||||
t.Error("Expected error for non-existent archive, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RenameFailure", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "lib-src222")
|
||||
if err == nil {
|
||||
t.Error("Expected error for rename failure, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckDownloadAndExtractLibInternalDir(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"project-1.0.0/src/file1.c": "int func1() { return 1; }",
|
||||
"project-1.0.0/include/lib.h": "#define LIB_VERSION 1",
|
||||
"project-1.0.0/README.md": "Project documentation",
|
||||
}
|
||||
|
||||
archivePath := createTestTarGz(t, files)
|
||||
defer os.Remove(archivePath)
|
||||
|
||||
archiveContent, err := os.ReadFile(archivePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read test archive: %v", err)
|
||||
}
|
||||
|
||||
server := createTestServer(t, map[string]string{
|
||||
"project.tar.gz": string(archiveContent),
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
destDir := filepath.Join(tempDir, "project-lib")
|
||||
|
||||
t.Run("CorrectInternalDir", func(t *testing.T) {
|
||||
err := checkDownloadAndExtractLib(server.URL+"/project.tar.gz", destDir, "project-1.0.0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download and extract lib: %v", err)
|
||||
}
|
||||
|
||||
for name, expectedContent := range files {
|
||||
relPath := strings.TrimPrefix(name, "project-1.0.0/")
|
||||
filePath := filepath.Join(destDir, relPath)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read extracted file %s: %v", relPath, err)
|
||||
continue
|
||||
}
|
||||
if string(content) != expectedContent {
|
||||
t.Errorf("File %s: expected content %q, got %q", relPath, expectedContent, string(content))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IncorrectInternalDir", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/project.tar.gz", destDir, "wrong-dir")
|
||||
if err == nil {
|
||||
t.Error("Expected error for missing internal dir, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Mock test for WASI SDK (without actual download)
|
||||
func TestWasiSDKExtractionLogic(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
@@ -490,3 +649,130 @@ func TestESPClangDownloadWhenNotExists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractZip(t *testing.T) {
|
||||
// Create temporary test directory
|
||||
tempDir := t.TempDir()
|
||||
zipPath := filepath.Join(tempDir, "test.zip")
|
||||
destDir := filepath.Join(tempDir, "extracted")
|
||||
|
||||
// 1. Test successful extraction
|
||||
t.Run("SuccessfulExtraction", func(t *testing.T) {
|
||||
// Create test ZIP file
|
||||
if err := createTestZip(zipPath); err != nil {
|
||||
t.Fatalf("Failed to create test zip: %v", err)
|
||||
}
|
||||
|
||||
// Execute extraction
|
||||
if err := extractZip(zipPath, destDir); err != nil {
|
||||
t.Fatalf("extractZip failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify extraction results
|
||||
verifyExtraction(t, destDir)
|
||||
})
|
||||
|
||||
// 2. Test invalid ZIP file
|
||||
t.Run("InvalidZipFile", func(t *testing.T) {
|
||||
// Create invalid ZIP file (actually a text file)
|
||||
if err := os.WriteFile(zipPath, []byte("not a zip file"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Execute extraction and expect error
|
||||
if err := extractZip(zipPath, destDir); err == nil {
|
||||
t.Error("Expected error for invalid zip file, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
// 3. Test non-writable destination
|
||||
t.Run("UnwritableDestination", func(t *testing.T) {
|
||||
// Create test ZIP file
|
||||
if err := createTestZip(zipPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create read-only destination directory
|
||||
readOnlyDir := filepath.Join(tempDir, "readonly")
|
||||
if err := os.MkdirAll(readOnlyDir, 0400); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Execute extraction and expect error
|
||||
if err := extractZip(zipPath, readOnlyDir); err == nil {
|
||||
t.Error("Expected error for unwritable destination, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create test ZIP file
|
||||
func createTestZip(zipPath string) error {
|
||||
// Create ZIP file
|
||||
zipFile, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Add directory
|
||||
dirHeader := &zip.FileHeader{
|
||||
Name: "testdir/",
|
||||
Method: zip.Deflate,
|
||||
Modified: time.Now(),
|
||||
}
|
||||
dirHeader.SetMode(os.ModeDir | 0755)
|
||||
if _, err := zipWriter.CreateHeader(dirHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add file1
|
||||
file1, err := zipWriter.Create("file1.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := file1.Write([]byte("Hello from file1")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add nested file
|
||||
nestedFile, err := zipWriter.Create("testdir/nested.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := nestedFile.Write([]byte("Nested content")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify extraction results
|
||||
func verifyExtraction(t *testing.T, destDir string) {
|
||||
// Verify directory exists
|
||||
if _, err := os.Stat(filepath.Join(destDir, "testdir")); err != nil {
|
||||
t.Errorf("Directory not extracted: %v", err)
|
||||
}
|
||||
|
||||
// Verify file1 content
|
||||
file1Path := filepath.Join(destDir, "file1.txt")
|
||||
content, err := os.ReadFile(file1Path)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read file1: %v", err)
|
||||
}
|
||||
if string(content) != "Hello from file1" {
|
||||
t.Errorf("File1 content mismatch. Got: %s", content)
|
||||
}
|
||||
|
||||
// Verify nested file content
|
||||
nestedPath := filepath.Join(destDir, "testdir", "nested.txt")
|
||||
content, err = os.ReadFile(nestedPath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read nested file: %v", err)
|
||||
}
|
||||
if string(content) != "Nested content" {
|
||||
t.Errorf("Nested file content mismatch. Got: %s", content)
|
||||
}
|
||||
}
|
||||
|
||||
42
internal/crosscompile/libc.go
Normal file
42
internal/crosscompile/libc.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package crosscompile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile/libc"
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile/rtlib"
|
||||
)
|
||||
|
||||
// GetCompileConfigByName retrieves libc compilation configuration by name
|
||||
// Returns compilation file lists and corresponding cflags
|
||||
func getLibcCompileConfigByName(baseDir, libcName, target, mcpu string) (*compile.CompileConfig, error) {
|
||||
if libcName == "" {
|
||||
return nil, fmt.Errorf("libc name cannot be empty")
|
||||
}
|
||||
libcDir := filepath.Join(baseDir, libcName)
|
||||
|
||||
switch libcName {
|
||||
case "picolibc":
|
||||
return libc.GetPicolibcConfig(libcDir, target), nil
|
||||
case "newlib-esp32":
|
||||
return libc.GetNewlibESP32Config(libcDir, target, mcpu), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported libc: %s", libcName)
|
||||
}
|
||||
}
|
||||
|
||||
func getRTCompileConfigByName(baseDir, rtName, target string) (*compile.CompileConfig, error) {
|
||||
if rtName == "" {
|
||||
return nil, fmt.Errorf("rt name cannot be empty")
|
||||
}
|
||||
rtDir := filepath.Join(baseDir, rtName)
|
||||
|
||||
switch rtName {
|
||||
case "compiler-rt":
|
||||
return rtlib.GetCompilerRTConfig(rtDir, target), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported rt: %s", rtName)
|
||||
}
|
||||
}
|
||||
109
internal/crosscompile/libc_test.go
Normal file
109
internal/crosscompile/libc_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
//go:build !llgo
|
||||
|
||||
package crosscompile
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetLibcCompileConfigByName(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "armv7"
|
||||
mcpu := "cortex-m4"
|
||||
|
||||
t.Run("EmptyName", func(t *testing.T) {
|
||||
_, err := getLibcCompileConfigByName(baseDir, "", target, mcpu)
|
||||
if err == nil || err.Error() != "libc name cannot be empty" {
|
||||
t.Errorf("Expected empty name error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UnsupportedLibc", func(t *testing.T) {
|
||||
_, err := getLibcCompileConfigByName(baseDir, "invalid", target, mcpu)
|
||||
if err == nil || err.Error() != "unsupported libc: invalid" {
|
||||
t.Errorf("Expected unsupported libc error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Picolibc", func(t *testing.T) {
|
||||
cfg, err := getLibcCompileConfigByName(baseDir, "picolibc", target, mcpu)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Groups) != 1 {
|
||||
t.Fatalf("Expected 1 group, got %d", len(cfg.Groups))
|
||||
}
|
||||
group := cfg.Groups[0]
|
||||
|
||||
expectedFile := filepath.Join(baseDir, "picolibc", "newlib", "libc", "string", "memmem.c")
|
||||
if !slices.Contains(group.Files, expectedFile) {
|
||||
t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files)
|
||||
}
|
||||
|
||||
expectedFlag := "-I" + filepath.Join("/test", "base", "picolibc")
|
||||
if !slices.Contains(group.CFlags, expectedFlag) {
|
||||
t.Errorf("Expected flags [%s], got: %v", expectedFlag, group.CFlags)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("NewlibESP32", func(t *testing.T) {
|
||||
cfg, err := getLibcCompileConfigByName(baseDir, "newlib-esp32", target, mcpu)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Groups) != 3 {
|
||||
t.Fatalf("Expected 3 group, got %d", len(cfg.Groups))
|
||||
}
|
||||
group := cfg.Groups[0]
|
||||
|
||||
expectedFile := filepath.Join(baseDir, "newlib-esp32", "libgloss", "xtensa", "crt1-boards.S")
|
||||
if !slices.Contains(group.Files, expectedFile) {
|
||||
t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files)
|
||||
}
|
||||
|
||||
expectedFlags := "-I" + filepath.Join(baseDir, "newlib-esp32", "libgloss")
|
||||
if !slices.Contains(group.CFlags, expectedFlags) {
|
||||
t.Errorf("Expected flags %v, got: %v", expectedFlags, group.CFlags)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRTCompileConfigByName(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "wasm32"
|
||||
|
||||
t.Run("EmptyName", func(t *testing.T) {
|
||||
_, err := getRTCompileConfigByName(baseDir, "", target)
|
||||
if err == nil || err.Error() != "rt name cannot be empty" {
|
||||
t.Errorf("Expected empty name error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UnsupportedRT", func(t *testing.T) {
|
||||
_, err := getRTCompileConfigByName(baseDir, "invalid", target)
|
||||
if err == nil || err.Error() != "unsupported rt: invalid" {
|
||||
t.Errorf("Expected unsupported rt error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CompilerRT", func(t *testing.T) {
|
||||
cfg, err := getRTCompileConfigByName(baseDir, "compiler-rt", target)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Groups) != 1 {
|
||||
t.Fatalf("Expected 1 group, got %d", len(cfg.Groups))
|
||||
}
|
||||
group := cfg.Groups[0]
|
||||
|
||||
expectedFile := filepath.Join(baseDir, "compiler-rt", "lib", "builtins", "absvdi2.c")
|
||||
if !slices.Contains(group.Files, expectedFile) {
|
||||
t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user