Nondeterministic build order makes iterating painful
- Dominant language
- Haskell
- Stars
- 1.7k
- Forks
- 750
- Avg merge
- 4d 3h
- Merged PRs (30d)
- 28
Description
**Describe the bug**
I seem to run into a sequence like the following frequently:
1. Edit module `A`
2. Call `cabal build`, `A` is rebuilt successfully, then module `B` is rebuilt because it imports `A`, and module `B` fails to build because it needs to be adapted to the change in `A`.
3. I edit module `B` to fix that error (or maybe I make a mistake)
4. Call `cabal build`, module `C` is rebuilt because it imports `A`, and only then is module `B` rebuilt
Sometimes module `C` will have its own errors, but even just the delay of waiting for `C` to build before I can see what my change to `B` did is very disruptive to my edit-compile cycle.
My expectation is that after the following sequence (with nothing else happening inbetween)
1. `cabal build` fails with an error in module 'B'
2. I edit module 'B'
3. I call `cabal build` again
that second call to `cabal build` will pick up exactly where it left off in step 1, that is, try compiling module 'B' again right away.
**To Reproduce**
A not-quite-minimal example project
```
diff --git a/app/A.hs b/app/A.hs
new file mode 100644
index 0000000..83a4c9c
--- /dev/null
+++ b/app/A.hs
@@ -0,0 +1,9 @@
+module A where
+
+import Lib
+
+g :: String
+g = f ++ ", ye"
+
+h :: String
+h = g ++ "!"
diff --git a/app/B.hs b/app/B.hs
new file mode 100644
index 0000000..6386bcc
--- /dev/null
+++ b/app/B.hs
@@ -0,0 +1,9 @@
+module B where
+
+import Lib
+
+g :: String
+g = f ++ ", ye"
+
+h :: String
+h = g ++ "!"
diff --git a/app/Lib.hs b/app/Lib.hs
new file mode 100644
index 0000000..90458f6
--- /dev/null
+++ b/app/Lib.hs
@@ -0,0 +1,4 @@
+module Lib where
+
+f :: String
+f = "hello"
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
index 0000000..48de6c7
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,7 @@
+module Main where
+
+import qualified A
+import qualified B
+
+main :: IO ()
+main = putStrLn $ A.h ++ B.h
diff --git a/x.cabal b/x.cabal
new file mode 100644
index 0000000..0868c55
--- /dev/null
+++ b/x.cabal
@@ -0,0 +1,18 @@
+cabal-version: 2.4
+name: x
+version: 0.1.0.0
+
+executable x
+ main-is: Main.hs
+
+ -- Modules included in this executable, other than Main.
+ other-modules: Lib, A, B
+ build-depends: base ^>=4.14.3.0
+ hs-source-dirs: app
+ default-language: Haskell2010
```
1. `cabal v2-build` (successful)
2. change type of `Lib.f`
3. `cabal v2-build` (fails compiling e.g. `A`)
4. fix one of the errors in `A`
5. `cabal v2-build` (fails compiling `B` instead of compiling `A`)
I suspect there's some randomness involved, but in my test right now 5 did the wrong thing quite reliably...
**Expected behavior**
If step 3 fails with an error in 'A', then any number of cycles of "edit 'A', call 'cabal build'" should keep immediately compiling module 'A' until 'A' is compiled successfully.
**System information**
- Operating system: macos
- `cabal` 3.6.2.0, `ghc` 8.10.7
**Additional context**
- I imagine this may be something to fix in GHC rather than cabal, feels like it's still an issue to track here?
- It feels like this should be a popular complaint, apologies if I missed a duplicate issue
Contributor guide
Research direction
Reproduce the sequence with the example app/A.hs, app/B.hs, app/Lib.hs, app/Main.hs, and x.cabal using cabal v2-build. Start by examining why a failed rebuild of A is followed by B or C on the next invocation, and determine whether the behavior belongs in Cabal or GHC. Done means repeated edits to the failed module cause that module to be compiled immediately until it succeeds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100