stackage2nix 0.3.0 → 0.4.0
raw patch · 15 files changed
+371/−169 lines, 15 filesdep ~inflectionsnew-uploader
Dependency ranges changed: inflections
Files
- CHANGELOG.md +6/−0
- README.md +52/−4
- src/Distribution/Nixpkgs/Haskell/FromStack.hs +19/−12
- src/Distribution/Nixpkgs/Haskell/FromStack/Package.hs +32/−11
- src/Distribution/Nixpkgs/Haskell/Stack.hs +30/−17
- src/Distribution/Nixpkgs/Haskell/Stack/PrettyPrinting.hs +37/−12
- src/Language/Nix/FilePath.hs +6/−6
- src/LtsHaskell.hs +16/−10
- src/Runner.hs +87/−55
- src/Runner/Cli.hs +37/−21
- src/Stack/Config.hs +28/−14
- src/Stack/Config/Yaml.hs +1/−0
- src/Stack/Types.hs +1/−0
- stackage2nix.cabal +4/−4
- test/Stack/Config/YamlSpec.hs +15/−3
CHANGELOG.md view
@@ -2,3 +2,9 @@ - Initial Hackage release - Add CHANGELOG.md++# 0.4.0+- Add `--resolver` option to generate full stackage packages set+- Add Nix wrapper, see `nix/README.md`+- Upd support cabal2nix >2.5+- Fix honor `--hackage-db` flag
README.md view
@@ -5,7 +5,47 @@ `stackage2nix` converts a Stack file into a Nix Haskell packages set. It creates LTS Stackage packages set, and applies appropriate overrides on top of it. +## Install++You can install `stackage2nix` from the Nix expression:+ ```+nix-env -i -f ./nix/stackage2nix+```++It provides pre-configured wrapper around the raw executable with runtime+`PATH` and all auxiliary flags set up.++## Build project++Generate derivations from `stack.yaml` config using Nix wrapper:++``` bash+stackage2nix ./stack.yaml+```++if you're using the raw executable, you should supply additional flags. See+section 'Flags' below for details.++This command will result in a Haskell packages set, similar to+`pkgs.haskell.packages.<compiler>`, containing only packages that are required+to build targets listed in `stackage.yaml`. To build a package run:++``` bash+nix-build -A my-package+```++## Build stackage++Generate complete Stackage packages set from resolver:++``` bash+stackage2nix --resolver lts-9.0+```++## Flags++``` stackage2nix \ --lts-haskell "$LTS_HASKELL_REPO" \ --all-cabal-hashes "$ALL_CABAL_HASHES_REPO" \@@ -28,12 +68,15 @@ nix-build -A <package-name> ``` +See also the [blog post](https://blog.typeable.io/posts/2017-08-24-stackage2nix.html)+about history and motivation behind the project.+ ## Runtime dependencies - `nix-env` is required to be on PATH by the [distribution-nixpkgs](https://hackage.haskell.org/package/distribution-nixpkgs) dependency-- `nix-prefetch-git` is required on PATH if you have git dependencies in+- `nix-prefetch-scripts` is required on PATH if you have git dependencies in `stack.yaml` ## Override result derivation@@ -59,8 +102,13 @@ For more complex overrides and detailed information on how to work with Haskell packages in Nix, see Nixpkgs manual [User’s Guide to the Haskell Infrastructure](http://nixos.org/nixpkgs/manual/#users-guide-to-the-haskell-infrastructure) +## Tests -## Examples+Integration tests that build stackage2nix form different stack configs: -For other examples of `stackage2nix` usage, see [4e6/stackage2nix-examples](https://github.com/4e6/stackage2nix-examples) repository.-It verifies `stackage2nix` by running it on different public projects.+```+STACKAGE_REPO=<path/to/stackage/repo> \+ALL_CABAL_HASHES=<path/to/all-cabal-hashes/repo> \+STACK_FILE=stack-ghc-7103.yaml \+./ci-stackage2nix+```
src/Distribution/Nixpkgs/Haskell/FromStack.hs view
@@ -26,19 +26,22 @@ , targetCompiler :: CompilerInfo } data PackageConfig = PackageConfig- { enableCheck :: Bool- , enableHaddock :: Bool }+ { enableCheck :: Bool+ , enableHaddock :: Bool } removeTests :: GenericPackageDescription -> GenericPackageDescription removeTests gd = gd { condTestSuites = [] } +removeBenches :: GenericPackageDescription -> GenericPackageDescription+removeBenches gd = gd { condBenchmarks = [] }+ planDependencies :: PackagePlan -> [Dependency] planDependencies = map makeDependency . Map.toList . sdPackages . ppDesc where makeDependency (name, depInfo) = Dependency name (diRange depInfo) -buildNodeM :: PackageSetConfig -> PackageConfig -> PackageName -> PackagePlan -> IO Node-buildNodeM conf pconf name plan = do+buildNode :: PackageSetConfig -> PackageConfig -> PackageName -> PackagePlan -> IO Node+buildNode conf pconf name plan = do let cabalHashes = maybe mempty cfiHashes $ ppCabalFileInfo plan mGitSha1 = Map.lookup "GitSHA1" cabalHashes@@ -50,15 +53,16 @@ let constraints = ppConstraints plan testsEnabled =- enableCheck pconf && pcTests constraints == ExpectSuccess haddocksEnabled =- enableHaddock pconf && pcHaddocks constraints == ExpectSuccess && not (Set.null (sdModules (ppDesc plan))) configureTests | pcTests constraints == Don'tBuild = removeTests | otherwise = id+ configureBenches+ | pcBenches constraints == Don'tBuild = removeBenches+ | otherwise = id flags = Map.toList (pcFlagOverrides constraints) (descr, missingDeps) = finalizeGenericPackageDescription (haskellResolver conf)@@ -66,7 +70,7 @@ (targetCompiler conf) flags (planDependencies plan)- (configureTests (pkgCabal pkg))+ (configureBenches . configureTests $ pkgCabal pkg) genericDrv = fromPackageDescription (haskellResolver conf) (nixpkgsResolver conf)@@ -83,11 +87,14 @@ testsEnabled && not (null missingDeps) && setOf (folded . to depName) missingDeps `hasIntersection` testDeps descr- in- genericDrv- & src .~ pkgSource pkg+ in finalizePackage pkg pconf+ $ genericDrv & doCheck .~ testsEnabled & runHaddock .~ haddocksEnabled & metaSection . Nix.broken .~ brokenEnabled- -- TODO: remove after Nixos/Nixpkgs #27196 released- & enableSeparateDataOutput .~ False++finalizePackage :: Package -> PackageConfig -> Derivation -> Derivation+finalizePackage pkg pconf drv = drv+ & src .~ pkgSource pkg+ & doCheck &&~ enableCheck pconf+ & runHaddock &&~ enableHaddock pconf
src/Distribution/Nixpkgs/Haskell/FromStack/Package.hs view
@@ -1,8 +1,9 @@ module Distribution.Nixpkgs.Haskell.FromStack.Package where import Control.Lens-import Data.Graph (Graph, Vertex) import Data.Foldable as F+import Data.Function (on)+import Data.Graph (Graph, Vertex) import Data.Maybe import Data.Monoid import Data.Ord as O@@ -21,12 +22,21 @@ import qualified Data.Set as Set data Node = Node- { _nodeDerivation :: Derivation- , _nodeTestDepends :: Set.Set String- , _nodeOtherDepends :: Set.Set String }+ { _nodeDerivation :: Derivation+ , _nodeTestDepends :: Set.Set String+ , _nodeBenchmarkDepends :: Set.Set String+ , _nodeExecutableDepends :: Set.Set String+ , _nodeSetupDepends :: Set.Set String+ , _nodeOtherDepends :: Set.Set String } makeLenses ''Node +instance Eq Node where+ (==) = (==) `on` nodeName++instance Ord Node where+ compare = compare `on` nodeName+ mkNode :: Derivation -> Node mkNode _nodeDerivation = Node{..} where@@ -34,17 +44,27 @@ . Set.filter isFromHackage $ view (s . (haskell <> tool)) _nodeDerivation _nodeTestDepends = haskellDependencies testDepends+ _nodeBenchmarkDepends = haskellDependencies benchmarkDepends+ _nodeExecutableDepends = haskellDependencies executableDepends+ _nodeSetupDepends = haskellDependencies setupDepends _nodeOtherDepends = haskellDependencies (executableDepends <> libraryDepends) nodeName :: Node -> String nodeName = unPackageName . packageName . view (nodeDerivation . pkgid) +nodeCycleDepends :: Node -> Set.Set String+nodeCycleDepends = _nodeTestDepends <> _nodeOtherDepends+ nodeDepends :: Node -> Set.Set String-nodeDepends = _nodeTestDepends <> _nodeOtherDepends+nodeDepends = _nodeTestDepends+ <> _nodeOtherDepends+ <> _nodeBenchmarkDepends+ <> _nodeExecutableDepends+ <> _nodeSetupDepends findCycles :: [Node] -> [[Node]] findCycles nodes = mapMaybe cyclic $- Graph.stronglyConnComp [(node, nodeName node, Set.toList $ nodeDepends node) | node <- nodes]+ Graph.stronglyConnComp [(node, nodeName node, Set.toList $ nodeCycleDepends node) | node <- nodes] where cyclic (Graph.AcyclicSCC _) = Nothing cyclic (Graph.CyclicSCC c) = Just c@@ -68,12 +88,13 @@ buildNodeGraph nodes = Graph.graphFromEdges [(node, nodeName node, Set.toList $ nodeDepends node) | node <- nodes] -reachableDependencies :: [Node] -> [Node] -> [Node]-reachableDependencies keyNodes nodes = view _1 . fromVertex <$> reachableVertices+reachableDependencies :: [Node] -> [Node] -> Set.Set Node+reachableDependencies keyNodes nodes = Set.map (view _1 . fromVertex) reachableVerticesS where (graph, fromVertex, fromKey) = buildNodeGraph nodes keys = mapMaybe (fromKey . nodeName) keyNodes- reachableVertices = concatMap F.toList $ Graph.dfs graph keys+ reachableVerticesS = F.foldr1 Set.union+ $ (Set.fromList . F.toList) <$> Graph.dfs graph keys -- pretty printing @@ -85,9 +106,9 @@ pPrintOutConfig :: SystemInfo -> [Node] -> Doc pPrintOutConfig systemInfo nodes = vcat- [ "{ pkgs }:"+ [ "{ pkgs, haskellLib }:" , ""- , "with pkgs.haskell.lib; self: super: {"+ , "with haskellLib; self: super: {" , "" , " # core packages" , nest 2 $ vcat $
src/Distribution/Nixpkgs/Haskell/Stack.hs view
@@ -1,11 +1,14 @@+{-# LANGUAGE CPP #-} module Distribution.Nixpkgs.Haskell.Stack where import Control.Lens+import Data.Maybe (fromMaybe) import Data.Text as T import Distribution.Compiler as Compiler import Distribution.Nixpkgs.Fetch import Distribution.Nixpkgs.Haskell.Derivation import Distribution.Nixpkgs.Haskell.FromCabal as FromCabal+import Distribution.Nixpkgs.Haskell.FromStack as FromStack import Distribution.Nixpkgs.Haskell.PackageSourceSpec as PackageSourceSpec import Distribution.PackageDescription as PackageDescription import Distribution.System as System@@ -40,32 +43,46 @@ -> StackPackage -> IO Package getStackPackageFromDb optHackageDb stackPackage =+#if MIN_VERSION_cabal2nix(2,6,0) PackageSourceSpec.getPackage+ False (unHackageDb <$> optHackageDb)- (stackLocationToSource $ stackPackage ^. spLocation)+ Nothing+ (stackLocationToSource (stackPackage ^. spLocation) (stackPackage ^. spDir))+#else+ PackageSourceSpec.getPackage+ (unHackageDb <$> optHackageDb)+ (stackLocationToSource (stackPackage ^. spLocation) (stackPackage ^. spDir))+#endif -stackLocationToSource :: PackageLocation -> Source-stackLocationToSource = \case+stackLocationToSource+ :: PackageLocation+ -- ^ Subdirectory in the package containing cabal file.+ -> Maybe FilePath+ -> Source+stackLocationToSource pl mCabalDir = case pl of HackagePackage p -> Source { sourceUrl = "cabal://" ++ T.unpack p , sourceRevision = mempty , sourceHash = UnknownHash- , sourceCabalDir = mempty }+ , sourceCabalDir = cabalDir } StackFilePath p -> Source { sourceUrl = p , sourceRevision = mempty , sourceHash = UnknownHash- , sourceCabalDir = mempty }+ , sourceCabalDir = cabalDir } StackUri uri -> Source { sourceUrl = URI.uriToString id uri mempty , sourceRevision = mempty , sourceHash = UnknownHash- , sourceCabalDir = mempty }+ , sourceCabalDir = cabalDir } StackRepo r -> Source { sourceUrl = T.unpack $ r ^. rUri , sourceRevision = T.unpack $ r ^. rCommit , sourceHash = UnknownHash- , sourceCabalDir = mempty }+ , sourceCabalDir = cabalDir }+ where+ cabalDir = fromMaybe mempty mCabalDir packageDerivation :: StackPackagesConfig@@ -76,16 +93,12 @@ pkg <- getStackPackageFromDb optHackageDb stackPackage let drv = genericPackageDerivation conf pkg- & src .~ pkgSource pkg- -- TODO: remove after Nixos/Nixpkgs #27196 released- & enableSeparateDataOutput .~ False- return $ if stackPackage ^. spExtraDep- then drv- & doCheck &&~ conf ^. spcDoCheckStackage- & runHaddock &&~ conf ^. spcDoHaddockStackage- else drv- & doCheck &&~ conf ^. spcDoCheckPackages- & runHaddock &&~ conf ^. spcDoHaddockPackages+ & subpath %~ flip fromMaybe (stackPackage ^. spDir)+ isExtraDep = stackPackage ^. spExtraDep+ pconf = PackageConfig+ { enableCheck = if isExtraDep then conf ^. spcDoCheckStackage else conf ^. spcDoCheckPackages+ , enableHaddock = if isExtraDep then conf ^. spcDoHaddockStackage else conf ^. spcDoHaddockPackages }+ return $ finalizePackage pkg pconf drv genericPackageDerivation :: StackPackagesConfig
src/Distribution/Nixpkgs/Haskell/Stack/PrettyPrinting.hs view
@@ -42,15 +42,11 @@ importStackagePackages :: FilePath -> Doc importStackagePackages path = hsep- [ funarg "self", "import", disp (fromString path :: Nix.FilePath), "{"- , "inherit pkgs stdenv;"- , "inherit (self) callPackage;"- , "}"- ]+ ["import", disp (fromString path :: Nix.FilePath)] -callStackageConfig :: FilePath -> Doc-callStackageConfig path = hsep- [ "callPackage", disp (fromString path :: Nix.FilePath), "{}"]+importStackageConfig :: FilePath -> Doc+importStackageConfig path = hsep+ ["import ", disp (fromString path :: Nix.FilePath), "{ inherit pkgs haskellLib; }"] overrideHaskellPackages :: OverrideConfig -> NonEmpty Derivation -> Doc overrideHaskellPackages oc packages =@@ -67,8 +63,9 @@ , "let" , nest 2 "inherit (stdenv.lib) extends;" , nest 2 $ vcat- [ attr "stackagePackages" . importStackagePackages $ oc ^. ocStackagePackages- , attr "stackageConfig" . callStackageConfig $ oc ^. ocStackageConfig ]+ [ attr "haskellLib" "callPackage (nixpkgs.path + \"/pkgs/development/haskell-modules/lib.nix\") {}"+ , attr "stackagePackages" . importStackagePackages $ oc ^. ocStackagePackages+ , attr "stackageConfig" . importStackageConfig $ oc ^. ocStackageConfig ] , nest 2 $ vcat [ "stackPackages =" , nest 2 $ overridePackages packages <> semi@@ -82,8 +79,36 @@ , "in callPackage (nixpkgs.path + \"/pkgs/development/haskell-modules\") {" , nest 2 $ vcat [ attr "ghc" ("pkgs.haskell.compiler." <> toNixGhcVersion (oc ^. ocGhc))- , attr "compilerConfig" "self: extends pkgOverrides (extends stackageConfig (stackagePackages self))"- , attr "haskellLib" "callPackage (nixpkgs.path + \"/pkgs/development/haskell-modules/lib.nix\") {}"+ , attr "compilerConfig" "self: extends pkgOverrides (stackageConfig self)"+ , attr "initialPackages" "stackagePackages"+ , attr "configurationCommon" "args: self: super: {}"+ , "inherit haskellLib;"+ ]+ , "}"+ ]++pPrintHaskellPackages :: OverrideConfig -> Doc+pPrintHaskellPackages oc =+ let+ nixpkgs = if oc ^. ocNixpkgs . to fromString == systemNixpkgs+ then systemNixpkgs+ else (disp . (fromString :: FilePath -> Nix.FilePath)) (oc ^. ocNixpkgs)+ in vcat+ [ funargs+ [ "nixpkgs ? import " <> nixpkgs <> " {}"+ ]+ , ""+ , "with nixpkgs; let"+ , nest 2 $ vcat+ [ attr "haskellLib" "callPackage (nixpkgs.path + /pkgs/development/haskell-modules/lib.nix) {}"+ ]+ , "in callPackage (nixpkgs.path + /pkgs/development/haskell-modules) {"+ , nest 2 $ vcat+ [ attr "ghc" ("pkgs.haskell.compiler." <> toNixGhcVersion (oc ^. ocGhc))+ , attr "compilerConfig" . importStackageConfig $ oc ^. ocStackageConfig+ , attr "initialPackages" . importStackagePackages $ oc ^. ocStackagePackages+ , attr "configurationCommon" "if builtins.pathExists ./configuration-common.nix then import ./configuration-common.nix else args: self: super: {}"+ , "inherit haskellLib;" ] , "}" ]
src/Language/Nix/FilePath.hs view
@@ -11,7 +11,7 @@ import Distribution.Text import GHC.Generics (Generic) import Prelude hiding (FilePath)-import qualified System.FilePath.Posix as Posix+import qualified System.FilePath as FP import Test.QuickCheck as QC import Text.PrettyPrint as PP @@ -25,7 +25,7 @@ rnf = rnf . unFilePath instance Arbitrary FilePath where- arbitrary = FilePath <$> QC.suchThat arbitrary Posix.isValid+ arbitrary = FilePath <$> QC.suchThat arbitrary FP.isValid shrink = fmap FilePath . shrink . unFilePath instance Text FilePath where@@ -38,12 +38,12 @@ parseFilePath :: ReadP r FilePath parseFilePath = do path <- ReadP.munch (const True)- if Posix.isValid path+ if FP.isValid path then pure (FilePath path) else pfail renderFilePath :: FilePath -> String-renderFilePath = Posix.combine "."- . Posix.normalise- . Posix.dropTrailingPathSeparator+renderFilePath = FP.combine "."+ . FP.normalise+ . FP.dropTrailingPathSeparator . unFilePath
src/LtsHaskell.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} module LtsHaskell where import AllCabalHashes@@ -46,17 +47,21 @@ (error (display pkgId ++ ": meta data has no SHA256 hash for the tarball")) (view (mHashes . at "SHA256") meta) source = DerivationSource "url" ("mirror://hackage/" ++ display pkgId ++ ".tar.gz") "" tarballSHA256+#if MIN_VERSION_cabal2nix(2,6,0)+ return $ Package source False pkgDesc+#else return $ Package source pkgDesc+#endif -getPackageFromDb :: PackageIdentifier -> IO Package-getPackageFromDb pkgId =- getStackPackageFromDb Nothing- $ StackPackage (HackagePackage (T.pack $ Text.display pkgId)) True+getPackageFromDb :: Maybe HackageDb -> PackageIdentifier -> IO Package+getPackageFromDb mHackageDb pkgId =+ getStackPackageFromDb mHackageDb+ $ StackPackage (HackagePackage (T.pack $ Text.display pkgId)) True Nothing -loadPackage :: FilePath -> Maybe SHA1Hash -> PackageIdentifier -> IO Package-loadPackage allCabalHashesPath mSha1Hash pkgId =+loadPackage :: Maybe HackageDb -> FilePath -> Maybe SHA1Hash -> PackageIdentifier -> IO Package+loadPackage mHackageDb allCabalHashesPath mSha1Hash pkgId = getPackageFromRepo allCabalHashesPath mSha1Hash pkgId- `catchIOError` const (getPackageFromDb pkgId)+ `catchIOError` const (getPackageFromDb mHackageDb pkgId) ghcCompilerInfo :: Version -> CompilerInfo ghcCompilerInfo v = CompilerInfo@@ -72,17 +77,18 @@ maybe False (`withinRange` versionRange) $ Map.lookup depName packageVersions buildPackageSetConfig- :: FilePath+ :: Maybe HackageDb -> FilePath+ -> FilePath -> BuildPlan -> IO PackageSetConfig-buildPackageSetConfig optAllCabalHashes optNixpkgsRepository buildPlan = do+buildPackageSetConfig mHackageDb optAllCabalHashes optNixpkgsRepository buildPlan = do nixpkgs <- readNixpkgPackageMap optNixpkgsRepository Nothing let systemInfo = bpSystemInfo buildPlan packageVersions = fmap ppVersion (bpPackages buildPlan) `Map.union` siCorePackages systemInfo return PackageSetConfig- { packageLoader = loadPackage optAllCabalHashes+ { packageLoader = loadPackage mHackageDb optAllCabalHashes , targetPlatform = Platform (siArch systemInfo) (siOS systemInfo) , targetCompiler = ghcCompilerInfo (siGhcVersion systemInfo) , nixpkgsResolver = resolve (Map.map (Set.map (over path ("pkgs":))) nixpkgs)
src/Runner.hs view
@@ -2,7 +2,7 @@ import Control.Lens import Data.Foldable as F-import Data.Functor+import Data.List as L import Distribution.Nixpkgs.Haskell.FromStack import Distribution.Nixpkgs.Haskell.FromStack.Package import Distribution.Nixpkgs.Haskell.Stack@@ -18,8 +18,8 @@ import Stack.Config import Stack.Types import Stackage.Types-import System.IO (withFile, IOMode(..), hPutStrLn, hPrint)-import Text.PrettyPrint.HughesPJClass (render)+import System.IO (withFile, IOMode(..), hPutStrLn)+import Text.PrettyPrint.HughesPJClass (Doc, render) import qualified Data.Set as Set import qualified Data.Map as Map@@ -29,60 +29,92 @@ run :: IO () run = do opts <- execParser pinfo- stackYaml <- envStackYaml >>= \case- Just p -> putStrLn "Getting project config file from STACK_YAML environment" $> p- Nothing -> pure $ opts ^. optStackYaml- stackConf <- either fail pure =<< readStackConfig stackYaml- let buildPlanFile = LH.buildPlanFilePath (opts ^. optLtsHaskellRepo) (stackConf ^. scResolver)- buildPlan <- LH.loadBuildPlan buildPlanFile- packageSetConfig <- LH.buildPackageSetConfig- (opts ^. optAllCabalHashesRepo)- (opts ^. optNixpkgsRepository)- buildPlan- -- generate haskell packages override- let- overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)- stackPackagesConfig = mkStackPackagesConfig opts- packages <- traverse (packageDerivation stackPackagesConfig (opts ^. optHackageDb))- $ stackConf ^. scPackages- let out = PP.overrideHaskellPackages overrideConfig packages- withFile (opts ^. optOutDerivation) WriteMode $ \h -> do- hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ stackYaml ^. syFilePath)- hPrint h out- let- reachable = Set.map mkPackageName- $ F.foldr1 Set.union- $ nodeDepends . mkNode <$> packages- s2nLoader mHash pkgId =- if pkgName pkgId `Set.member` reachable- then packageLoader packageSetConfig Nothing pkgId- else packageLoader packageSetConfig mHash pkgId- s2nPackageSetConfig = packageSetConfig { packageLoader = s2nLoader }- s2nPackageConfig = PackageConfig- { enableCheck = opts ^. optDoCheckStackage- , enableHaddock = opts ^. optDoHaddockStackage }- allNodes <- traverse (uncurry (buildNodeM s2nPackageSetConfig s2nPackageConfig))- $ Map.toList (bpPackages buildPlan)+ case opts ^. optConfigOrigin of+ -- Generate build derivation from stack.yaml file+ OriginStackYaml stackYaml -> do+ stackConf <- either fail pure =<< readStackConfig stackYaml+ let buildPlanFile = LH.buildPlanFilePath (opts ^. optLtsHaskellRepo) (stackConf ^. scResolver)+ buildPlan <- LH.loadBuildPlan buildPlanFile+ packageSetConfig <- LH.buildPackageSetConfig+ (opts ^. optHackageDb)+ (opts ^. optAllCabalHashesRepo)+ (opts ^. optNixpkgsRepository)+ buildPlan+ let+ overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)+ stackPackagesConfig = mkStackPackagesConfig opts+ stackConfPackages <- traverse (packageDerivation stackPackagesConfig (opts ^. optHackageDb))+ $ stackConf ^. scPackages+ let+ reachable = Set.map mkPackageName+ $ F.foldr1 Set.union+ $ nodeDepends . mkNode <$> stackConfPackages+ s2nLoader mHash pkgId =+ if pkgName pkgId `Set.member` reachable+ then packageLoader packageSetConfig Nothing pkgId+ else packageLoader packageSetConfig mHash pkgId+ s2nPackageSetConfig = packageSetConfig { packageLoader = s2nLoader }+ s2nPackageConfig = PackageConfig+ { enableCheck = opts ^. optDoCheckStackage+ , enableHaddock = opts ^. optDoHaddockStackage }+ stackagePackages <- traverse (uncurry (buildNode s2nPackageSetConfig s2nPackageConfig))+ $ Map.toAscList (bpPackages buildPlan) - -- Find all reachable dependencies in stackage set to stick into- -- stackage packages file. This is performed on the full stackage- -- set rather than pruning stackage packages beforehand because- -- stackage does not concern itself with build tools while cabal2nix- -- does: pruning only after generating full set of packages allows- -- us to make sure all those extra dependencies are explicitly- -- listed as well.- let nodes = case opts ^. optOutPackagesClosure of- True -> flip reachableDependencies allNodes- -- Originally reachable nodes are root nodes- $ filter (\n -> mkPackageName (nodeName n) `Set.member` reachable) allNodes- False -> allNodes+ let+ -- Nixpkgs generic-builder puts hscolour on path for all libraries+ withHscolour pkgs =+ let hscolour = F.find ((== "hscolour") . nodeName) stackagePackages+ in maybe pkgs (`Set.insert` pkgs) hscolour+ -- Find all reachable dependencies in stackage set to stick into+ -- stackage packages file. This is performed on the full stackage+ -- set rather than pruning stackage packages beforehand because+ -- stackage does not concern itself with build tools while cabal2nix+ -- does: pruning only after generating full set of packages allows+ -- us to make sure all those extra dependencies are explicitly+ -- listed as well.+ nodes = case opts ^. optOutPackagesClosure of+ True -> Set.toAscList+ $ withHscolour+ $ flip reachableDependencies stackagePackages+ -- Originally reachable nodes are root nodes+ $ L.filter (\n -> mkPackageName (nodeName n) `Set.member` reachable) stackagePackages+ False -> stackagePackages+ writeOutFile buildPlanFile (opts ^. optOutStackagePackages)+ $ pPrintOutPackages (view nodeDerivation <$> nodes)+ writeOutFile buildPlanFile (opts ^. optOutStackageConfig)+ $ pPrintOutConfig (bpSystemInfo buildPlan) nodes+ writeOutFile (stackYaml ^. syFilePath) (opts ^. optOutDerivation)+ $ PP.overrideHaskellPackages overrideConfig stackConfPackages - withFile (opts ^. optOutStackagePackages) WriteMode $ \h -> do- hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ buildPlanFile)- hPutStrLn h $ render $ pPrintOutPackages (view nodeDerivation <$> nodes)- withFile (opts ^. optOutStackageConfig) WriteMode $ \h -> do- hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ buildPlanFile)- hPutStrLn h $ render $ pPrintOutConfig (bpSystemInfo buildPlan) nodes+ -- Generate Stackage packages from resolver+ OriginResolver stackResolver -> do+ let+ buildPlanFile = LH.buildPlanFilePath (opts ^. optLtsHaskellRepo) stackResolver+ packageConfig = PackageConfig+ { enableCheck = True+ , enableHaddock = True }+ buildPlan <- LH.loadBuildPlan buildPlanFile+ packageSetConfig <- LH.buildPackageSetConfig+ (opts ^. optHackageDb)+ (opts ^. optAllCabalHashesRepo)+ (opts ^. optNixpkgsRepository)+ buildPlan+ nodes <- traverse (uncurry (buildNode packageSetConfig packageConfig))+ $ Map.toAscList (bpPackages buildPlan)+ let overrideConfig = mkOverrideConfig opts (siGhcVersion $ bpSystemInfo buildPlan)++ writeOutFile buildPlanFile (opts ^. optOutStackagePackages)+ $ pPrintOutPackages (view nodeDerivation <$> nodes)+ writeOutFile buildPlanFile (opts ^. optOutStackageConfig)+ $ pPrintOutConfig (bpSystemInfo buildPlan) nodes+ writeOutFile buildPlanFile (opts ^. optOutDerivation)+ $ PP.pPrintHaskellPackages overrideConfig++writeOutFile :: Show source => source -> FilePath -> Doc -> IO ()+writeOutFile source filePath contents =+ withFile filePath WriteMode $ \h -> do+ hPutStrLn h ("# Generated by stackage2nix " ++ Text.display version ++ " from " ++ show source)+ hPutStrLn h $ render contents mkOverrideConfig :: Options -> Version -> OverrideConfig mkOverrideConfig opts ghcVersion = OverrideConfig
src/Runner/Cli.hs view
@@ -10,27 +10,37 @@ import qualified Distribution.Text as Text import Options.Applicative as Opts import Paths_stackage2nix ( version )+import Stack.Config import Stack.Types import System.Environment import System.FilePath +-- | Source that would be used to produce target derivation+data ConfigOrigin+ = OriginResolver StackResolver+ -- ^ Resolver to generate Stackage LTS+ | OriginStackYaml StackYaml+ -- ^ Stack config to generate build derivation+ deriving (Show) +makePrisms ''ConfigOrigin+ data Options = Options- { _optAllCabalHashesRepo :: !FilePath- , _optLtsHaskellRepo :: !FilePath- , _optOutStackagePackages :: !FilePath- , _optOutStackageConfig :: !FilePath- , _optOutPackagesClosure :: !Bool- , _optOutDerivation :: !FilePath- , _optDoCheckPackages :: !Bool- , _optDoHaddockPackages :: !Bool- , _optDoCheckStackage :: !Bool- , _optDoHaddockStackage :: !Bool- , _optHackageDb :: !(Maybe HackageDb)- , _optNixpkgsRepository :: !FilePath- , _optCompilerId :: !CompilerId- , _optPlatform :: !Platform- , _optStackYamlArg :: !FilePath+ { _optAllCabalHashesRepo :: FilePath+ , _optLtsHaskellRepo :: FilePath+ , _optOutStackagePackages :: FilePath+ , _optOutStackageConfig :: FilePath+ , _optOutPackagesClosure :: Bool+ , _optOutDerivation :: FilePath+ , _optDoCheckPackages :: Bool+ , _optDoHaddockPackages :: Bool+ , _optDoCheckStackage :: Bool+ , _optDoHaddockStackage :: Bool+ , _optHackageDb :: (Maybe HackageDb)+ , _optNixpkgsRepository :: FilePath+ , _optCompilerId :: CompilerId+ , _optPlatform :: Platform+ , _optConfigOrigin :: ConfigOrigin } deriving (Show) makeLenses ''Options@@ -38,9 +48,6 @@ envStackYaml :: IO (Maybe StackYaml) envStackYaml = fmap mkStackYaml <$> lookupEnv "STACK_YAML" -optStackYaml :: Getter Options StackYaml-optStackYaml = optStackYamlArg . to mkStackYaml- mkStackYaml :: FilePath -> StackYaml mkStackYaml p = case splitFileName p of (dir, "") -> StackYaml dir "stack.yaml"@@ -64,7 +71,7 @@ <*> nixpkgsRepository <*> compilerId <*> platform- <*> stackYamlArg+ <*> configOrigin pinfo :: ParserInfo Options pinfo = info@@ -143,10 +150,19 @@ ( long "do-haddock-stackage" <> help "enable haddock for Stackage packages" ) -stackYamlArg :: Parser FilePath-stackYamlArg = Opts.argument str+resolver :: Parser StackResolver+resolver = StackResolver <$> option text+ ( long "resolver"+ <> help "stackage LTS resolver" )++stackYamlArg :: Parser StackYaml+stackYamlArg = mkStackYaml <$> Opts.argument str ( metavar "STACK_YAML" <> help "path to stack.yaml file or directory" )++configOrigin :: Parser ConfigOrigin+configOrigin = OriginResolver <$> resolver+ <|> OriginStackYaml <$> stackYamlArg -- inherited from cabal2nix
src/Stack/Config.hs view
@@ -9,6 +9,7 @@ import Data.Foldable as F import Data.List.NonEmpty as NE import Data.Maybe+import Data.Semigroup (sconcat) import Data.Text as T import Data.Yaml import Network.URI@@ -48,6 +49,8 @@ data StackPackage = StackPackage { _spLocation :: !PackageLocation , _spExtraDep :: !Bool+ -- | Subdirectory containing the cabal file if any specified.+ , _spDir :: !(Maybe FilePath) } deriving (Eq, Ord, Show) makeLenses ''StackPackage@@ -62,28 +65,39 @@ fromYamlConfig :: Yaml.Config -> StackConfig fromYamlConfig c = StackConfig{..} where- _scResolver = coerce $ c ^. cResolver- _scPackages = F.foldr (NE.<|) neYamlPackages yamlExtraDeps- neYamlPackages = fromMaybe (pure defaultPackage) $ NE.nonEmpty yamlPackages- yamlPackages = fromYamlPackage <$> fromMaybe mempty (c ^. cPackages)+ _scResolver = coerce $ c ^. cResolver+ _scPackages = F.foldr (NE.<|) neYamlPackages yamlExtraDeps+ neYamlPackages = fromMaybe (pure defaultPackage)+ . fmap sconcat $ NE.nonEmpty yamlPackages+ yamlPackages = fromYamlPackage <$> (fromMaybe mempty (c ^. cPackages)) yamlExtraDeps = fromYamlExtraDep <$> fromMaybe mempty (c ^. cExtraDeps)- defaultPackage = StackPackage (StackFilePath ".") False+ defaultPackage = StackPackage (StackFilePath ".") False Nothing -fromYamlPackage :: Yaml.Package -> StackPackage++fromYamlPackage+ :: Yaml.Package+ -- ^ A single 'Yaml.Package' can result in multiple actual+ -- packages if it has multiple subdirs specified.+ -> NonEmpty StackPackage fromYamlPackage = \case Yaml.Simple p ->- StackPackage (parseSimplePath p) False- Yaml.LocationSimple (Yaml.Location p extraDep) ->- StackPackage (parseSimplePath p) (fromMaybe False extraDep)- Yaml.LocationGit (Location git extraDep) ->- StackPackage (StackRepo $ fromYamlGit git) (fromMaybe False extraDep)- Yaml.LocationHg (Location hg extraDep) ->- StackPackage (StackRepo $ fromYamlHg hg) (fromMaybe False extraDep)+ unroll Nothing $ StackPackage (parseSimplePath p) False+ Yaml.LocationSimple (Location p extraDep ms) ->+ unroll ms $ StackPackage (parseSimplePath p) (fromMaybe False extraDep)+ Yaml.LocationGit (Location git extraDep ms) ->+ unroll ms $ StackPackage (StackRepo $ fromYamlGit git) (fromMaybe False extraDep)+ Yaml.LocationHg (Location hg extraDep ms) ->+ unroll ms $ StackPackage (StackRepo $ fromYamlHg hg) (fromMaybe False extraDep) where parseSimplePath (T.unpack -> p) = maybe (StackFilePath p) StackUri $ parseURI p+ -- Each package gets a single directory with cabal file in it. If+ -- it's not specified, path is empty.+ unroll subs p = case subs of+ Just (x : xs) -> NE.map (p . Just) (x :| xs)+ _ -> p Nothing :| [] fromYamlExtraDep :: Text -> StackPackage-fromYamlExtraDep = flip StackPackage True . HackagePackage+fromYamlExtraDep t = StackPackage (HackagePackage t) True Nothing fromYamlGit :: Yaml.Git -> Repo fromYamlGit yg = Repo{..}
src/Stack/Config/Yaml.hs view
@@ -10,6 +10,7 @@ data Location a = Location { _lLocation :: !a , _lExtraDep :: !(Maybe Bool)+ , _lSubdirs :: !(Maybe [FilePath]) } deriving (Eq, Show) makeLenses ''Location
src/Stack/Types.hs view
@@ -10,6 +10,7 @@ data StackYaml = StackYaml { _syDirName :: !FilePath , _syFileName :: !FilePath }+ deriving (Ord, Eq, Show) makeLenses ''StackYaml
stackage2nix.cabal view
@@ -1,7 +1,7 @@ name: stackage2nix-version: 0.3.0+version: 0.4.0 synopsis: Convert Stack files into Nix build instructions.-homepage: https://github.com/4e6/stackage2nix#readme+homepage: https://github.com/typeable/stackage2nix#readme license: BSD3 license-file: LICENSE author: Dmitry Bushev@@ -44,7 +44,7 @@ , gitlib > 3 , gitlib-libgit2 > 3 , hopenssl > 2.2- , inflections+ , inflections >= 0.3 , language-nix , lens , network-uri@@ -91,4 +91,4 @@ source-repository head type: git- location: https://github.com/4e6/stackage2nix+ location: https://github.com/typeable/stackage2nix
test/Stack/Config/YamlSpec.hs view
@@ -28,6 +28,12 @@ - location: git: git@github.com:commercialhaskell/stack.git commit: 6a86ee32e5b869a877151f74064572225e1a0398+ - location:+ git: git@github.com:example/mega-repo+ commit: 6a86ee32e5b869a877151f74064572225e1a0000+ subdirs:+ - subdir1+ - subdir2 # Comment extra-deps: - acme-missiles-0.3@@ -45,13 +51,19 @@ { _cResolver = "lts-3.7" , _cPackages = Just [ Simple "."- , LocationSimple (Location "dir1/dir2" Nothing)- , LocationSimple (Location "https://example.com/foo/bar/baz-0.0.2.tar.gz" (Just True))+ , LocationSimple (Location "dir1/dir2" Nothing Nothing)+ , LocationSimple (Location "https://example.com/foo/bar/baz-0.0.2.tar.gz" (Just True) Nothing) , LocationGit $ Location (Git "git@github.com:commercialhaskell/stack.git" "6a86ee32e5b869a877151f74064572225e1a0398")- Nothing]+ Nothing Nothing+ , LocationGit (Location+ (Git+ "git@github.com:example/mega-repo"+ "6a86ee32e5b869a877151f74064572225e1a0000")+ Nothing (Just ["subdir1", "subdir2"]))+ ] , _cExtraDeps = Just ["acme-missiles-0.3"] }