package main import ( "os" "unsafe" ) //export mxc_run func cRun(argv unsafe.Pointer, argc int32) int32 //export mxc_getenv func cGetenv(name unsafe.Pointer, nameLen int32, buf unsafe.Pointer, bufCap int32) int32 //export mxc_listdir func cListdir(dir unsafe.Pointer, dirLen int32, buf unsafe.Pointer, bufCap int32) int32 //export write func cWrite(fd int32, buf unsafe.Pointer, count uint32) int32 //export mxc_writefile func cWritefile(path unsafe.Pointer, pathLen int32, data unsafe.Pointer, dataLen int32, mode uint32) int32 //export mxc_mkdir func cMkdir(path unsafe.Pointer, pathLen int32, mode uint32) int32 //export mxc_readfile func cReadfile(path unsafe.Pointer, pathLen int32, buf unsafe.Pointer, bufCap int32) int32 //export mxc_filesize func cFilesize(path unsafe.Pointer, pathLen int32) int32 func writeStr(fd int32, s string) { b := []byte(s) if len(b) > 0 { cWrite(fd, unsafe.Pointer(unsafe.SliceData(b)), uint32(len(b))) } } func writeFile(path string, data []byte, mode uint32) { if len(path) == 0 { return } cWritefile(unsafe.Pointer(unsafe.SliceData([]byte(path))), int32(len(path)), unsafe.Pointer(unsafe.SliceData(data)), int32(len(data)), mode) } func mkdirAll(path string, mode uint32) { if len(path) == 0 { return } cMkdir(unsafe.Pointer(unsafe.SliceData([]byte(path))), int32(len(path)), mode) } func readFile(path string) ([]byte, bool) { if len(path) == 0 { return nil, false } pb := []byte(path) sz := cFilesize(unsafe.Pointer(unsafe.SliceData(pb)), int32(len(pb))) if sz < 0 { return nil, false } if sz == 0 { sz = 65536 } buf := []byte{:sz} n := cReadfile(unsafe.Pointer(unsafe.SliceData(pb)), int32(len(pb)), unsafe.Pointer(unsafe.SliceData(buf)), sz) if n < 0 { return nil, false } return buf[:n], true } func fatal(msg string) { writeStr(2, "mxc: " | msg | "\n") os.Exit(1) } func getenv(name string) string { nb := []byte(name) buf := []byte{:4096} n := cGetenv(unsafe.Pointer(unsafe.SliceData(nb)), int32(len(nb)), unsafe.Pointer(unsafe.SliceData(buf)), int32(len(buf))) if n <= 0 { return "" } return string(buf[:n]) } func getArgs() []string { data, ok := readFile("/proc/self/cmdline") if !ok { return nil } var args []string start := int32(0) for i := int32(0); i < int32(len(data)); i++ { if data[i] == 0 { if i > start { args = append(args, string(data[start:i])) } start = i + 1 } } if start < int32(len(data)) { args = append(args, string(data[start:])) } return args } func listFiles(dir, ext string) []string { db := []byte(dir) buf := []byte{:65536} n := cListdir(unsafe.Pointer(unsafe.SliceData(db)), int32(len(db)), unsafe.Pointer(unsafe.SliceData(buf)), int32(len(buf))) if n <= 0 { return nil } var result []string start := int32(0) for i := int32(0); i < n; i++ { if buf[i] == 0 { name := string(buf[start:i]) if hasSuffix(name, ext) { result = append(result, name) } start = i + 1 } } return result } func run(args []string) int32 { argv := []unsafe.Pointer{:len(args) + 1} cstrs := [][]byte{:len(args)} for i, a := range args { b := []byte{:len(a) + 1} copy(b, a) b[len(a)] = 0 cstrs[i] = b argv[i] = unsafe.Pointer(unsafe.SliceData(b)) } return cRun(unsafe.Pointer(unsafe.SliceData(argv)), int32(len(args))) } func splitLines(s string) []string { var result []string start := int32(0) for i := int32(0); i < int32(len(s)); i++ { if s[i] == '\n' { if i > start { result = append(result, s[start:i]) } start = i + 1 } } if start < int32(len(s)) { result = append(result, s[start:]) } return result } func hasPrefix(s, prefix string) bool { return len(s) >= len(prefix) && s[:len(prefix)] == prefix } func hasSuffix(s, suffix string) bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } func trimSpace(s string) string { i := int32(0) for i < int32(len(s)) && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' || s[i] == '\r') { i++ } j := int32(len(s)) for j > i && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n' || s[j-1] == '\r') { j-- } return s[i:j] } func joinPath(a, b string) string { if len(a) == 0 { return b } if a[len(a)-1] == '/' { return a | b } return a | "/" | b } func pathBase(path string) string { for i := int32(len(path)) - 1; i >= 0; i-- { if path[i] == '/' { return path[i+1:] } } return path } func joinStrings(ss []string, sep string) string { if len(ss) == 0 { return "" } n := int32(0) for _, s := range ss { n += int32(len(s)) } n += int32(len(sep)) * (int32(len(ss)) - 1) buf := []byte{:0:n} for i, s := range ss { if i > 0 { buf = append(buf, sep...) } buf = append(buf, s...) } return string(buf) } func appendUniq(ss []string, s string) []string { for _, x := range ss { if x == s { return ss } } return append(ss, s) } func fnvHash(data []byte) string { h := uint32(2166136261) for i := int32(0); i < int32(len(data)); i++ { h ^= uint32(data[i]) h *= uint32(16777619) } hex := "0123456789abcdef" buf := []byte{:8} for i := int32(7); i >= 0; i-- { buf[i] = hex[h&0xf] h >>= 4 } return string(buf) } func pkgCacheDir() string { home := getenv("HOME") if home == "" { home = "/tmp" } return joinPath(home, ".cache/moxie/pkg") } func pkgCacheKey(dir string, files []string) string { var sorted []string for _, f := range files { sorted = append(sorted, f) } for i := int32(1); i < int32(len(sorted)); i++ { for j := i; j > 0 && sorted[j] < sorted[j-1]; j-- { sorted[j], sorted[j-1] = sorted[j-1], sorted[j] } } var all []byte for _, f := range sorted { p := joinPath(dir, f) data, ok := readFile(p) if !ok { continue } all = append(all, data...) all = append(all, 0) } return fnvHash(all) } func cachedBcPath(cacheBase, pkgPath, hash string) string { return joinPath(joinPath(cacheBase, safeName(pkgPath)), hash | ".bc") } func isStdlib(path string) bool { return !hasPrefix(path, "/") && !hasPrefix(path, "./") && !hasPrefix(path, "../") } var builtinOnly []string func isBuiltinOnly(path string) bool { for _, b := range builtinOnly { if path == b { return true } } return false } func loadSysroot(path string) { data, ok := readFile(path) if !ok { return } for _, line := range splitLines(string(data)) { line = trimSpace(line) if len(line) == 0 || line[0] == '#' { continue } eq := int32(-1) for i := int32(0); i < int32(len(line)); i++ { if line[i] == '=' { eq = i break } } if eq < 0 { continue } key := line[:eq] val := line[eq+1:] os.Setenv(key, val) } } func splitBySpace(s string) []string { var result []string start := int32(0) inWord := false for i := int32(0); i < int32(len(s)); i++ { if s[i] == ' ' || s[i] == '\t' { if inWord { result = append(result, s[start:i]) inWord = false } } else { if !inWord { start = i inWord = true } } } if inWord { result = append(result, s[start:]) } return result } func safeName(pkg string) string { b := []byte{:len(pkg)} for i := int32(0); i < int32(len(pkg)); i++ { if pkg[i] == '/' { b[i] = '_' } else { b[i] = pkg[i] } } return string(b) } func extractPkgName(data []byte) string { for i := int32(0); i < int32(len(data)); i++ { if i+8 < int32(len(data)) && string(data[i:i+8]) == "package " { j := i + 8 for j < int32(len(data)) && data[j] != '\n' && data[j] != '\r' && data[j] != ' ' { j++ } return string(data[i+8 : j]) } if data[i] == '\n' { continue } if data[i] == '/' && i+1 < int32(len(data)) && data[i+1] == '/' { for i < int32(len(data)) && data[i] != '\n' { i++ } continue } break } return "main" } func extractImports(data []byte) []string { var imports []string lines := splitLines(string(data)) inBlock := false for _, line := range lines { line = trimSpace(line) if line == "import (" { inBlock = true continue } if inBlock && line == ")" { inBlock = false continue } if inBlock { imp := extractQuoted(line) if imp != "" { imports = appendUniq(imports, imp) } } if hasPrefix(line, "import \"") { imp := extractQuoted(line[7:]) if imp != "" { imports = appendUniq(imports, imp) } } } return imports } func extractQuoted(s string) string { start := int32(-1) for i := int32(0); i < int32(len(s)); i++ { if s[i] == '"' { if start < 0 { start = i + 1 } else { return s[start:i] } } } return "" } func concatSources(dir string, files []string) []byte { imports := map[string]bool{} var bodies [][]byte pkgName := "" for _, f := range files { data, ok := readFile(joinPath(dir, f)) if !ok { continue } if pkgName == "" { pkgName = extractPkgName(data) } lines := splitLines(string(data)) var body []string i := int32(0) for i < int32(len(lines)) { line := trimSpace(lines[i]) if hasPrefix(line, "package ") { i++ continue } if line == "import (" { i++ for i < int32(len(lines)) { imp := trimSpace(lines[i]) i++ if imp == ")" { break } if imp != "" { imports[imp] = true } } continue } if hasPrefix(line, "import ") && !hasPrefix(line, "import (") { imp := line[7:] imports[imp] = true i++ continue } body = append(body, lines[i]) i++ } bodies = append(bodies, []byte(joinStrings(body, "\n"))) } var out []byte out = append(out, ("package " | pkgName | "\n")...) if len(imports) > 0 { var impKeys []string for imp := range imports { impKeys = append(impKeys, imp) } for i := int32(1); i < int32(len(impKeys)); i++ { for j := i; j > 0 && impKeys[j] < impKeys[j-1]; j-- { impKeys[j], impKeys[j-1] = impKeys[j-1], impKeys[j] } } out = append(out, "import (\n"...) for _, imp := range impKeys { out = append(out, '\t') out = append(out, imp...) out = append(out, '\n') } out = append(out, ")\n"...) } for _, b := range bodies { out = append(out, b...) out = append(out, '\n') } return out } func fileContainsPart(base string, part string) bool { plen := len(part) for i := 0; i <= len(base)-plen; i++ { if base[i:i+plen] == part { before := i == 0 || base[i-1] == '_' after := i+plen == len(base) || base[i+plen] == '_' if before && after { return true } } } return false } func shouldSkipFile(name string) bool { if hasSuffix(name, "_test.go") || hasSuffix(name, "_test.mx") { return true } base := name if hasSuffix(base, ".go") { base = base[:len(base)-3] } else if hasSuffix(base, ".mx") { base = base[:len(base)-3] } archSkip := []string{"arm64"} for _, a := range archSkip { if fileContainsPart(base, a) { return true } } suffixSkip := []string{"_wasm"} for _, s := range suffixSkip { if hasSuffix(base, s) { return true } } exact := []string{"pipe", "multi", "scan", "reader", "replace", "search", "ioutil"} for _, s := range exact { if base == s { return true } } return false } func hasBuildConstraint(data []byte, forbidden []string) bool { for i := int32(0); i < int32(len(data)) && i < 512; i++ { if data[i] == 'p' { return false } if i+11 < int32(len(data)) && string(data[i:i+11]) == "//go:build " { var buf []byte for j := i + 11; j < int32(len(data)) && data[j] != '\n'; j++ { buf = append(buf, data[j]) } line := string(buf) for _, f := range forbidden { if line == f || hasPrefix(line, f | " ") || hasPrefix(line, f | "\t") { return true } } return false } if data[i] == '\n' { continue } if data[i] == '/' && i+1 < int32(len(data)) && data[i+1] == '/' { for i < int32(len(data)) && data[i] != '\n' { i++ } continue } } return false } func hasImport(data []byte, pkg string) bool { target := "\"" | pkg | "\"" for i := int32(0); i <= int32(len(data))-int32(len(target)); i++ { if string(data[i:i+int32(len(target))]) == target { return true } } return false } type pkgInfo struct { path string name string dir string files []string cfiles []string imports []string } func discoverPkg(pkgPath, root string) *pkgInfo { var dir string if hasPrefix(pkgPath, "/") || hasPrefix(pkgPath, "./") || hasPrefix(pkgPath, "../") { dir = pkgPath } else { dir = joinPath(root, joinPath("src", pkgPath)) } goFiles := listFiles(dir, ".go") mxFiles := listFiles(dir, ".mx") cFiles := listFiles(dir, ".c") allMx := append(mxFiles, goFiles...) forbidden := []string{"wasm", "js", "ignore", "compiler_bootstrap", "scheduler.tasks", "scheduler.cores", "scheduler.asyncify", "faketime", "baremetal", "nintendoswitch"} var files []string for _, f := range allMx { if shouldSkipFile(f) { continue } data, ok := readFile(joinPath(dir, f)) if !ok { continue } bc := hasBuildConstraint(data, forbidden) if bc { writeStr(2, " skip-constraint: " | f | "\n") continue } fileImps := extractImports(data) skipIter := false for _, imp := range fileImps { if imp == "iter" { skipIter = true break } } if skipIter { writeStr(2, " skip-iter-import: " | f | "\n") continue } writeStr(2, " include: " | f | "\n") files = append(files, f) } if len(files) == 0 { return nil } var allImports []string pkgName := "" for _, f := range files { data, ok := readFile(joinPath(dir, f)) if !ok { continue } if pkgName == "" { pkgName = extractPkgName(data) } for _, imp := range extractImports(data) { allImports = appendUniq(allImports, imp) } } writeStr(2, " discover " | pkgPath | " [") for fi, ff := range files { if fi > 0 { writeStr(2, ", ") } writeStr(2, ff) } writeStr(2, "]\n") return &pkgInfo{ path: pkgPath, name: pkgName, dir: dir, files: files, cfiles: cFiles, imports: allImports, } } type resolver struct { visited map[string]bool order []*pkgInfo root string } func (r *resolver) walk(path string) { if r.visited[path] { return } r.visited[path] = true if isBuiltinOnly(path) { return } pkg := discoverPkg(path, r.root) if pkg == nil { return } for _, imp := range pkg.imports { r.walk(imp) } r.order = append(r.order, pkg) } func resolveAll(rootPkg string, root string) []*pkgInfo { r := &resolver{ visited: map[string]bool{}, root: root, } r.walk(rootPkg) return r.order } func registerBuiltins() { initUniverse() ensureImportRegistry() importRegistry = map[string]*TCPackage{} // unsafe - compiler builtin, no source unsafePkg := NewTCPackage("unsafe", "unsafe") unsafePkg.Scope().Insert(NewTypeName(unsafePkg, "Pointer", Typ[UnsafePointer])) unsafePkg.Scope().Insert(NewTCFunc(unsafePkg, "Sizeof", parseSignatureDesc("interface{}->int32"))) unsafePkg.Scope().Insert(NewTCFunc(unsafePkg, "Offsetof", parseSignatureDesc("interface{}->int32"))) unsafePkg.Scope().Insert(NewTCFunc(unsafePkg, "Alignof", parseSignatureDesc("interface{}->int32"))) unsafePkg.Scope().Insert(NewTCFunc(unsafePkg, "Slice", parseSignatureDesc("ptr,int32->[]uint8"))) unsafePkg.Scope().Insert(NewTCFunc(unsafePkg, "SliceData", parseSignatureDesc("[]uint8->ptr"))) importRegistry["unsafe"] = unsafePkg // moxie - spawn/codec runtime, special handling importRegistry["moxie"] = NewTCPackage("moxie", "moxie") // runtime - assembly + C, pre-compiled in runtime.bc rtPkg := NewTCPackage("runtime", "runtime") rtPkg.Scope().Insert(NewTCFunc(rtPkg, "InitCShared", parseSignatureDesc(""))) importRegistry["runtime"] = rtPkg } func addPtrMethod(pkg *TCPackage, named *Named, name, sigDesc string) { var sig *Signature if sigDesc == "" { sig = NewSignature(nil, nil, nil, false) } else { sig = parseSignatureDesc(sigDesc) } fn := NewTCFunc(pkg, name, sig) fn.hasPtrRecv = true named.AddMethod(fn) } func compileSource(src []byte, pkgName, triple string) string { ir := CompileToIR(src, pkgName, triple) if len(ir) > 0 && ir[0] == ';' { writeStr(2, ir) fatal("compile error for " | pkgName) } if len(ir) == 0 { fatal("empty IR for " | pkgName) } return ir } func removeAll(path string) { entries := listFiles(path, "") if entries == nil { return } for _, e := range entries { os.Remove(joinPath(path, e)) } os.Remove(path) } func cacheClean() { base := pkgCacheDir() writeStr(2, "cleaning cache: " | base | "\n") entries := listFiles(base, "") if entries == nil { writeStr(2, " (empty)\n") return } for _, e := range entries { removeAll(joinPath(base, e)) } os.Remove(base) writeStr(2, " done\n") } func main() { builtinOnly = []string{"unsafe", "runtime", "moxie"} args := getArgs() if len(args) < 2 { writeStr(2, "usage: mxc ...\n") os.Exit(1) } cmd := args[1] if cmd == "version" { writeStr(1, "mxc 1.6.1\n") return } if cmd == "cache" { if len(args) >= 3 && args[2] == "clean" { cacheClean() return } writeStr(2, "usage: mxc cache clean\n") os.Exit(1) } if cmd != "build" { writeStr(2, "usage: mxc ...\n") os.Exit(1) } outpath := "" pkgPath := "" printIR := false forceRebuild := false i := int32(2) for i < int32(len(args)) { if args[i] == "-o" && i+1 < int32(len(args)) { outpath = args[i+1] i += 2 } else if args[i] == "-print-ir" { printIR = true i++ } else if args[i] == "-a" { forceRebuild = true i++ } else { pkgPath = args[i] i++ } } if pkgPath == "" { fatal("no package specified") } if outpath == "" { outpath = pathBase(pkgPath) } root := getenv("MOXIEROOT") if root == "" { root = "." } triple := "x86_64-unknown-linux-musleabihf" clang := "clang-21" registerBuiltins() pkgs := resolveAll(pkgPath, root) if len(pkgs) == 0 { fatal("no packages to compile") } tmpdir := "/tmp/mxc-build" mkdirAll(tmpdir, 0755) cacheBase := pkgCacheDir() var allBcFiles []string for _, pkg := range pkgs { compilePath := pkg.path if hasPrefix(compilePath, "/") || hasPrefix(compilePath, "./") || hasPrefix(compilePath, "../") { compilePath = pkg.name } if isStdlib(pkg.path) { hash := pkgCacheKey(pkg.dir, pkg.files) cached := cachedBcPath(cacheBase, pkg.path, hash) _, cacheOk := readFile(cached) if cacheOk && !forceRebuild { writeStr(2, " cached " | pkg.path | "\n") src := concatSources(pkg.dir, pkg.files) writeStr(2, " tc-start " | compilePath | " srcLen=" | simpleItoa(len(src)) | "\n") TypeCheckOnly(src, compilePath) writeStr(2, " tc-done " | compilePath | "\n") src = nil allBcFiles = append(allBcFiles, cached) continue } writeStr(2, " compile " | pkg.path | "\n") src := concatSources(pkg.dir, pkg.files) ir := compileSource(src, compilePath, triple) src = nil llFile := joinPath(tmpdir, safeName(pkg.path) | ".ll") writeFile(llFile, []byte(ir), 0644) ir = "" bcFile := joinPath(tmpdir, safeName(pkg.path) | ".bc") rc := run([]string{clang, "--target=" | triple, "-emit-llvm", "-c", llFile, "-o", bcFile}) if rc != 0 { fatal("clang failed for " | pkg.path) } cacheDir := joinPath(cacheBase, safeName(pkg.path)) mkdirAll(cacheDir, 0755) data, rok := readFile(bcFile) if rok { writeFile(cached, data, 0644) } data = nil allBcFiles = append(allBcFiles, bcFile) } else { writeStr(2, " compile " | pkg.path | "\n") src := concatSources(pkg.dir, pkg.files) ir := compileSource(src, compilePath, triple) src = nil if printIR { writeStr(1, ir) } llFile := joinPath(tmpdir, safeName(pkg.path) | ".ll") writeFile(llFile, []byte(ir), 0644) ir = "" bcFile := joinPath(tmpdir, safeName(pkg.path) | ".bc") rc := run([]string{clang, "--target=" | triple, "-emit-llvm", "-c", llFile, "-o", bcFile}) if rc != 0 { fatal("clang failed for " | pkg.path) } allBcFiles = append(allBcFiles, bcFile) for _, cf := range pkg.cfiles { cPath := joinPath(pkg.dir, cf) cbcFile := joinPath(tmpdir, safeName(pkg.path) | "_" | cf | ".bc") rc = run([]string{clang, "--target=" | triple, "-emit-llvm", "-c", cPath, "-o", cbcFile}) if rc != 0 { fatal("clang failed for " | cf) } allBcFiles = append(allBcFiles, cbcFile) } } } loadSysroot(joinPath(root, "_sysroot.env")) sysroot := joinPath(root, "sysroot") runtimeBc := getenv("RUNTIME_BC") if runtimeBc == "" { runtimeBc = joinPath(sysroot, "runtime.bc") _, rok := readFile(runtimeBc) if !rok { runtimeBc = joinPath(root, "_runtime.bc") } } muslDir := getenv("MUSL_DIR") if muslDir == "" { muslDir = joinPath(sysroot, "musl") } comprtLib := getenv("COMPRT_LIB") if comprtLib == "" { comprtLib = joinPath(joinPath(sysroot, "compiler-rt"), "lib.a") } bdwgcLib := getenv("BDWGC_LIB") if bdwgcLib == "" { bdwgcLib = joinPath(joinPath(sysroot, "bdwgc"), "lib.a") } muslLib := getenv("MUSL_LIB") if muslLib == "" { muslLib = joinPath(joinPath(sysroot, "musl"), "lib.a") } writeStr(2, " generate initAll\n") var initCalls string for _, pkg := range pkgs { compilePath := pkg.path if hasPrefix(compilePath, "/") || hasPrefix(compilePath, "./") || hasPrefix(compilePath, "../") { compilePath = pkg.name } if compilePath == "main" { continue } initSym := irGlobalSymbol(compilePath, "main") initCalls = initCalls | " call void " | initSym | "(ptr %context)\n" } initAllIR := "\ndefine void @runtime.initAll(ptr %context) {\nentry:\n" | initCalls | " ret void\n}\n" declared := map[string]bool{} for _, pkg := range pkgs { compilePath := pkg.path if hasPrefix(compilePath, "/") || hasPrefix(compilePath, "./") || hasPrefix(compilePath, "../") { compilePath = pkg.name } if compilePath == "main" { continue } declSym := irGlobalSymbol(compilePath, "main") if declared[declSym] { continue } declared[declSym] = true initAllIR = initAllIR | "declare void " | declSym | "(ptr)\n" } initAllFile := joinPath(tmpdir, "initall.ll") writeFile(initAllFile, []byte(initAllIR), 0644) initAllBc := joinPath(tmpdir, "initall.bc") rc := run([]string{clang, "--target=" | triple, "-emit-llvm", "-c", initAllFile, "-o", initAllBc}) if rc != 0 { fatal("clang failed for initall") } writeStr(2, " merge bitcode\n") llvmLink := "llvm-link-21" mergedBc := joinPath(tmpdir, "merged.bc") linkArgs := []string{llvmLink, "-o", mergedBc, runtimeBc} shimsBc := joinPath(sysroot, "hashmap_shims.bc") linkArgs = append(linkArgs, shimsBc) runtimeGoBc := joinPath(sysroot, "runtime_go.bc") if _, gok := readFile(runtimeGoBc); gok { linkArgs = append(linkArgs, runtimeGoBc) } linkArgs = append(linkArgs, allBcFiles...) linkArgs = append(linkArgs, initAllBc) rc = run(linkArgs) if rc != 0 { fatal("llvm-link failed") } writeStr(2, " link\n") objFiles := getenv("OBJ_FILES") if objFiles == "" { objEntries := listFiles(joinPath(sysroot, "obj"), ".bc") if objEntries != nil { for _, e := range objEntries { objFiles = objFiles | joinPath(joinPath(sysroot, "obj"), e) | " " } } } linker := "ld.lld-21" ldArgs := []string{linker, "--gc-sections", "-o", outpath, joinPath(muslDir, "crt1.o"), mergedBc} for _, f := range splitBySpace(objFiles) { if f != "" { ldArgs = append(ldArgs, f) } } ldArgs = append(ldArgs, comprtLib, bdwgcLib, muslLib, "--lto-O0") rc = run(ldArgs) if rc != 0 { fatal("linker failed") } writeStr(2, " -> " | outpath | "\n") }