The compiler runs on Windows (and has seeds for macOS)

Windows: the compiler made from bootstrap/windows-x86_64.ll runs under Wine, compiles itself to the same IR, builds an
exe of itself, and every test program passes through it. It needed: Windows-sized unsigned long for libclang's unsaved
file, no 'cross' when Windows builds for Windows, --target passed to libclang (libclang.dll assumes MSVC), the MinGW
triple always in the IR, FINCH_LDFLAGS, .exe names and FINCH_LLVM_INCLUDE in bootstrap/build.sh, winseed.sh/winbuild.sh.
macOS: seeds for arm64 and x86-64 made with the 11.3 SDK; IR is accepted by clang, nothing has run on a Mac yet.
CI gets a windows and a macos job. UNTESTED on real Windows/macOS runners.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015f73FesGZGYPkmDvUcj6QX
This commit is contained in:
Nexoniarz 2026-10-26 00:36:15 +01:00
commit b53e0910d8
18 changed files with 398686 additions and 45576 deletions

View file

@ -534,7 +534,7 @@ fn importHeaders([]Header headers, []str dirs, str target, bool windows, str ccF
index := clang_createIndex(0, 0)
for h in headers {
src := "#include \"" + h.path + "\"\n"
file := CXUnsavedFile(Filename: "finch_import.c".ptr, Contents: src.ptr, Length: u64(src.len))
file := host.unsavedFile("finch_import.c", src)
ptr tu = null
flags := CXTranslationUnit_DetailedPreprocessingRecord | CXTranslationUnit_SkipFunctionBodies
err := clang_parseTranslationUnit2(index, "finch_import.c", argv.ptr, i32(argv.len), addr(file), 1, u32(flags), addr(tu))

View file

@ -5773,7 +5773,7 @@ fn runtimeDecls() -> str {
fn assemble(ptr[Gen] g) -> str {
text := "; generated by the Finch compiler written in Finch\n\n"
if g.tg.cross {
if g.tg.cross || g.tg.windows {
text = text + "target triple = \"" + g.tg.triple + "\"\n\n"
}
text = text + g.head.join("\n") + "\n\n" + runtimeDecls() + "\n"

View file

@ -3,6 +3,12 @@
// the Windows one).
import "stdlib.h"
import "unistd.h"
import "clang-c/Index.h"
// libclang's description of a file that isn't on disk: its Length is C's `unsigned long`, which is 64 bits here
fn unsavedFile(str name, str contents) -> CXUnsavedFile {
return CXUnsavedFile(Filename: name.ptr, Contents: contents.ptr, Length: u64(contents.len))
}
fn quote(str a) -> str {
return "'" + a.replace("'", "'\\''") + "'"

View file

@ -5,8 +5,14 @@ import "stdio.h"
import "process.h"
import "direct.h"
import "io.h"
import "clang-c/Index.h"
// a Windows argument: in double quotes, with the quotes inside doubled up the way CreateProcess reads them
// libclang's description of a file that isn't on disk: its Length is C's `unsigned long`, which is 32 bits on Windows
fn unsavedFile(str name, str contents) -> CXUnsavedFile {
return CXUnsavedFile(Filename: name.ptr, Contents: contents.ptr, Length: u32(contents.len))
}
fn quote(str a) -> str {
return "\"" + a.replace("\"", "\\\"") + "\""
}
@ -78,7 +84,8 @@ fn makeDirs(str path) {
if part.len == 0 || part.ends_with(":") {
continue // "" before a leading / and the drive letter
}
_mkdir(p.sub(0, at).ptr) // fails when it is there already, which is fine
sofar := p.sub(0, at)
_mkdir(sofar) // fails when it is there already, which is fine
}
}
@ -93,5 +100,5 @@ fn removeTree(str path) {
// move a file over another
fn move(str from, str to) {
delete_file(to)
rename(from.ptr, to.ptr)
rename(from, to)
}

View file

@ -226,7 +226,7 @@ fn build(Build b, platform.Target tg, cimport.CImports ci) -> int {
obj := b.out + ".o"
write_file(ll, b.ir)
target := ""
if tg.cross {
if tg.cross || tg.windows {
target = " --target=" + tg.triple
}
log := b.out + ".log"
@ -243,6 +243,9 @@ fn build(Build b, platform.Target tg, cimport.CImports ci) -> int {
if !tg.msvc {
cmd = cmd + " -lm"
}
if getenv("FINCH_LDFLAGS").len > 0 {
cmd = cmd + " " + getenv("FINCH_LDFLAGS") // more flags for the linker, like -L/where/libclang/is
}
if b.shared {
if tg.darwin {
cmd = cmd + " -dynamiclib"

View file

@ -277,8 +277,8 @@ fn main([]str args) -> int {
}
}
triple := ""
if tg.cross {
triple = tg.triple
if tg.cross || tg.windows {
triple = tg.triple // on Windows libclang.dll would otherwise assume MSVC, while clang may be MinGW
}
ci := cimport.importHeaders(headers, dirs, triple, tg.windows, tg.cc) or {
print(err)

View file

@ -52,6 +52,9 @@ fn select(str name) -> Target {
t = "x86_64-apple-macosx14.0.0"
}
}
if t == "x86_64-w64-windows-gnu" {
t = "x86_64-w64-mingw32" // what clang calls the same system when it runs on Windows
}
tg := Target(triple: t)
tg.windows = t.contains("windows") || t.contains("mingw") || t.contains("msvc") || t.contains("win32")
tg.msvc = t.contains("msvc")
@ -59,7 +62,8 @@ fn select(str name) -> Target {
tg.arm64 = t.starts_with("aarch64") || t.starts_with("arm64")
hostArm := hostName.starts_with("aarch64") || hostName.starts_with("arm64")
hostDarwin := hostName.contains("apple") || hostName.contains("darwin")
tg.cross = tg.windows || tg.darwin != hostDarwin || tg.arm64 != hostArm
hostWin := hostName.contains("windows") || hostName.contains("mingw") || hostName.contains("msvc")
tg.cross = tg.windows != hostWin || tg.darwin != hostDarwin || tg.arm64 != hostArm
tg.exe = ""
if tg.windows {
tg.exe = ".exe"