Files
brolang/compiler/backend/backend.odin
T

124 lines
3.6 KiB
Odin

package backend
import "../linker"
import "../target"
import "../cimport"
import "core:fmt"
import "core:mem"
import "core:os"
import "core:os/os2"
import "core:strings"
append_owned :: proc(command: ^[dynamic]string, value: string, allocator: mem.Allocator) {
append(command, strings.clone(value, allocator))
}
macos_sdk_root :: proc(allocator := context.allocator) -> (string, bool) {
candidates := []string{
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
}
for path in candidates {
if os.exists(path) {
return strings.clone(path, allocator), true
}
}
return "", false
}
append_macos_sdk_paths :: proc(command: ^[dynamic]string, selected: target.Target, allocator: mem.Allocator) {
if selected.kind != .Aarch64_Macos {
return
}
sdk, ok := macos_sdk_root(allocator)
if !ok {
return
}
defer delete(sdk, allocator)
append(command, fmt.aprintf("-F%s/System/Library/Frameworks", sdk, allocator=allocator))
append(command, fmt.aprintf("-L%s/usr/lib", sdk, allocator=allocator))
}
build_command :: proc(
llvm_path, output_path: string,
link_arguments: []linker.Argument,
selected := target.DEFAULT,
c_options := cimport.Options{},
allocator := context.allocator,
) -> []string {
command: [dynamic]string
command.allocator = allocator
append_owned(&command, "/usr/bin/env", allocator)
append_owned(&command, "zig", allocator)
append_owned(&command, "cc", allocator)
append_owned(&command, "-target", allocator)
append_owned(&command, target.name(selected), allocator)
append_owned(&command, "-Wno-override-module", allocator)
append_owned(&command, "-Wno-unused-command-line-argument", allocator)
append_macos_sdk_paths(&command, selected, allocator)
append_owned(&command, llvm_path, allocator)
for path in c_options.include_paths {
append(&command, fmt.aprintf("-I%s", path, allocator=allocator))
}
for define in c_options.defines {
append(&command, fmt.aprintf("-D%s", define, allocator=allocator))
}
for argument in link_arguments {
switch argument.kind {
case .Input:
append_owned(&command, argument.value, allocator)
case .Library_Path:
append(&command, fmt.aprintf("-L%s", argument.value, allocator=allocator))
case .Library:
append(&command, fmt.aprintf("-l%s", argument.value, allocator=allocator))
}
}
append_owned(&command, "-o", allocator)
append_owned(&command, output_path, allocator)
return command[:]
}
destroy_command :: proc(command: []string, allocator := context.allocator) {
for argument in command {
delete(argument, allocator)
}
delete(command, allocator)
}
compile :: proc(
llvm_path, output_path: string,
link_arguments: []linker.Argument = nil,
selected := target.DEFAULT,
c_options := cimport.Options{},
) -> bool {
pid := os2.get_pid()
temporary_output := fmt.tprintf("%s.brolang-tmp-%d", output_path, pid)
defer _ = os.remove(temporary_output)
command := build_command(llvm_path, temporary_output, link_arguments, selected, c_options)
defer destroy_command(command)
state, stdout, stderr, err := os2.process_exec(
os2.Process_Desc{command=command},
context.allocator,
)
defer delete(stdout)
defer delete(stderr)
if len(stdout) > 0 {
fmt.print(string(stdout))
}
if len(stderr) > 0 {
fmt.eprint(string(stderr))
}
if err != nil || state.exit_code != 0 {
if err != nil {
fmt.eprintln("failed to execute zig cc:", err)
}
return false
}
if rename_err := os2.rename(temporary_output, output_path); rename_err != nil {
fmt.eprintln("failed to atomically replace output:", rename_err)
return false
}
return true
}