Skip to content
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

stack build; stack test rebuilds everything twice #4977

Closed
minad opened this issue Jul 24, 2019 · 28 comments
Closed

stack build; stack test rebuilds everything twice #4977

minad opened this issue Jul 24, 2019 · 28 comments
Assignees

Comments

@minad
Copy link

minad commented Jul 24, 2019

Steps to reproduce

  1. stack build
  2. stack test

I am using stackage nightly. The library of my project is rebuild by stack test.

Expected

The library should be compiled once and the compiled and cached output should be used.

Actual

stack test recompiles the library.

Stack version

2.1.1

Method of installation

  • Official binary, downloaded from stackage.org or fpcomplete's package repository
@minad
Copy link
Author

minad commented Jul 24, 2019

Maybe it has something to do with using a custom Setup.hs? I try to create a repro if possible. If I recall correctly, the issue started to occur after I upgraded to stack version 2.

stack says configure (lib + exe)... when calling stack build the first time. Then stack test says configure (lib + exe + test)...

@minad minad changed the title stack test; stack build rebuilds everything stack build; stack test rebuilds everything Jul 24, 2019
@minad minad changed the title stack build; stack test rebuilds everything stack build; stack test rebuilds everything twice Jul 24, 2019
@gromakovsky
Copy link

gromakovsky commented Jul 26, 2019

I sometimes observed this behavior, but it was not deterministic and I never tried to investigate it. I think I didn't observe it during last few months though.

P. S. It's with default Setup.hs.

@sjakobi
Copy link
Member

sjakobi commented Aug 8, 2019

I can observe this reliably. Switching between stack test and stack bench rebuilds the library too. This issue is a big time waster in my experience.

@mihaimaruseac
Copy link
Contributor

If the compile flags need to change between building test and just building lib + exe then Stack would have to recompile everything, just like any other build system (just closed a similar issue with Bazel on another repo)

If you can provide a minimal example, we can investigate to see if Stack is too eager in invalidating the build cache

@sjakobi
Copy link
Member

sjakobi commented Aug 8, 2019

Here's an example using https://github.com/nikita-volkov/vector-builder:

$ cd vector-builder
$ stack clean --full
$ stack build
vector-builder> configure (lib)
Configuring vector-builder-0.3.7.2...
vector-builder> build (lib)
Preprocessing library for vector-builder-0.3.7.2..
Building library for vector-builder-0.3.7.2..
[1 of 8] Compiling VectorBuilder.Prelude
[2 of 8] Compiling VectorBuilder.Core.Update
[3 of 8] Compiling VectorBuilder.Core.Builder
[4 of 8] Compiling VectorBuilder.MVector
[5 of 8] Compiling VectorBuilder.Builder
[6 of 8] Compiling VectorBuilder.Vector
[7 of 8] Compiling VectorBuilder.MonadPlus
[8 of 8] Compiling VectorBuilder.Alternative
vector-builder> copy/register
Installing library in /home/simon/src/vector-builder/.stack-work/install/x86_64-linux/da6ab40722cf26def321f3afbad12b99af2f327f84d5d7d92f8138157548da6d/8.6.5/lib/x86_64-linux-ghc-8.6.5/vector-builder-0.3.7.2-JJTkYJEXEoEG9xl9pXropy
Registering library for vector-builder-0.3.7.2..
$ stack test --no-run-tests
vector-builder-0.3.7.2: unregistering (dependencies changed)
vector-builder> configure (lib + test)
Configuring vector-builder-0.3.7.2...
vector-builder> build (lib + test)
Preprocessing library for vector-builder-0.3.7.2..
Building library for vector-builder-0.3.7.2..
[1 of 8] Compiling VectorBuilder.Prelude
[2 of 8] Compiling VectorBuilder.Core.Update
[3 of 8] Compiling VectorBuilder.Core.Builder
[4 of 8] Compiling VectorBuilder.MVector
[5 of 8] Compiling VectorBuilder.Builder
[6 of 8] Compiling VectorBuilder.Vector
[7 of 8] Compiling VectorBuilder.MonadPlus
[8 of 8] Compiling VectorBuilder.Alternative
Preprocessing test suite 'tests' for vector-builder-0.3.7.2..
Building test suite 'tests' for vector-builder-0.3.7.2..
[1 of 2] Compiling Main.Sample
[2 of 2] Compiling Main
Linking .stack-work/dist/x86_64-linux/Cabal-2.4.0.1/build/tests/tests ...
vector-builder> copy/register
Installing library in /home/simon/src/vector-builder/.stack-work/install/x86_64-linux/da6ab40722cf26def321f3afbad12b99af2f327f84d5d7d92f8138157548da6d/8.6.5/lib/x86_64-linux-ghc-8.6.5/vector-builder-0.3.7.2-Kobe1ABTFVWHtSalbGSjYI
Registering library for vector-builder-0.3.7.2..
vector-builder> Test running disabled by --no-run-tests flag.
Completed 2 action(s).

