haskell-generic-builder: fix bug when buildTarget does not depend on library#392228
haskell-generic-builder: fix bug when buildTarget does not depend on library#392228thomie wants to merge 3 commits intoNixOS:haskell-updatesfrom
Conversation
There was a problem hiding this comment.
packageConfFile can be an empty directory, so mv "$packageConfFile/"* "$packageConfDir" would fail.
There was a problem hiding this comment.
This comment deserves to be in-code, I guess.
Do we really need to loop and mv each file separately, though?
We could also just do something like:
# Glob expansion can fail, when $packageConfFile is an empty directory.
mv "$packageConfFile/"* "$packageConfDir" || true
Although, I am missing why the directory can be empty? Is this a separate bug fix?
There was a problem hiding this comment.
mv "$packageConfFile/"* "$packageConfDir" || true
Hm, that would silence other errors that might occur during the mv.
How about instead of this for loop, we test whether the directory is non-empty:
if [ -d "$packageConfFile" ] && [ "$(ls -A "$packageConfFile")" ];Although, I am missing why the directory can be empty? Is this a separate bug fix?
The directory can now be empty when buildTarget doesn't include any libraries (or targets that depend on libraries), but the package does contain at least one library (so isLibrary=true). It's not a separate bug fix. Before, the register step would already have failed in this scenario.
One could argue there is another bug in this file, and that is the existence or usage of the isLibrary variable. It's not quite clear what isLibrary means or is supposed to do when a buildTarget is set. Perhaps we should try to remove the isLibrary variable, and make sure all code works whether we're configuring/building/installing a library or not. For example, after this change set, I think we can already remove the if clause from the start of the installPhase, but I didn't try it:
${if !isLibrary && buildTarget == "" then "${setupCommand} install"
# ^^ if the project is not a library, and no build target is specified, we can just use "install".
else if !isLibrary then "${setupCommand} copy ${buildTarget}"
# ^^ if the project is not a library, and we have a build target, then use "copy" to install
# just the target specified; "install" will error here, since not all targets have been built.
78905bc to
51c9843
Compare
There was a problem hiding this comment.
This ugly if/else is here to prevent rebuilding too many packages. It's from the 4th commit: "haskell-generic-build: try to prevent mass rebuild". It should probably not be merged?
There was a problem hiding this comment.
Yeah, we don't need to merge the last commit, we are rebuilding everything anyway on current haskell-updates.
There was a problem hiding this comment.
Hm, silently disabling the tests feels odd to me.
How about changing the default values for doCheck and doBenchmark to be false when a buildTarget is set, instead? Then, they could still be overwritten.
(It is possible to specify the build target in a way that would include the tests, right?)
There was a problem hiding this comment.
Hm, silently disabling the tests feels odd to me.
Note that this change does not disable the tests, it merely disables configuring the tests with --enable-tests when a buildTarget is set.
How about changing the default values for doCheck and doBenchmark to be false when a buildTarget is set, instead? Then, they could still be overwritten.
No I don't think that works.
doCheck currently does 2 things:
- configure the haskell package with
--enable-tests - tell nix to run the
checkPhase
Say a package sets some buildTarget (including a test-suite), and explicitly sets doCheck=True.
With your suggestion, we would still pass --enable-tests together with the buildTarget to Setup --configure, and Cabal would complain about incompatible options.
On the other hand, if the package sets the same buildTarget (including a test-suite) but doesn't override doCheck, then the test-suite would never run.
(It is possible to specify the build target in a way that would include the tests, right?)
Yes, see for example here:
There was a problem hiding this comment.
I still don't understand what would happen in the simple case:
- I rely on the default for
doCheck=true. - I set a
buildTarget
Are the tests run or not?
There was a problem hiding this comment.
Yes they will run, but you do need to list the tests in your buildTarget (as in the hercules-ci-agent example).
If you don't list them in your buildTarget then the checkPhase will fail with "Error: Setup: No test suites enabled." (before these changes, it would fail with "cannot execute: required file not found").
There was a problem hiding this comment.
This comment deserves to be in-code, I guess.
Do we really need to loop and mv each file separately, though?
We could also just do something like:
# Glob expansion can fail, when $packageConfFile is an empty directory.
mv "$packageConfFile/"* "$packageConfDir" || true
Although, I am missing why the directory can be empty? Is this a separate bug fix?
There was a problem hiding this comment.
Yeah, we don't need to merge the last commit, we are rebuilding everything anyway on current haskell-updates.
thomie
left a comment
There was a problem hiding this comment.
@wolfgangwalther Thank you for your review.
There was a problem hiding this comment.
Hm, silently disabling the tests feels odd to me.
Note that this change does not disable the tests, it merely disables configuring the tests with --enable-tests when a buildTarget is set.
How about changing the default values for doCheck and doBenchmark to be false when a buildTarget is set, instead? Then, they could still be overwritten.
No I don't think that works.
doCheck currently does 2 things:
- configure the haskell package with
--enable-tests - tell nix to run the
checkPhase
Say a package sets some buildTarget (including a test-suite), and explicitly sets doCheck=True.
With your suggestion, we would still pass --enable-tests together with the buildTarget to Setup --configure, and Cabal would complain about incompatible options.
On the other hand, if the package sets the same buildTarget (including a test-suite) but doesn't override doCheck, then the test-suite would never run.
(It is possible to specify the build target in a way that would include the tests, right?)
Yes, see for example here:
There was a problem hiding this comment.
mv "$packageConfFile/"* "$packageConfDir" || true
Hm, that would silence other errors that might occur during the mv.
How about instead of this for loop, we test whether the directory is non-empty:
if [ -d "$packageConfFile" ] && [ "$(ls -A "$packageConfFile")" ];Although, I am missing why the directory can be empty? Is this a separate bug fix?
The directory can now be empty when buildTarget doesn't include any libraries (or targets that depend on libraries), but the package does contain at least one library (so isLibrary=true). It's not a separate bug fix. Before, the register step would already have failed in this scenario.
One could argue there is another bug in this file, and that is the existence or usage of the isLibrary variable. It's not quite clear what isLibrary means or is supposed to do when a buildTarget is set. Perhaps we should try to remove the isLibrary variable, and make sure all code works whether we're configuring/building/installing a library or not. For example, after this change set, I think we can already remove the if clause from the start of the installPhase, but I didn't try it:
${if !isLibrary && buildTarget == "" then "${setupCommand} install"
# ^^ if the project is not a library, and no build target is specified, we can just use "install".
else if !isLibrary then "${setupCommand} copy ${buildTarget}"
# ^^ if the project is not a library, and we have a build target, then use "copy" to install
# just the target specified; "install" will error here, since not all targets have been built.
51c9843 to
cd12ed2
Compare
Show that not building the test-suite (that is mentioned in the .cabal file) is a failure when doCheck=true.
Adding a seemingly harmless and unused library component to the test for setBuildTarget makes the test fail in the installPhase (`./Setup register ..`) with: Error: Setup: '/nix/store/ah2iqzixws3fkhvlml0859hqzjjrvgsz-ghc-9.8.4/bin/ghc' exited with an error: <command line>: Could not find module ‘SomeModule’. Use -v to see a list of the files searched for.
When a package specifies a buildTarget, we now configure only that
buildTarget instead of all targets.
== Justification ==
Currently, the generic builder essentially runs:
./Setup configure
./Setup build ${buildTarget}
./Setup copy
./Setup register
Now consider the scenario of haskell package containing a library and an
executable (foo), but the executable does not depend on the library.
In case the nix package defines `buildTarget=foo`, we do configure the
library (since we always configure all targets), but we don't build it
(we only build the foo executable).
This causes a problem in the register step, because Setup will try to
register a library that hasn't been built.
To fix, we change the generic builder to run:
./Setup configure ${buildTarget}
./Setup build ${buildTarget}
./Setup copy
./Setup register
Should save some cpu cycles as well.
Relevant: NixOS#120249.
Fixes `pkgs.tests.haskell.setBuildTarget.pass-setBuildTarget`
cd12ed2 to
9abc2f3
Compare
|
(not actionable for this PR, just thinking out loud) When looking at this, I wonder whether we should teach |
This is something I would also be keen on. I created an issue for it here: NixOS/cabal2nix#603. Someone else also has some similar work here: NixOS/cabal2nix#624. A key issue with something like this is that it would cause an explosion in the size of the generated nix code, so it would have to be an optional thing that isn't enabled in nixpkgs (if I understand the maintainer's viewpoint correctly). |
Proposal
Run
./Setup configure ${buildTarget}instead of./Setup configureJustification
Currently, the generic builder essentially runs:
Now consider the scenario of a haskell package containing a library and an executable (
foo), but the executable does not depend on the library. See the changes inpkgs/test/haskell/setBuildTargetfor an example.In case the nix package defines
buildTarget=foo, the generic builder does configure the library (since it always configures all targets), but doesn't build it (it only builds thefooexecutable).This causes a problem in the register step, because
Setupwill try to register a library that hasn't been built, and GHC will fail with:To fix, we change the generic builder to run:
Should save some cpu cycles as well.
Relevant: #120249.
I'm afraid it will rebuild all haskell packages. I'm not sure what to do about that.Commits:
4. Prevent rebuilding too many packages (this commit should probably not be merged?)Things done
nix.conf? (See Nix manual)sandbox = relaxedsandbox = truenix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage./result/bin/)Add a 👍 reaction to pull requests you find important.