uCBQN/build/src/build.bqn

858 lines
35 KiB
BQN
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

t0g •MonoTime@
# todo: forcing bytecode/singeli/replxx directories to whatever's applicable
Spawn, WaitForOne •Import "fork.bqn"
Serialize, Deserialize •Import "serialize.bqn"
# modes:
# 0: single string option
# 1: toggle
# 2: flag list
opts
# makefile compatibility options:
1, "from-makefile", 0, @
2, "LD_LIBS", @, @
1, "NO_LDL", 0, @
1, "no_fPIC", 0, @
0, "v", "", "Version string to report by --version; v=0 to disable"
0, "j", @, "Number of parallel jobs"
1, "verbose", 0, "Log more things"
2, "rebuild", , "Forcibly rebuild cbqn/replxx/singeli/linker"
2, "clean", , "Clean cbqn/replxx/singeli/linker and exit"
1, "dry-run", 0, "Don't run/modify/delete anything (except submodules)"
1,"stored-warn",0,"Print stored warnings from cached build steps"
1, "notui", @, "Disable live-updating status display"
1, "nogit", 0, "Error if something attempts to use git"
1, "quiet", 0, "Don't output final status line (or any, combined with notui)"@+10
0, "CC", @, "The used C compiler"
0, "LD", @, "Linker of the final binary; defaults to CC, or CXX if REPLXX=1"
1, "color", @, "Whether to enable colored diagnostics; default based on notui"
0, "os", @, "Target OS (linux, bsd, macos)" # used for .so vs .dylib, and changing linker flags for bsd
0, "arch", @, "Target architecture (x86-64, aarch64, generic)" # used for selecting Singeli target
1, "target-from-cc",0, "Detect arch, OS, and target-supported extensions from CC"
1, "pie", @, "Position-independent executable; default based on OS & arch"@+10
0, "OUTPUT", "", "Output location; defaults to ./BQN for regular builds"
2, "f", , "C flags for CBQN files"
2, "CCFLAGS", , "Flags for all CC/CXX invocations (both object file & linking)"
2, "CXXFLAGS",, "Flags for C++ object file creation invocations"
2, "lf", , "Linker flags"
2, "sf", , "Singeli flags"
2, "LDFLAGS", , @
2, "rm_f", , "Forcibly remove C compiler flag(s)"
2, "rm_lf", , "Forcibly remove linker flag(s)"@+10
1, "shared", 0, "Build a shared library"
1, "static-lib", 0, "Build a static library"
1, "static-bin", 0, "Statically link the executable"
1, "wasi", 0, "Build with WASI; default output name is BQN.wasm"
1, "emcc", 0, "Build with emscripten; outputs two files - BQN.wasm and BQN.js"
1, "clangd", 0, "Generate a compile_commands.json for clangd"@+10
1, "FFI", @, "Enable FFI through libffi; off by default for Windows"
1, "pkgconfig", 1, "Whether to attempt to use pkg-config to find libffi flags"
1, "singeli", 0, "Enable Singeli usage"
1, "replxx", 0, "Enable REPLXX"@+10
0, "usz", "64", "Size of array length fields in bits; 32 or 64 (default: 64)"
1, "c", 0, "Disable some default flags"
1, "O3", @, "Use '-O3'; On by default unless 'c=1'"
1, "native", @, "Use '-march=native'"
2, "has", , "Assume specific arch features; options: AVX2, BMI2, slow-pdep"@+10
1, "debug", 0, "Debug: '-DDEBUG -g'; enables various sanity checks"
1, "g", @, "Debug: '-g'; enable debug symbols"
1, "rtverify", 0, "Debug: compare builtin results with the self-hosted runtime"
1, "heapverify", 0, "Debug: check reference count correctness"
1, "ubsan", 0, "Debug: enable compiler undefined behavior sanitizer"@+10
0, "CXX", "c++", "C++ compiler (for replxx)"
2, "REPLXX_FLAGS", "-std=c++11", "-Os", "default replxx C++ build flags"
Log •Out
_verboseLog {𝔽_𝕣:}
onExitList
OnExit {𝕊: {𝕏@}¨ onExitList}
_assert_ { 𝔾𝕩?𝕩; Log 𝕨𝔽𝕩 OnExit@ •Exit 1}
Split ((¬-˜×·+`»>))
SepArgs ' 'Split
Lowercase {𝕩 - (-´"Aa")×(𝕩'A')𝕩'Z'}
Uppercase {𝕩 - (-´"aA")×(𝕩'a')𝕩'z'}
getOpt {
args ר/ •args
treq (args¨<"v=") '='=¯1¨args
compat (<"from-makefile") args
{
compat? args (¬treq)/ ; # makefile will leave things like "shared=" if the envvar was empty; build/build by default sees this as "shared=1", but we want to ignore it
{𝕊: "Error: Trailing '=' for an option"} _assert_¬ ´treq
}
[ot, on0, od, oh] >opts
on Lowercase¨ on0
{𝕊:
•Out "Usage: path/to/cbqn/build/build [options]
Options are specified as arguments of 'key=value'. Keys are case-insensitive.
Toggles without a value implicitly enable it; e.g. 'singeli' is 'singeli=1'.
For flag lists, 'k=v' will split on spaces, i.e. 'f=-a -b' adds two flags,
whereas 'f+=-a -b' adds one flag that contains a space.
Options:
"oh {" "𝕩" "𝕨@+10}¨((´¨)(¨))((@¨oh)/) on0
•Exit 0
} ´ "help""h""?"'-'/¨ •args
ot1 1=ot
ot2 2=ot
s1 '='¨ args # start of =
hs s1<¨args # has value
fl hs'+'=(0s1-1)¨args # is appended flag, i.e. "f+=a b c"
s0 s1-fl # end of key
ks s0¨args # keys
ks Lowercase¨ # lowercase keys
vs (1+s1)¨args # values
{𝕊: "Unknown option -- '",(¬kson)/ks, "'; see --help for options"}_assert_(´) kson
ci onks # index in option specification
{𝕊: "Error: Unexpected '+=' for '",ks˜/¬𝕩,"'"}_assert_(´) (ciot2)¬fl
Gr {(ot)ci𝕩}
gvs Gr vs # values for each option
{𝕊: "Error: Multiple values given for '",on˜/¬𝕩,"'"}_assert_(´) ot21¨gvs
{𝕊: "Error: Expected value to be provided for ",𝕩/ks}_assert_(¬´) (¬ciot1) ¬hs
# f='a b' f+='c d' → "a" "b" "c d"
gvs (Gr fl) ({𝕨?𝕩;×𝕩?SepArgs 𝕩;}¨)¨(ot2/) gvs
# map toggles to their actual values, error on invalid
gvs on {𝕨{𝕊: "Error: Invalid value for '",𝕨,"'"}_assert_ ´𝕩"""0""1" ("0") 𝕩}¨(ot1/) gvs
gvs ((¬ot2) ¨ od) {×𝕩?𝕩;𝕨}¨ gvs # map in defaults
gvs (¬ot2) ¨ gvs # disclose options with only one expected result
# •Show [ks, fl, hs, ci, vs]
# •Show {𝕩/˜×(≠1⊑⊢)˘𝕩} ⍉[on, gvs]
{(on<Lowercase 𝕩)gvs}
}
SH { # if 𝕨≢@, returns 𝕨 if failed to spawn
defres 𝕨 @
coe •SH{defres@? ¯1defres@; ("Error: Failed to spawn ",𝕩)_assert_ 0} 𝕩
{
c¯1? o;
Log(×) e
𝕩 {𝕊: "Error: ",𝕨," exited with error code ",•Repr 𝕩}_assert_(0=) c
o
}𝕩
}
TrySH {1•SH1𝕨"" 𝕩}
nogit1
GitCmd {
𝕨 {"Error: git needed for "𝕨", but nogit=1 specified"}_assert_¬ nogit
¯1SH "git", "-C", rootDir𝕩
}
updateSubmodule {
done
{
𝕩@? @;
"Local"¯5𝕩? @;
𝕩<done? @;
done <𝕩
("updating "𝕩) GitCmd "submodule", "update", "--init", AtRoot 𝕩
}
}
rootDir •file.Parent2 •file.path
AtRoot rootDir•file.At
allCaches "cbqn", "singeli", "replxx", "linker"
po { # parsed options
verbose, J, clean, rebuild, warnStored, tui, loud, output, clangd, versionName,
os, arch, native, has,
emcc, wasm, replxx, singeli, staticLib,
usz, ffi, o3, debug, rtverify, heapverify,
bytecodeDir, replxxDir, singeliDir,
CBQNc, ccColor, singeliFlags, singeliCFlags, singeliArch, REPLXXc, Linker
compat GetOpt "from-makefile"
compatTgt compat/"target_"
custom GetOpt "c"
clangd GetOpt "clangd"
DOpt {@,𝕨 GetOpt 𝕩}
J {𝕊: vGetOpt "j" v@? •ParseFloat v; •ParseFloat4 ¯1"4" TrySH "nproc"}
tui @¬{𝕊: f@•FFI"i32""isatty"">i32" F 1} GetOpt "notui"
warnStored GetOpt "stored-warn"
loud ¬GetOpt "quiet"
nogit GetOpt "nogit"
truecc GetOpt "target-from-cc"
dry GetOpt "dry-run"
CacheList {
vs ','SplitLowercase¨ GetOpt 𝕩
{
vs"all"? allCaches;
𝕩 {"Error: Invalid target in ",𝕨," list: ",𝕩}_assert_(0=) vs (¬/) allCaches
vs
}𝕩
}
clean CacheList "clean"
rebuild CacheList "rebuild"
{𝕊: _verboseLog {Log𝕨𝔽𝕩𝕩}} verboseGetOpt "verbose"
ErrBoth {ab: "Error: '",a,"' and '",b,"' cannot both be enabled"}
Max1 {𝕩 ErrBoth(2/˜) _assert_(1+´) GetOpt¨ 𝕩}
NotWith {𝕩 {ErrBoth ¨ 𝕩/¨𝕨}_assert_(¬·´´¨) GetOpt¨¨ 𝕩}
Max1 "REPLXX""wasi""emcc"
Max1 "shared""static-lib"
"static-lib""shared" NotWith "REPLXX""emcc"
sharedLib GetOpt "shared"
staticLib GetOpt "static-lib"
staticBin GetOpt "static-bin"
lib staticLib sharedLib
native 0 DOpt "native"
emcc GetOpt "emcc"
wasi GetOpt "wasi"
usz •ParseFloat GetOpt "usz"
{𝕊: "Error: usz can only be 32 or 64"}_assert_ usz3264
versionName "0"@ GetOpt "v"
wasm wasiemcc
"Error: Static linking for wasm is pointless"_assert_¬ staticBin wasm
cc {emcc? "emcc"; "clang"} DOpt "CC"
ccClang {
clangd? 1;
TryVersion {𝕩 SH cc"--version"}
vs TryVersion {@GetOpt"CC"? ¯1; @}
{𝕊: Logloud "Note: default CC '"cc"' wasn't found; using 'cc'; add 'CC=cc' to hide" cc"cc" vsTryVersion@} vs¯1
´"clang"vs
}
cxx GetOpt "CXX"
debug GetOpt "debug"
rtverify GetOpt "rtverify"
heapverify GetOpt "heapverify"
ubsan GetOpt "ubsan"
o3 (¬custom) DOpt "O3"
allFlags GetOpt "CCFLAGS"
minCCFlags allFlags GetOpt "f"
ubsanFlags ubsan / "-fsanitize=undefined", "-fno-sanitize=float-cast-overflow", "-fno-sanitize=vla-bound"
CDefines {𝕊: cDefines (@+10) Split SH cc minCCFlags "-dM", "-E", "-"}
Uname {𝕊: uname Lowercase "" TrySH "uname""-sm"}
InUname {𝕊: ´ 𝕩Uname@}
singeli GetOpt "singeli"
replxx GetOpt "REPLXX"
SubmoduleDir "build"•file.At(@) {𝕨0?@; •file.Exists rAtRoot "build"•file.At 𝕩"Local"? r; 𝕩"Submodule"}
LogDir {𝕩@? 𝕨": not used"; 𝕨" directory: "AtRoot 𝕩}_verboseLog
bytecodeDir 1 SubmoduleDir "bytecode" "Bytecode" LogDir bytecodeDir
replxxDir replxx SubmoduleDir "replxx" "REPLXX" LogDir replxxDir
singeliDir singeli SubmoduleDir "singeli" "Singeli" LogDir singeliDir
arch {'-'¨(('_'=𝕩)/)𝕩} Lowercase {trueccwasm? "generic"; InUname"x86_64"? "x86-64"; ´InUname¨"aarch64""arm64"? "aarch64"; "generic"} DOpt "arch"
{𝕊: "Error: Unsupported arch; options:"1", "¨𝕩}_assert_(arch<) "x86-64""aarch64""riscv64""generic"
arch {"riscv64": "RV64"; "generic": "NONE"; Uppercase {'_'¨(('-'=𝕩)/)𝕩} 𝕩} # translate to Singeli
has ','Split¨Lowercase GetOpt "has"
has has ("slow-pdep"< has) / "bmi2"
{𝕊: "Error: Invalid 'has' value; options:"1", "¨𝕩}_assert_(´has) "ssse3""sse4.1""sse4.2""avx2""bmi2""pclmul""slow-pdep"
"Error: Cannot use 'has=...' if Singeli isn't enabled"_assert_ singeli has
{𝕊: has (´(("#define __tune_znver""__ 1")¨ "12")CDefines@) / "slow-pdep"} truecc
singeliArch @
singeliCFlags
SingeliArchInfo ! # takes arg of infer
{
¬singeli? @;
UpdateSubmodule singeliDir
SingeliUtil {𝕨 •Import AtRoot singeliDir •file.At 𝕩}
SingeliArchInfo {{native? ; singeliArch}, 𝕩 SingeliUtil "arch.bqn"}
singeliArch {
truecc @GetOpt"arch"?
archutil SingeliUtil "archutil.bqn"
foundArch archutil.ParseCDefines CDefines@
arch foundArch
foundArch
;
Uppercase arch
}
{
arch"X86_64"?
singeliArch Uppercase "ssse3""sse4.1""sse4.2""avx2""bmi2""pclmul"/ has
singeliCFlags (SingeliArchInfo 1).GetCFlags @
{𝕊: Log "Note: inferred the following C flags:"' '¨𝕩}(loud ×) singeliCFlags (¬/) (SingeliArchInfo 0).GetCFlags @
;@
}
}
{𝕊: "Error: Cannot have 'has' options on architecture '"arch"'; remove 'has=...' or use '"compatTgt"arch=x86-64'"}_assert_¬ (arch"X86_64") 0has
os GetOpt "os"
{
os@?
os Lowercase ;
¬truecc?
os {InUname"linux"? "linux"; InUname"darwin"? "macos"; InUname"bsd"? "bsd"; "linux"};
osKeysosVals <˘>
"_WIN32", "windows"
"__FreeBSD__", "bsd"
"__OpenBSD__", "bsd"
"__APPLE__", "macos"
"__MACH__", "macos"
"__linux", "linux"
osOpts osVals /˜ osKeys{𝕩˜𝕩' '}¨ 8¨ ("#define "8)¨/ CDefines@
{𝕊: "Error: Couldn't detect OS from C-defined macros; add explicit '"compatTgt"os=...'"} _assert_ 1=osOpts
os osOpts
}
linuxbsdmacoswindows os¨ {𝕊: "Error: Unsupported OS; options:"1", "¨𝕩}_assert_(os<) "linux""bsd""macos""windows"
{𝕊: "Target: ", os, " ", Lowercase arch, 1 ", "¨ hasnative/"native"} _verboseLog@
output GetOpt "OUTPUT"
output •wdpath•file.At {𝕊:
wasi? "BQN.wasm";
emcc? ".";
staticLib? "libcbqn.a";
sharedLib? {macos? "libcbqn.dylib"; windows? "cbqn.dll"; "libcbqn.so"};
windows? "BQN.exe";
"BQN"
}("") output
{𝕊: "Output location: "𝕩}_verboseLog output
pie {(¬sharedLibstaticLib)linuxarch"X86_64"? 0; 1} DOpt "pie"
{𝕊: pie 0} wasm
ffi ((¬windows)¬wasm) DOpt "FFI"
exportSymbols ffilib
defLibs @GetOpt "LD_LIBS"
GetLibs { 𝕊:
getLibs (1)(¬defLibs) {
¬ffi? ,;
GetOpt "pkgconfig"? 0= •SH1 "pkg-config""--exists""libffi"?
{SepArgs ¯1SH"pkg-config",𝕩,"libffi"}¨ "--cflags", "--libs";
, "-lffi"
}
}
ccColor ((¬clangd) tui DOpt "color") / ccClang"-fdiagnostics-color=always""-fcolor-diagnostics"
cbqnc {
args
cc,
"-std=gnu11",
"-Wall", "-Wno-unused-function",
"-fms-extensions", "-ffp-contract=off", "-fno-math-errno", "-fvisibility=hidden", "-fno-strict-aliasing",
"-DBYTECODE_DIR="•file.Name bytecodeDir,
"-DSINGELI="•Repr singeli,
"-DFFI="•Repr 2×ffi
args ccClang
"-Wno-parentheses" # gcc
# clang
"-Wno-microsoft-anon-tag", "-Wno-bitwise-instead-of-logical", "-Wno-unknown-warning-option"
"-Wno-pass-failed" # vfor
(¬wasm)/"-mllvm", "--x86-cmov-converter=0" # thing that converts `cmov`s to branching sometimes (?), but we don't want that
args 0GetLibs@
args minCCFlags
args ubsanFlags
args (singeli arch "X86_64") / "-DSINGELI_X86_64" {singeli? "-mavx2" singeliCFlags; 0} / "-DSINGELI_AVX2"
args (singeli arch"AARCH64") / "-DSINGELI_NEON"
args (singeli arch<"X86_64""AARCH64") / "-DSINGELI_SIMD"
args ( wasm) / "-DWASM"
args ( wasi) / "-DWASI", "-DNO_MMAP", "-DUSE_SETJMP=0", "-D_WASI_EMULATED_MMAN", "--target=wasm32-wasi"
args ( emcc) / "-DEMCC", "-O3"
args ( replxx) / "-DUSE_REPLXX", "-DREPLXX_STATIC=1", "-I"replxxDir"/include" # TODO maybe move to main.c only, and have it be in its own separate cache dir, so that adding replxx doesn't recompile everything?
args ( debug DOpt "g") / "-g"
args ( o3) / "-O3"
args ( debug) / "-DDEBUG"
args ( rtverify) / "-DRT_VERIFY"
args ( heapverify) / "-DHEAP_VERIFY", "-DUSE_SETJMP=0"
args ( exportSymbols) / "-DCBQN_EXPORT"
args ( ¬pie) / "-fno-pie"
args (pie ¬sharedLib) / "-fPIE"
args ( lib) / "-DCBQN_LIB"
args ( sharedLib) / (¬GetOpt"no_fPIC")/"-fPIC"
args ( windows) / "-DNO_MMAP"
args (replxx windows) / "-DUSE_REPLXX_IO"
args ( @versionName) / "-DHAS_VERSION"
args ( usz64) / "-DUSZ_64"
args ("slow-pdep"< has) / "-DSLOW_PDEP"
args {native? "-march=native"; singeliCFlags}
args args (¬/) GetOpt "rm_f"
{"CBQN C compiler: "•Repr 𝕩} _verboseLog args
args
}
REPLXXc { 𝕊:
args cxx, "-DREPLXX_STATIC=1", "-I"replxxDir"/include"
args GetOpt "REPLXX_FLAGS"
args GetOpt "CXXFLAGS"
args ( ¬pie) / "-fno-pie"
args allFlags
args (pie ¬sharedLib) / "-fPIE"
args ( sharedLib) / (¬GetOpt"no_fPIC")/"-fPIC"
{"REPLXX C++ compiler: "•Repr 𝕩} _verboseLog args
replxxc args
}
singeliFlags GetOpt "sf"
linker {
bin @
args
Cmd !
{
staticLib?
bin "ar" DOpt "LD"
args GetOpt "lf"
args GetOpt "LDFLAGS"
args "-rcs"
Cmd {srcsdst: binargsdstsrcs}
;
rdynamic (¬wasi) exportSymbols ¬windows
bin {replxx? cxx; cc} DOpt "LD"
args ubsanFlags
args defLibs / "-lm"
args DOpt "LD_LIBS"
args ((¬GetOpt "NO_LDL") defLibs ffi ¬bsd) / "-ldl"
args 1GetLibs@
args GetOpt "lf"
args allFlags
args GetOpt "LDFLAGS"
args ( emcc) / "-s", "EXPORTED_FUNCTIONS=_main,_cbqn_runLine,_cbqn_evalSrc", "-s", "EXPORTED_RUNTIME_METHODS=ccall,cwrap", "-s", "ALLOW_MEMORY_GROWTH=1"
args ( wasi) / "-lwasi-emulated-mman", "--target=wasm32-wasi", "-Wl,-z,stack-size=8388608", "-Wl,--initial-memory=67108864"
args (sharedLib wasi) / "-mexec-model=reactor", "-Wl,--export-dynamic", "-Wl,--export=malloc", "-Wl,--export=free"
args (sharedLib¬wasi) / "-shared"
args (sharedLibmacos) / "-install_name", "@rpath/libcbqn.dylib"
args ( rdynamic) / "-rdynamic"
args ((¬wasm) ¬pie) / "-no-pie"
args (pie ¬sharedLib) / "-fPIE", "-pie"
args ( windows) / "-lpthread"
args ( staticBin) / "-static"
args args (¬/) GetOpt "rm_lf"
Cmd {srcsdst: bin, "-o", dstsrcsargs}
}
{"key":binargs; srcsdst: Cmd srcsdst}
}
{"linker: "•Repr Linker "src1""src2""...", "dst"} _verboseLog@
{𝕊: CBQNc@ REPLXXcreplxx @} verbose
}
Hash {(32"0a"+¨1026)˜{𝕨+2×𝕩}˝5321•bit._cast(4)•Hash¨<𝕩}
MkDir {•file.Exists𝕩?@; •file.CreateDir𝕩}
allObjDir "build/obj2"
MkDir AtRoot allObjDir
isFileTheSame {
ksvs
{
i ks<𝕨
i<ks? 𝕩 ivs;
ks <𝕨
vs <t•file.Modified"unknown" 𝕨
𝕩 t
}
}
# gets/creates a directory of cacheable objects; key is the unique identifier of when it can be reused
madeCaches
GetCache {madeCaches𝕩𝕩} { 𝕊 basenamedesckey:
folderHash Hash key
folder allObjDir•file.At basename"-"folderHash
MkDir AtRoot folder
name basename
! (<name) allCaches
File folder•file.At
{𝕊: desc": "AtRoot𝕩} _verboseLog folder
dataPath AtRoot File "data"
dataVersion 1
prevKsprevVs {ab: adataVersion? b; } •file.Exists@, {Deserialize 8'c',8•bit._cast •FBytes 𝕩} dataPath
Find {
i prevKs<𝕩.cacheKey
i<prevKs? iprevVs;
@
}
IsUpToDate {
𝕊:
𝕩.found0@?
infodepInfo 𝕩.found0
info@?
fileInfo,· info
´ AtRootIsFileTheSame´¨ fileInfo?
depInfo𝕩.DepHash@
;
0
}
newKsnewVs
finalNewData @
FinalData { 𝕊:
finalNewData@?finalNewData;
jKs prevKs˜ {𝕩.cacheKey}¨ newKs
jVs prevVs˜ newKs {𝕨.SetVal 𝕩}¨ newVs
finalNewData Serialize dataVersion (<jKs) /¨ jKsjVs
}
Update {key𝕊data: # data≡@ means failed to build
!finalNewData@
newKs <key
newVs <data
}
onExitList {{𝕊: dataPath •FBytes 8,8'c'•bit._cast FinalData@}{𝕊: (0newKs) ¬po.dry}}
}
ruleKsruleVs
anyStoredWarnings 0
# dependency resolution & thread management
Run { 𝕊:
0po.clean?
{
Log(po.verbosepo.dry) "recursively deleting ", AtRoot 𝕩.folder
SH(¬po.dry) "rm", "-r", "--", AtRoot 𝕩.folder
}¨ ( /˜ {𝕩.name}¨ po.clean˙) madeCaches
•Exit 0
;
ruleDeps {{𝕩.dst}¨ 𝕩.ruleDeps}¨ ruleVs
ruleSrcs0 ruleKsruleDeps
! ´ ruleSrcs0<ruleKs
ruleSrcs ((˜¨/)ruleDeps) ruleSrcs0
req
ruleN 0¨ ruleKs # number of children (i.e. how many have this in their ruleP list)
ruleP ¨ ruleKs # parent rules (i.e. which ones require this)
Require {
v 𝕩ruleVs
# rebuild ← po.rebuildAll
# {𝕊: rebuild↩ ".singeli"≡¯8↑v.disp}⍟⊢ po.rebuildSingeli
rebuild (<v.cache.name)po.rebuild
chi 𝕩ruleSrcs
chr Require¨ chi
rebuild ´ chr
# rebuild↩ ¬•file.Exists AtRoot 𝕩⊑ruleKs # not really needed unless someone deletes a specific file without deleting the data file
rebuild ¬v.cache.IsUpToDate v
rebuild
{𝕊: {
po.warnStored? Log "Previous warning from ",v.disp,":" Log 𝕩;
anyStoredWarnings 1
}(×) 10v.found0}
{
ruleN (+´chr)+(𝕩)
ruleP 𝕩¨((chr/chi))
req 𝕩
}
𝕩
rebuild
}
anyRebuilt Require ruleKs<𝕩
left (reqruleN=0)/req
RequestJob { 𝕊:
0=left? @;
(left˜ ¯1) ¯1left
}
FinishJob { i𝕊tb:
v iruleVs
Log(×) 1tb
{ 𝕊:
v v.cache.Update @
}
{ 𝕊:
v v.cache.Update 3tb, 1tb
ps iruleP
ruleN -1(ps)
left (0=psruleN)/ps
}
tb
tb
}
threads Spawn(•file.At "runner.bqn")¨ {0:0; 𝕩po.J@} req
work
free threads
Ts {𝕩.t}¨
updateLive { 𝕊:
storedOut
Log {storedOut <𝕩@+10}
currLive
e @+27
{
•term.OutRaw (¯4(currLive)<((@+13)e"[0K"e"[1F")) (•ToUTF8 storedOut) 1((@+10)•ToUTF8)¨ 𝕩
•term.Flush @
currLive 𝕩
storedOut
}
}po.tui 0
FmtTime {{("""0" ¯1𝕩)'.'¯1𝕩} •Repr 0.5+ 10×𝕩}
onExitList {{𝕊: UpdateLive }}
tmap # threads in the order they're displayed on-screen (dynamically calculated so that if there's only ever only one job in parallel, there are no pointless empty lines)
doneCount 0
stopping 0
Fail {stopping1 Log 𝕩}
nextRedraw ¯∞
DoneLine {𝕊: •Repr doneCount, "/", •Repr req}
{ 𝕊:
{𝕊:
t ¯1free free˜ ¯1
i RequestJob@ viruleVs
t.Request "dry"po.dry v.CMD@
work {tt, ii, vv, t0•MonoTime@}
}•_while_{𝕊: ´0<¨ leftfree}¬ stopping
t1 •MonoTime@
tmap tmapTs work
FileLine {"[", FmtTime t1-𝕩.t0, "] ", 𝕩.v.disp}
{ 𝕊:
nextRedraw t1+0.1
UpdateLive (<DoneLine@){
𝕩work? "";
FileLine 𝕩work
}¨ (Ts work)tmap
} t1>nextRedraw-0.02
dm (0.1nextRedraw-t1) WaitForOne Ts work
lm ¬dm
{𝕊: Fail "Error: Fork processing ", •Repr ((dm=¯1)/work).v.disp, " died" dmlm<dm¯1} ´dm=¯1
done dm/work
work lm/work
{
𝕩.i FinishJob 𝕩.t.Take@?
{𝕊: Log FileLine 𝕩 •term.Flush@}(po.loud¬po.tui) 𝕩
doneCount+1;
Fail "Error: During '"𝕩.v.disp"'"
}¨ done
free Ts done
}•_while_{𝕊: (0<work) (¬stopping)0<left}@
Logpo.loud DoneLine@, " in ", FmtTime t0g-˜•MonoTime@, "s", stopping/"; failed to build", anyStoredWarnings/"; cached build steps had warnings - view with stored-warn=1"
¬stopping
}
AddRule { 𝕊 cachecacheKeydstGetCMDdispdeps:
ruleKs <dst
setFound @
res {
dst dst
cache cache
cacheKey cacheKey
found0 0
SetFound {found0𝕩}
DepHash {𝕊: Hash {𝕩.found1@? 𝕩.found0; 𝕩.found1}¨ ruleDeps} # todo instead of conditionally choosing found1 or found2, decide statically; and properly cache
found1 @
SetVal {found1 𝕩DepHash@}
disp disp
CMD GetCMD
ruleDeps deps
}
SetFound cache.Find res
ruleVs res
res
}
clangd {¬po.clangd? {_addC{𝕨𝔽_𝕣𝕩:@} AddSingeli{𝕊:@} SingeliInit CFiles}; AtRoot •Import "clangd.bqn"}
MakeCCInv { 𝕊 GetArgsInitcacheidsrccustomDeps: # src should be CBQN-base-dir-relative, so that cache doesn't store full paths
GetArgs clangd._addC src
dst cache.File id".o"
GetCMD { 𝕊:
Init @
dep AtRoot cache.File id".d"
"sh", rootDir, (GetArgs@)"-MT", "o", "-MMD", "-MF", dep, "-o", AtRoot dst, "-c", src, dep
}
AddRule cache, id, dst, GetCMD, •file.Name src, customDeps
}
MakeSingeliInv { 𝕊 argsInitcacheidsrccustomDeps: # src should be CBQN-base-dir-relative, so that cache doesn't store full paths
args clangd.AddSingeli src
dst cache.File id".c"
GetCMD { 𝕊:
Init @
"singeli", rootDir, AtRoot dst, AtRoot po.singeliDir, args, AtRoot src
}
AddRule cache, id, dst, GetCMD, •file.Name src, customDeps
}
MakeLinkerInv { 𝕊 GetArgscachenamesrcs:
dst cache.File name
GetCMD { 𝕊:
cmd "sh", rootDir, GetArgs {𝕩.dst}¨ srcs, dst, @
{po.staticLib? "staticlib", AtRoot dst, cmd; cmd}
}
AddRule cache, name, dst, GetCMD, "link", srcs
}
# actual CBQN/Singeli/REPLXX definitions
cachedBinlinkerCache {
Shorten {po.clangd? 𝕩; r {𝕩˜¯1-'.'˜𝕩}¨ •file.Name¨ 𝕩 ! ´ r r}
cbqnSrc {(𝕩)•file.At¨ 1𝕩}¨
"src/builtins/", "arithd.c", "arithm.c", "cmp.c", "sfns.c", "squeeze.c", "select.c", "slash.c", "group.c", "sort.c", "search.c", "selfsearch.c", "transpose.c", "fold.c", "scan.c", "md1.c", "md2.c", "compare.c", "cells.c", "fns.c", "sysfn.c", "internal.c", "inverse.c"
"src/core/", "tyarr.c", "harr.c", "fillarr.c", "stuff.c", "derv.c", "mm.c", "heap.c"
"src/", "load.c", "main.c", "rtwrap.c", "vm.c", "ns.c", "nfns.c", "ffi.c"
"src/jit/", "nvm.c"
"src/utils/", "ryu.c", "utf.c", "hash.c", "file.c", "mut.c", "each.c", "bits.c"
cbqnSrc cbqnSrc clangd.CFiles "src"
singeliMap 1¨ ({ ({"X86_64":'x'; "AARCH64":'a'; "RV64":'g'; "NONE":'g'} po.arch) 𝕩}¨ ¨)/
"xa.""src/builtins/arithm.c""monarith", "xa.""src/builtins/compare.c""equal",
"xa.""src/builtins/arithd.c""dyarith", "xa.""src/builtins/cmp.c""cmp",
"xa.""src/builtins/squeeze.c""squeeze", "xa.""src/utils/mut.c""copy",
"xa.""src/utils/bits.c""bits", "xag""src/builtins/transpose.c""transpose",
"xag""src/builtins/search.c""search", "xag""src/builtins/selfsearch.c""selfsearch"
"xag""src/builtins/scan.c""scan", "xag""src/builtins/fold.c""fold",
"xag""src/builtins/slash.c""slash", "xag""src/builtins/slash.c""replicate",
"xag""src/builtins/sort.c""bins", "xa.""src/builtins/slash.c""count"
"xag""src/builtins/select.c""select", "xag""src/builtins/group.c""group"
objs
replxxCache {
¬po.replxx? @;
replxxCache GetCache "replxx", "REPLXX object file location", po.REPLXXc@
replxxSrc (po.replxxDir•file.At"src")•file.At¨"ConvertUTF.cpp", "wcwidth.cpp", "conversion.cxx", "escape.cxx", "history.cxx", "prompt.cxx", "replxx.cxx", "replxx_impl.cxx", "terminal.cxx", "util.cxx", "windows.cxx"
objs (Shorten replxxSrc) {MakeCCInv po.REPLXXc, , replxxCache, 𝕨, 𝕩, }¨ replxxSrc
replxxCache
}
singeliObjs @
singeliCache {
¬po.singeli? @;
singeliCache GetCache "singeli", "Singeli generated code location", po.native, po.singeliArch, po.arch, po.usz, po.has, po.singeliFlags, po.singeliDir
# genArithTables
ga "src/singeli/src/genArithTables.bqn"
gaDefs singeliCache.File "arDefs.singeli"
gaTables singeliCache.File "arTables.c"
gaRule AddRule
singeliCache, "genArithTables",
gaDefs, # am cheating and only using arDefs.singeli as destination; ¯\_(ツ)_/¯
{𝕊: "runbqn", rootdir, AtRoot ga, AtRoot¨ gaDefsgaTables, ga},
•file.Name ga,
singeliArgs
po.singeliFlags
"-l", "gen="AtRoot singeliCache.folder, "-c", "usz=u"•Repr po.usz
("slow-pdep"< po.has) / "-c", "SLOW_PDEP=1"
{po.native? ; "-a" 1 ','¨ po.singeliArch}
clangd.SingeliInit po
{𝕊: "Singeli args: "•Repr singeliArgs} _verboseLog @
{𝕊: "Singeli-determined C arch: "•Repr po.singeliCFlags; @} _verboseLog @
singeliObjs {MakeSingeliInv singeliArgs, {𝕊:UpdateSubmodule po.singeliDir}, singeliCache, 𝕩, "src/singeli/src/"•file.At 𝕩".singeli", (𝕩"dyarith")/gaRule}¨ 1¨singeliMap
singeliCache
}
cbqnCache {
cbqnCache GetCache "cbqn", "CBQN object file location", po.CBQNc@, {po.singeli? singeliCache.folderHash; @}
ruleDeps {
¬po.singeli? ¨ cbqnSrc;
((cbqnSrc) ˜ cbqnSrc¨singeliMap) singeliObjs
}
singeliArgs {po.singeli? "-DSINGELI_DIR="•file.Name singeliCache.folder; }
objs {abc: MakeCCInv po.CBQNcpo.ccColorsingeliArgs˙, {𝕊:UpdateSubmodule po.bytecodeDir UpdateSubmodule po.replxxDir}, cbqnCache, a, b, c}¨ <˘[Shorten cbqnSrc, cbqnSrc, ruleDeps] # updates replxx because needs replxx.h
cbqnCache
}
linkerDeps @¨/ cbqnCache, replxxCache, singeliCache
linkerCache GetCache "linker", "linker cached result location", po.Linker "key", {𝕩.folderHash}¨ linkerDeps
{
po.versionName@? @;
srcFile linkerCache.File "versionInfo.c"
List {¯2 ", "¨ /´ <˘> 𝕩}
vnameXvname {po.versionName""? "on " "commit "("??"GitCmd "rev-parse", "HEAD"){"""determining version"GitCmd "status", "--porcelain", "src"? ""; "-dirty"}; ˜ po.versionName}
src
"char* const cbqn_versionInfo = """
"\n"¨ ר/
"CBQN "vnameX
"built with "(×) List
po.ffi, "FFI"
po.singeli, "singeli "{po.native? "native "po.arch; po.arch"generic"? po.arch" (no SIMD)"; po.arch}' '¨po.has
po.replxx, "replxx"
¬po.o3, "optimizations disabled"
{×𝕩, "debugging checks enabled ("𝕩")"} List po.debug"DEBUG", po.heapverify"heapverify", po.rtverify"rtverify"
# "build invocation: "⊸∾⍟(×≠) ¯1↓∾∾⟜' '¨ •args
""";", @+10
"char* const cbqn_versionString = """vname""";", @+10
{
•file.Exists AtRoot srcFile? src•FChars AtRoot srcFile? @; # don't update
(AtRoot srcFile) •FChars src
}
objs MakeCCInv po.CBQNc, , linkerCache, "versionInfo", srcFile,
}
res MakeLinkerInv po.Linker, linkerCache, {po.emcc? "BQN.js"; "windows"po.os? •file.Name po.output; "res"}, objs
res.dst linkerCache
}
outPath •wdpath •file.At po.output
{
po.clangd? clangd.Finish ¬po.dry;
# else, regular build
success Run cachedBin
{ 𝕊:
po.emcc?
SH "cp", AtRoot cachedBin, outPath •file.At •file.Name cachedBin
SH "cp", AtRoot linkerCache.File "BQN.wasm", outPath •file.At "BQN.wasm"
;
SH "cp", "-f", AtRoot cachedBin, outPath
} success ¬po.dry
OnExit@
•Exit ¬success
}