Now I modify the last module in the build order, and stack rebuilds from zero!

$ echo '-- bla' >> library/VectorBuilder/Alternative.hs 
$ stack build
vector-builder-0.3.7.2: unregistering (local file changes: library/VectorBuilder/Alternative.hs)
vector-builder> configure (lib)
Configuring vector-builder-0.3.7.2...
vector-builder> build (lib)
Preprocessing library for vector-builder-0.3.7.2..
Building library for vector-builder-0.3.7.2..
[1 of 8] Compiling VectorBuilder.Prelude
[2 of 8] Compiling VectorBuilder.Core.Update
[3 of 8] Compiling VectorBuilder.Core.Builder
[4 of 8] Compiling VectorBuilder.MVector
[5 of 8] Compiling VectorBuilder.Builder
[6 of 8] Compiling VectorBuilder.Vector
[7 of 8] Compiling VectorBuilder.MonadPlus
[8 of 8] Compiling VectorBuilder.Alternative
vector-builder> copy/register
Installing library in /home/simon/src/vector-builder/.stack-work/install/x86_64-linux/da6ab40722cf26def321f3afbad12b99af2f327f84d5d7d92f8138157548da6d/8.6.5/lib/x86_64-linux-ghc-8.6.5/vector-builder-0.3.7.2-JJTkYJEXEoEG9xl9pXropy
Registering library for vector-builder-0.3.7.2..

@mihaimaruseac mihaimaruseac self-assigned this Aug 8, 2019
@mihaimaruseac
Copy link
Contributor

Thanks. Will investigate when I get a few cycles for open source

@minad
Copy link
Author

minad commented Aug 9, 2019

One thing to add - for me it builds things only once if I do change something in the code; stack test; stack build but twice if I do change something in the code; stack build; stack test.

As a workaround I am always doing stack test now (which is slower since it also relinks the testsuite etc) and I interrupt when the test suite is starting :/

@sjakobi
Copy link
Member

sjakobi commented Aug 9, 2019

and I interrupt when the test suite is starting

You could use --no-run-tests for that.

@minad
Copy link
Author

minad commented Aug 9, 2019

@sjakobi Ok that sounds good 🙈 I have to admit I didn't really dig deep into all the things stack has to offer. I am using it for its great out of the box experience (Thank you stack developers for that!). And besides this problem I have not many other issues with it.

@mihaimaruseac
Copy link
Contributor

Can also try stack build --test --bench --no-run-tests --no-run-benchmarks if you want to build everything but not run tests/benchmarks.

That's what I used to do at previous place where we were using Haskell daily

@rjmk
Copy link
Contributor

rjmk commented Mar 17, 2020

Building with stack test appears to add the test dependencies to the dependencies of the library, thus invalidating the previous build

@jproyo
Copy link

jproyo commented Mar 19, 2020

Just in case if it helps i am facing the same issue with 2.1.3 but after doing this, some re linkings are gone:

stack build && stack test components --test-arguments "-m MyComponent" --file-watch

In the case i run with

stack test components --test-arguments "-m MyComponent" --file-watch

It tries to relink everything on each change. With stack build on front it seems it is avoid some recompilation, even when i change only one space in the test.

@kindaro
Copy link

kindaro commented May 15, 2020

Is this a duplicate of #2800? Looks similar from the outset, but the explanation given appears to be different (wrong dependencies added to library here and upstream issue there) — although it can be that both are true.

I am also seeing similar behaviour, and I am confused as to where to put a +1.

@wraithm
Copy link
Contributor

