-
Notifications
You must be signed in to change notification settings - Fork 547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Granular nix #15848
Granular nix #15848
Conversation
1106bc1
to
d97766c
Compare
docs/README.md
Outdated
@@ -3,3 +3,4 @@ | |||
The docs for the Mina Protocol website are published on [docs.minaprotocol.com](https://docs.minaprotocol.com/). | |||
|
|||
The docs repository is [https://github.com/o1-labs/docs2/](https://github.com/o1-labs/docs2/). | |||
Hey |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: revert the random change
69c9b7b
to
73917d7
Compare
66b2076
to
9e925c5
Compare
Reverts change introduced in fcbea58. In develop it isn't used. And it prevents further work in incremental nix builds of Mina. If a patch will need to be applied to a source tree, it's recommended to come up with a more involved process where patch file is given as an argument to nix/ocaml.nix, then split to a number of separate files (using patchutils) and then every patch is applied in patch phases of derivations that work on chunks of tree.
Using the o1-labs/dune-nix library allows for "granular" builds. Every Ocaml package, test, executable etc. are treated by nix as standalone build units. This allows to use nix's capabilities of parallelizing builds and caching build results.
9e925c5
to
97f63d0
Compare
!ci-build-me |
!ci-nix-me |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stamping only product
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super great feature!
@@ -85,16 +91,150 @@ let | |||
[ zlib bzip2 gmp openssl libffi ] | |||
++ lib.optional (!(stdenv.isDarwin && stdenv.isAarch64)) jemalloc; | |||
|
|||
dune-nix = inputs.dune-nix.lib.${pkgs.system}; | |||
|
|||
base-libs = dune-nix.squashOpamNixDeps scope.ocaml.version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when someone changes opam.export
? Does dune-nix
re-generate some nix parts, or nix reads opam.export
every time?
From looking at dune-description
and how it's used in duneDescLoaded
and info
, it seems the latter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's being read in full after every change. However parsing it with dune-description
is fast, and result will be cached. So this isn't an issue.
sqlSchemaFiles = with inputs.nix-filter.lib; | ||
filter { | ||
root = ../src/app/archive; | ||
include = [ (matchExt "sql") ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a bit offtop, but assuming these sql files create/drop tables, what happens during DB migrations? Migrations are all outside of scope of nix building, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have any migrations as of now. This folder contains SQL schema files which should always be enough for a unit-like testing of archive DB.
commitShort = builtins.substring 0 8 commit; | ||
cmdLineTest = '' | ||
mina --version | ||
mv _build/default/src/test/command_line_tests/command_line_tests.exe tests.exe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation for including just this set of tests? (I understand this just checks that the node does not crash immediately? and can recover from a crash?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are very simple tests of CLI interface. I think idea behind this test was to run a comprehensive check of all of the CLI commands, that they do what they are supposed to do.
As of now, it's very basic. Something along the lines of what you said.
It's wrapped in a vmtest because it opens tcp/ip ports which is prohibited within nix environment.
./tests.exe --mina-path mina | ||
''; | ||
in [ | ||
(dune-nix.testWithVm { } "mina_net2" [ pkgs.libp2p_helper ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this testWithVm
somehow use the vmOverlays
above? It seems that vmOverlays
is only passed to granularOverlay
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, it doesn't. Every entry in vmOverlays
is independent from another.
Most tests we define in vmOverlays
use tcp/ip
localhost connections within the test, which we can't run explicitly from within nix. With an exception of __src-lib-staged_ledger-test__
, which needs a VM because it uses far too many file descriptors, and nix is restrictive about that (VM specifes a specifically set high limit on open file descriptors).
pkgs.__src-lib-ppx_mina-tests__ = | ||
makefileTest "__src-lib-ppx_mina-tests__" super; | ||
pkgs.__src-lib-ppx_version-test__ = | ||
makefileTest "__src-lib-ppx_version-test__" super; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit worried that these declarations are so explicit. I'd assume there would be a script that auto-generates them, or at least tells if you if a new package you created is not explicitly declared in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not exactly this way. Absolute majority of tests are autodetected. Anything that dune test
runs will be discovered automatically.
For some of these tests we need to specify external dependencies. E.g. for mina_lib
tests we need libp2p_helper
compiled from Go code. With make build
we'd inject it into environment for all the packages, in nix wrapping I decided to make it more delicate: make only relevant tests/modules dependent on an external dependency, not every single package. My approach has a benefit of say mina_wire_types
tests not requiring libp2p_helper
to be compiled/executed.
In this case though it's different. Both ppx_mina
and ppx_version
have their tests turned off the execution via dune test
, because dune test
interface wasn't flexible enough (sometimes we want to test that some test code doesn't compile with a ppx, this is not easy to do from within dune test
). For this reason tests for these packages are overridden with explicit makefileTest
scenario.
@@ -31,7 +31,7 @@ | |||
mina_metrics.none | |||
logger.fake | |||
) | |||
(forbidden_libraries node_config) | |||
(forbidden_libraries mina_node_config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this renamed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wasn't renamed, just public name will be used instead of private name. Some context: Ocaml packages always have a private name and sometimes have a public name. When two packages are compiled simultaneously from their codes, both private and public names are good to go. This is the case of running dune build src/lib
that we currently have in CI.
Nix wrapping introduced in this PR has a different strategy: it compiles every package in isolation with code of other packages not being available. Dependencies are supplied as precompiled units via OCAMLPATH
variable. When package is used as a precompiled dependency from path, it only can be referred to by its public name. Hence the need to update this dune
file.
🤩 🤩 🤩 |
This PR is ought to be rebased after the following PRs are merged: #15510
This PR updates
flake.nix
,nix/ocaml.nix
to integrate o1-labs/dune-nix library into the repository.This adds a family of derivations that allow:
Crucially, it allows Mina to be built package-by-package, caching build/test results for code that didn't change. This brings CI run (similar to
buildkite/scripts/unit-test.sh
) to be shortened from ~40 minutes to something between 2 minutes and 17 minutes (depending on how deep the changes are).Build methodology
Dune files are analyzed by the o1-labs/describe-dune tool. Libraries, executables, tests and generated files, along with their mutual dependencies (as present by dune files) are extracted.
After that, dependencies between all of the units (libraries, executables, tests) and files are determined. Then a greedy algorithm attempts to "upgrade" each library/executable dependency to a package dependency. It ends with an error if it fails. But if it succeeds, it comes up with a dependency tree (that can be plotted with
nix build mina#info.deps-graph
) that allows compilation of project's Ocaml code to be executed package-by-package.Then packages are compiled one by one using
dune
, with dependencies of a package built before it and then provided to the package viaOCAMLPATH
environment variable. Code of each package is isolated from its dependencies and packages that depend on it.New nix derivations
There is a bunch of new derivations available using nix flake for Mina repository.
In subsections there are some examples and the full list of new derivations.
A note on building process and treatment of packages. All of the code is build on a package level. That is, for compilation units (executables, libraries, tests) that are assigned to a package, they are compiled with
dune build --only-packages <package>
. Any of dependencies are pre-built the same way and are provided todune
viaOCAMLPATH
.For compilation units that have no package defined, a synthetic package name is generated. Path to dune directory with these units is quoted by replacing
.
with__
and/
with-
in the package path, and also prepending and appending the resulting string with__
. E.g. for pathsrc/test/command_line_tests
a synthetic package__src-test-command_line_tests__
is generated. Such synthetic packages are built withdune build <path-to-dune-directory>
(isolation is ensured by filtering out all of irrelevant Ocaml sources).Examples
CLI commands below assume to be executed from a directory with Mina repository checked out and
./nix/pin.sh
executed.unit-test.sh
nix build mina#granular
nix build mina#all-tested
nix build mina#all
mina_lib
packagenix build mina#pkgs.mina_lib
mina_net2
package and run its testsnix build mina#tested.mina_net2
validate_keypair
executablenix build mina#exes.validate_keypair
src/lib/staged_ledger/test
nix build mina#tested.__src-lib-staged_ledger-test__
mina_lib
nix build mina#info.deps-graphs.mina_lib
nix build mina#info.deps-graph
nix build mina#info.deps
Dependency description generated via
nix build mina#info.deps --out-link deps.json
is useful for investigation of depencies in a semi-automated way. E.g. to check which executables implicitly depend onmina_lib
, just run the followingjq
command:Combined derivations
Derivations that combine all packages: all of the Ocaml code is built, three options vary in which unit tests are executed.
#all
#granular
#all
+ running all of tests except for tests insrc/app
(behavior similar tobuildkite/scripts/unit-test.sh
)#all-tested
#all
+ running all discovered teststest
stanzas and libraries withinline_tests
stanzaIndividual compilation units
These allow every package to be compiled/tested in isolation, without requiring all of the other packages to be built (except for dependencies, of course).
#pkgs.<package>
dune build <package>
OCAMLPATH
_build
directory#src.pkgs.<package>
dune
from the parent directory), as filtered for building the#pkgs.<package>
#files.<path>
<path>
used by stanzas in other directories<path>/dune
scope#src.files.<path>
#files.<path>
#tested.<package>
#pkgs.<package>
, but also runs tests for the packageThere are also a few derivations that help build a particular executable. These are merely shortcuts for building a package with an executable and then copying the executable to another directory.
#all-exes.<package>.<exe>
bin/<exe>
that is executable with name<exe>
from package<package>
<exe>
stands for executable's public name (and private name otherwise)#exes.<exe>
#all-exes.<package>.<exe>
<exe>
is defined for multiple packages, error is printed<exe>
is defined in a single package<pkg>
, it's same as#all-exes.<pkg>.<exe>
Metadata/debug information
#info.src
dir
to metadata related to files outside of dune directory that is needed for compilationsubdirs
, containing list of file paths (relative to thedir
) that contain dune files with compilation unitsfile_deps
, containing list of file paths from other dirs which should be included into source when compiling units fromdir
(e.g. some top-leveldune
files which useenv
stanza)file_outs
, containing a mapping from absolute path of a file generated by somerule
stanza of the dune file to type of this file output (for type being one ofpromote
,standard
andfallback
)#info.exe
src/app/archive/archive.exe
) to an object with fieldsname
andpackage
package
field contains name of the package that declares the executablename
is eitherpublic_name
of executable (if defined) or simplyname
#info.package
deps
contains opam library names exactly as defined by dune (ppx-related libraries are concatenated tolibraries
)#info.separated-libs
#info.deps
containing information about edges in dependency graph that form a cyclePackage ${pkg} has separated lib dependency to packages
which may occur after future edits to dune files#info.deps
#dune-description
#base-libs
opam.export
(plus some extra opam packages for building)Dependency graphs
#info.deps-graph
nix build mina#info.deps-graph --out-link all.dot && dot -Tsvg -o all.svg all.dot
)#info.deps-graphs.<package>
<package>
Here's example of graph for
mina_wire_types
package:Note that there are a few details of this graph. Graph generated for a package
p
displays may omit some of transitive dependencies of a dependency package if they're formed by units on whichp
has no dependency itself. And dependenciesA -> B
andB -> C
do not always implyA -> C
: packageB
may have a test dependent on packageC
, butA
doesn't depend on that tests, only libraries it uses.True meaning of this graph is that one can build package by package following edges backwards, building all of the units of a package all at once on each step. Interpretation of a graph for dependency analysis is handy, just it's useful to keep in mind certain details.
Testing
Via CI:
!ci-nix-me
Manually:
nix build mina#tested.<pkg>
, observed failuresAlso, to check the caching:
nix build mina#granular
Future work
Some work items are left for future:
promote
propagation are generated the same way they appear in repository!ci-nix-me
calldune-nix
from nix to Ocaml (processing is inefficient in the current form)Checklist