t0g ← •MonoTime@ #!build/obj2/for_build # todo: # clean # 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, "j", @, "Number of parallel jobs"⟩ ⟨1, "verbose", 0, "Log more things"⟩ ⟨1, "rebuild", 0, "Forcibly rebuild everything"⟩ ⟨1, "notui", 0, "disable live-updating status display"∾@+10⟩ ⟨1, "rebuild-singeli", 0, @⟩ ⟨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, "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 C compiler & linker 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)"⟩ ⟨0, "v", "", "Version string to report by --version; v=0 to disable"∾@+10⟩ ⟨1, "shared", 0, "Build a shared library; default output depends on 'os' setting"⟩ ⟨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, "Don't build, instead generate a compile_commands.json for clangd"∾@+10⟩ ⟨1, "FFI", @, "Enable FFI through libffi; On by default, except for WASM builds"⟩ ⟨1, "pkgconfig", 1, "Attempt to use pkg-config to find libffi flags"⟩ ⟨1, "singeli", 0, "Enable compilation with Singeli"⟩ ⟨1, "replxx", 0, "Enable REPLXX"∾@+10⟩ ⟨0, "usz", "32", "Size of array length fields in bits; default 32, can be 32 or 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"∾@+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} SepArgs ← {' ' ((¬-˜⊢×·+`»⊸>)∘≠⊔⊢) 𝕩} 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∧'+'=(0⌈s1-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 -- '",⊑(¬ks∊on)/ks, "'; see --help for options"⟩}_assert_(∧´) ks∊on ci ← on⊐ks # index in option specification {𝕊: ∾⟨"Error: Unexpected '+=' for '",ks⊑˜⊑/¬𝕩,"'"⟩}_assert_(∧´) (ci⊏ot2)∨¬fl Gr ← {(≠ot)↑ci⊔𝕩} gvs ← Gr vs # values for each option {𝕊: ∾⟨"Error: Multiple values given for '",on⊑˜⊑/¬𝕩,"'"⟩}_assert_(∧´) ot2∨1≥≠¨gvs {𝕊: ∾⟨"Error: Expected value to be provided for ",⊑𝕩/ks⟩}_assert_(¬∨´) (¬ci⊏ot1) ∧ ¬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⊐nextRedraw-0.02 dm ← (0.1⌊nextRedraw-t1) WaitForOne Ts work lm ← ¬dm {𝕊: Fail "Error: Fork died" ⋄ dm‿lm∧↩ 𝕩} src ← "char* cbqn_versionString = """∾(∾∾⟜"\n"¨ ×∘≠¨⊸/ ⟨ "CBQN "∾{po.versionName≡""? "on commit "∾(GitCmd ⟨"rev-parse", "HEAD"⟩)∾{""≡GitCmd ⟨"status", "--porcelain", "src"⟩? ""; "-dirty"}; po.versionName} "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 { •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? "res.exe"; "res"}, objs⟩ res.dst ⋈ linkerCache } outPath ← •wdpath •file.At po.output { po.clangd? clangd.Finish@; # 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 OnExit@ •Exit ¬success }