Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -838,15 +838,16 @@ func isWasmTarget(goos string) bool {
return slices.Contains([]string{"wasi", "js", "wasip1"}, goos)
}

func needStart(conf *Config) bool {
if conf.Target == "" {
return !isWasmTarget(conf.Goos)
func needStart(ctx *context) bool {
if ctx.buildConf.Target == "" {
return !isWasmTarget(ctx.buildConf.Goos)
}
switch conf.Target {
switch ctx.buildConf.Target {
case "wasip2":
return false
default:
return true
// since newlib-esp32 provides _start, we don't need to provide a fake _start function
return ctx.crossCompile.Libc != "newlib-esp32"
}
}

Expand Down Expand Up @@ -903,10 +904,10 @@ define weak void @_start() {
}
`
mainDefine := "define i32 @main(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr"
if !needStart(ctx.buildConf) && isWasmTarget(ctx.buildConf.Goos) {
if !needStart(ctx) && isWasmTarget(ctx.buildConf.Goos) {
mainDefine = "define hidden noundef i32 @__main_argc_argv(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr"
}
if !needStart(ctx.buildConf) {
if !needStart(ctx) {
startDefine = ""
}
mainCode := fmt.Sprintf(`; ModuleID = 'main'
Expand Down
100 changes: 100 additions & 0 deletions internal/crosscompile/compile/compile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package compile

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"slices"
"strings"

"github.com/goplus/llgo/internal/clang"
)

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, g.OutputFileName)
_, err := os.Stat(archive)
return !os.IsNotExist(err)
}

func (g CompileGroup) Compile(outputDir, cc, linkerName string, extraCCFlags, extraLDFlags []string) (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(extraLDFlags), g.LDFlags...)
compileCCFlags := append(slices.Clone(extraCCFlags), g.CCFlags...)
cfg := clang.NewConfig(cc, compileCCFlags, g.CFlags, compileLDFlags, linkerName)

var objFiles []string

compiler := clang.NewCompiler(cfg)

compiler.Verbose = false

archive := filepath.Join(outputDir, 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
}
fmt.Fprintf(os.Stderr, "Compile file %s to %s...\n", file, tempObjFile.Name())

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(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
}

func (c CompileConfig) IsCompiled(outputDir string) bool {
for _, group := range c.Groups {
if !group.IsCompiled(outputDir) {
return false
}
}
return true
}
142 changes: 142 additions & 0 deletions internal/crosscompile/compile/compile_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package compile

import (
"os"
"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.IsCompiled(".") || 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.IsCompiled(".") && !cfg.Groups[0].IsCompiled(".") {
t.Errorf("unexpected result: should true")
}
})
}

func TestCompile(t *testing.T) {
t.Run("Skip compile", func(t *testing.T) {
tmpFile, err := os.CreateTemp(".", "test*.a")
if err != nil {
t.Error(err)
return
}
defer os.Remove(tmpFile.Name())
group := CompileGroup{
OutputFileName: tmpFile.Name(),
}
err = group.Compile(".", "clang", "lld", nil, nil)
if err != nil {
t.Errorf("unexpected result: should nil")
}
})

t.Run("TmpDir Fail", func(t *testing.T) {
err := os.Mkdir("test-compile", 0)
if err != nil {
t.Error(err)
return
}
defer os.RemoveAll("test-compile")

os.Setenv("TMPDIR", "test-compile")
defer os.Unsetenv("TMPDIR")

group := CompileGroup{
OutputFileName: "nop.a",
}
err = group.Compile(".", "clang", "lld", nil, nil)
if err == nil {
t.Errorf("unexpected result: should not nil")
}
})

t.Run("Compile", func(t *testing.T) {
tmpFile, err := os.CreateTemp("", "test*.c")
if err != nil {
t.Error(err)
return
}
defer os.Remove(tmpFile.Name())

_, 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(".", "clang", "lld", []string{"-nostdinc"}, nil)
if err == nil {
t.Errorf("unexpected result: should not nil")
}
err = group.Compile(".", "clang", "lld", nil, nil)
if err != nil {
t.Errorf("unexpected result: should not nil")
}
if _, err := os.Stat("nop.a"); os.IsNotExist(err) {
t.Error("unexpected result: compiled nop.a not found")
return
}
defer os.Remove("nop.a")

items, err := nm.New("").List("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")
}
})
}
Loading
Loading