Skip to content

Commit b680565

Browse files
committed
Fix #604 Where applicable, use elif in Cabal files
1 parent e7c9326 commit b680565

8 files changed

Lines changed: 165 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Changes in 0.39.0
2+
- Where applicable, use `elif` in Cabal files (introduced in
3+
`cabal-version: 2.2`).
4+
15
## Changes in 0.38.0
26
- Generate `build-tool-depends` instead of `build-tools` starting with
37
`cabal-version: 2` (fixes #596)

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,35 @@ becomes
477477
else
478478
ghc-options: -O0
479479
480+
Conditionals with a `else` field that contains only one `when` field will make
481+
use of `elif` in Cabal files (introduced in `cabal-version: 2.2`).
482+
483+
For example,
484+
485+
when:
486+
- condition: os(windows)
487+
then:
488+
source-dirs: windows
489+
else:
490+
when:
491+
- condition: "os(darwin) || os(linux)"
492+
then:
493+
source-dirs: unix-like
494+
else:
495+
source-dirs: unsupported-os
496+
497+
becomes
498+
499+
if os(windows)
500+
hs-source-dirs:
501+
windows
502+
elif os(darwin) || os(linux)
503+
hs-source-dirs:
504+
unix-like
505+
else
506+
hs-source-dirs:
507+
unsupported-os
508+
480509
**Note:** Conditionals with `condition: false` are omitted from the generated
481510
`.cabal` file.
482511

hpack.cabal

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
spec-version: 0.36.0
22
name: hpack
3-
version: 0.38.0
3+
version: 0.39.0
44
synopsis: A modern format for Haskell packages
55
description: See README at <https://github.com/sol/hpack#readme>
66
author: Simon Hengel <sol@typeful.net>

src/Hpack/Config.hs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ module Hpack.Config (
5858
, VerbatimValue(..)
5959
, verbatimValueToString
6060
, CustomSetup(..)
61+
, HasEmpty(..)
6162
, Section(..)
63+
, maybeSectionAConditional
6264
, Library(..)
6365
, Executable(..)
6466
, Conditional(..)
@@ -527,6 +529,9 @@ instance Monoid Empty where
527529
mempty = Empty
528530
mappend = (<>)
529531

532+
instance HasEmpty Empty where
533+
empty = Empty
534+
530535
instance Semigroup Empty where
531536
Empty <> Empty = Empty
532537

@@ -868,7 +873,7 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg {
868873
executableHasGeneratedModules :: Section Executable -> Bool
869874
executableHasGeneratedModules = any (not . null . executableGeneratedModules)
870875

871-
sectionCabalVersion :: (Section a -> [Module]) -> Section a -> Maybe CabalVersion
876+
sectionCabalVersion :: (Eq a, HasEmpty a) => (Section a -> [Module]) -> Section a -> Maybe CabalVersion
872877
sectionCabalVersion getMentionedModules sect = maximum $ [
873878
makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxSources) sect)
874879
, makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxOptions) sect)
@@ -880,6 +885,7 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg {
880885
uses "RebindableSyntax"
881886
&& (uses "OverloadedStrings" || uses "OverloadedLists")
882887
&& pathsModule `elem` getMentionedModules sect)
888+
, makeVersion [2,2] <$ guard (sectionSatisfies (isJust . maybeSectionAConditional) sect)
883889
] ++ map versionFromSystemBuildTool systemBuildTools
884890
where
885891
defaultExtensions :: [String]
@@ -1040,6 +1046,10 @@ data CustomSetup = CustomSetup {
10401046
customSetupDependencies :: Dependencies
10411047
} deriving (Eq, Show)
10421048

