Consider two packages, A and B. Both have a library and B has a test suite. If the library of A depends on B and the test suite of B depends on A, then:
stack build should work; and
stack test should also work. This is because, although if Stack built libraries and executables 'all-in-one` (as it normally does) there would be a 'cycle' (A depends on B, B depends on A), Stack is supposed to detect that and defer the building of B's test suite until after the libraries are built.
There is an integration test that is supposed to check for that: multi-test. However, I have discovered that:
- if A is named
myPackageA and B is named myPackageB, stack test fails; but
- if A is named
myPackageD and B is named myPackageC it passes.
That is, whether stack test fails or passes depends on the alphabetical ordering of the names of A and B.
When it fails, it fails with:
Error: [S-4804]
Stack failed to construct a build plan.
While constructing the build plan, Stack encountered the following errors:
In the dependencies for myPackageB-0.0.0:
* myPackageA dependency cycle detected: myPackageA, myPackageB, myPackageA
The above is/are needed since myPackageB is a build target.
With A and B, stack --verbose --plan-in-log ... test includes (ignoring the base GHC boot package):
[debug] Constructing the build plan
[debug] (addDep) Package info for myPackageA: PIOnlySource PSFilePath ...
[debug] (installPackage) No test or bench component for myPackageA so doing an all-in-one
[debug] (addDep) Package info for myPackageB: PIOnlySource PSFilePath ...
[debug] (checkCallStackAndAddDep) Detected cycle myPackageA: ["myPackageB","myPackageA"]
[debug] (installPackage) Before trying cyclic plan, resetting lib result map to: fromList []
[debug] (checkCallStackAndAddDep) Detected cycle myPackageA: ["myPackageB","myPackageA"]
[debug] (getCachedDepOrAddDep) Using cached result for myPackageB: Right (ADRToInstall ... allInOne = False
With D and C, stack --verbose --plan-in-log ... test includes (again, ignoring 'base') (annotated with [A] and [B] for clarity):
[debug] Constructing the build plan
[debug] (addDep) Package info for [B] myPackageC: ...
[debug] (addDep) Package info for [A] myPackageD: ...
[debug] (installPackage) No test or bench component for [A] myPackageD so doing an all-in-one build.
[debug] (checkCallStackAndAddDep) Detected cycle [B] myPackageC: [ [A] "myPackageD", [B] "myPackageC"]
[debug] (installPackage) Before trying cyclic plan, resetting lib result map to: fromList []
[debug] (addDep) Package info for [A] myPackageD: ...
[debug] (installPackage) No test or bench component for [A] myPackageD so doing an all-in-one build.
[debug] (getCachedDepOrAddDep) Using cached result for [B] myPackageC: ...
[debug] (getCachedDepOrAddDep) Using cached result for [A] myPackageD: ...
This does not appear to be a recent regression. I think it may have been present since at least Stack 2.1.1. EDIT: It may have been present since Stack 0.0.2:
EDIT: With additional debug information:
With A and B (ignoring 'base'):
[debug] Constructing the build plan
[debug] (constructPlan) Constructing for target myPackageA
[debug] (checkCallStackAndAddDep) Pushing myPackageA on to the call stack.
[debug] (addDep) Package info for myPackageA: PIOnlySource PSFilePath ...
[debug] (installPackage) No test or bench component for myPackageA so doing an all-in-one build.
[debug] (checkCallStackAndAddDep) Pushing myPackageB on to the call stack.
[debug] (addDep) Package info for myPackageB: PIOnlySource PSFilePath ...
[debug] (checkCallStackAndAddDep) Detected cycle myPackageA: ["myPackageB","myPackageA"]
[debug] (updateLibMap) Updating for: myPackageA (error)
[debug] (installPackage) Before trying cyclic plan, resetting lib result map to: fromList []
[debug] (updateLibMap) Updating for: myPackageB (ok)
[debug] (checkCallStackAndAddDep) Detected cycle myPackageA: ["myPackageB","myPackageA"]
[debug] (updateLibMap) Updating for: myPackageA (error)
[debug] (addFinal) Adding to construction output myPackageB (error)
[debug] (checkCallStackAndAddDep) Popped myPackageB from the call stack.
[debug] (updateLibMap) Updating for: myPackageB (ok)
[debug] (checkCallStackAndAddDep) Popped myPackageA from the call stack.
[debug] (updateLibMap) Updating for: myPackageA (ok)
[debug] (constructPlan) Constructing for target myPackageB
[debug] (getCachedDepOrAddDep) Using cached result for myPackageB: ...
With D and C (ignoring 'base'):
[debug] Constructing the build plan
[debug] (constructPlan) Constructing for target myPackageC
[debug] (checkCallStackAndAddDep) Pushing myPackageC on to the call stack.
[debug] (addDep) Package info for myPackageC: PIOnlySource PSFilePath ...
[debug] (checkCallStackAndAddDep) Pushing myPackageD on to the call stack.
[debug] (addDep) Package info for myPackageD: PIOnlySource PSFilePath ...
[debug] (installPackage) No test or bench component for myPackageD so doing an all-in-one build.
[debug] (checkCallStackAndAddDep) Detected cycle myPackageC: ["myPackageD","myPackageC"]
[debug] (updateLibMap) Updating for: myPackageC (error)
[debug] (checkCallStackAndAddDep) Popped myPackageD from the call stack.
[debug] (updateLibMap) Updating for: myPackageD (error)
[debug] (installPackage) Before trying cyclic plan, resetting lib result map to: fromList []
[debug] (updateLibMap) Updating for: myPackageC (ok)
[debug] (checkCallStackAndAddDep) Pushing myPackageD on to the call stack.
[debug] (addDep) Package info for myPackageD: PIOnlySource PSFilePath ...
[debug] (installPackage) No test or bench component for myPackageD so doing an all-in-one build.
[debug] (getCachedDepOrAddDep) Using cached result for myPackageC: ...
[debug] (checkCallStackAndAddDep) Popped myPackageD from the call stack.
[debug] (updateLibMap) Updating for: myPackageD (ok)
[debug] (addFinal) Adding to construction output myPackageC (ok)
[debug] (checkCallStackAndAddDep) Popped myPackageC from the call stack.
[debug] (updateLibMap) Updating for: myPackageC (ok)
[debug] (constructPlan) Constructing for target myPackageD
[debug] (getCachedDepOrAddDep) Using cached result for myPackageD: ...
Consider two packages, A and B. Both have a library and B has a test suite. If the library of A depends on B and the test suite of B depends on A, then:
stack buildshould work; andstack testshould also work. This is because, although if Stack built libraries and executables 'all-in-one` (as it normally does) there would be a 'cycle' (A depends on B, B depends on A), Stack is supposed to detect that and defer the building of B's test suite until after the libraries are built.There is an integration test that is supposed to check for that:
multi-test. However, I have discovered that:myPackageAand B is namedmyPackageB,stack testfails; butmyPackageDand B is namedmyPackageCit passes.That is, whether
stack testfails or passes depends on the alphabetical ordering of the names of A and B.When it fails, it fails with:
With A and B,
stack --verbose --plan-in-log ... testincludes (ignoring thebaseGHC boot package):With D and C,
stack --verbose --plan-in-log ... testincludes (again, ignoring 'base') (annotated with[A]and[B]for clarity):This does not appear to be a recent regression. I think it may have been present since at least Stack 2.1.1. EDIT: It may have been present since Stack 0.0.2:
EDIT: With additional debug information:
With A and B (ignoring 'base'):
With D and C (ignoring 'base'):