test: fix compile test

test: add asm test

test: add libc.go test

test: add DownloadAndExtractLibInternalDir test

test: fix checkDownload test

test: fix asm test

fix: check isCompile

fix: remove debug

fix: remove debug
This commit is contained in:
Haolan
2025-09-02 09:45:42 +08:00
parent 1d3ecb287a
commit f875347ad9
6 changed files with 355 additions and 36 deletions

View File

@@ -28,9 +28,9 @@ type CompileGroup struct {
} }
func (g CompileGroup) IsCompiled(outputDir string) bool { func (g CompileGroup) IsCompiled(outputDir string) bool {
archive := filepath.Join(outputDir, g.OutputFileName) archive := filepath.Join(outputDir, filepath.Base(g.OutputFileName))
_, err := os.Stat(archive) _, err := os.Stat(archive)
return !os.IsNotExist(err) return err == nil
} }
func (g CompileGroup) Compile( func (g CompileGroup) Compile(
@@ -57,7 +57,7 @@ func (g CompileGroup) Compile(
compiler.Verbose = true compiler.Verbose = true
archive := filepath.Join(outputDir, g.OutputFileName) archive := filepath.Join(outputDir, filepath.Base(g.OutputFileName))
fmt.Fprintf(os.Stderr, "Start to compile group %s to %s...\n", g.OutputFileName, archive) fmt.Fprintf(os.Stderr, "Start to compile group %s to %s...\n", g.OutputFileName, archive)
for _, file := range g.Files { for _, file := range g.Files {

View File

@@ -38,7 +38,7 @@ func TestIsCompile(t *testing.T) {
}, },
} }
if cfg.Groups[0].IsCompiled(filepath.Dir(tmpFile.Name())) { if !cfg.Groups[0].IsCompiled(filepath.Dir(tmpFile.Name())) {
t.Errorf("unexpected result: should true") t.Errorf("unexpected result: should true")
} }
}) })
@@ -46,39 +46,48 @@ func TestIsCompile(t *testing.T) {
func TestCompile(t *testing.T) { func TestCompile(t *testing.T) {
t.Run("Skip compile", func(t *testing.T) { t.Run("Skip compile", func(t *testing.T) {
tmpFile, err := os.CreateTemp(".", "test*.a") 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
defer os.Remove(tmpFile.Name())
group := CompileGroup{ group := CompileGroup{
OutputFileName: tmpFile.Name(), OutputFileName: tmpFile.Name(),
} }
err = group.Compile(".", CompileOptions{ err = group.Compile(tmpDir, CompileOptions{
CC: "clang", CC: "clang",
Linker: "lld", Linker: "lld",
}) })
if err != nil { if err != nil {
t.Errorf("unexpected result: should nil") t.Errorf("unexpected result: should nil: %v", err)
} }
}) })
t.Run("TmpDir Fail", func(t *testing.T) { t.Run("TmpDir Fail", func(t *testing.T) {
err := os.Mkdir("test-compile", 0) tmpDir := filepath.Join(t.TempDir(), "test-compile")
os.RemoveAll(tmpDir)
err := os.Mkdir(tmpDir, 0)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
defer os.RemoveAll("test-compile") defer os.RemoveAll(tmpDir)
os.Setenv("TMPDIR", "test-compile") os.Setenv("TMPDIR", tmpDir)
defer os.Unsetenv("TMPDIR") defer os.Unsetenv("TMPDIR")
group := CompileGroup{ group := CompileGroup{
OutputFileName: "nop.a", OutputFileName: "nop.a",
} }
err = group.Compile(".", CompileOptions{ err = group.Compile(tmpDir, CompileOptions{
CC: "clang", CC: "clang",
Linker: "lld", Linker: "lld",
}) })
@@ -88,20 +97,26 @@ func TestCompile(t *testing.T) {
}) })
t.Run("Compile", func(t *testing.T) { t.Run("Compile", func(t *testing.T) {
tmpFile, err := os.CreateTemp("", "test*.c") 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
defer os.Remove(tmpFile.Name())
_, err = tmpFile.Write([]byte(`#include <math.h> _, err = tmpFile.Write([]byte(`#include <math.h>
void Foo() { void Foo() {
double x = 2.0; double x = 2.0;
double y = sqrt(x); double y = sqrt(x);
(void) y ; (void) y ;
} }
`)) `))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@@ -111,7 +126,7 @@ func TestCompile(t *testing.T) {
OutputFileName: "nop.a", OutputFileName: "nop.a",
Files: []string{tmpFile.Name()}, Files: []string{tmpFile.Name()},
} }
err = group.Compile(".", CompileOptions{ err = group.Compile(tmpDir, CompileOptions{
CC: "clang", CC: "clang",
Linker: "lld", Linker: "lld",
CCFLAGS: []string{"-nostdinc"}, CCFLAGS: []string{"-nostdinc"},
@@ -119,20 +134,19 @@ func TestCompile(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("unexpected result: should not nil") t.Errorf("unexpected result: should not nil")
} }
err = group.Compile(".", CompileOptions{ err = group.Compile(tmpDir, CompileOptions{
CC: "clang", CC: "clang",
Linker: "lld", Linker: "lld",
}) })
if err != nil { if err != nil {
t.Errorf("unexpected result: should not nil") t.Errorf("unexpected result: should not nil")
} }
if _, err := os.Stat("nop.a"); os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(tmpDir, "nop.a")); os.IsNotExist(err) {
t.Error("unexpected result: compiled nop.a not found") t.Error("unexpected result: compiled nop.a not found")
return return
} }
defer os.Remove("nop.a")
items, err := nm.New("").List("nop.a") items, err := nm.New("").List(filepath.Join(tmpDir, "nop.a"))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@@ -153,4 +167,41 @@ func TestCompile(t *testing.T) {
t.Errorf("cannot find symbol Foo") 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)
}
})
} }

View File

@@ -176,13 +176,14 @@ func TestUseTarget(t *testing.T) {
expectLLVM string expectLLVM string
expectCPU string expectCPU string
}{ }{
{ // FIXME(MeteorsLiu): wasi in useTarget
name: "WASI Target", // {
targetName: "wasi", // name: "WASI Target",
expectError: false, // targetName: "wasi",
expectLLVM: "", // expectError: false,
expectCPU: "generic", // expectLLVM: "",
}, // expectCPU: "generic",
// },
{ {
name: "RP2040 Target", name: "RP2040 Target",
targetName: "rp2040", targetName: "rp2040",
@@ -279,13 +280,13 @@ func TestUseTarget(t *testing.T) {
func TestUseWithTarget(t *testing.T) { func TestUseWithTarget(t *testing.T) {
// Test target-based configuration takes precedence // Test target-based configuration takes precedence
export, err := Use("linux", "amd64", false, "wasi") export, err := Use("linux", "amd64", false, "esp32")
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
// Check if LLVM target is in CCFLAGS // Check if LLVM target is in CCFLAGS
found := slices.Contains(export.CCFLAGS, "-mcpu=generic") found := slices.Contains(export.CCFLAGS, "-mcpu=esp32")
if !found { if !found {
t.Errorf("Expected CPU generic in CCFLAGS, got %v", export.CCFLAGS) t.Errorf("Expected CPU generic in CCFLAGS, got %v", export.CCFLAGS)
} }

View File

@@ -100,7 +100,7 @@ func checkDownloadAndExtractLib(url, dstDir, internalArchiveSrcDir string) error
} }
fmt.Fprintf(os.Stderr, "%s not found in LLGO_ROOT or cache, will download and compile.\n", dstDir) 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)) description := fmt.Sprintf("lib %s", path.Base(url))
// Use temporary extraction directory // Use temporary extraction directory
tempExtractDir := dstDir + ".extract" tempExtractDir := dstDir + ".extract"
@@ -115,7 +115,9 @@ func checkDownloadAndExtractLib(url, dstDir, internalArchiveSrcDir string) error
srcDir = filepath.Join(tempExtractDir, internalArchiveSrcDir) srcDir = filepath.Join(tempExtractDir, internalArchiveSrcDir)
} }
os.Rename(srcDir, dstDir) if err := os.Rename(srcDir, dstDir); err != nil {
return fmt.Errorf("failed to rename lib directory: %w", err)
}
return nil return nil
} }

View File

@@ -6,9 +6,11 @@ package crosscompile
import ( import (
"archive/tar" "archive/tar"
"compress/gzip" "compress/gzip"
"fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
@@ -303,6 +305,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) // Mock test for WASI SDK (without actual download)
func TestWasiSDKExtractionLogic(t *testing.T) { func TestWasiSDKExtractionLogic(t *testing.T) {
tempDir := t.TempDir() tempDir := t.TempDir()

View File

@@ -0,0 +1,107 @@
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)
}
})
}