wraithm commented May 18, 2020

@kindaro I would assume it's the same.

@natefaubion
Copy link

The PureScript compiler suffers from the same symptoms as reported here. I currently work around it by downgrading to Stack 1.9, which doesn't appear to have the same problems.

gasi added a commit to zoomhub/zoomhub that referenced this issue Nov 29, 2020
We do this by matching all build configurations and always include
building tests. Performing a regular build without tests and then running
the tests invalidates the previous build as test dependencies are added.

See:
- commercialhaskell/stack#4977 (comment)
- commercialhaskell/stack#2800 (comment)
gasi added a commit to zoomhub/zoomhub that referenced this issue Dec 18, 2020
We do this by matching all build configurations and always include
building tests. Performing a regular build without tests and then running
the tests invalidates the previous build as test dependencies are added.

See:
- commercialhaskell/stack#4977 (comment)
- commercialhaskell/stack#2800 (comment)
@anka-213
Copy link
Contributor

anka-213 commented Sep 6, 2021

I have this problem and it is completely consistent: every time I switch between stack build and stack test it recompiles everything.
I do have a custom Setup.hs which adds an extra pre-processor, but those files aren't regenerated when switching, just recompiled, so that shouldn't be the issue.

@qrilka
Copy link
Contributor

qrilka commented Sep 6, 2021

switching between build and test invocations normally changes "source map" of dependencies/flags used. Component-based builds should improve the situation but that effort stalled. One possible workaround is to use different STACK_WORK values for build and test.

@jproyo
Copy link

jproyo commented Sep 6, 2021

I have this problem and it is completely consistent: every time I switch between stack build and stack test it recompiles everything.
I do have a custom Setup.hs which adds an extra pre-processor, but those files aren't regenerated when switching, just recompiled, so that shouldn't be the issue.

What is working for me pretty much lately is to use a different build command:

stack build --test --no-run-tests

With this approach, you build all sources, including test, and after that, if you run stack test it won't recompile.

The only problem with this is that if you are using some integrated environment with your IDE (haskell-server for example) this will launch a stack build behind the scenes and the previous will recompile if you IDE compiles you run stack test

@hasufell
Copy link
Contributor

hasufell commented Sep 6, 2021

Try this in your stack.yaml:

build:
  test: true
  test-arguments:
    no-run-tests: true
  bench: true
  benchmark-opts:
    no-run-benchmarks: true

See https://docs.haskellstack.org/en/latest/yaml_configuration/#build

@anka-213
Copy link
Contributor

anka-213 commented Sep 7, 2021

@hasufell That sounds like a great solution! How do I run the tests then? Does stack test still work?

gasi added a commit to zoomhub/zoomhub that referenced this issue Sep 16, 2021
@j-hui
Copy link

j-hui commented Oct 18, 2021

@hasufell Unfortunately that seems to break stack test, which won't run because it thinks --no-run-tests was passed. Stack doesn't seem to distinguish between stack build --test and stack test at all, which means you just can't configure one without affecting the other.

@OsePedro
Copy link

OsePedro commented Oct 19, 2021

@hasufell Instead of setting the arguments in stack.yaml, you can just write a Bash function (or the equivalent on your platform) that calls stack build with all the necessary arguments. E.g. you can add something like this to .bashrc:

stackBuild() { stack build --test --no-run-tests "$@"; }

then run stackBuild at the command-line instead of stack build. Note that the "$@" forwards stackBuild arguments to stack build.

@Javran
Copy link

Javran commented Mar 29, 2023

Now that I use --no-run-tests to avoid rebuilds, I wish there's a way to suppress "Test running disabled" messages.

My current workaround is a mess, but at least it works, for easy copy-and-paste:

stack build --color=always --test --no-run-tests 2>&1 | grep --color=always -vP 'Test running disabled by --no-run-tests flag'

@mpilgrem
Copy link
Member

