diff --git a/CHANGELOG.md b/CHANGELOG.md index ce04e218..55e8f51f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Changes in UNRELEASED + - Add new section: `foreign-libraries`, for Cabal 2.0's `foreign-library` + stanzas (see #518) + ## Changes in 0.40.0 - Infer `cabal-version: 3.4` (rather than `cabal-version: 3.0`) when a section has sublibraries as dependencies. Make the same inference when custom setup diff --git a/README.md b/README.md index 881b4bfd..54c831ce 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,8 @@ library: | `executable` | `executable ` | | Shortcut for `executables: { package-name: ... }` | | `0.18.0` | | `tests` | `test-suite ` | | Map from test name to test (see [Test fields](#test-fields)) | | | | `benchmarks` | `benchmark ` | | Map from benchmark name to benchmark (see [Benchmark fields](#benchmark-fields)) | | | +| `foreign-libraries` | `foreign-library ` | | Map from foreign library name to foreign library (see [Foreign library fields](#foreign-library-fields)) | | UNRELEASED | +| `foreign-library` | `foreign-library ` | | Shortcut for `foreign-libraries: { package-name: ... }` | | UNRELEASED | | `defaults` | | | See [Defaults](#defaults), may be a list | | | #### cabal-version @@ -357,6 +359,14 @@ This is done to allow compatibility with a wider range of `Cabal` versions. | `other-modules` | · | All modules in `source-dirs` less `main` less any modules mentioned in `when` | | | `generated-other-modules` | | | Added to `other-modules` and `autogen-modules`. Since `0.23.0`. +#### Foreign library fields + +| Hpack | Cabal | Default | Notes | +| --- | --- | --- | --- | +| `lib-version-info` | . | | | +| `other-modules` | · | All modules in `source-dirs` less any modules mentioned in `when` | | +| `generated-other-modules` | | | Added to `other-modules` and `autogen-modules`.| + #### Flags | Hpack | Cabal | Default | Notes | diff --git a/src/Hpack/Config.hs b/src/Hpack/Config.hs index 9faf6365..0796c288 100644 --- a/src/Hpack/Config.hs +++ b/src/Hpack/Config.hs @@ -59,8 +59,11 @@ module Hpack.Config ( , verbatimValueToString , CustomSetup(..) , Section(..) +, emptySectionForeignLibrary , Library(..) , Executable(..) +, ForeignLibrary(..) +, emptyForeignLibrary , Conditional(..) , Cond(..) , Flag(..) @@ -167,6 +170,7 @@ package name version = Package { , packageCustomSetup = Nothing , packageLibrary = Nothing , packageInternalLibraries = mempty + , packageForeignLibraries = mempty , packageExecutables = mempty , packageTests = mempty , packageBenchmarks = mempty @@ -176,6 +180,7 @@ package name version = Package { renamePackage :: String -> Package -> Package renamePackage name p@Package{..} = p { packageName = name + , packageForeignLibraries = fmap (renameDependencies packageName name) packageForeignLibraries , packageExecutables = fmap (renameDependencies packageName name) packageExecutables , packageTests = fmap (renameDependencies packageName name) packageTests , packageBenchmarks = fmap (renameDependencies packageName name) packageBenchmarks @@ -194,6 +199,7 @@ renameDependencies old new sect@Section{..} = sect {sectionDependencies = (Depen packageDependencies :: Package -> [(String, DependencyInfo)] packageDependencies Package{..} = nub . sortBy (comparing (lexicographically . fst)) $ (concatMap deps packageExecutables) + ++ (concatMap deps packageForeignLibraries) ++ (concatMap deps packageTests) ++ (concatMap deps packageBenchmarks) ++ maybe [] deps packageLibrary @@ -237,6 +243,23 @@ instance Semigroup LibrarySection where , librarySectionSignatures = librarySectionSignatures a <> librarySectionSignatures b } +data ForeignLibrarySection = ForeignLibrarySection { + foreignLibrarySectionLibVersionInfo :: Last String +, foreignLibrarySectionOtherModules :: Maybe (List Module) +, foreignLibrarySectionGeneratedOtherModules :: Maybe (List Module) +} deriving (Eq, Show, Generic, FromValue) + +instance Monoid ForeignLibrarySection where + mempty = ForeignLibrarySection mempty Nothing Nothing + mappend = (<>) + +instance Semigroup ForeignLibrarySection where + a <> b = ForeignLibrarySection { + foreignLibrarySectionLibVersionInfo = foreignLibrarySectionLibVersionInfo a <> foreignLibrarySectionLibVersionInfo b + , foreignLibrarySectionOtherModules = foreignLibrarySectionOtherModules a <> foreignLibrarySectionOtherModules b + , foreignLibrarySectionGeneratedOtherModules = foreignLibrarySectionGeneratedOtherModules a <> foreignLibrarySectionGeneratedOtherModules b + } + data ExecutableSection = ExecutableSection { executableSectionMain :: Alias 'True "main-is" (Last FilePath) , executableSectionOtherModules :: Maybe (List Module) @@ -573,10 +596,12 @@ type SectionConfigWithDefaults asmSources cSources cxxSources jsSources a = Prod type PackageConfigWithDefaults asmSources cSources cxxSources jsSources = PackageConfig_ (SectionConfigWithDefaults asmSources cSources cxxSources jsSources LibrarySection) + (SectionConfigWithDefaults asmSources cSources cxxSources jsSources ForeignLibrarySection) (SectionConfigWithDefaults asmSources cSources cxxSources jsSources ExecutableSection) type PackageConfig asmSources cSources cxxSources jsSources = PackageConfig_ (WithCommonOptions asmSources cSources cxxSources jsSources LibrarySection) + (WithCommonOptions asmSources cSources cxxSources jsSources ForeignLibrarySection) (WithCommonOptions asmSources cSources cxxSources jsSources ExecutableSection) data PackageVersion = PackageVersion {unPackageVersion :: String} @@ -587,7 +612,7 @@ instance FromValue PackageVersion where String s -> return (T.unpack s) _ -> typeMismatch "Number or String" v -data PackageConfig_ library executable = PackageConfig { +data PackageConfig_ library foreignLib executable = PackageConfig { packageConfigName :: Maybe String , packageConfigVersion :: Maybe PackageVersion , packageConfigSynopsis :: Maybe String @@ -615,6 +640,8 @@ data PackageConfig_ library executable = PackageConfig { , packageConfigCustomSetup :: Maybe CustomSetupSection , packageConfigLibrary :: Maybe library , packageConfigInternalLibraries :: Maybe (Map String library) +, packageConfigForeignLibraries :: Maybe (Map String foreignLib) +, packageConfigForeignLibrary :: Maybe foreignLib , packageConfigExecutable :: Maybe executable , packageConfigExecutables :: Maybe (Map String executable) , packageConfigTests :: Maybe (Map String executable) @@ -643,6 +670,8 @@ traversePackageConfig :: Traversal PackageConfig traversePackageConfig t p@PackageConfig{..} = do library <- traverse (traverseWithCommonOptions t) packageConfigLibrary internalLibraries <- traverseNamedConfigs t packageConfigInternalLibraries + foreignLibrary <- traverse (traverseWithCommonOptions t) packageConfigForeignLibrary + foreignLibraries <- traverseNamedConfigs t packageConfigForeignLibraries executable <- traverse (traverseWithCommonOptions t) packageConfigExecutable executables <- traverseNamedConfigs t packageConfigExecutables tests <- traverseNamedConfigs t packageConfigTests @@ -650,6 +679,8 @@ traversePackageConfig t p@PackageConfig{..} = do return p { packageConfigLibrary = library , packageConfigInternalLibraries = internalLibraries + , packageConfigForeignLibrary = foreignLibrary + , packageConfigForeignLibraries = foreignLibraries , packageConfigExecutable = executable , packageConfigExecutables = executables , packageConfigTests = tests @@ -743,6 +774,7 @@ addPathsModuleToGeneratedModules pkg | otherwise = pkg { packageLibrary = fmap mapLibrary <$> packageLibrary pkg , packageInternalLibraries = fmap mapLibrary <$> packageInternalLibraries pkg + , packageForeignLibraries = fmap mapForeignLibrary <$> packageForeignLibraries pkg , packageExecutables = fmap mapExecutable <$> packageExecutables pkg , packageTests = fmap mapExecutable <$> packageTests pkg , packageBenchmarks = fmap mapExecutable <$> packageBenchmarks pkg @@ -759,6 +791,15 @@ addPathsModuleToGeneratedModules pkg where generatedModules = libraryGeneratedModules lib + mapForeignLibrary :: ForeignLibrary -> ForeignLibrary + mapForeignLibrary foreignLibrary + | pathsModule `elem` foreignLibraryOtherModules foreignLibrary = foreignLibrary { + foreignLibraryGeneratedModules = if pathsModule `elem` generatedModules then generatedModules else pathsModule : generatedModules + } + | otherwise = foreignLibrary + where + generatedModules = foreignLibraryGeneratedModules foreignLibrary + mapExecutable :: Executable -> Executable mapExecutable executable | pathsModule `elem` executableOtherModules executable = executable { @@ -841,6 +882,7 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg { , makeVersion [3,14] <$ guard (not (null packageExtraFiles)) , packageLibrary >>= libraryCabalVersion , internalLibsCabalVersion packageInternalLibraries + , foreignLibsCabalVersion packageForeignLibraries , executablesCabalVersion packageExecutables , executablesCabalVersion packageTests , executablesCabalVersion packageBenchmarks @@ -882,6 +924,15 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg { where versions = libraryCabalVersion <$> Map.elems internalLibraries + foreignLibsCabalVersion :: Map String (Section ForeignLibrary) -> Maybe CabalVersion + foreignLibsCabalVersion = foldr max Nothing . map foreignLibCabalVersion . Map.elems + + foreignLibCabalVersion :: Section ForeignLibrary -> Maybe CabalVersion + foreignLibCabalVersion sect = maximum [ + makeVersion [2,0] <$ guard (foreignLibraryHasGeneratedModules sect) + , sectionCabalVersion (concatMap getForeignLibraryModules) sect + ] + executablesCabalVersion :: Map String (Section Executable) -> Maybe CabalVersion executablesCabalVersion = foldr max Nothing . map executableCabalVersion . Map.elems @@ -892,6 +943,9 @@ ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg { , sectionCabalVersion (concatMap getExecutableModules) sect ] + foreignLibraryHasGeneratedModules :: Section ForeignLibrary -> Bool + foreignLibraryHasGeneratedModules = any (not . null . foreignLibraryGeneratedModules) + executableHasGeneratedModules :: Section Executable -> Bool executableHasGeneratedModules = any (not . null . executableGeneratedModules) @@ -1062,6 +1116,7 @@ data Package = Package { , packageCustomSetup :: Maybe CustomSetup , packageLibrary :: Maybe (Section Library) , packageInternalLibraries :: Map String (Section Library) +, packageForeignLibraries :: Map String (Section ForeignLibrary) , packageExecutables :: Map String (Section Executable) , packageTests :: Map String (Section Executable) , packageBenchmarks :: Map String (Section Executable) @@ -1082,6 +1137,24 @@ data Library = Library { , librarySignatures :: [String] } deriving (Eq, Show) +data ForeignLibrary = ForeignLibrary { + -- This is a hack. The 'type' field is required but we also want to exclude it + -- from: + -- + -- if os(windows) + -- options: standalone + -- + -- So, we treat it as if it were optional: + foreignLibraryType :: Maybe String +, foreignLibraryOptions :: Maybe [String] +, foreignLibraryLibVersionInfo :: Maybe String +, foreignLibraryOtherModules :: [Module] +, foreignLibraryGeneratedModules :: [Module] +} deriving (Eq, Show) + +emptyForeignLibrary :: ForeignLibrary +emptyForeignLibrary = ForeignLibrary Nothing Nothing Nothing [] [] + data Executable = Executable { executableMain :: Maybe FilePath , executableOtherModules :: [Module] @@ -1126,6 +1199,42 @@ data Section a = Section { , sectionVerbatim :: [Verbatim] } deriving (Eq, Show, Functor, Foldable, Traversable) +emptySectionForeignLibrary :: Section ForeignLibrary +emptySectionForeignLibrary = Section { + sectionData = emptyForeignLibrary + , sectionSourceDirs = mempty + , sectionDependencies = mempty + , sectionPkgConfigDependencies = mempty + , sectionDefaultExtensions = mempty + , sectionOtherExtensions = mempty + , sectionLanguage = Nothing + , sectionMhsOptions = mempty + , sectionGhcOptions = mempty + , sectionGhcProfOptions = mempty + , sectionGhcSharedOptions = mempty + , sectionGhcjsOptions = mempty + , sectionCppOptions = mempty + , sectionAsmOptions = mempty + , sectionAsmSources = mempty + , sectionCcOptions = mempty + , sectionCSources = mempty + , sectionCxxOptions = mempty + , sectionCxxSources = mempty + , sectionJsSources = mempty + , sectionExtraLibDirs = mempty + , sectionExtraLibraries = mempty + , sectionExtraFrameworksDirs = mempty + , sectionFrameworks = mempty + , sectionIncludeDirs = mempty + , sectionInstallIncludes = mempty + , sectionLdOptions = mempty + , sectionBuildable = Nothing + , sectionConditionals = mempty + , sectionBuildTools = mempty + , sectionSystemBuildTools = mempty + , sectionVerbatim = mempty + } + data Conditional a = Conditional { conditionalCondition :: Cond , conditionalThen :: a @@ -1206,6 +1315,8 @@ expandSectionDefaults expandSectionDefaults formatYamlParseError userDataDir dir p@PackageConfig{..} = do library <- traverse (expandDefaults formatYamlParseError userDataDir dir) packageConfigLibrary internalLibraries <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigInternalLibraries + foreignLibrary <- traverse (expandDefaults formatYamlParseError userDataDir dir) packageConfigForeignLibrary + foreignLibraries <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigForeignLibraries executable <- traverse (expandDefaults formatYamlParseError userDataDir dir) packageConfigExecutable executables <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigExecutables tests <- traverse (traverse (expandDefaults formatYamlParseError userDataDir dir)) packageConfigTests @@ -1213,6 +1324,8 @@ expandSectionDefaults formatYamlParseError userDataDir dir p@PackageConfig{..} = return p{ packageConfigLibrary = library , packageConfigInternalLibraries = internalLibraries + , packageConfigForeignLibrary = foreignLibrary + , packageConfigForeignLibraries = foreignLibraries , packageConfigExecutable = executable , packageConfigExecutables = executables , packageConfigTests = tests @@ -1270,6 +1383,7 @@ type GlobalOptions = CommonOptions AsmSources CSources CxxSources JsSources Empt toPackage_ :: (MonadIO m, Warnings m, State m) => FilePath -> Product GlobalOptions (PackageConfig AsmSources CSources CxxSources JsSources) -> m Package toPackage_ dir (Product g PackageConfig{..}) = do + foreignLibraryMap <- toExecutableMap packageName packageConfigForeignLibraries packageConfigForeignLibrary executableMap <- toExecutableMap packageName packageConfigExecutables packageConfigExecutable let globalVerbatim = commonOptionsVerbatim g @@ -1284,11 +1398,13 @@ toPackage_ dir (Product g PackageConfig{..}) = do toSections = maybe (return mempty) (traverse toSect) toLib = toLibrary dir packageName + toForeignLibraries = toSections >=> traverse (toForeignLibrary dir packageName) toExecutables = toSections >=> traverse (toExecutable dir packageName) mLibrary <- traverse (toSect >=> toLib) packageConfigLibrary internalLibraries <- toSections packageConfigInternalLibraries >>= traverse toLib + foreignLibraries <- toForeignLibraries foreignLibraryMap executables <- toExecutables executableMap tests <- toExecutables packageConfigTests benchmarks <- toExecutables packageConfigBenchmarks @@ -1298,6 +1414,7 @@ toPackage_ dir (Product g PackageConfig{..}) = do missingSourceDirs <- liftIO $ nub . sort <$> filterM (fmap not <$> doesDirectoryExist . (dir )) ( maybe [] sectionSourceDirs mLibrary ++ concatMap sectionSourceDirs internalLibraries + ++ concatMap sectionSourceDirs foreignLibraries ++ concatMap sectionSourceDirs executables ++ concatMap sectionSourceDirs tests ++ concatMap sectionSourceDirs benchmarks @@ -1357,6 +1474,7 @@ toPackage_ dir (Product g PackageConfig{..}) = do , packageCustomSetup = mCustomSetup , packageLibrary = mLibrary , packageInternalLibraries = internalLibraries + , packageForeignLibraries = foreignLibraries , packageExecutables = executables , packageTests = tests , packageBenchmarks = benchmarks @@ -1474,6 +1592,9 @@ getMentionedLibraryModules (LibrarySection _ _ exposedModules generatedExposedMo getLibraryModules :: Library -> [Module] getLibraryModules Library{..} = libraryExposedModules ++ libraryOtherModules +getForeignLibraryModules :: ForeignLibrary -> [Module] +getForeignLibraryModules ForeignLibrary{..} = foreignLibraryOtherModules + getExecutableModules :: Executable -> [Module] getExecutableModules Executable{..} = executableOtherModules @@ -1488,7 +1609,9 @@ removeConditionalsThatAreAlwaysFalse sect = sect { p = (/= CondBool False) . conditionalCondition inferModules :: (MonadIO m, State m) => - FilePath + Bool + -- ^ Support only the modern handling of Paths_ modules? + -> FilePath -> String -> (a -> [Module]) -> (b -> [Module]) @@ -1496,13 +1619,15 @@ inferModules :: (MonadIO m, State m) => -> ([Module] -> a -> b) -> Section a -> m (Section b) -inferModules dir packageName_ getMentionedModules getInferredModules fromData fromConditionals sect_ = do +inferModules modernPaths dir packageName_ getMentionedModules getInferredModules fromData fromConditionals sect_ = do specVersion <- State.get let pathsModule :: [Module] pathsModule = case specVersion of SpecVersion v | v >= Version.makeVersion [0,36,0] -> [] - _ -> [pathsModuleFromPackageName packageName_] + _ + | modernPaths -> [] + | otherwise -> [pathsModuleFromPackageName packageName_] removeConditionalsThatAreAlwaysFalse <$> traverseSectionAndConditionals (fromConfigSection fromData pathsModule) @@ -1521,7 +1646,7 @@ inferModules dir packageName_ getMentionedModules getInferredModules fromData fr toLibrary :: (MonadIO m, State m) => FilePath -> String -> Section LibrarySection -> m (Section Library) toLibrary dir name = - inferModules dir name getMentionedLibraryModules getLibraryModules fromLibrarySectionTopLevel fromLibrarySectionInConditional + inferModules False dir name getMentionedLibraryModules getLibraryModules fromLibrarySectionTopLevel fromLibrarySectionInConditional where fromLibrarySectionTopLevel :: [Module] -> [Module] -> LibrarySection -> Library fromLibrarySectionTopLevel pathsModule inferableModules LibrarySection{..} = @@ -1559,13 +1684,35 @@ fromLibrarySectionPlain LibrarySection{..} = Library { , librarySignatures = fromMaybeList librarySectionSignatures } +getMentionedForeignLibraryModules :: ForeignLibrarySection -> [Module] +getMentionedForeignLibraryModules (ForeignLibrarySection _ otherModules generatedModules)= + fromMaybeList (otherModules <> generatedModules) + +toForeignLibrary :: (MonadIO m, State m) => FilePath -> String -> Section ForeignLibrarySection -> m (Section ForeignLibrary) +toForeignLibrary dir packageName_ = + inferModules True dir packageName_ getMentionedForeignLibraryModules getForeignLibraryModules fromForeignLibrarySection (fromForeignLibrarySection []) + where + fromForeignLibrarySection :: [Module] -> [Module] -> ForeignLibrarySection -> ForeignLibrary + fromForeignLibrarySection pathsModule inferableModules ForeignLibrarySection{..} = + (ForeignLibrary + -- Cabal accepts native-static or native-shared but supports only the latter: + (Just "native-shared") + Nothing + (getLast foreignLibrarySectionLibVersionInfo) + (otherModules ++ generatedModules) + generatedModules + ) + where + otherModules = maybe (inferableModules ++ pathsModule) fromList foreignLibrarySectionOtherModules + generatedModules = maybe [] fromList foreignLibrarySectionGeneratedOtherModules + getMentionedExecutableModules :: ExecutableSection -> [Module] getMentionedExecutableModules (ExecutableSection (Alias (Last main)) otherModules generatedModules)= maybe id (:) (toModule . Path.fromFilePath <$> main) $ fromMaybeList (otherModules <> generatedModules) toExecutable :: (MonadIO m, State m) => FilePath -> String -> Section ExecutableSection -> m (Section Executable) toExecutable dir packageName_ = - inferModules dir packageName_ getMentionedExecutableModules getExecutableModules fromExecutableSection (fromExecutableSection []) + inferModules False dir packageName_ getMentionedExecutableModules getExecutableModules fromExecutableSection (fromExecutableSection []) . expandMain where fromExecutableSection :: [Module] -> [Module] -> ExecutableSection -> Executable diff --git a/src/Hpack/Render.hs b/src/Hpack/Render.hs index 2ede8d5c..4dd539c5 100644 --- a/src/Hpack/Render.hs +++ b/src/Hpack/Render.hs @@ -102,6 +102,7 @@ renderPackageWith RenderSettings{..} headerFieldsAlignment existingFieldOrder se stanzas = flip runReader (RenderEnv packageCabalVersion packageName) $ do library <- maybe (return []) (fmap return . renderLibrary) packageLibrary internalLibraries <- renderInternalLibraries packageInternalLibraries + foreignLibraries <- renderForeignLibraries packageForeignLibraries executables <- renderExecutables packageExecutables tests <- renderTests packageTests benchmarks <- renderBenchmarks packageBenchmarks @@ -111,6 +112,7 @@ renderPackageWith RenderSettings{..} headerFieldsAlignment existingFieldOrder se , map renderFlag packageFlags , library , internalLibraries + , foreignLibraries , executables , tests , benchmarks @@ -175,6 +177,26 @@ renderInternalLibrary :: (String, Section Library) -> RenderM Element renderInternalLibrary (name, sect) = do Stanza ("library " ++ name) <$> renderLibrarySection sect +renderForeignLibraries :: Map String (Section ForeignLibrary) -> RenderM [Element] +renderForeignLibraries = traverse renderForeignLibrary . Map.toList + +renderForeignLibrary :: (String, Section ForeignLibrary) -> RenderM Element +renderForeignLibrary (name, sect) = + Stanza ("foreign-library " ++ name) <$> (renderForeignLibrarySection [] sect') + where + -- Cabal supports 'options: standalone' only and requires it to be set on + -- Windows and to be not set on all other operating systems: + sect' = + sect { sectionConditionals = conditionalOptions : sectionConditionals sect } + conditionalOptions = Conditional { + conditionalCondition = CondExpression "os(windows)" + , conditionalThen = + emptySectionForeignLibrary { sectionData = optionsStandalone } + , conditionalElse = Nothing + } + optionsStandalone = + emptyForeignLibrary { foreignLibraryOptions = Just ["standalone"] } + renderExecutables :: Map String (Section Executable) -> RenderM [Element] renderExecutables = traverse renderExecutable . Map.toList @@ -208,6 +230,19 @@ renderExecutableFields Executable{..} = mainIs ++ [otherModules, generatedModule otherModules = renderOtherModules executableOtherModules generatedModules = renderGeneratedModules executableGeneratedModules +renderForeignLibrarySection :: [Element] -> Section ForeignLibrary -> RenderM [Element] +renderForeignLibrarySection extraFields = renderSection renderForeignLibraryFields extraFields + +renderForeignLibraryFields :: ForeignLibrary -> [Element] +renderForeignLibraryFields ForeignLibrary{..} = + typeField ++ libVersionInfo ++ options ++ [otherModules, generatedModules] + where + typeField = maybe [] (return . Field "type" . Literal) foreignLibraryType + libVersionInfo = maybe [] (return . Field "lib-version-info" . Literal) foreignLibraryLibVersionInfo + options = maybe [] (\opts -> [renderForeignLibOptions opts]) foreignLibraryOptions + otherModules = renderOtherModules foreignLibraryOtherModules + generatedModules = renderGeneratedModules foreignLibraryGeneratedModules + renderCustomSetup :: CustomSetup -> Element renderCustomSetup CustomSetup{..} = Stanza "custom-setup" $ renderDependencies "setup-depends" customSetupDependencies @@ -324,6 +359,9 @@ renderDirectories name = Field name . LineSeparatedList . replaceDots "." -> "./" _ -> xs +renderForeignLibOptions :: [String] -> Element +renderForeignLibOptions = Field "options" . LineSeparatedList + renderExposedModules :: [Module] -> Element renderExposedModules = Field "exposed-modules" . LineSeparatedList . map unModule diff --git a/test/EndToEndSpec.hs b/test/EndToEndSpec.hs index d6121416..c2515360 100644 --- a/test/EndToEndSpec.hs +++ b/test/EndToEndSpec.hs @@ -1822,6 +1822,30 @@ spec = around_ (inTempDirectoryNamed "my-package") $ do windows |] + describe "foreign libraries" $ do + it "Foreign Library stanza with type and options" $ do + [i| + foreign-libraries: + my-library: {} + |] `shouldRenderTo` (foreignLibrary "my-library" [i| + type: native-shared + default-language: Haskell2010 + if os(windows) + options: + standalone + |]) + + it "Foreign Library stanza named after package" $ do + [i| + foreign-library: {} + |] `shouldRenderTo` (foreignLibrary "my-package" [i| + type: native-shared + default-language: Haskell2010 + if os(windows) + options: + standalone + |]) + describe "executables" $ do it "accepts main-is as an alias for main" $ do [i| @@ -2225,6 +2249,14 @@ library #{name} default-language: Haskell2010 |] +foreignLibrary :: String -> String -> Package +foreignLibrary name e = package content + where + content = [i| +foreign-library #{name} +#{indentBy 2 $ unindent e} +|] + executable_ :: String -> String -> Package executable_ name e = package content where