Skip to content

Commit 9a1b938

Browse files
committed
Don't infer Paths_-module with spec-version: 0.36.0 or later
1 parent 56cdf88 commit 9a1b938

File tree

8 files changed

+97
-34
lines changed

8 files changed

+97
-34
lines changed

.ghci

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
:set -XHaskell2010 -fno-warn-incomplete-uni-patterns -DTEST -isrc -itest -i./dist-newstyle/build/x86_64-linux/ghc-9.6.2/hpack-0.35.3/build/autogen/
1+
:set -XHaskell2010 -Wredundant-constraints -fno-warn-incomplete-uni-patterns -DTEST -isrc -itest -i./dist-newstyle/build/x86_64-linux/ghc-9.6.2/hpack-0.36.0/build/autogen

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## Changes in 0.36.0
2+
- Don't infer `Paths_`-module with `spec-version: 0.36.0` or later
3+
14
## Changes in 0.35.5
25
- Add (undocumented) `list` command
36

README.md

+31-6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ at the Singapore Haskell meetup: http://typeful.net/talks/hpack
4747
* [Examples](#examples)
4848
* [Documentation](#documentation)
4949
* [Handling of Paths_ modules](#handling-of-paths_-modules)
50+
* [Modern behavior](#modern-behavior)
51+
* [Legacy behavior](#legacy-behavior)
5052
* [Quick-reference](#quick-reference)
5153
* [Top-level fields](#top-level-fields)
5254
* [cabal-version](#cabal-version)
@@ -70,18 +72,41 @@ at the Singapore Haskell meetup: http://typeful.net/talks/hpack
7072
* [Stack support](#stack-support)
7173
* [Binaries for use on Travis CI](#binaries-for-use-on-travis-ci)
7274

73-
<!-- Added by: sol, at: Fri 19 Feb 2021 10:31:47 PM +07 -->
75+
<!-- Added by: sol, at: Mon Sep 18 11:40:17 AM +07 2023 -->
7476

7577
<!--te-->
7678

7779
### Handling of `Paths_` modules
7880

79-
Cabal generates a `Paths_` module for every package. By default Hpack adds
80-
that module to `other-modules` when generating a `.cabal` file. This is
81-
sometimes useful and most of the time not harmful.
81+
Cabal generates a `Paths_` module for every package. How exactly Hpack behaves
82+
in regards to that module depends on the value of the `spec-version` field.
8283

83-
However, there are situations when this can lead to compilation errors (e.g
84-
when using a custom `Prelude`).
84+
If the `spec-version` is explicitly specified and at least `0.36.0` the modern
85+
behavior is used, otherwise Hpack falls back to the legacy behavior.
86+
87+
To use the modern behavior, require at least
88+
```yaml
89+
spec-version: 0.36.0
90+
```
91+
in your `package.yaml`.
92+
93+
#### Modern behavior
94+
95+
If you want to use the `Paths_` module for a component, you have to explicitly
96+
specify it under `generated-other-modules`.
97+
98+
***Example:***
99+
100+
```yaml
101+
library:
102+
source-dirs: src
103+
generated-other-modules: Paths_name # substitute name with the package name
104+
```
105+
106+
#### Legacy behavior
107+
108+
For historic reasons Hpack adds the `Paths_` module to `other-modules` when
109+
generating a `.cabal` file.
85110

86111
To prevent Hpack from adding the `Paths_` module to `other-modules` add the
87112
following to `package.yaml`:

cabal.project

+2
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ packages:
33

44
package hpack
55
ghc-options: -Werror
6+
7+
tests: True

hpack.cabal

+7-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.yaml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
spec-version: 0.36.0
12
name: hpack
2-
version: 0.35.5
3+
version: 0.36.0
34
synopsis: A modern format for Haskell packages
45
description: See README at <https://github.com/sol/hpack#readme>
56
author: Simon Hengel <[email protected]>
@@ -50,6 +51,7 @@ library:
5051
- Hpack.Render
5152
- Hpack.Yaml
5253
- Hpack.Error
54+
generated-other-modules: Paths_hpack
5355

5456
executable:
5557
main: Main.hs
@@ -64,6 +66,7 @@ tests:
6466
source-dirs:
6567
- test
6668
- src
69+
generated-other-modules: Paths_hpack
6770
dependencies:
6871
- hspec == 2.*
6972
- QuickCheck

src/Hpack/Config.hs

+40-21
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ import Data.Text.Encoding (decodeUtf8)
9999
import Data.Scientific (Scientific)
100100
import System.Directory
101101
import System.FilePath
102+
import Control.Monad.State (MonadState, StateT, evalStateT)
103+
import qualified Control.Monad.State as State
102104
import Control.Monad.Writer (MonadWriter, WriterT, runWriterT, tell)
103105
import Control.Monad.Except
104106
import Data.Version (Version, makeVersion, showVersion)
@@ -639,7 +641,7 @@ liftIOEither action = liftIO action >>= liftEither
639641

640642
type FormatYamlParseError = FilePath -> Yaml.ParseException -> String
641643

642-
decodeYaml :: (FromValue a, MonadIO m, Warnings m, Errors m) => FormatYamlParseError -> FilePath -> m a
644+
decodeYaml :: (FromValue a, MonadIO m, Warnings m, Errors m, State m) => FormatYamlParseError -> FilePath -> m a
643645
decodeYaml formatYamlParseError file = do
644646
(warnings, a) <- liftIOEither $ first (ParseError . formatYamlParseError file) <$> Yaml.decodeYamlWithParseError file
645647
tell warnings
@@ -668,11 +670,12 @@ readPackageConfig options = first (formatHpackError $ decodeOptionsProgramName o
668670

669671
type Errors = MonadError HpackError
670672
type Warnings = MonadWriter [String]
673+
type State = MonadState SpecVersion
671674

672-
type ConfigM m = WriterT [String] (ExceptT HpackError m)
675+
type ConfigM m = StateT SpecVersion (WriterT [String] (ExceptT HpackError m))
673676

674-
runConfigM :: ConfigM m a -> m (Either HpackError (a, [String]))
675-
runConfigM = runExceptT . runWriterT
677+
runConfigM :: Monad m => ConfigM m a -> m (Either HpackError (a, [String]))
678+
runConfigM = runExceptT . runWriterT . (`evalStateT` NoSpecVersion)
676679

677680
readPackageConfigWithError :: DecodeOptions -> IO (Either HpackError DecodeResult)
678681
readPackageConfigWithError (DecodeOptions _ file mUserDataDir readValue formatYamlParseError) = fmap (fmap addCabalFile) . runConfigM $ do
@@ -902,15 +905,16 @@ determineCabalVersion inferredLicense pkg@Package{..} = (
902905
sectionAll :: Monoid b => (Section a -> b) -> Section a -> b
903906
sectionAll f sect = f sect <> foldMap (foldMap $ sectionAll f) (sectionConditionals sect)
904907

905-
decodeValue :: (FromValue a, Warnings m, Errors m) => FilePath -> Value -> m a
908+
decodeValue :: (FromValue a, State m, Warnings m, Errors m) => FilePath -> Value -> m a
906909
decodeValue file value = do
907910
(r, unknown, deprecated) <- liftEither $ first (DecodeValueError file) (Config.decodeValue value)
908911
case r of
909912
UnsupportedSpecVersion v -> do
910913
throwError $ HpackVersionNotSupported file v Hpack.version
911-
SupportedSpecVersion a -> do
914+
SupportedSpecVersion v a -> do
912915
tell (map formatUnknownField unknown)
913916
tell (map formatDeprecatedField deprecated)
917+
State.modify $ max v
914918
return a
915919
where
916920
prefix :: String
@@ -922,14 +926,20 @@ decodeValue file value = do
922926
formatDeprecatedField :: (String, String) -> String
923927
formatDeprecatedField (name, substitute) = prefix <> name <> " is deprecated, use " <> substitute <> " instead"
924928

925-
data CheckSpecVersion a = SupportedSpecVersion a | UnsupportedSpecVersion Version
929+
data SpecVersion = NoSpecVersion | SpecVersion Version
930+
deriving (Eq, Show, Ord)
931+
932+
toSpecVersion :: Maybe ParseSpecVersion -> SpecVersion
933+
toSpecVersion = maybe NoSpecVersion (SpecVersion . unParseSpecVersion)
934+
935+
data CheckSpecVersion a = SupportedSpecVersion SpecVersion a | UnsupportedSpecVersion Version
926936

927937
instance FromValue a => FromValue (CheckSpecVersion a) where
928938
fromValue = withObject $ \ o -> o .:? "spec-version" >>= \ case
929939
Just (ParseSpecVersion v) | Hpack.version < v -> return $ UnsupportedSpecVersion v
930-
_ -> SupportedSpecVersion <$> fromValue (Object o)
940+
v -> SupportedSpecVersion (toSpecVersion v) <$> fromValue (Object o)
931941

932-
newtype ParseSpecVersion = ParseSpecVersion Version
942+
newtype ParseSpecVersion = ParseSpecVersion {unParseSpecVersion :: Version}
933943

934944
instance FromValue ParseSpecVersion where
935945
fromValue value = do
@@ -1079,7 +1089,7 @@ toPackage formatYamlParseError userDataDir dir =
10791089
setLanguage = (mempty { commonOptionsLanguage = Alias . Last $ Just (Just language) } <>)
10801090

10811091
expandDefaultsInConfig
1082-
:: (MonadIO m, Warnings m, Errors m) =>
1092+
:: (MonadIO m, Warnings m, Errors m, State m) =>
10831093
FormatYamlParseError
10841094
-> FilePath
10851095
-> FilePath
@@ -1088,7 +1098,7 @@ expandDefaultsInConfig
10881098
expandDefaultsInConfig formatYamlParseError userDataDir dir = bitraverse (expandGlobalDefaults formatYamlParseError userDataDir dir) (expandSectionDefaults formatYamlParseError userDataDir dir)
10891099

10901100
expandGlobalDefaults
1091-
:: (MonadIO m, Warnings m, Errors m) =>
1101+
:: (MonadIO m, Warnings m, Errors m, State m) =>
10921102
FormatYamlParseError
10931103
-> FilePath
10941104
-> FilePath
@@ -1098,7 +1108,7 @@ expandGlobalDefaults formatYamlParseError userDataDir dir = do
10981108
fmap (`Product` Empty) >>> expandDefaults formatYamlParseError userDataDir dir >=> \ (Product c Empty) -> return c
10991109

11001110
expandSectionDefaults
1101-
:: (MonadIO m, Warnings m, Errors m) =>
1111+
:: (MonadIO m, Warnings m, Errors m, State m) =>
11021112
FormatYamlParseError
11031113
-> FilePath
11041114
-> FilePath
@@ -1121,7 +1131,7 @@ expandSectionDefaults formatYamlParseError userDataDir dir p@PackageConfig{..} =
11211131
}
11221132

11231133
expandDefaults
1124-
:: forall a m. (MonadIO m, Warnings m, Errors m) =>
1134+
:: forall a m. (MonadIO m, Warnings m, Errors m, State m) =>
11251135
(FromValue a, Monoid a)
11261136
=> FormatYamlParseError
11271137
-> FilePath
@@ -1169,7 +1179,7 @@ toExecutableMap name executables mExecutable = do
11691179

11701180
type GlobalOptions = CommonOptions CSources CxxSources JsSources Empty
11711181

1172-
toPackage_ :: (MonadIO m, Warnings m) => FilePath -> Product GlobalOptions (PackageConfig CSources CxxSources JsSources) -> m (Package, String)
1182+
toPackage_ :: (MonadIO m, Warnings m, State m) => FilePath -> Product GlobalOptions (PackageConfig CSources CxxSources JsSources) -> m (Package, String)
11731183
toPackage_ dir (Product g PackageConfig{..}) = do
11741184
executableMap <- toExecutableMap packageName_ packageConfigExecutables packageConfigExecutable
11751185
let
@@ -1377,7 +1387,7 @@ removeConditionalsThatAreAlwaysFalse sect = sect {
13771387
where
13781388
p = (/= CondBool False) . conditionalCondition
13791389

1380-
inferModules :: MonadIO m =>
1390+
inferModules :: (MonadIO m, State m) =>
13811391
FilePath
13821392
-> String
13831393
-> (a -> [Module])
@@ -1386,10 +1396,19 @@ inferModules :: MonadIO m =>
13861396
-> ([Module] -> a -> b)
13871397
-> Section a
13881398
-> m (Section b)
1389-
inferModules dir packageName_ getMentionedModules getInferredModules fromData fromConditionals = fmap removeConditionalsThatAreAlwaysFalse . traverseSectionAndConditionals
1390-
(fromConfigSection fromData [pathsModuleFromPackageName packageName_])
1391-
(fromConfigSection (\ [] -> fromConditionals) [])
1392-
[]
1399+
inferModules dir packageName_ getMentionedModules getInferredModules fromData fromConditionals sect_ = do
1400+
specVersion <- State.get
1401+
let
1402+
pathsModule :: [Module]
1403+
pathsModule = case specVersion of
1404+
SpecVersion v | v >= makeVersion [0,36,0] -> []
1405+
_ -> [pathsModuleFromPackageName packageName_]
1406+
1407+
removeConditionalsThatAreAlwaysFalse <$> traverseSectionAndConditionals
1408+
(fromConfigSection fromData pathsModule)
1409+
(fromConfigSection (\ [] -> fromConditionals) [])
1410+
[]
1411+
sect_
13931412
where
13941413
fromConfigSection fromConfig pathsModule_ outerModules sect@Section{sectionData = conf} = do
13951414
modules <- liftIO $ listModules dir sect
@@ -1400,7 +1419,7 @@ inferModules dir packageName_ getMentionedModules getInferredModules fromData fr
14001419
r = fromConfig pathsModule inferableModules conf
14011420
return (outerModules ++ getInferredModules r, r)
14021421

1403-
toLibrary :: MonadIO m => FilePath -> String -> Section LibrarySection -> m (Section Library)
1422+
toLibrary :: (MonadIO m, State m) => FilePath -> String -> Section LibrarySection -> m (Section Library)
14041423
toLibrary dir name =
14051424
inferModules dir name getMentionedLibraryModules getLibraryModules fromLibrarySectionTopLevel fromLibrarySectionInConditional
14061425
where
@@ -1444,7 +1463,7 @@ getMentionedExecutableModules :: ExecutableSection -> [Module]
14441463
getMentionedExecutableModules (ExecutableSection (Alias (Last main)) otherModules generatedModules)=
14451464
maybe id (:) (toModule . Path.fromFilePath <$> main) $ fromMaybeList (otherModules <> generatedModules)
14461465

1447-
toExecutable :: MonadIO m => FilePath -> String -> Section ExecutableSection -> m (Section Executable)
1466+
toExecutable :: (MonadIO m, State m) => FilePath -> String -> Section ExecutableSection -> m (Section Executable)
14481467
toExecutable dir packageName_ =
14491468
inferModules dir packageName_ getMentionedExecutableModules getExecutableModules fromExecutableSection (fromExecutableSection [])
14501469
. expandMain

test/EndToEndSpec.hs

+9
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ spec = around_ (inTempDirectoryNamed "my-package") $ do
7777
default-language: Haskell2010
7878
|]
7979

80+
context "when spec-version is >= 0.36.0" $ do
81+
it "does not add Paths_" $ do
82+
[i|
83+
spec-version: 0.36.0
84+
library: {}
85+
|] `shouldRenderTo` library [i|
86+
default-language: Haskell2010
87+
|]
88+
8089
context "when cabal-version is >= 2" $ do
8190
it "adds Paths_ to autogen-modules" $ do
8291
[i|

0 commit comments

Comments
 (0)