diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 9ca002a7..8f01f7d4 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -48,7 +48,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-13-macos-v2 + key: llvm-build-13-macos-v3 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f9af94ce..661bdc56 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -51,7 +51,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-13-linux-v1 + key: llvm-build-13-linux-v2 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -192,7 +192,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-13-linux-asserts-v1 + key: llvm-build-13-linux-asserts-v2 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index b2aaf131..b5194408 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -48,7 +48,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-13-windows-v1 + key: llvm-build-13-windows-v2 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' diff --git a/.gitmodules b/.gitmodules index 8143ce84..e82a938b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,3 +32,6 @@ [submodule "lib/mingw-w64"] path = lib/mingw-w64 url = https://github.com/mingw-w64/mingw-w64.git +[submodule "lib/macos-minimal-sdk"] + path = lib/macos-minimal-sdk + url = https://github.com/aykevl/macos-minimal-sdk.git diff --git a/Makefile b/Makefile index bbce72fd..86ce7f9d 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,8 @@ ifeq ($(OS),Windows_NT) else ifeq ($(shell uname -s),Darwin) MD5SUM = md5 + CGO_LDFLAGS += -lxar + USE_SYSTEM_BINARYEN ?= 1 else ifeq ($(shell uname -s),FreeBSD) @@ -89,7 +91,7 @@ CLANG_LIB_NAMES = clangAnalysis clangAST clangASTMatchers clangBasic clangCodeGe CLANG_LIBS = $(START_GROUP) $(addprefix -l,$(CLANG_LIB_NAMES)) $(END_GROUP) -lstdc++ # Libraries that should be linked in for the statically linked LLD. -LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO lldMinGW lldReaderWriter lldWasm lldYAML +LLD_LIB_NAMES = lldCOFF lldCommon lldCore lldDriver lldELF lldMachO2 lldMinGW lldReaderWriter lldWasm lldYAML LLD_LIBS = $(START_GROUP) $(addprefix -l,$(LLD_LIB_NAMES)) $(END_GROUP) # Other libraries that are needed to link TinyGo. @@ -566,6 +568,7 @@ endif @$(MD5SUM) test.hex GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo + GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo ifneq ($(OS),Windows_NT) # TODO: this does not yet work on Windows. Somehow, unused functions are # not garbage collected. @@ -581,6 +584,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN @mkdir -p build/release/tinygo/lib/clang/include @mkdir -p build/release/tinygo/lib/CMSIS/CMSIS @mkdir -p build/release/tinygo/lib/compiler-rt/lib + @mkdir -p build/release/tinygo/lib/macos-minimal-sdk @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common @mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults @mkdir -p build/release/tinygo/lib/musl/arch @@ -604,6 +608,7 @@ endif @cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib @cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt @cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt + @cp -rp lib/macos-minimal-sdk/* build/release/tinygo/lib/macos-minimal-sdk @cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch @cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch diff --git a/builder/build.go b/builder/build.go index 42a3dd33..c4831de0 100644 --- a/builder/build.go +++ b/builder/build.go @@ -107,6 +107,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil root := goenv.Get("TINYGOROOT") var libcDependencies []*compileJob switch config.Target.Libc { + case "darwin-libSystem": + job := makeDarwinLibSystemJob(config, dir) + libcDependencies = append(libcDependencies, job) case "musl": job, unlock, err := Musl.load(config, dir) if err != nil { diff --git a/builder/darwin-libsystem.go b/builder/darwin-libsystem.go new file mode 100644 index 00000000..32882581 --- /dev/null +++ b/builder/darwin-libsystem.go @@ -0,0 +1,58 @@ +package builder + +import ( + "path/filepath" + "strings" + + "github.com/tinygo-org/tinygo/compileopts" + "github.com/tinygo-org/tinygo/goenv" +) + +// Create a job that builds a Darwin libSystem.dylib stub library. This library +// contains all the symbols needed so that we can link against it, but it +// doesn't contain any real symbol implementations. +func makeDarwinLibSystemJob(config *compileopts.Config, tmpdir string) *compileJob { + return &compileJob{ + description: "compile Darwin libSystem.dylib", + run: func(job *compileJob) (err error) { + arch := strings.Split(config.Triple(), "-")[0] + job.result = filepath.Join(tmpdir, "libSystem.dylib") + objpath := filepath.Join(tmpdir, "libSystem.o") + inpath := filepath.Join(goenv.Get("TINYGOROOT"), "lib/macos-minimal-sdk/src", arch, "libSystem.s") + + // Compile assembly file to object file. + flags := []string{ + "-nostdlib", + "--target=" + config.Triple(), + "-c", + "-o", objpath, + inpath, + } + if config.Options.PrintCommands != nil { + config.Options.PrintCommands("clang", flags...) + } + err = runCCompiler(flags...) + if err != nil { + return err + } + + // Link object file to dynamic library. + platformVersion := strings.TrimPrefix(strings.Split(config.Triple(), "-")[2], "macosx") + flags = []string{ + "-flavor", "darwinnew", + "-demangle", + "-dynamic", + "-dylib", + "-arch", arch, + "-platform_version", "macos", platformVersion, platformVersion, + "-install_name", "/usr/lib/libSystem.B.dylib", + "-o", job.result, + objpath, + } + if config.Options.PrintCommands != nil { + config.Options.PrintCommands("ld.lld", flags...) + } + return link("ld.lld", flags...) + }, + } +} diff --git a/builder/lld.cpp b/builder/lld.cpp index 5a9ad8a2..0543a3ea 100644 --- a/builder/lld.cpp +++ b/builder/lld.cpp @@ -11,6 +11,11 @@ bool tinygo_link_elf(int argc, char **argv) { return lld::elf::link(args, false, llvm::outs(), llvm::errs()); } +bool tinygo_link_macho(int argc, char **argv) { + std::vector args(argv, argv + argc); + return lld::macho::link(args, false, llvm::outs(), llvm::errs()); +} + bool tinygo_link_mingw(int argc, char **argv) { std::vector args(argv, argv + argc); return lld::mingw::link(args, false, llvm::outs(), llvm::errs()); diff --git a/builder/tools-builtin.go b/builder/tools-builtin.go index e5ecd424..5474e3e5 100644 --- a/builder/tools-builtin.go +++ b/builder/tools-builtin.go @@ -14,6 +14,7 @@ import ( #include bool tinygo_clang_driver(int argc, char **argv); bool tinygo_link_elf(int argc, char **argv); +bool tinygo_link_macho(int argc, char **argv); bool tinygo_link_mingw(int argc, char **argv); bool tinygo_link_wasm(int argc, char **argv); */ @@ -27,8 +28,13 @@ const hasBuiltinTools = true // linking statically with LLVM (with the byollvm build tag). func RunTool(tool string, args ...string) error { linker := "elf" - if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" { - linker = "mingw" + if tool == "ld.lld" && len(args) >= 2 { + if args[0] == "-m" && args[1] == "i386pep" { + linker = "mingw" + } else if args[0] == "-flavor" { + linker = args[1] + args = args[2:] + } } args = append([]string{"tinygo:" + tool}, args...) @@ -48,6 +54,8 @@ func RunTool(tool string, args ...string) error { ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf)) case "ld.lld": switch linker { + case "darwinnew": + ok = C.tinygo_link_macho(C.int(len(args)), (**C.char)(buf)) case "elf": ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf)) case "mingw": diff --git a/compileopts/config.go b/compileopts/config.go index f33a4b1a..392ef223 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -225,6 +225,11 @@ func (c *Config) CFlags() []string { cflags = append(cflags, strings.ReplaceAll(flag, "{root}", goenv.Get("TINYGOROOT"))) } switch c.Target.Libc { + case "darwin-libSystem": + root := goenv.Get("TINYGOROOT") + cflags = append(cflags, + "--sysroot="+filepath.Join(root, "lib/macos-minimal-sdk/src"), + ) case "picolibc": root := goenv.Get("TINYGOROOT") picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc") diff --git a/compileopts/target.go b/compileopts/target.go index 9e341537..79631001 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -272,8 +272,16 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { spec.Features = "+neon" } if goos == "darwin" { - spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk") - spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip") + spec.Linker = "ld.lld" + spec.Libc = "darwin-libSystem" + arch := strings.Split(triple, "-")[0] + platformVersion := strings.TrimPrefix(strings.Split(triple, "-")[2], "macosx") + spec.LDFlags = append(spec.LDFlags, + "-flavor", "darwinnew", + "-dead_strip", + "-arch", arch, + "-platform_version", "macos", platformVersion, platformVersion, + ) } else if goos == "linux" { spec.Linker = "ld.lld" spec.RTLib = "compiler-rt" diff --git a/lib/macos-minimal-sdk b/lib/macos-minimal-sdk new file mode 160000 index 00000000..a9a5a723 --- /dev/null +++ b/lib/macos-minimal-sdk @@ -0,0 +1 @@ +Subproject commit a9a5a723a6d743a8562bbb62218c6f1361a05994