1049+
-- | A class for types that have a value corresponding to being \'empty\'.
1050+
class HasEmpty a where
1051+
empty :: a
1052+
10431053
data Library = Library {
10441054
libraryExposed :: Maybe Bool
10451055
, libraryVisibility :: Maybe String
@@ -1050,12 +1060,30 @@ data Library = Library {
10501060
, librarySignatures :: [String]
10511061
} deriving (Eq, Show)
10521062

1063+
instance HasEmpty Library where
1064+
empty = Library
1065+
{ libraryExposed = Nothing
1066+
, libraryVisibility = Nothing
1067+
, libraryExposedModules = []
1068+
, libraryOtherModules = []
1069+
, libraryGeneratedModules = []
1070+
, libraryReexportedModules = []
1071+
, librarySignatures = []
1072+
}
1073+
10531074
data Executable = Executable {
10541075
executableMain :: Maybe FilePath
10551076
, executableOtherModules :: [Module]
10561077
, executableGeneratedModules :: [Module]
10571078
} deriving (Eq, Show)
10581079

1080+
instance HasEmpty Executable where
1081+
empty = Executable
1082+
{ executableMain = Nothing
1083+
, executableOtherModules = []
1084+
, executableGeneratedModules = []
1085+
}
1086+
10591087
data BuildTool = BuildTool String String | LocalBuildTool String
10601088
deriving (Show, Eq, Ord)
10611089

@@ -1093,6 +1121,41 @@ data Section a = Section {
10931121
, sectionVerbatim :: [Verbatim]
10941122
} deriving (Eq, Show, Functor, Foldable, Traversable)
10951123

1124+
instance HasEmpty a => HasEmpty (Section a) where
1125+
empty = Section
1126+
{ sectionData = Hpack.Config.empty
1127+
, sectionSourceDirs = []
1128+
, sectionDependencies = mempty
1129+
, sectionPkgConfigDependencies = []
1130+
, sectionDefaultExtensions = []
1131+
, sectionOtherExtensions = []
1132+
, sectionLanguage = Nothing
1133+
, sectionGhcOptions = []
1134+
, sectionGhcProfOptions = []
1135+
, sectionGhcSharedOptions = []
1136+
, sectionGhcjsOptions = []
1137+
, sectionCppOptions = []
1138+
, sectionAsmOptions = []
1139+
, sectionAsmSources = []
1140+
, sectionCcOptions = []
1141+
, sectionCSources = []
1142+
, sectionCxxOptions = []
1143+
, sectionCxxSources = []
1144+
, sectionJsSources = []
1145+
, sectionExtraLibDirs = []
1146+
, sectionExtraLibraries = []
1147+
, sectionExtraFrameworksDirs = []
1148+
, sectionFrameworks = []
1149+
, sectionIncludeDirs = []
1150+
, sectionInstallIncludes = []
1151+
, sectionLdOptions = []
1152+
, sectionBuildable = Nothing
1153+
, sectionConditionals = []
1154+
, sectionBuildTools = mempty
1155+
, sectionSystemBuildTools = mempty
1156+
, sectionVerbatim = []
1157+
}
1158+
10961159
data Conditional a = Conditional {
10971160
conditionalCondition :: Cond
10981161
, conditionalThen :: a
@@ -1657,3 +1720,12 @@ pathsModuleFromPackageName name = Module ("Paths_" ++ map f name)
16571720
where
16581721
f '-' = '_'
16591722
f x = x
1723+
1724+
-- | If the given section contains only a single conditional, yields just that
1725+
-- conditional, otherwise 'Nothing'.
1726+
maybeSectionAConditional :: (Eq a, HasEmpty a) => Section a -> Maybe (Conditional (Section a))
1727+
maybeSectionAConditional s@(Section { sectionConditionals = [c] }) =
1728+
if s == Hpack.Config.empty { sectionConditionals = sectionConditionals s }
1729+
then Just c
1730+
else Nothing
1731+
maybeSectionAConditional _ = Nothing

src/Hpack/Render.hs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ renderExposed = Field "exposed" . Literal . show
240240
renderVisibility :: String -> Element
241241
renderVisibility = Field "visibility" . Literal
242242

243-
renderSection :: (a -> [Element]) -> [Element] -> Section a -> RenderM [Element]
243+
renderSection :: (Eq a, HasEmpty a) => (a -> [Element]) -> [Element] -> Section a -> RenderM [Element]
244244
renderSection renderSectionData extraFieldsStart Section{..} = do
245245
buildTools <- renderBuildTools sectionBuildTools sectionSystemBuildTools
246246
conditionals <- traverse (renderConditional renderSectionData) sectionConditionals
@@ -307,12 +307,18 @@ renderVerbatimObject = map renderPair . Map.toList
307307
[x] -> Field key (Literal x)
308308
xs -> Field key (LineSeparatedList xs)
309309

310-
renderConditional :: (a -> [Element]) -> Conditional (Section a) -> RenderM Element
311-
renderConditional renderSectionData (Conditional condition sect mElse) = case mElse of
312-
Nothing -> if_
313-
Just else_ -> Group <$> if_ <*> (Stanza "else" <$> renderSection renderSectionData [] else_)
310+
renderConditional :: (Eq a, HasEmpty a) => (a -> [Element]) -> Conditional (Section a) -> RenderM Element
311+
renderConditional = renderConditional' "if "
314312
where
315-
if_ = Stanza ("if " ++ renderCond condition) <$> renderSection renderSectionData [] sect
313+
renderConditional' stanza renderSectionData (Conditional condition sect mElse) = case mElse of
314+
Nothing -> stanza_
315+
Just else_ -> Group <$> stanza_ <*> case maybeSectionAConditional else_ of
316+
Just conditional ->
317+
renderConditional' "elif " renderSectionData conditional
318+
Nothing ->
319+
Stanza "else" <$> renderSection renderSectionData [] else_
320+
where
321+
stanza_ = Stanza (stanza ++ renderCond condition) <$> renderSection renderSectionData [] sect
316322

317323
renderCond :: Cond -> String
318324
renderCond = \ case

test/EndToEndSpec.hs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,36 @@ spec = around_ (inTempDirectoryNamed "my-package") $ do
18021802
build-depends:
18031803
unix
18041804
|]
1805+
it "uses elif when applicable and infers cabal-version 2.2" $ do
1806+
[i|
1807+
when:
1808+
condition: os(windows)
1809+
then:
1810+
source-dirs: windows
1811+
else:
1812+
when:
1813+
condition: "os(darwin) || os(linux)"
1814+
then:
1815+
source-dirs: unix-like
1816+
else:
1817+
source-dirs: unsupported-os
1818+
executable: {}
1819+
|] `shouldRenderTo` (executable "my-package" [i|
1820+
other-modules:
1821+
Paths_my_package
1822+
autogen-modules:
1823+
Paths_my_package
1824+
default-language: Haskell2010
1825+
if os(windows)
1826+
hs-source-dirs:
1827+
windows
1828+
elif os(darwin) || os(linux)
1829+
hs-source-dirs:
1830+
unix-like
1831+
else
1832+
hs-source-dirs:
1833+
unsupported-os
1834+
|]) {packageCabalVersion = "2.2"}
18051835

18061836
context "with empty then-branch" $ do
18071837
it "provides a hint" $ do

test/Hpack/RenderSpec.hs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,21 @@ spec = do
246246
, " unix"
247247
]
248248

249+
it "renders conditionals with else-branch using elif when applicable" $ do
250+
let conditional = Conditional "os(windows)" (section Empty) {sectionSourceDirs = ["windows"]} $ Just (section Empty) {sectionConditionals = [innerConditional]}
251+
innerConditional = Conditional "os(darwin) || os(linux)" (section Empty) {sectionSourceDirs = ["unix-like"]} $ Just (section Empty) {sectionSourceDirs = ["unsupported-os"]}
252+
render defaultRenderSettings 0 (run $ renderConditional renderEmptySection conditional) `shouldBe` [
253+
"if os(windows)"
254+
, " hs-source-dirs:"
255+
, " windows"
256+
, "elif os(darwin) || os(linux)"
257+
, " hs-source-dirs:"
258+
, " unix-like"
259+
, "else"
260+
, " hs-source-dirs:"
261+
, " unsupported-os"
262+
]
263+
249264
it "renders nested conditionals" $ do
250265
let conditional = Conditional "arch(i386)" (section Empty) {sectionGhcOptions = ["-threaded"], sectionConditionals = [innerConditional]} Nothing
251266
innerConditional = Conditional "os(windows)" (section Empty) {sectionDependencies = deps ["Win32"]} Nothing

0 commit comments

Comments
 (0)