nimble icon indicating copy to clipboard operation
nimble copied to clipboard

`nimble test`, `nimble build` etc should build all artifacts under `./build`

Open timotheecour opened this issue 4 years ago • 2 comments

Out of source builds (and tests) is standard practice with many benefits over in-source builds (eg https://cgold.readthedocs.io/en/latest/tutorials/out-of-source.html or many other resources), such as easy gitignore (just gitignore a single dir, avoid having to do this https://github.com/nim-lang/nimble/pull/786), preventing accidental checking in of binaries or generated files (eg in nimble repo itself, see https://github.com/nim-lang/nimble/commit/4a2aaa07dc6b52247bc0b7d40fac30a1f7eabac0#diff-b39306cdc7f9dc8317cd6f5356f31a61), makes it trivial to cleanup (remove a single well-known, gitignore'd dir) while avoiding accidental removal of important files (thus, avoiding doing things like this https://github.com/nim-lang/nimble/pull/745); furthermore, it enables keeping separate builds, say when comparing 2 options, and makes it easy to ignore generated files in tools like grep/ripgrep etc

proposal

  • by default, all nimble commands (eg nimble test, nimble build, nimble doc etc) should build all artifacts (binaries, shared libs, and other files generated by running tests) rooted under a standardized dir (let's say, @build=@pkg/build where @pkg expands to full path of root of current nimble package
  • that dir can be overriden with nimble --buildDir:otherdir, expanding to @pgk/otherdir if not absolute
  • nim's pathSubs logic can be reused so that special tokens like --buildDir:@nimcache/@pkgname work
  • nimble init gets updated to add a .gitignore file (if doesn't exist) with a single entry: /build so that git status stays clean after nimble build, nimble test etc
  • I'm using @ instead of $ so that it works cross platform and without quoting needed, which always cause issues cross platform. nim's pathSubs supports @.
  • nimble doc, if task isn't provided, can use the newly introduced nim doc --project --outDir:@build/docs --docroot --index:on pathto/foo.nim (see https://github.com/nim-lang/Nim/pull/13223)

implementation

  • when running tasks (eg nimble test), nimble adds an implicit cmdline flag --outdir:@build/@pkgprojdir. This causes nim c -r tests/foo/tbar.nim to generate the binary @build/tests/foo/tbar. The usual overriding rules apply, in case cmd line invocation or the task add -o or --outDir flags

note

  • --outDir needs to be smarter to make this seamless; right now, --outDir:relativePath doesn't work as you'd expect, and --outDir:/hardCodedAbsolutePath is obviously bad, and --outDir:"$nimcache"/foo does a buggy path substitution (via nim's pathSubs logic), but I intend to fix this in nim so nimble or individual packages can use this feature

path substitution variables that can be used:

(via nim's pathSubs)

  • @pkg indicates path to a nimble package, eg for cligen: @pkg=/Users/timothee/git_clone/cligen
  • @build (new) indicates root under which all files are built; loose analog to DESTDIR=... make for makefile projects. This is overridden via nimble --buildDir:otherdir as explained above
  • @pkgprojdir (new) indicates relative path to main project file being compiled, eg, in
cd /Users/timothee/git_clone/cligen
cd tests
nim c -r --outdir:@build/@pkgprojdir foo/main.nim`

@pkgprojdir resolves tests/foo/ (regardless which dir we are in), ie indicates parentDir of relative path of file being compiled relative to @pkg

optional ones:

  • @pkgname (new) indicates current package's name, eg cligen

  • note that this is independent of git repo layout since 1 git repo can contain multiple nimble packages

  • for nimble build, we can also symlink (or just copy) the generated build files artifacts eg, autogenerate symlink @bin/toast that points to @build/@pkgprojdir/toast, so that binaries are in a predictable (flat) location; if not specified in nimble file, @bin shall default to @build/bin

benefits

  • the logic for nimble is extremely simple: just forward one implicit flag --outdir:@build/@pkgprojdir (in the same way as --path:@src is being passed already)
  • git status status clean even after running nimble test and nimble build
  • gitignore files in nimble projects can be kept extremely simple (just 1 line)
  • enables running concurrent tests/builds, say with different options, each going in their own isolated dir, eg:
nimble --buildDir:buildDanger -d:danger test # or `build` or `doc` etc
nimble --buildDir:buildCustom -u:release test
  • I'm honoring the relative hierarchy in the package (via @pkgprojdir) so that generated output files (eg binaries) don't clobber each other in case they have same filename eg:
tests/trunner.nim      => @build/trunner
tests/foo/trunner.nim  => @build/foo/trunner
tests/bar/trunner.nim  => @build/bar/trunner
  • no need for a default nimble clean (ie, this could close https://github.com/nim-lang/nimble/issues/555), since all artifacts go under a single gitignore dir by default. Users can of course override nimble clean if they have test artifacts produced in nonstandard location (and not gitignored). In fact, I consider https://github.com/nim-lang/nimble/pull/745 rather dangerous and fragile, it's pretty much guaranteed to either err on side of safety (lots of false negatives) or cause harm (deleting important files). I'll comment on that PR later.

note

There's a lot of other things we could borrow from homebrew's design, which is an excellent package manager that managed to present a uniform API that abstracts over significant OS/build/language differences.

brew test can be run from anywhere, doesn't require a local git checkout (it'll install it (after its dependencies), and run the tests); I really wish we could do that with nimble btw.

  • brew test runs in a sandbox, eg
brew test gdb --verbose
Testing gdb
/usr/bin/sandbox-exec -f /private/tmp/homebrew20200326-60443-132dh8p.sb ruby -W0 -I $LOAD_PATH -- /usr/local/Homebrew/Library/Homebrew/test.rb /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/gdb.rb --verbose
==> /usr/local/Cellar/gdb/9.1_1/bin/gdb /usr/local/Cellar/gdb/9.1_1/bin/gdb -configuration
This GDB was configured as follows:
   configure --host=x86_64-apple-darwin19.3.0 --target=x86_64-apple-darwin19.3.0
  • it builds out of source so that brew cleanup is safe to run (and doesn't need to be clever), simply by making sure everything gets generated in well known brew-managed directories instead of in-source
  • and in fact, cache files are automatically garbage collected when needed, so users don't even need to worry about it.

timotheecour avatar Mar 27 '20 05:03 timotheecour

I like this idea, but there is too much going on in your proposal. Please focus on a simple implementation that does not include all these fancy path substitution variables.

This should be as simple as possible, and I'm not sure we should even make it customisable.

  • nimble test -> build/tests/mytest, build/tests/mytest2

Furthermore, I'm not sure this should apply to anything other than nimble test. But it's late so I don't have the energy to make a decision on this :)

dom96 avatar Mar 27 '20 23:03 dom96

I second this because it's littering my file explorer with random .exe files since VSCode does not always recognize that the temporary binaries nimble test creates were deleted (and then I have to do a refresh).

LiamDobbelaere avatar Sep 12 '20 11:09 LiamDobbelaere