I am new to this discussion. To summarise my own understanding:

  • a 'test' is a package executable that is run automatically once it is built. You can stop that with flag --no-run-tests. Stack announces that it is not running the 'test' executable, as it normally would.
  • stack build with no target specified does not build 'tests', unless flag --test is passed. stack test is a synonym for stack build --test.
  • if you want to specify that a non-'test' executable should be run after it is built, you can make use of the --exec option (eg stack build --exec my-executable).
  • if you build one executable with its dependencies and then ask to build a different executable with the same dependencies, Stack does not need to build some things again. So, it may be sensible to specify common dependencies for 'tests' and non-'tests', even if they are not actually used.
  • if you build one executable with its dependencies and then ask to build a different executable with different depedencies, Stack (necessarily) rebuilds things.

@mpilgrem
Copy link
Member

@Javran, Stack's announcement that Test running disabled by --no-run-tests flag. is at the level of 'information'. So, if you command stack --verbosity warn test or, equivalently, stack --verbosity warn build --test, Stack will not provide that information. However, it will also not provide any other Stack message at the level of 'information'.

@mpilgrem
Copy link
Member

mpilgrem commented Apr 5, 2024

I am going to close this issue given the passage of time and because I can't reproduce the original issue with Stack 2.15.5, namely:

> stack new foo
> cd foo
> stack build
... builds lib + exe
> stack build --tests --no-run-tests
... builds test suite only

If it is still an issue with current versions, please reopen a fresh issue.

@wraithm
Copy link
Contributor

wraithm commented Apr 11, 2024

omg @mpilgrem it's fixed. Holy cow. This is incredible. I can confirm that this is working well.

@carlwr
Copy link

carlwr commented Jul 31, 2024

Sharing my findings in case it is helpful to others. (Part of this is also conveyed in previous comments.)

Once stack has compiled all components (lib + exes + tests + benchmarks) in one go (= a single invocation of stack), components can be re-built independently without causing unnecessary re-compilation.

So if we do...

stack clean
stack build --bench --no-run-benchmarks --test --no-run-tests
  # compiles everything

...then thereafter, as long as stack doesn't have an external reason to re-compile anything (such as a source file edit), the below command sequence will not cause any re-compilation (no sequence of the below commands will):

stack build --bench --no-run-benchmarks
stack build --test --no-run-tests
stack build
stack build --bench --no-run-benchmarks --test --no-run-tests
stack build --bench --no-run-benchmarks --test --no-run-tests :myComponent
# ...

Above I have added in --no-run-benchmarks and --no-run-tests so that test/bench components are compiled but not run - but actually runing these doesn't change anything.

If at any point stack re-compiles anything, e.g. because a source file has changed, then this "good state" is broken however - unless that re-compilation was a "compile all in one invocation"-compilation.

The "good state" can be brought back with:

# either: clean, then compile all in one go:
stack clean
stack build --bench --no-run-benchmarks --test --no-run-tests

# ...or: compile all in one go _twice_:
stack build --bench --no-run-benchmarks --test --no-run-tests
stack build --bench --no-run-benchmarks --test --no-run-tests


It seems that one way to always stay in the good state is to make sure that each invocation of stack that has the potential to re-compile "anything" is always a request to stack to "compile everything".

E.g. depending on what we actually want stack to do we can use invocations such as these:

# run all benchmarks:
stack build --bench                     --test --no-run-tests
    
# run all tests:
stack build --bench --no-run-benchmarks --test
    
# run only test suite component :myTest:
stack build --bench --no-run-benchmarks --test :myTest

Here's an example of a sequence of commands/actions + what stack will do:

# take us into the good state:
stack clean
stack build --bench --no-run-benchmarks --test --no-run-tests

# now we want to run all tests:
stack build --bench --no-run-benchmarks --test
  # (re-compiles nothing)
  # runs all tests

# HERE: edit a *test* source file

# now we want to run all benchmarks:
stack build --bench --test --no-run-tests
  # re-compiles the test component of the edited source file
  # (re-compiles nothing else)
  # some re-linking
  # runs all benchmarks

# now we want to run all tests:
stack build --bench --no-run-benchmarks --test
  # (compiles nothing)
  # runs all tests


Also possibly helpful in order to avoid unnecessary re-compilation is to make sure that all components have non-overlapping source-dirs: in package.yaml (for HPACK projects).


versions
$ stack --version && stack exec -- ghc --version
# Version 2.15.5, Git revision 5649cc6b2522f51f0fc5543154b0fff868f9af31 aarch64 hpack-0.36.0
# The Glorious Glasgow Haskell Compilation System, version 9.6.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests