Build plans carry stale flags from previous package versions when package flags change
Closed this issue · 13 comments
Stack init is pulling text
into the extra-deps
of the build plan, even though the same version is available via the specified snapshot. (Note that lts-5.8 has text-1.2.2.0). This causes project-local recompilation rather than reusing my snapshot's compiled packages for anything that transitively depends on text
.
To reproduce
stack unpack strive-2.2.0 && cd strive-2.2.0
stack init --resolver lts-5.8 --solver
Expected
Extra-deps:
- gpolyline-0.1.0.1
Actual
extra-deps:
- gpolyline-0.1.0.1
- text-1.2.2.0
Details
stack --version
Version 1.0.4.2 x86_64
(mac)
cabal --version
cabal-install version 1.22.8.0
using version 1.22.7.0 of the Cabal library
That looks like a bug. Let me take look.
Looks like there is something wrong with the lts-5.8 build plan. For the package text it says:
constraints:
library-profiling: true
flags:
integer-simple: false
build-benchmarks: true
skip-build: false
tests: expect-success
haddocks: expect-success
version-range: ! '>=1.2.2.0 && <1.3'
version: '1.2.2.0'
Notice the integer-simple
flag being set as false. But in text-1.2.2.0
this flag was removed and integer-pure
flag was added. From text.cabal in text-1.2.2.0:
flag developer
description: operate in developer mode
default: False
manual: True
flag integer-pure
description: Use the pure-Haskell integer library instead of GMP
default: False
manual: True
Why is the build plan using the old flag but new package version? The build plan seems to be screwed up.
stack init
is adding the text package in extra-deps due to the mismatch in the flags in the build plan and the package flags that the cabal-solver returned. Since flags are not the same we add it in the extra-deps.
lts-3.19 build plan was correct with text-1.2.1.3. It changed to text-1.2.2.0 in lts-3.20 and then onwards all the build plans have wrong/stale integer-simple flag set for text.
I am not familiar with how build plans are generated. @mgsloan ?
Seems like something that needs to be fixed in stackage-curator: fpco/stackage-curator#11
Since I don't think we're going to retroactively update build plans, we may also want to handle this circumstance. AFAICT the non-existent flags aren't causing problems for stack, but in this case it is confusing the cabal-install solver.
There are two flag info items that we consider when we determine whether a snapshot package can be used:
- flags as per the build plans (all automatic flag settings and optionally overrides on manual ones)
- flags returned by cabal solver (automatic flags, manual only if changed from default e.g. via constraints)
We ask cabal solver to use the build plan flags as constraint. Usually if the constraints are honored it returns the same settings as the snapshot. But in case we pass a non-existing flag as a constraint cabal silently ignores it. If a flag setting is not reported in cabal solver output then it indicates two cases:
- The flag is a manual flag with default setting (snapshot may have it at non-default setting)
- The flag does not exist and was silently ignored
If we know that a flag is automatic and is not present in the cabal solver output then we will know that it is a non-existent flag (2nd case above) and raise a warning and ignore it. If it is a manual flag then we will still have the ambiguity between the two cases.
MiniPackageInfo
(or build plan) does not tell if a flag is manual so we cannot disambiguate the manual case. But if we assume build plans never or rarely set manual flags then we can ignore the ambiguous manual flag case. Is that a reasonable assumption? or is there any other way to handle this?
@harendra-kumar Interesting, thanks for the details! How about using withCabalLoader
and readPackageUnresolvedBS
to fetch the packages that are needed to resolve the ambiguity? This seems fine to me, since that stuff will need to get fetched for build anyway.
Loading the cabal files may not be a problem as cabal solver is anyway consulting the packages for solving, we can do the same, just a bit of effort doing it.
Not sure though if just to handle this build plan corruption case its worth writing and maintaining that code. In general we cannot write code on the assumption that build plan is unreliable. If there are no manual flag overrides in the build plans I can easily issue a warning and ignore it.
I quickly coded up a fix to issue a warning (assuming no manual flag overrides in build plan). The output will look like this:
Asking cabal to calculate a build plan...
Trying with packages from lts-5.8 as hard constraints...
WARNING! Ignoring the following spurious flags found in the build plan:
- text: integer-simple = False
Successfully determined a build plan with 1 external dependencies.
@mgsloan can you confirm if there are any cases where a manual flag is overridden in the build plan?
Even when a manual flag is present in the build plan the effect of this fix should be minimal:
- I am not sure if the cabal solver even suggests changes to manual flags. In case it doesn't this problem does not exist.
- In case it does we won't have a problem if it finds a solution using snapshot as hard constraint.
- The only problem will be when we relax all constraints and snapshot has a package with a non-default manual flag whereas cabal suggests to go with default. In that case instead of using an extra dep we will end up using the snapshot package.
I am not sure if the cabal solver even suggests changes to manual flags. In case it doesn't this problem does not exist.
Great point! Sounds like this resolves the theoretical ambiguity :)
I've merged the PR
How is integer-simple spurious in lts-5.11? As far as I understood from comments, a non-existent flag is spurious. text 1.2.2.1 revived integer-simple, and lts-5.11 specifies integer-simple in text 1.2.2.1.
Yet, stack solver says integer-simple is a spurious flag.
This behavior solves my issue, but it makes me question whether it'll introduce other bugs later.
The spurious reporting can be wrong when the build plan explicitly specifies the flag value even though the value specified is the same as default. The assumption of the reporting algo was that the build plan only specifies overrides and not the defaults. The functionality is correct reporting is wrong.
We report spurious when the flag is not present in the cabal solver output but is present in the snapshot. Cabal solver will not explicitly report a flag in its output when it wants the flag to remain at the default value or (of course) if the flag is does not exist. In both cases the decision is the same - to not put the package in extra deps.
We should disable the spurious warning to avoid confusion as it is not possible to unambiguously report spurious.
Removed the spurious warning via commit 40d7ae8 .