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, "quiet", 0, "Don't output final status line (or any, combined with notui)"⟩ ⟨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 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)"⟩ ⟨0, "v", "", "Version string to report by --version; v=0 to disable"∾@+10⟩ ⟨1, "shared", 0, "Build a shared library"⟩ ⟨1, "static-lib", 0, "Build a static library"⟩ ⟨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 }