Ship fstar.lib as source in binary packages, and add fstar.exe --install_lib[_with_deps] to install it#4300
Ship fstar.lib as source in binary packages, and add fstar.exe --install_lib[_with_deps] to install it#4300tahina-pro wants to merge 14 commits into
fstar.lib as source in binary packages, and add fstar.exe --install_lib[_with_deps] to install it#4300Conversation
A regular build/install (e.g. `opam install fstar.opam`, which runs
`make install`) installs a *compiled* fstar.lib via `dune install`, built
against the user's own OCaml toolchain. Binary packages, however, should
not ship a precompiled fstar.lib, to avoid OCaml ABI mismatches with the
user's compiler/findlib; instead they ship fstar.lib as OCaml sources plus
a dune project the user builds themselves.
- stage{1,2,3}/dune/libapp/dune: keep (public_name fstar.lib) so that a
plain `dune install` / `make install` (and hence `opam install
fstar.opam`) installs a usable compiled fstar.lib.
- .scripts/bin-install.sh (run only for binary packages): when given the
extracted ulib.ml directory, strip the compiled fstar.lib (and its
"lib" sub-package from META) and replace it with sources + dune project.
- Makefile (__do-archive): pass the stage's ulib.ml dir to bin-install.sh.
- mk/stage.mk, stage3/Makefile: drop the source-shipping from the shared
install rule (it now only happens in the binary-package path).
- mk/fstar-lib-src/{dune,dune-project,README.md}: self-contained dune
project (with public_name fstar.lib) for the user to build & install.
- mk/fstar-lib-src/install.sh: mediated installer that builds fstar.lib
and installs it WITHOUT clobbering the rest of the F* findlib metadata.
fstar.lib is the `lib` sub-package of the findlib package `fstar` (which
also provides `compiler`/`pluginlib`), and dune writes <libdir>/fstar/META
with no merge/skip option, so a bare `dune install` would overwrite that
META down to just `lib`, dropping `compiler`/`pluginlib` and breaking
`fstar.exe --ocamlopt_plugin`. install.sh runs the install and then merges
the freshly generated `lib` stanza back into the pre-existing META.
- .scripts/install-fstar-lib-src.sh: copy the fstar.lib sources, the dune
project, and install.sh into <prefix>/lib/fstar/lib. Keeps app/ints/dune,
which generates the fixed-width integer modules at build time.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a new command-line mode that builds and installs the F* application
library (fstar.lib) into the current OCaml/opam environment, intended for
binary packages that ship fstar.lib as sources rather than precompiled.
Behavior:
- If `ocamlfind query fstar.lib` already succeeds, do nothing (exit 0).
- Else if `ocamlfind query fstar` succeeds, error out (exit 1): the
findlib package `fstar` already exists (e.g. providing fstar.compiler
and fstar.pluginlib), and installing fstar.lib there would overwrite
its META and drop the other sub-packages.
- Else, run the installer shipped alongside the sources at
<lib_root>/lib/install.sh, which builds fstar.lib with dune and
installs it (merging the `lib` stanza into any existing META).
The findlib queries are run in the ambient environment (no OCAMLPATH
override), so they reflect the user's current opam switch rather than the
library bundled with this fstar.exe.
- FStarC.OCaml.{fsti,fst}: new `install_lib` (returns an exit code) plus
an `ocamlfind_query` helper, using Util.system_run.
- FStarC.Main.fst: dispatch `--install_lib` in go_normal, beside the
other do-something-and-exit modes (--locate_*).
- FStarC.Options.{fst,fsti}: register the `install_lib` boolean option
and its --help text.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The findlib package presence checks for `--install_lib` previously shelled
out to `ocamlfind query <pkg>` via Sys.command, which needs a shell and is
unavailable on native (non-Cygwin) Windows builds of fstar.exe.
Use the findlib library directly instead: a new
FStarC.Util.findlib_package_exists wraps `Findlib.init` +
`Findlib.package_directory`, returning whether the package resolves in the
current (ambient) OCaml environment, with no subprocess. As before the
queries run before any OCAMLPATH override, so they reflect the user's opam
switch rather than the library bundled with fstar.exe.
- src/ml/FStarC_Util.ml, src/basic/FStarC.Util.fsti: add
findlib_package_exists.
- src/fstar/FStarC.OCaml.fst: ocamlfind_query now calls it.
- stage{1,2,3}/dune/fstar-guts/dune: link the `findlib` library into
fstar.compiler.
- fstar.opam: depend on `ocamlfind`.
- FStarC.Options.fst: update the --help text (no longer mentions invoking
ocamlfind for the check).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The install step of --install_lib previously shelled out to the shipped `install.sh`, which requires bash and so does not work with the native (non-Cygwin) Windows build of fstar.exe. Build and install fstar.lib by spawning `dune` directly (no shell), via Util.create_process / Util.waitpid: dune build --root <lib_root>/lib dune install --root <lib_root>/lib - `--root` avoids needing to chdir into the source directory. - No `--prefix` is passed, so dune installs into the active opam switch (its default destination), matching what install.sh did via `opam var`. - We no longer merge META: --install_lib already refuses when an `fstar` findlib package is present, so at install time dune writes a fresh fstar/META with just the `lib` sub-package, clobbering nothing. The buildable-package check now looks for `dune-project` (rather than the old install.sh). install.sh is still shipped for the standalone `./install.sh` flow documented in mk/fstar-lib-src/README.md, which keeps the META-merge behavior for users installing into a switch that already has the `fstar` package. Also updates the --help text accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Now that `fstar.exe --install_lib` drives dune directly (commit 431c320), nothing in the tree invokes the shipped `install.sh`: it was only copied into binary packages for a manual `./install.sh` end-user flow. Drop the script and its references: - .scripts/install-fstar-lib-src.sh: stop copying install.sh into the packaged fstar.lib sources directory. - mk/fstar-lib-src/README.md: document `fstar.exe --install_lib` (or a plain `dune build && dune install`) instead of `./install.sh`, and reframe the META section as a caveat about installing into a switch that already has the `fstar` findlib package (which --install_lib guards against). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`--install_lib` builds and installs fstar.lib but assumes its OCaml dependencies are already present in the current opam switch. This adds a companion option, `--install_lib_with_deps`, that first installs those dependencies and then performs the same build/install. It works by shipping `fstar-lib.opam` in the binary package (in the parent of the dune project dir, so `dune build` does not pick it up as a project package). `--install_lib_with_deps` runs the same fstar.lib/fstar findlib checks as `--install_lib`, then `opam install --deps-only` on that file, then the dune build/install. opam is driven directly (no shell), so this works on native Windows too. `fstar-lib.opam` has `build: [ "false" ]` so that a direct `opam install ./fstar-lib.opam` fails on purpose: the file only carries fstar.lib's dependencies and is not meant to be installed as a package. `--deps-only` does not run the build step, so it still installs the deps. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A binary package's fstar.exe is a standalone native binary, but the findlib packages produced alongside it (fstar.compiler, fstar.pluginlib and a compiled fstar.lib, advertised by META/dune-package) only work for a user whose OCaml compiler and dependency package versions exactly match those used to build the package. That is impossible for a recipient of a binary package, so .scripts/bin-install.sh now removes compiler/, pluginlib/, META and dune-package from the binary package. fstar.lib is still re-shipped as OCaml sources + a dune project, so the user builds it themselves against their own toolchain via `fstar.exe --install_lib`. This only affects the binary-package path (bin-install.sh, invoked from the Makefile __do-archive target with a ulib.ml source dir). `make install` and `opam install fstar.opam` go through mk/stage.mk's install rule, which does not call this script, and keep shipping the compiled OCaml packages. Removing META also fixes findlib resolution of the user-built fstar.lib: --ocamlopt/--ocamlenv prepend <exec>/../lib to OCAMLPATH, and with no META there ocamlfind falls through to the user's switch (where --install_lib installed fstar.lib with a proper META) instead of being shadowed by the package's own META. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… installs)
`fstar.exe --install_lib[_with_deps]` now extends OCAMLPATH with the bundled
library dir (<exec>/../lib), exactly as --ocamlenv does, before its "is
fstar.lib already available?" detection guard. Findlib.init re-reads OCAMLPATH
on each query, so the guard now sees a compiled fstar.lib shipped alongside
this fstar.exe (source build, `make install`) in addition to the current opam
switch (`opam install`).
This makes the command behave uniformly across all install methods:
- source build / `make install`: the bundled compiled fstar.lib + META is
detected, so the command is a clean no-op ("already installed; nothing to
do"). Previously it errored, because the guard only queried the ambient
switch and then looked for a fstar.lib-src dune project that these flavors
do not ship.
- `opam install`: fstar.lib is in the switch (ambient) -> no-op, as before.
- binary package: the bundled dir ships no findlib metadata (we strip it),
so the extension is a no-op and we build+install fstar.lib into the switch
from the shipped sources, as before.
The recipe "run `fstar --install_lib_with_deps`, then build the app with
`fstar --ocamlopt`/`--ocamlenv`" is now uniform regardless of how F* was
installed. install_lib runs as a terminal command (Main.fst exits right
after), so mutating the process environment here is harmless.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`make test` (via the shared _test rule) now runs a small _test_install_lib check that asserts `fstar.exe --install_lib_with_deps` is a no-op: a source-built (or `make install`ed) F* ships a compiled fstar.lib next to fstar.exe, which the command must detect (returning 0 and "already installed; nothing to do") rather than rebuilding fstar.lib or invoking opam. This guards against regressions in that detection (e.g. in the OCAMLPATH extension that lets the guard see the bundled library). The check uses the per-stage FSTAR_EXE already plumbed through _test, so it covers test-1/test-2/test-3. It needs no OCaml toolchain (findlib is linked into fstar.exe), so it is unconditional. The binary-package path, where fstar.lib is shipped as sources and the command actually builds+installs it, is exercised separately (not here). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… package Exercise the F* application-build workflow (tests/simple_hello, tests/dune_hello, tests/extraction) across install methods: - build-opam.yml (the `opam` job): after `opam install .`, build the three sample apps against the fstar.lib installed in the switch. Pass an absolute FSTAR_EXE, since tests/extraction includes mk/test.mk which resolves FSTAR_EXE to an absolute path (so a bare `fstar.exe` would be mis-resolved relative to the test directory). - nightly-ci.yml: new `binary-install-lib` job that, starting from only the binary package (which ships no precompiled OCaml packages), runs `fstar.exe --install_lib_with_deps` to build+install fstar.lib from source into a fresh opam switch, then builds the same three sample apps against it. This covers the uniform, install-method-independent app-build workflow end to end. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`make package` (= package-3, BROOT=stage3) failed at the binary-package archive step with: cp: cannot stat '.../stage3/ulib.ml': No such file or directory Commit 83defea made __do-archive pass `$(BROOT)/ulib.ml` to bin-install.sh as the directory of F*-extracted ulib OCaml sources to re-ship as fstar.lib in the binary package. That works for package-1 and package-2 (stage1/ulib.ml, stage2/ulib.ml are produced by the build), but stage3 never re-extracts ulib to OCaml -- it reuses stage2's extraction (cf. the committed stage3/dune/libapp/ulib.ml -> ../../../stage2/ulib.ml symlink) -- so stage3/ulib.ml does not exist. Introduce a ULIB_ML variable (default `$(BROOT)/ulib.ml`, unchanged behavior for package-1/2) and have package-3 override it to `stage2/ulib.ml`, encoding the stage3-reuses-stage2 invariant where it belongs. Verified end to end: make -j16 package package-src FSTAR_TAG= FSTAR_PACKAGE_Z3=false ADMIT=1 now completes successfully; the resulting fstar.tar.gz ships all 96 ulib.ml sources under lib/fstar/lib/ and no precompiled OCaml packages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
F* now links the findlib OCaml library: FStarC_Util.findlib_package_exists
calls Findlib.init/Findlib.package_directory directly (instead of spawning
ocamlfind), and stage{1,2,3}/dune/fstar-guts/dune list `findlib` in their
(libraries ...) stanza. This was introduced together in the commit that
switched --install_lib's package detection to the findlib library.
The nix derivation did not reflect this new dependency. Add `findlib` to
.nix/fstar.nix's buildInputs (and the function arguments; callPackage fills
it from ocamlPackages.findlib), following the nixpkgs convention for
packages that link the findlib library (cf. utop). It goes in buildInputs
rather than propagatedBuildInputs because findlib is statically linked into
fstar.exe and needs no runtime stublibs -- Findlib.init reads OCAMLPATH /
findlib.conf from the user's environment.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The --help text and the fstar.lib README mentioned dune (and opam for --install_lib_with_deps) but never stated that ocamlfind/findlib must be installed, even though it genuinely is: F* uses the findlib library to detect whether fstar.lib is already installed, 'dune install' records the fstar/META that 'ocamlfind query fstar.lib' resolves, and OCaml code extracted by F* is compiled by invoking ocamlfind. Add the ocamlfind/findlib requirement to both --install_lib and --install_lib_with_deps help strings and to the README prerequisites. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tection
--install_lib_with_deps installs fstar.lib's OCaml dependencies via
`opam install --deps-only` on fstar-lib.opam. Add ocamlfind to that
file's depends so findlib itself is installed when missing -- but only
for --install_lib_with_deps; plain --install_lib never runs opam and
still assumes ocamlfind/dune are already present.
For this to work, the pre-flight "is fstar.lib already installed?" check
must not crash when findlib is absent. Findlib.init raises Failure when
no findlib.conf can be located (verified empirically), so move it inside
the try and also catch Failure, returning false ("not resolvable") so
the caller proceeds to install findlib via opam.
Update the --install_lib_with_deps help text and the fstar.lib README to
reflect that it now installs ocamlfind/dune for you, while --install_lib
still requires them preinstalled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
New nightly-ci tasks succeed (ocaml client tests with |
|
I am a bit torn on the Can we point dune to the fstar.lib ML source directory? (That is, 3dgen's dune file would build fstar.lib from source.) |
|
I have an EverParse PR into its |
Alas, I am not sure this solution works for cascading F* applications: if an user develops a F* application meant to be an ocaml package, and if another user develops an F* application that uses that ocaml package, fstar.lib will be compiled and linked twice, which may be a problem. Another problematic scenario: what if an OCaml program depends on two libraries that both depend on fstar.lib. |
|
Gabriel:
I acknowledge I am not sure about use cases for However, for
Notably, the process with In light of the problematic scenarios in my previous comment, a possible alternative to your proposal would be to make |
|
My proposal.
|
|
Thanks Gabriel for your proposal!
By local F* installations, I presume you mean both F* built from source and F* installed with
Here, you are proposing that applications must explicitly choose the place where to install fstar.lib, because we should not automatically install it into either the F* binary package (which you deem read-only) or the current user's opam switch (which you want to keep clean, except maybe for the fstar.lib dependencies), and that F* should not care about where fstar.lib is installed. Is my understanding correct? Other than that, I presume the check on fstar.lib and fstar not already installed will stay. This is necessary to maintain uniformity in the |
Motivation
F* binary packages (e.g. the nightly tarballs) bundle a standalone native
fstar.exe. Until now they also shipped the F* OCaml findlib packages(
fstar.compiler,fstar.pluginlib, and a compiledfstar.lib) that wereproduced alongside that binary. Those compiled packages only work for a user
whose OCaml compiler and dependency versions exactly match the ones used to
build the package — which is essentially never true for the recipient of a
binary package. As a result, downstream consumers that need
fstar.libtocompile F*-extracted OCaml code (e.g. EverParse, and any project using
fstar.exe --ocamlopt/--ocamlenv) could not reliably build against a binarypackage.
This PR makes binary packages ship
fstar.libas OCaml sources + a duneproject, and adds a new F* command-line mode,
--install_lib(and
--install_lib_with_deps), that builds and installs thatfstar.libinto the user's own opam switch against their own toolchain. The end result is
a single, install-method-independent recipe for building OCaml apps against
F*:
This recipe is now uniform whether F* was obtained via a source build,
make install,opam install, or a binary package.What changed
Packaging: binary packages ship
fstar.libas source.scripts/bin-install.sh(run only on the binary-package path) strips
compiler/,pluginlib/,METAand
dune-packagefrom the package, and re-shipsfstar.libas OCaml sourcesplus a self-contained dune project (
mk/fstar-lib-src/{dune,dune-project,README.md}).A regular
make install/opam install fstar.opamis unaffected and keepsinstalling the compiled
fstar.libviadune install(the(public_name fstar.lib)stanzas instage{1,2,3}/dune/libapp/duneare retained).METAfrom the bundled lib dir also fixes findlib resolution ofthe user-built
fstar.lib:--ocamlopt/--ocamlenvprepend<exec>/../libto
OCAMLPATH; with noMETAthere, ocamlfind falls through to the user'sswitch (where
--install_libinstalled a properfstar.lib) instead of beingshadowed by the package's own stale
META.make package(stage3) fix: stage3 reuses stage2's ulib extraction ratherthan re-extracting, so a new
ULIB_MLvariable (defaulting to$(BROOT)/ulib.ml, overridden tostage2/ulib.mlfor package-3) encodes thatinvariant and avoids a
cp: cannot stat '.../stage3/ulib.ml'failure.New command:
fstar.exe --install_libBuilds and installs
fstar.libinto the current OCaml/opam environment from theshipped sources. Behaviour:
fstar.libalready resolves via findlib, it is a clean no-op("already installed; nothing to do").
fstarexists butfstar.libdoes not, it errors out,rather than overwriting that package's
METAand dropping its othersub-packages.
dune build/dune install --root <lib_root>/lib, no shell, no--prefixso it installs into the activeswitch) to build and install
fstar.lib.Implementation notes:
fstar.exe(
FStarC.Util.findlib_package_exists→Findlib.init/package_directory),not an
ocamlfindsubprocess, so it works on native (non-Cygwin) Windows.findlibis added to thefstar-gutsdune(libraries ...)and tofstar.opam's and the nix derivation's dependencies.dunedirectly (nobash install.sh), againfor native-Windows support.
OCAMLPATHwith the bundled lib dir(
<exec>/../lib) exactly as--ocamlenvdoes, so it uniformly detects acompiled
fstar.libshipped next tofstar.exe(source build /make install) as well as one in the opam switch (opam install). This is why thecommand is a no-op for non-binary installs.
New command:
fstar.exe --install_lib_with_depsSame as
--install_lib, but first installsfstar.lib's OCaml dependencies(and
ocamlfinditself) viaopam install --deps-onlyon afstar-lib.opamshipped in the package. opam is driven directly (no shell) for native Windows.
fstar-lib.opamcarriesbuild: [ "false" ]so a directopam install ./fstar-lib.opamfails on purpose — it is a dependency carrier, not aninstallable package — while
--deps-only(which skips the build step) stillinstalls the deps. The pre-flight findlib check is hardened to treat a missing
findlib.conf(which makesFindlib.initraiseFailure) as "not resolvable",so it proceeds to install findlib rather than crashing.
Tests / CI
make testasserts--install_lib_with_depsis a no-op for a sourcebuild across test-1/2/3 (guards the bundled-lib detection /
OCAMLPATHextension). It needs no OCaml toolchain since findlib is linked into
fstar.exe.build-opam.yml: afteropam install ., build the sample apps(
tests/simple_hello,tests/dune_hello,tests/extraction) against theswitch's
fstar.lib.nightly-ci.yml: a newbinary-install-libjob that, starting from onlythe binary package, runs
--install_lib_with_depsto build+installfstar.libfrom source into a fresh switch and builds the same sample appsagainst it — exercising the uniform app-build workflow end to end.
Documentation
--helptext for both options, andmk/fstar-lib-src/README.md, document theworkflow and the
ocamlfind/findlib/dune(and, for--install_lib_with_deps,opam) requirements — noting that--install_lib_with_depsinstallsocamlfind/dunefor you, whereas plain--install_libassumes they arealready present.
Files touched
Compatibility
make installandopam install fstar.opamare unchanged: they still installa compiled
fstar.lib, and--install_lib[_with_deps]is a no-op there.OCaml packages); the new commands rebuild
fstar.libfrom those sources.fstar.exe(
buildInputs, notpropagatedBuildInputs, in nix), needing no runtimestublibs.