diff --git a/src/Stack/Build/Execute.hs b/src/Stack/Build/Execute.hs index f314e0a499..3cd3c9eb7c 100644 --- a/src/Stack/Build/Execute.hs +++ b/src/Stack/Build/Execute.hs @@ -1199,7 +1199,12 @@ singleBuild runInBase ac@ActionContext {..} ee@ExecuteEnv {..} task@Task {..} in , ["bench" | enableBenchmarks] ] (hasLib, hasExe) = case taskType of - TTFiles lp Local -> (packageHasLibrary (lpPackage lp), not (Set.null (exesToBuild executableBuildStatuses lp))) + TTFiles lp Local -> + let hasLibrary = + case packageLibraries (lpPackage lp) of + NoLibraries -> False + HasLibraries _ -> True + in (hasLibrary, not (Set.null (exesToBuild executableBuildStatuses lp))) -- This isn't true, but we don't want to have this info for -- upstream deps. _ -> (False, False) @@ -1411,7 +1416,11 @@ singleBuild runInBase ac@ActionContext {..} ee@ExecuteEnv {..} task@Task {..} in | opt <- hoAdditionalArgs (boptsHaddockOpts eeBuildOpts) ] ] - let shouldCopy = not isFinalBuild && (packageHasLibrary package || not (Set.null (packageExes package))) + let hasLibrary = + case packageLibraries package of + NoLibraries -> False + HasLibraries _ -> True + shouldCopy = not isFinalBuild && (hasLibrary || not (Set.null (packageExes package))) when shouldCopy $ withMVar eeInstallLock $ \() -> do announce "copy/register" eres <- try $ cabal KeepTHLoading ["copy"] @@ -1419,7 +1428,7 @@ singleBuild runInBase ac@ActionContext {..} ee@ExecuteEnv {..} task@Task {..} in Left err@CabalExitedUnsuccessfully{} -> throwM $ CabalCopyFailed (packageBuildType package == Just C.Simple) (show err) _ -> return () - when (packageHasLibrary package) $ cabal KeepTHLoading ["register"] + when hasLibrary $ cabal KeepTHLoading ["register"] let (installedPkgDb, installedDumpPkgsTVar) = case taskLocation task of @@ -1430,13 +1439,13 @@ singleBuild runInBase ac@ActionContext {..} ee@ExecuteEnv {..} task@Task {..} in ( bcoLocalDB eeBaseConfigOpts , eeLocalDumpPkgs ) let ident = PackageIdentifier (packageName package) (packageVersion package) - mpkgid <- if packageHasLibrary package - then do + mpkgid <- case packageLibraries package of + HasLibraries _ -> do mpkgid <- loadInstalledPkg eeEnvOverride wc [installedPkgDb] installedDumpPkgsTVar (packageName package) case mpkgid of Nothing -> throwM $ Couldn'tFindPkgId $ packageName package Just pkgid -> return $ Library ident pkgid - else do + NoLibraries -> do markExeInstalled (taskLocation task) taskProvides -- TODO unify somehow with writeFlagCache? return $ Executable ident @@ -1825,11 +1834,16 @@ extraBuildOptions wc bopts = do -- Library and executable build components. primaryComponentOptions :: Map Text ExecutableBuildStatus -> LocalPackage -> [String] -primaryComponentOptions executableBuildStatuses lp = ["lib:" ++ packageNameString (packageName (lpPackage lp)) +primaryComponentOptions executableBuildStatuses lp = -- TODO: get this information from target parsing instead, -- which will allow users to turn off library building if -- desired - | packageHasLibrary (lpPackage lp)] ++ + (case packageLibraries (lpPackage lp) of + NoLibraries -> [] + HasLibraries names -> + map T.unpack + $ T.append "lib:" (packageNameText (packageName (lpPackage lp))) + : map (T.append "flib:") (Set.toList names)) ++ map (T.unpack . T.append "exe:") (Set.toList $ exesToBuild executableBuildStatuses lp) -- | History of this function: diff --git a/src/Stack/Build/Source.hs b/src/Stack/Build/Source.hs index a3cc4ea500..6dcd0fb399 100644 --- a/src/Stack/Build/Source.hs +++ b/src/Stack/Build/Source.hs @@ -218,9 +218,14 @@ loadLocalPackage boptsCli targets (name, lpv) = do Nothing -> False -- FIXME: When issue #1406 ("stack 0.1.8 lost ability to -- build individual executables or library") is resolved, - -- 'packageHasLibrary' is only relevant if the library is + -- 'hasLibrary' is only relevant if the library is -- part of the target spec. - Just _ -> packageHasLibrary pkg || not (Set.null allComponents) + Just _ -> + let hasLibrary = + case packageLibraries pkg of + NoLibraries -> False + HasLibraries _ -> True + in hasLibrary || not (Set.null allComponents) filterSkippedComponents = Set.filter (not . (`elem` boptsSkipComponents bopts)) diff --git a/src/Stack/Coverage.hs b/src/Stack/Coverage.hs index d2d9c62a9e..ad355d751d 100644 --- a/src/Stack/Coverage.hs +++ b/src/Stack/Coverage.hs @@ -100,11 +100,15 @@ generateHpcReport pkgDir package tests = do let pkgName = packageNameText (packageName package) pkgId = packageIdentifierString (packageIdentifier package) ghcVersion = getGhcVersion compilerVersion + hasLibrary = + case packageLibraries package of + NoLibraries -> False + HasLibraries _ -> True eincludeName <- -- Pre-7.8 uses plain PKG-version in tix files. if ghcVersion < $(mkVersion "7.10") then return $ Right $ Just pkgId -- We don't expect to find a package key if there is no library. - else if not (packageHasLibrary package) then return $ Right Nothing + else if not hasLibrary then return $ Right Nothing -- Look in the inplace DB for the package key. -- See https://github.com/commercialhaskell/stack/issues/1181#issuecomment-148968986 else do diff --git a/src/Stack/Ghci.hs b/src/Stack/Ghci.hs index 715f2e057b..cbaacfd15a 100644 --- a/src/Stack/Ghci.hs +++ b/src/Stack/Ghci.hs @@ -587,7 +587,9 @@ makeGhciPkgInfo buildOptsCLI sourceMap installedMap locals addPkgs mfileTargets wantedPackageComponents :: BuildOpts -> Target -> Package -> Set NamedComponent wantedPackageComponents _ (TargetComps cs) _ = cs wantedPackageComponents bopts (TargetAll ProjectPackage) pkg = S.fromList $ - (if packageHasLibrary pkg then [CLib] else []) ++ + (case packageLibraries pkg of + NoLibraries -> [] + HasLibraries _names -> CLib : []) ++ -- FIXME. This ignores sub libraries and foreign libraries. Is that OK? map CExe (S.toList (packageExes pkg)) <> (if boptsTests bopts then map CTest (M.keys (packageTests pkg)) else []) <> (if boptsBenchmarks bopts then map CBench (S.toList (packageBenchmarks pkg)) else []) diff --git a/src/Stack/Package.hs b/src/Stack/Package.hs index 50b8db1ab6..f2ce05fe2c 100644 --- a/src/Stack/Package.hs +++ b/src/Stack/Package.hs @@ -67,6 +67,7 @@ import Distribution.System (OS (..), Arch, Platform (..)) import qualified Distribution.Text as D import qualified Distribution.Types.CondTree as Cabal import qualified Distribution.Types.ExeDependency as Cabal +import Distribution.Types.ForeignLib import qualified Distribution.Types.LegacyExeDependency as Cabal import qualified Distribution.Types.UnqualComponentName as Cabal import qualified Distribution.Verbosity as D @@ -249,7 +250,17 @@ packageFromPackageDescription packageConfig pkgFlags (PackageDescriptionPair pkg , packageDefaultFlags = M.fromList [(fromCabalFlagName (flagName flag), flagDefault flag) | flag <- pkgFlags] , packageAllDeps = S.fromList (M.keys deps) - , packageHasLibrary = maybe False (buildable . libBuildInfo) (library pkg) + , packageLibraries = + let mlib = do + lib <- library pkg + guard $ buildable $ libBuildInfo lib + Just lib + in + case mlib of + Nothing + | null extraLibNames -> NoLibraries + | otherwise -> error "Package has buildable sublibraries but no buildable libraries, I'm giving up" + Just _ -> HasLibraries foreignLibNames , packageTests = M.fromList [(T.pack (Cabal.unUnqualComponentName $ testName t), testInterface t) | t <- testSuites pkgNoMod @@ -281,6 +292,21 @@ packageFromPackageDescription packageConfig pkgFlags (PackageDescriptionPair pkg , packageSetupDeps = msetupDeps } where + extraLibNames = S.union subLibNames foreignLibNames + + subLibNames + = S.fromList + $ map (T.pack . Cabal.unUnqualComponentName) + $ mapMaybe libName -- this is a design bug in the Cabal API: this should statically be known to exist + $ filter (buildable . libBuildInfo) + $ subLibraries pkg + + foreignLibNames + = S.fromList + $ map (T.pack . Cabal.unUnqualComponentName . foreignLibName) + $ filter (buildable . foreignLibBuildInfo) + $ foreignLibs pkg + -- Gets all of the modules, files, build files, and data files that -- constitute the package. This is primarily used for dirtiness -- checking during build, as well as use by "stack ghci" @@ -310,7 +336,7 @@ packageFromPackageDescription packageConfig pkgFlags (PackageDescriptionPair pkg return (componentModules, componentFiles, buildFiles <> dataFiles', warnings) pkgId = package pkg name = fromCabalPackageName (pkgName pkgId) - deps = M.filterWithKey (const . (/= name)) (M.union + deps = M.filterWithKey (const . (not . isMe)) (M.union (packageDependencies pkg) -- We include all custom-setup deps - if present - in the -- package deps themselves. Stack always works with the @@ -322,6 +348,10 @@ packageFromPackageDescription packageConfig pkgFlags (PackageDescriptionPair pkg (M.fromList . map (depName &&& depRange) . setupDepends) (setupBuildInfo pkg) + -- Is the package dependency mentioned here me: either the package + -- name itself, or the name of one of the sub libraries + isMe name' = name' == name || packageNameText name' `S.member` extraLibNames + -- | Generate GHC options for the package's components, and a list of -- options which apply generally to the package, not one specific -- component. @@ -582,7 +612,7 @@ packageDescModulesAndFiles => PackageDescription -> m (Map NamedComponent (Set ModuleName), Map NamedComponent (Set DotCabalPath), Set (Path Abs File), [PackageWarning]) packageDescModulesAndFiles pkg = do - (libraryMods,libDotCabalFiles,libWarnings) <- + (libraryMods,libDotCabalFiles,libWarnings) <- -- FIXME add in sub libraries maybe (return (M.empty, M.empty, [])) (asModuleAndFileMap libComponent libraryFiles) @@ -829,7 +859,7 @@ data PackageDescriptionPair = PackageDescriptionPair resolvePackageDescription :: PackageConfig -> GenericPackageDescription -> PackageDescriptionPair -resolvePackageDescription packageConfig (GenericPackageDescription desc defaultFlags mlib _subLibs _foreignLibs exes tests benches) = +resolvePackageDescription packageConfig (GenericPackageDescription desc defaultFlags mlib subLibs foreignLibs' exes tests benches) = PackageDescriptionPair { pdpOrigBuildable = go False , pdpModifiedBuildable = go True @@ -838,6 +868,12 @@ resolvePackageDescription packageConfig (GenericPackageDescription desc defaultF go modBuildable = desc {library = fmap (resolveConditions rc updateLibDeps) mlib + ,subLibraries = + map (\(n, v) -> (resolveConditions rc updateLibDeps v){libName=Just n}) + subLibs + ,foreignLibs = + map (\(n, v) -> (resolveConditions rc updateForeignLibDeps v){foreignLibName=n}) + foreignLibs' ,executables = map (\(n, v) -> (resolveConditions rc updateExeDeps v){exeName=n}) exes @@ -860,6 +896,9 @@ resolvePackageDescription packageConfig (GenericPackageDescription desc defaultF updateLibDeps lib deps = lib {libBuildInfo = (libBuildInfo lib) {targetBuildDepends = deps}} + updateForeignLibDeps lib deps = + lib {foreignLibBuildInfo = + (foreignLibBuildInfo lib) {targetBuildDepends = deps}} updateExeDeps exe deps = exe {buildInfo = (buildInfo exe) {targetBuildDepends = deps}} diff --git a/src/Stack/Types/Package.hs b/src/Stack/Types/Package.hs index 50ec3c6adc..24021ff949 100644 --- a/src/Stack/Types/Package.hs +++ b/src/Stack/Types/Package.hs @@ -69,6 +69,13 @@ instance Show PackageException where , "For more information, see: https://github.com/commercialhaskell/stack/issues/317" ] +-- | Libraries in a package. Since Cabal 2.0, internal libraries are a +-- thing. +data PackageLibraries + = NoLibraries + | HasLibraries !(Set Text) -- ^ the foreign library names, sub libraries get built automatically without explicit component name passing + deriving (Show,Typeable) + -- | Some package info. data Package = Package {packageName :: !PackageName -- ^ Name of the package. @@ -81,7 +88,7 @@ data Package = ,packageGhcOptions :: ![Text] -- ^ Ghc options used on package. ,packageFlags :: !(Map FlagName Bool) -- ^ Flags used on package. ,packageDefaultFlags :: !(Map FlagName Bool) -- ^ Defaults for unspecified flags. - ,packageHasLibrary :: !Bool -- ^ does the package have a buildable library stanza? + ,packageLibraries :: !PackageLibraries -- ^ does the package have a buildable library stanza? ,packageTests :: !(Map Text TestSuiteInterface) -- ^ names and interfaces of test suites ,packageBenchmarks :: !(Set Text) -- ^ names of benchmarks ,packageExes :: !(Set Text) -- ^ names of executables diff --git a/test/integration/tests/internal-libraries/Main.hs b/test/integration/tests/internal-libraries/Main.hs new file mode 100644 index 0000000000..e61b083aa4 --- /dev/null +++ b/test/integration/tests/internal-libraries/Main.hs @@ -0,0 +1,4 @@ +import StackTest + +main :: IO () +main = stack ["build"] diff --git a/test/integration/tests/internal-libraries/files/Setup.hs b/test/integration/tests/internal-libraries/files/Setup.hs new file mode 100644 index 0000000000..9a994af677 --- /dev/null +++ b/test/integration/tests/internal-libraries/files/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/test/integration/tests/internal-libraries/files/files.cabal b/test/integration/tests/internal-libraries/files/files.cabal new file mode 100644 index 0000000000..5b68577677 --- /dev/null +++ b/test/integration/tests/internal-libraries/files/files.cabal @@ -0,0 +1,29 @@ +name: files +version: 0.1.0.0 +build-type: Simple +cabal-version: >=2.0 + +library + hs-source-dirs: src + exposed-modules: Files + build-depends: base + default-language: Haskell2010 + +library foo + hs-source-dirs: src-foo + exposed-modules: Foo + build-depends: base, files, stm + default-language: Haskell2010 + +executable bar + hs-source-dirs: src-bar + main-is: Main.hs + build-depends: base, files, foo + default-language: Haskell2010 + +foreign-library baz + type: native-shared + other-modules: Baz + build-depends: base, files, foo + hs-source-dirs: src-baz + default-language: Haskell2010 diff --git a/test/integration/tests/internal-libraries/files/src-bar/Main.hs b/test/integration/tests/internal-libraries/files/src-bar/Main.hs new file mode 100644 index 0000000000..cc7e42ba70 --- /dev/null +++ b/test/integration/tests/internal-libraries/files/src-bar/Main.hs @@ -0,0 +1,11 @@ +module Main where + +import Files +import Foo + +main :: IO () +main = do + putStrLn "files:" + print files + putStrLn "foo" + foo >>= print diff --git a/test/integration/tests/internal-libraries/files/src-baz/Baz.hs b/test/integration/tests/internal-libraries/files/src-baz/Baz.hs new file mode 100644 index 0000000000..79e71841fb --- /dev/null +++ b/test/integration/tests/internal-libraries/files/src-baz/Baz.hs @@ -0,0 +1,11 @@ +module Baz where + +import Files +import Foo + +baz :: IO () +baz = do + putStrLn "files:" + print files + putStrLn "foo" + foo >>= print diff --git a/test/integration/tests/internal-libraries/files/src-foo/Foo.hs b/test/integration/tests/internal-libraries/files/src-foo/Foo.hs new file mode 100644 index 0000000000..9a9b8e2205 --- /dev/null +++ b/test/integration/tests/internal-libraries/files/src-foo/Foo.hs @@ -0,0 +1,7 @@ +module Foo where + +import Control.Monad.STM +import Files + +foo :: IO String +foo = atomically $ return $ "foo using " ++ files diff --git a/test/integration/tests/internal-libraries/files/src/Files.hs b/test/integration/tests/internal-libraries/files/src/Files.hs new file mode 100644 index 0000000000..b9c9eeac03 --- /dev/null +++ b/test/integration/tests/internal-libraries/files/src/Files.hs @@ -0,0 +1,4 @@ +module Files where + +files :: String +files = "files" diff --git a/test/integration/tests/internal-libraries/files/stack.yaml b/test/integration/tests/internal-libraries/files/stack.yaml new file mode 100644 index 0000000000..31fffa4ece --- /dev/null +++ b/test/integration/tests/internal-libraries/files/stack.yaml @@ -0,0 +1,3 @@ +resolver: ghc-8.2.1 +extra-deps: +- stm-2.4.4.1