(library
 (name flan)
 (libraries unix)
 ; -linkall because lib/macro.ml installs itself into Parse.expander at module
 ; initialisation and nothing references it. Without it the linker drops the
 ; module from every executable that does not name it -- bin/main.exe among
 ; them -- and a program calling a macro would fail with an unknown name
 ; instead of expanding. The alternative was an install call at every entry
 ; point, including ones in files this cannot reach.
 (library_flags (-linkall))
 ; Running a macro means dlopening it into the compiler, and OCaml has no
 ; dlopen for ELF -- Dynlink loads OCaml. These are the stubs for it, and the
 ; only C the compiler itself is built from. See lib/dynload_stubs.c.
 (foreign_stubs
  (language c)
  (names dynload_stubs))
 ; No (c_library_flags (-ldl)): since glibc 2.34 dlopen lives in libc itself
 ; and libdl is a stub, and naming it breaks the merged build -- the partial
 ; link -output-complete-obj performs cannot resolve -ldl, so `flan dev` in
 ; one process fails at the link with "cannot find -ldl" while the ordinary
 ; build is unaffected. Add it back only with a platform guard.
 )

; The host shim is Flan's, not the user's, so the compiler carries it rather
; than looking for it in an install directory. Generated from the real .c files
; so there is only ever one copy to edit. flan_dev.c goes into a dev build
; only — it is the run-time name lookup a REPL needs and a release build has
; no use for.
;
; flan_dyn.c is the third: the dynamic-value runtime, tagged values and the
; mark-sweep heap under them. It is carried the same way and for the same
; reason, and it is a *separate* string rather than being appended to
; [source] because it has to stay a separate translation unit — a program that
; calls no dyn operation references no symbol in it, which is what lets a
; build refuse to link it at all. See docs/SPIKE-DYNAMIC.md.
(rule
 (target runtime_src.ml)
 (deps
  %{workspace_root}/runtime/flan_rt.c
  %{workspace_root}/runtime/flan_dev.c
  %{workspace_root}/runtime/flan_dyn.c
  %{workspace_root}/runtime/flan_dyn.h)
 (action
  (with-stdout-to
   runtime_src.ml
   (progn
    (echo "let source = {c|\n")
    (cat %{workspace_root}/runtime/flan_rt.c)
    (echo "|c}\n\nlet dev_source = {c|\n")
    (cat %{workspace_root}/runtime/flan_dev.c)
    (echo "|c}\n\nlet dyn_source = {c|\n")
    (cat %{workspace_root}/runtime/flan_dyn.c)
    ; And the header, which is *not* compiled: it is written into the
    ; directory each translation unit is compiled in, so that a package's C —
    ; and test/dyn_ops.c — can include it. flan_dyn.c does not include it and
    ; declares its own prototypes instead, because the object cache is keyed
    ; on the source text and a header change would not invalidate an object
    ; compiled against the old one.
    (echo "|c}\n\nlet dyn_header = {c|\n")
    (cat %{workspace_root}/runtime/flan_dyn.h)
    (echo "|c}\n")))))
