Skip to content

Commit ab4ea19

Browse files
ropwareJBmpilgrem
authored andcommitted
Support foreign library stanza
1 parent 48986d0 commit ab4ea19

File tree

4 files changed

+161
-3
lines changed

4 files changed

+161
-3
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ This is done to allow compatibility with a wider range of `Cabal` versions.
328328
| `reexported-modules` | · | | |
329329
| `signatures` | · | | |
330330
331+
#### Foreign Library
332+
333+
| Hpack | Cabal | Default | Notes |
334+
| --- | --- | --- | --- |
335+
| `type` | `type` | | |
336+
| `lib-version-info` | `lib-version-info` | | |
337+
| `options` | `options` | | [https://cabal.readthedocs.io/en/3.4/cabal-package.html#pkg-field-foreign-library-options](Foreign Library Options) |
338+
| `other-modules` | · | All modules in `source-dirs` less `main` less any modules mentioned in `when` | |
339+
| `generated-other-modules` | | | Added to `other-modules` and `autogen-modules`. Since `0.23.0`.
340+
331341
#### Executable fields
332342
333343
| Hpack | Cabal | Default | Notes |

src/Hpack/Config.hs

+101-3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ module Hpack.Config (
6161
, Section(..)
6262
, Library(..)
6363
, Executable(..)
64+
, ForeignLibrary(..)
6465
, Conditional(..)
6566
, Cond(..)
6667
, Flag(..)
@@ -167,6 +168,7 @@ package name version = Package {
167168
, packageCustomSetup = Nothing
168169
, packageLibrary = Nothing
169170
, packageInternalLibraries = mempty
171+
, packageForeignLibraries = mempty
170172
, packageExecutables = mempty
171173
, packageTests = mempty
172174
, packageBenchmarks = mempty
@@ -176,6 +178,7 @@ package name version = Package {
176178
renamePackage :: String -> Package -> Package
177179
renamePackage name p@Package{..} = p {
178180
packageName = name
181+
, packageForeignLibraries = fmap (renameDependencies packageName name) packageForeignLibraries
179182
, packageExecutables = fmap (renameDependencies packageName name) packageExecutables
180183
, packageTests = fmap (renameDependencies packageName name) packageTests
181184
, packageBenchmarks = fmap (renameDependencies packageName name) packageBenchmarks
@@ -194,6 +197,7 @@ renameDependencies old new sect@Section{..} = sect {sectionDependencies = (Depen
194197
packageDependencies :: Package -> [(String, DependencyInfo)]
195198
packageDependencies Package{..} = nub . sortBy (comparing (lexicographically . fst)) $
196199
(concatMap deps packageExecutables)
200+
++ (concatMap deps packageForeignLibraries)
197201
++ (concatMap deps packageTests)
198202
++ (concatMap deps packageBenchmarks)
199203
++ maybe [] deps packageLibrary
@@ -237,6 +241,27 @@ instance Semigroup LibrarySection where
237241
, librarySectionSignatures = librarySectionSignatures a <> librarySectionSignatures b
238242
}
239243

244+
data ForeignLibrarySection = ForeignLibrarySection {
245+
foreignLibrarySectionType :: Last String
246+
, foreignLibrarySectionLibVersionInfo :: Last String
247+
, foreignLibrarySectionOptions :: Maybe (List String)
248+
, foreignLibrarySectionOtherModules :: Maybe (List Module)
249+
, foreignLibrarySectionGeneratedOtherModules :: Maybe (List Module)
250+
} deriving (Eq, Show, Generic, FromValue)
251+
252+
instance Monoid ForeignLibrarySection where
253+
mempty = ForeignLibrarySection mempty mempty Nothing Nothing Nothing
254+
mappend = (<>)
255+
256+
instance Semigroup ForeignLibrarySection where
257+
a <> b = ForeignLibrarySection {
258+
foreignLibrarySectionType = foreignLibrarySectionType a <> foreignLibrarySectionType b
259+
, foreignLibrarySectionLibVersionInfo = foreignLibrarySectionLibVersionInfo a <> foreignLibrarySectionLibVersionInfo b
260+
, foreignLibrarySectionOptions = foreignLibrarySectionOptions a <> foreignLibrarySectionOptions b
261+
, foreignLibrarySectionOtherModules = foreignLibrarySectionOtherModules a <> foreignLibrarySectionOtherModules b
262+
, foreignLibrarySectionGeneratedOtherModules = foreignLibrarySectionGeneratedOtherModules a <> foreignLibrarySectionGeneratedOtherModules b
263+
}
264+
240265
data ExecutableSection = ExecutableSection {
241266
executableSectionMain :: Alias 'True "main-is" (Last FilePath)
242267
, executableSectionOtherModules :: Maybe (List Module)
@@ -570,10 +595,12 @@ type SectionConfigWithDefaults asmSources cSources cxxSources jsSources a = Prod
570595

571596
type PackageConfigWithDefaults asmSources cSources cxxSources jsSources = PackageConfig_
572597
(SectionConfigWithDefaults asmSources cSources cxxSources jsSources LibrarySection)
598+
(SectionConfigWithDefaults asmSources cSources cxxSources jsSources ForeignLibrarySection)
573599
(SectionConfigWithDefaults asmSources cSources cxxSources jsSources ExecutableSection)
574600

575601
type PackageConfig asmSources cSources cxxSources jsSources = PackageConfig_
576602
(WithCommonOptions asmSources cSources cxxSources jsSources LibrarySection)
603+
(WithCommonOptions asmSources cSources cxxSources jsSources ForeignLibrarySection)
577604
(WithCommonOptions asmSources cSources cxxSources jsSources ExecutableSection)
578605

579606
data PackageVersion = PackageVersion {unPackageVersion :: String}
@@ -584,7 +611,7 @@ instance FromValue PackageVersion where
584611
String s -> return (T.unpack s)
585612
_ -> typeMismatch "Number or String" v
586613

587-
data PackageConfig_ library executable = PackageConfig {
614+
data PackageConfig_ library foreignLib executable = PackageConfig {
588615
packageConfigName :: Maybe String
589616
, packageConfigVersion :: Maybe PackageVersion
590617
, packageConfigSynopsis :: Maybe String
@@ -611,6 +638,8 @@ data PackageConfig_ library executable = PackageConfig {
611638
, packageConfigCustomSetup :: Maybe CustomSetupSection
612639
, packageConfigLibrary :: Maybe library
613640
, packageConfigInternalLibraries :: Maybe (Map String library)
641+
, packageConfigForeignLibraries :: Maybe (Map String foreignLib)
642+
, packageConfigForeignLibrary :: Maybe foreignLib
614643
, packageConfigExecutable :: Maybe executable
615644
, packageConfigExecutables :: Maybe (Map String executable)
616645
, packageConfigTests :: Maybe (Map String executable)
@@ -639,13 +668,17 @@ traversePackageConfig :: Traversal PackageConfig
639668
traversePackageConfig t p@PackageConfig{..} = do
640669
library <- traverse (traverseWithCommonOptions t) packageConfigLibrary
641670
internalLibraries <- traverseNamedConfigs t packageConfigInternalLibraries
671+
foreignLibrary <- traverse (traverseWithCommonOptions t) packageConfigForeignLibrary
672+
foreignLibraries <- traverseNamedConfigs t packageConfigForeignLibraries
642673
executable <- traverse (traverseWithCommonOptions t) packageConfigExecutable
643674
executables <- traverseNamedConfigs t packageConfigExecutables
644675
tests <- traverseNamedConfigs t packageConfigTests
645676
benchmarks <- traverseNamedConfigs t packageConfigBenchmarks
646677
return p {
647678
packageConfigLibrary = library
648679
, packageConfigInternalLibraries = internalLibraries
680+
, packageConfigForeignLibrary = foreignLibrary
681+
, packageConfigForeignLibraries = foreignLibraries
649682
, packageConfigExecutable = executable
650683
, packageConfigExecutables = executables
651684
, packageConfigTests = tests
@@ -739,6 +772,7 @@ addPathsModuleToGeneratedModules pkg
739772
| otherwise = pkg {
740773
packageLibrary = fmap mapLibrary <$> packageLibrary pkg
741774
, packageInternalLibraries = fmap mapLibrary <$> packageInternalLibraries pkg
775+
, packageForeignLibraries = fmap mapForeignLibrary <$> packageForeignLibraries pkg
742776
, packageExecutables = fmap mapExecutable <$> packageExecutables pkg
743777
, packageTests = fmap mapExecutable <$> packageTests pkg
744778
, packageBenchmarks = fmap mapExecutable <$> packageBenchmarks pkg
@@ -755,6 +789,15 @@ addPathsModuleToGeneratedModules pkg
755789
where
756790
generatedModules = libraryGeneratedModules lib
757791

792+
mapForeignLibrary :: ForeignLibrary -> ForeignLibrary
793+
mapForeignLibrary foreignLibrary
794+
| pathsModule `elem` foreignLibraryOtherModules foreignLibrary = foreignLibrary {
795+
foreignLibraryGeneratedModules = if pathsModule `elem` generatedModules then generatedModules else pathsModule : generatedModules
796+
}
797+
| otherwise = foreignLibrary
798+
where
799+
generatedModules = foreignLibraryGeneratedModules foreignLibrary
800+
758801
mapExecutable :: Executable -> Executable
759802
mapExecutable executable
760803
| pathsModule `elem` executableOtherModules executable = executable {
@@ -836,6 +879,7 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg {
836879
, makeVersion [3,14] <$ guard (not (null packageExtraFiles))
837880
, packageLibrary >>= libraryCabalVersion
838881
, internalLibsCabalVersion packageInternalLibraries
882+
, foreignLibsCabalVersion packageForeignLibraries
839883
, executablesCabalVersion packageExecutables
840884
, executablesCabalVersion packageTests
841885
, executablesCabalVersion packageBenchmarks
@@ -859,6 +903,15 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg {
859903
where
860904
versions = libraryCabalVersion <$> Map.elems internalLibraries
861905

906+
foreignLibsCabalVersion :: Map String (Section ForeignLibrary) -> Maybe CabalVersion
907+
foreignLibsCabalVersion = foldr max Nothing . map foreignLibCabalVersion . Map.elems
908+
909+
foreignLibCabalVersion :: Section ForeignLibrary -> Maybe CabalVersion
910+
foreignLibCabalVersion sect = maximum [
911+
makeVersion [2,0] <$ guard (foreignLibraryHasGeneratedModules sect)
912+
, sectionCabalVersion (concatMap getForeignLibraryModules) sect
913+
]
914+
862915
executablesCabalVersion :: Map String (Section Executable) -> Maybe CabalVersion
863916
executablesCabalVersion = foldr max Nothing . map executableCabalVersion . Map.elems
864917

@@ -868,6 +921,9 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg {
868921
, sectionCabalVersion (concatMap getExecutableModules) sect
869922
]
870923

924+
foreignLibraryHasGeneratedModules :: Section ForeignLibrary -> Bool
925+
foreignLibraryHasGeneratedModules = any (not . null . foreignLibraryGeneratedModules)
926+
871927
executableHasGeneratedModules :: Section Executable -> Bool
872928
executableHasGeneratedModules = any (not . null . executableGeneratedModules)
873929

@@ -1034,6 +1090,7 @@ data Package = Package {
10341090
, packageCustomSetup :: Maybe CustomSetup
10351091
, packageLibrary :: Maybe (Section Library)
10361092
, packageInternalLibraries :: Map String (Section Library)
1093+
, packageForeignLibraries :: Map String (Section ForeignLibrary)
10371094
, packageExecutables :: Map String (Section Executable)
10381095
, packageTests :: Map String (Section Executable)
10391096
, packageBenchmarks :: Map String (Section Executable)
@@ -1054,6 +1111,14 @@ data Library = Library {
10541111
, librarySignatures :: [String]
10551112
} deriving (Eq, Show)
10561113

1114+
data ForeignLibrary = ForeignLibrary {
1115+
foreignLibraryType :: Maybe String
1116+
, foreignLibraryLibVersionInfo :: Maybe String
1117+
, foreignLibraryOptions :: Maybe [String]
1118+
, foreignLibraryOtherModules :: [Module]
1119+
, foreignLibraryGeneratedModules :: [Module]
1120+
} deriving (Eq, Show)
1121+
10571122
data Executable = Executable {
10581123
executableMain :: Maybe FilePath
10591124
, executableOtherModules :: [Module]
@@ -1177,13 +1242,17 @@ expandSectionDefaults
11771242
expandSectionDefaults formatYamlParseError userDataDir dir p@PackageConfig{..} = do
11781243
library <- traverse (expandDefaults formatYamlParseError userDataDir dir) packageConfigLibrary
11791244
internalLibraries <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigInternalLibraries
1245+
foreignLibrary <- traverse (expandDefaults formatYamlParseError userDataDir dir) packageConfigForeignLibrary
1246+
foreignLibraries <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigForeignLibraries
11801247
executable <- traverse (expandDefaults formatYamlParseError userDataDir dir) packageConfigExecutable
11811248
executables <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigExecutables
11821249
tests <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigTests
11831250
benchmarks <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigBenchmarks
11841251
return p{
11851252
packageConfigLibrary = library
11861253
, packageConfigInternalLibraries = internalLibraries
1254+
, packageConfigForeignLibrary = foreignLibrary
1255+
, packageConfigForeignLibraries = foreignLibraries
11871256
, packageConfigExecutable = executable
11881257
, packageConfigExecutables = executables
11891258
, packageConfigTests = tests
@@ -1241,25 +1310,28 @@ type GlobalOptions = CommonOptions AsmSources CSources CxxSources JsSources Empt
12411310

12421311
toPackage_ :: (MonadIO m, Warnings m, State m) => FilePath -> Product GlobalOptions (PackageConfig AsmSources CSources CxxSources JsSources) -> m Package
12431312
toPackage_ dir (Product g PackageConfig{..}) = do
1313+
foreignLibraryMap <- toExecutableMap packageName packageConfigForeignLibraries packageConfigForeignLibrary
12441314
executableMap <- toExecutableMap packageName packageConfigExecutables packageConfigExecutable
12451315
let
12461316
globalVerbatim = commonOptionsVerbatim g
12471317
globalOptions = g {commonOptionsVerbatim = Nothing}
12481318

1249-
executableNames = maybe [] Map.keys executableMap
1319+
componentNames = maybe [] Map.keys executableMap ++ maybe [] Map.keys foreignLibraryMap
12501320

12511321
toSect :: (Warnings m, Monoid a) => WithCommonOptions AsmSources CSources CxxSources JsSources a -> m (Section a)
1252-
toSect = toSection packageName executableNames . first ((mempty <$ globalOptions) <>)
1322+
toSect = toSection packageName componentNames . first ((mempty <$ globalOptions) <>)
12531323

12541324
toSections :: (Warnings m, Monoid a) => Maybe (Map String (WithCommonOptions AsmSources CSources CxxSources JsSources a)) -> m (Map String (Section a))
12551325
toSections = maybe (return mempty) (traverse toSect)
12561326

12571327
toLib = toLibrary dir packageName
1328+
toForeignLibraries = toSections >=> traverse (toForeignLibrary dir packageName)
12581329
toExecutables = toSections >=> traverse (toExecutable dir packageName)
12591330

12601331
mLibrary <- traverse (toSect >=> toLib) packageConfigLibrary
12611332
internalLibraries <- toSections packageConfigInternalLibraries >>= traverse toLib
12621333

1334+
foreignLibraries <- toForeignLibraries foreignLibraryMap
12631335
executables <- toExecutables executableMap
12641336
tests <- toExecutables packageConfigTests
12651337
benchmarks <- toExecutables packageConfigBenchmarks
@@ -1269,6 +1341,7 @@ toPackage_ dir (Product g PackageConfig{..}) = do
12691341
missingSourceDirs <- liftIO $ nub . sort <$> filterM (fmap not <$> doesDirectoryExist . (dir </>)) (
12701342
maybe [] sectionSourceDirs mLibrary
12711343
++ concatMap sectionSourceDirs internalLibraries
1344+
++ concatMap sectionSourceDirs foreignLibraries
12721345
++ concatMap sectionSourceDirs executables
12731346
++ concatMap sectionSourceDirs tests
12741347
++ concatMap sectionSourceDirs benchmarks
@@ -1328,6 +1401,7 @@ toPackage_ dir (Product g PackageConfig{..}) = do
13281401
, packageCustomSetup = mCustomSetup
13291402
, packageLibrary = mLibrary
13301403
, packageInternalLibraries = internalLibraries
1404+
, packageForeignLibraries = foreignLibraries
13311405
, packageExecutables = executables
13321406
, packageTests = tests
13331407
, packageBenchmarks = benchmarks
@@ -1437,6 +1511,9 @@ getMentionedLibraryModules (LibrarySection _ _ exposedModules generatedExposedMo
14371511
getLibraryModules :: Library -> [Module]
14381512
getLibraryModules Library{..} = libraryExposedModules ++ libraryOtherModules
14391513

1514+
getForeignLibraryModules :: ForeignLibrary -> [Module]
1515+
getForeignLibraryModules ForeignLibrary{..} = foreignLibraryOtherModules
1516+
14401517
getExecutableModules :: Executable -> [Module]
14411518
getExecutableModules Executable{..} = executableOtherModules
14421519

@@ -1522,6 +1599,27 @@ fromLibrarySectionPlain LibrarySection{..} = Library {
15221599
, librarySignatures = fromMaybeList librarySectionSignatures
15231600
}
15241601

1602+
getMentionedForeignLibraryModules :: ForeignLibrarySection -> [Module]
1603+
getMentionedForeignLibraryModules (ForeignLibrarySection _ _ _ otherModules generatedModules)=
1604+
fromMaybeList (otherModules <> generatedModules)
1605+
1606+
toForeignLibrary :: (MonadIO m, State m) => FilePath -> String -> Section ForeignLibrarySection -> m (Section ForeignLibrary)
1607+
toForeignLibrary dir packageName_ =
1608+
inferModules dir packageName_ getMentionedForeignLibraryModules getForeignLibraryModules fromForeignLibrarySection (fromForeignLibrarySection [])
1609+
where
1610+
fromForeignLibrarySection :: [Module] -> [Module] -> ForeignLibrarySection -> ForeignLibrary
1611+
fromForeignLibrarySection pathsModule inferableModules ForeignLibrarySection{..} =
1612+
(ForeignLibrary
1613+
(getLast foreignLibrarySectionType)
1614+
(getLast foreignLibrarySectionLibVersionInfo)
1615+
(fromList <$> foreignLibrarySectionOptions)
1616+
(otherModules ++ generatedModules)
1617+
generatedModules
1618+
)
1619+
where
1620+
otherModules = maybe (inferableModules ++ pathsModule) fromList foreignLibrarySectionOtherModules
1621+
generatedModules = maybe [] fromList foreignLibrarySectionGeneratedOtherModules
1622+
15251623
getMentionedExecutableModules :: ExecutableSection -> [Module]
15261624
getMentionedExecutableModules (ExecutableSection (Alias (Last main)) otherModules generatedModules)=
15271625
maybe id (:) (toModule . Path.fromFilePath <$> main) $ fromMaybeList (otherModules <> generatedModules)

src/Hpack/Render.hs

+26
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ renderPackageWith settings headerFieldsAlignment existingFieldOrder sectionsFiel
9898
stanzas = flip runReader (RenderEnv packageCabalVersion packageName) $ do
9999
library <- maybe (return []) (fmap return . renderLibrary) packageLibrary
100100
internalLibraries <- renderInternalLibraries packageInternalLibraries
101+
foreignLibraries <- renderForeignLibraries packageForeignLibraries
101102
executables <- renderExecutables packageExecutables
102103
tests <- renderTests packageTests
103104
benchmarks <- renderBenchmarks packageBenchmarks
@@ -107,6 +108,7 @@ renderPackageWith settings headerFieldsAlignment existingFieldOrder sectionsFiel
107108
, map renderFlag packageFlags
108109
, library
109110
, internalLibraries
111+
, foreignLibraries
110112
, executables
111113
, tests
112114
, benchmarks
@@ -181,6 +183,13 @@ renderInternalLibrary :: (String, Section Library) -> RenderM Element
181183
renderInternalLibrary (name, sect) = do
182184
Stanza ("library " ++ name) <$> renderLibrarySection sect
183185

186+
renderForeignLibraries :: Map String (Section ForeignLibrary) -> RenderM [Element]
187+
renderForeignLibraries = traverse renderForeignLibrary . Map.toList
188+
189+
renderForeignLibrary :: (String, Section ForeignLibrary) -> RenderM Element
190+
renderForeignLibrary (name, sect) =
191+
Stanza ("foreign-library " ++ name) <$> (renderForeignLibrarySection [] sect)
192+
184193
renderExecutables :: Map String (Section Executable) -> RenderM [Element]
185194
renderExecutables = traverse renderExecutable . Map.toList
186195

@@ -214,6 +223,19 @@ renderExecutableFields Executable{..} = mainIs ++ [otherModules, generatedModule
214223
otherModules = renderOtherModules executableOtherModules
215224
generatedModules = renderGeneratedModules executableGeneratedModules
216225

226+
renderForeignLibrarySection :: [Element] -> Section ForeignLibrary -> RenderM [Element]
227+
renderForeignLibrarySection extraFields = renderSection renderForeignLibraryFields extraFields
228+
229+
renderForeignLibraryFields :: ForeignLibrary -> [Element]
230+
renderForeignLibraryFields ForeignLibrary{..} =
231+
typeField ++ libVersionInfo ++ options ++ [otherModules, generatedModules]
232+
where
233+
typeField = maybe [] (return . Field "type" . Literal) foreignLibraryType
234+
libVersionInfo = maybe [] (return . Field "lib-version-info" . Literal) foreignLibraryLibVersionInfo
235+
options = maybe [] (\opts -> [renderForeignLibOptions opts]) foreignLibraryOptions
236+
otherModules = renderOtherModules foreignLibraryOtherModules
237+
generatedModules = renderGeneratedModules foreignLibraryGeneratedModules
238+
217239
renderCustomSetup :: CustomSetup -> Element
218240
renderCustomSetup CustomSetup{..} =
219241
Stanza "custom-setup" $ renderDependencies "setup-depends" customSetupDependencies
@@ -329,6 +351,10 @@ renderDirectories name = Field name . LineSeparatedList . replaceDots
329351
"." -> "./"
330352
_ -> xs
331353

354+
355+
renderForeignLibOptions :: [String] -> Element
356+
renderForeignLibOptions = Field "options" . LineSeparatedList
357+
332358
renderExposedModules :: [Module] -> Element
333359
renderExposedModules = Field "exposed-modules" . LineSeparatedList . map unModule
334360

0 commit comments

Comments
 (0)