horizon-gen-nix (empty) → 0.2
raw patch · 15 files changed
+878/−0 lines, 15 filesdep +Cabal-syntaxdep +basedep +cabal2nix
Dependencies added: Cabal-syntax, base, cabal2nix, containers, dhall, directory, distribution-nixpkgs, either, horizon-gen-nix, horizon-spec, language-nix, lens, optparse-applicative, path, path-dhall-instance, pretty, silently, sydtest, text
Files
- ChangeLog.md +16/−0
- LICENSE +19/−0
- README.md +32/−0
- app/Main.hs +6/−0
- horizon-gen-nix.cabal +100/−0
- src/Horizon/Gen/Nix.hs +56/−0
- src/Horizon/Gen/Nix/Cabal2Nix/Options.hs +115/−0
- src/Horizon/Gen/Nix/Options.hs +170/−0
- src/Horizon/Gen/Nix/Pretty.hs +125/−0
- src/Horizon/Gen/Nix/Types/HorizonMode.hs +9/−0
- src/Horizon/Gen/Nix/Types/OverlayFile.hs +10/−0
- src/Horizon/Gen/Nix/Types/PackageSetFile.hs +10/−0
- src/Horizon/Gen/Nix/Types/PackagesDirectory.hs +10/−0
- src/Horizon/Gen/Nix/Writers.hs +79/−0
- test/Spec.hs +121/−0
+ ChangeLog.md view
@@ -0,0 +1,16 @@+# Changelog for horizon-gen-nix++## v0.2++* Support horizon-spec-0.2+* horizon-gen-nix now supports two modes with several command line options.+ * Running in `make-package-set`mode will by default create a file called `inital-packages.nix` suitable for use with the `makePackageSet` function from nixpkgs.+ * Running in `overlay` mode will by default create a file called `overlay.nix` containing an overlay suitable for use with `haskell.packages.${compiler}.override`.++## v0.1++* Support horizon-spec-0.1++## v0.0.0.1++* Generate nix expressions for haskell packages from a dhall manifest.
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright 2022 Homotopic.Tech Ltd++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,32 @@+# horizon-gen-nix++horizon-gen-nix is an executable for generating nix expressions+from [horizon-spec](https://hackage.haskell.org/package/horizon-spec)+dhall definitions.++```+Usage: horizon-gen-nix [--input-file INPUT_FILE] [--packages-dir PACKAGES_DIR]+ COMMAND++Available options:+ -h,--help Show this help text+ --input-file INPUT_FILE The name of the input file.+ --packages-dir PACKAGES_DIR+ The directory to put haskell packages.++Available commands:+ make-package-set Run in MakePackageSet Mode+ overlay Run in Overlay Mode+```++## Building++```+nix build+```++## Development++```+nix develop+```
+ app/Main.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import qualified Horizon.Gen.Nix (main)++main :: IO ()+main = Horizon.Gen.Nix.main
+ horizon-gen-nix.cabal view
@@ -0,0 +1,100 @@+cabal-version: 3.0+name: horizon-gen-nix+version: 0.2+synopsis: Generate nix expressions from horizon-spec definitions+description: Generate nix expressions from horizon-spec definitions+category: Package Management, Nix+author: Daniel Firth+maintainer: dan.firth@homotopic.tech+copyright: 2022 Daniel Firth+license: MIT+license-file: LICENSE+build-type: Simple+extra-source-files:+ ChangeLog.md+ README.md++source-repository head+ type: git+ location: https://gitlab.homotopic.tech/horizon/horizon-gen-nix++library+ exposed-modules:+ Horizon.Gen.Nix+ Horizon.Gen.Nix.Cabal2Nix.Options+ Horizon.Gen.Nix.Options+ Horizon.Gen.Nix.Pretty+ Horizon.Gen.Nix.Types.HorizonMode+ Horizon.Gen.Nix.Types.OverlayFile+ Horizon.Gen.Nix.Types.PackagesDirectory+ Horizon.Gen.Nix.Types.PackageSetFile+ Horizon.Gen.Nix.Writers++ hs-source-dirs: src+ default-extensions:+ DataKinds+ DerivingStrategies+ GADTs+ StandaloneKindSignatures+ TypeApplications++ ghc-options:+ -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude+ -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module+ -Wno-safe -Wno-unsafe++ build-depends:+ , base >=4.7 && <5+ , Cabal-syntax+ , cabal2nix+ , containers+ , dhall+ , directory+ , distribution-nixpkgs+ , either+ , horizon-spec >=0.2 && <0.3+ , language-nix+ , lens+ , optparse-applicative+ , path+ , path-dhall-instance+ , pretty+ , silently+ , text++ default-language: Haskell2010++executable horizon-gen-nix+ main-is: Main.hs+ hs-source-dirs: app+ default-extensions:+ DataKinds+ DerivingStrategies++ ghc-options:+ -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude+ -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module+ -Wno-safe -Wno-unsafe -threaded -rtsopts -with-rtsopts=-N++ build-depends:+ , base >=4.7 && <5+ , horizon-gen-nix++ default-language: GHC2021++executable horizon-gen-nix-tests+ main-is: Spec.hs+ hs-source-dirs: test+ ghc-options:+ -Weverything -Wno-all-missed-specialisations -Wno-implicit-prelude+ -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module+ -Wno-safe -Wno-unsafe++ build-depends:+ , base >=4.7 && <5+ , directory+ , horizon-gen-nix+ , path+ , sydtest++ default-language: Haskell2010
+ src/Horizon/Gen/Nix.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE RecordWildCards #-}+module Horizon.Gen.Nix (+ main+ , horizonGenNix+ , makeMakePackageSet+ , makeOverlay+ ) where++import Dhall (auto, inputFile)+import Horizon.Gen.Nix.Options (HorizonCommand (MakePackageSetCommand, OverlayCommand),+ HorizonOptions (MkHorizonOptions),+ MakePackageSetOptions,+ OverlayOptions,+ fromInputFile,+ horizonGenNixOptsInfo,+ optHorizonCommand,+ optInputFile,+ optOverlayFile,+ optPackageSetFile,+ optPackagesDirectory)+import Horizon.Gen.Nix.Types.PackagesDirectory (PackagesDirectory)+import Horizon.Gen.Nix.Writers (writeHaskellPackages,+ writeMakePackageSet,+ writeOverlay)+import Horizon.Spec (Overlay, PackageSet,+ fromOverlay, packages)+import Options.Applicative (execParser)+import Path (toFilePath)+import Path.Dhall ()+++makeMakePackageSet :: MakePackageSetOptions -> PackagesDirectory -> PackageSet -> IO ()+makeMakePackageSet opts d xs = do+ writeHaskellPackages d (packages xs)+ writeMakePackageSet d (optPackageSetFile opts) xs++makeOverlay :: OverlayOptions -> PackagesDirectory -> Overlay -> IO ()+makeOverlay opts d xs = do+ writeHaskellPackages d (fromOverlay xs)+ writeOverlay d (optOverlayFile opts) xs++horizonGenNix :: HorizonOptions -> IO ()+horizonGenNix MkHorizonOptions{..} =+ let fp = toFilePath . fromInputFile $ optInputFile+ in case optHorizonCommand of+ MakePackageSetCommand opts -> do+ x <- inputFile @PackageSet auto fp+ makeMakePackageSet opts optPackagesDirectory x+ OverlayCommand opts -> do+ x <- inputFile @Overlay auto fp+ makeOverlay opts optPackagesDirectory x++main :: IO ()+main = do+ x <- execParser horizonGenNixOptsInfo+ horizonGenNix x
+ src/Horizon/Gen/Nix/Cabal2Nix/Options.hs view
@@ -0,0 +1,115 @@+module Horizon.Gen.Nix.Cabal2Nix.Options+ ( haskellPackageToCabal2NixOptions+ , mkDefaultCabal2NixOptions+ , applyGitSourceToCabal2NixOptions+ , applyHackageSourceToCabal2NixOptions+ , applyModifiersToCabal2NixOptions+ ) where++import Cabal2nix (Options (Options),+ optCompiler,+ optDoBenchmark,+ optDoCheck,+ optEnableExecutableProfiling,+ optEnableLibraryProfiling,+ optEnableProfiling,+ optExtraArgs,+ optFetchSubmodules,+ optFlags,+ optHackageDb,+ optHackageSnapshot,+ optHaddock,+ optHpack,+ optHyperlinkSource,+ optJailbreak,+ optMaintainer,+ optNixShellOutput,+ optNixpkgsIdentifier,+ optRevision,+ optSha256,+ optSubpath,+ optSystem,+ optUrl)+import Control.Lens ((#))+import qualified Data.Text as T+import Distribution.Compiler (buildCompilerId)+import Distribution.Nixpkgs.Fetch (FetchSubmodules (FetchSubmodules))+import Distribution.Nixpkgs.Haskell.PackageSourceSpec (HpackUse (NoHpack))+import Distribution.System (buildPlatform)+import Horizon.Spec (GitSource (MkGitSource),+ HackageSource (MkHackageSource),+ HaskellPackage (MkHaskellPackage),+ HaskellSource (FromGit, FromHackage),+ Modifiers,+ Name (MkName),+ Repo (MkRepo),+ Url (MkUrl),+ Version (MkVersion),+ doCheck,+ doJailbreak,+ enableProfiling,+ fromRevision,+ fromSubdir)+import Language.Nix.Binding (binding)+import Language.Nix.Identifier (ident)+import Language.Nix.Path (path)+import Path (toFilePath)++mkDefaultCabal2NixOptions :: Options+mkDefaultCabal2NixOptions = Options+ { optSha256 = Nothing+ , optMaintainer = []+ , optHpack = NoHpack+ , optDoCheck = False+ , optJailbreak = False+ , optDoBenchmark = False+ , optRevision = Nothing+ , optHyperlinkSource = False+ , optEnableLibraryProfiling = False+ , optEnableExecutableProfiling = False+ , optEnableProfiling = Nothing+ , optExtraArgs = []+ , optHackageDb = Nothing+ , optNixShellOutput = False+ , optFlags = []+ , optCompiler = buildCompilerId+ , optSystem = buildPlatform+ , optSubpath = Nothing+ , optHackageSnapshot = Nothing+ , optNixpkgsIdentifier = \i -> Just (binding # (i, path # [ident # "pkgs", i]))+ , optUrl = mempty+ , optHaddock = False+ , optFetchSubmodules = FetchSubmodules+ }++haskellPackageToCabal2NixOptions :: HaskellPackage -> Options+haskellPackageToCabal2NixOptions (MkHaskellPackage x ms _) =+ applyModifiersToCabal2NixOptions ms $ (+ case x of+ FromHackage z -> applyHackageSourceToCabal2NixOptions z+ FromGit z -> applyGitSourceToCabal2NixOptions z+ ) mkDefaultCabal2NixOptions++applyGitSourceToCabal2NixOptions+ :: GitSource+ -> Options+ -> Options+applyGitSourceToCabal2NixOptions+ (MkGitSource (MkRepo (MkUrl u)) r s) opts = opts+ { optRevision = Just $ (T.unpack . fromRevision) r+ , optSubpath = fmap (toFilePath . fromSubdir) s+ , optUrl = T.unpack u+ }++applyHackageSourceToCabal2NixOptions :: HackageSource -> Options -> Options+applyHackageSourceToCabal2NixOptions (MkHackageSource (MkName n) (MkVersion v)) opts = opts+ { optUrl = "cabal://" <> T.unpack n <> "-" <> T.unpack v }++applyModifiersToCabal2NixOptions :: Modifiers -> Options -> Options+applyModifiersToCabal2NixOptions ms opts = opts+ { optDoCheck = doCheck ms+ , optJailbreak = doJailbreak ms+ , optEnableProfiling = Just $ enableProfiling ms+ , optEnableLibraryProfiling = enableProfiling ms+ , optEnableExecutableProfiling = enableProfiling ms+ }
+ src/Horizon/Gen/Nix/Options.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+module Horizon.Gen.Nix.Options+ ( horizonGenNixOpts+ , horizonGenNixOptsInfo+ , InputFile (..)+ , HorizonCommand (..)+ , HorizonOptions (..)+ , MakePackageSetOptions (..)+ , OverlayOptions (..)+ , defaultMakePackageSetOptions+ , defaultPackagesDirectory+ , defaultPackageSetFile+ , defaultOverlayFile+ , defaultOverlayOptions+ , ) where++import Data.Either.Combinators (mapLeft)+import Data.Kind (Type)+import Horizon.Gen.Nix.Types.OverlayFile (OverlayFile (MkOverlayFile))+import Horizon.Gen.Nix.Types.PackagesDirectory (PackagesDirectory (MkPackagesDirectory))+import Horizon.Gen.Nix.Types.PackageSetFile (PackageSetFile (MkPackageSetFile))+import Options.Applicative (Parser, ParserInfo,+ ReadM, command,+ eitherReader, help,+ helper, info, long,+ metavar, option,+ progDesc, subparser,+ value)+import Path (File, Path, Rel,+ mkRelDir, mkRelFile,+ parseRelDir,+ parseRelFile)+import Path.Dhall ()+++parsePackagesDirectory :: ReadM PackagesDirectory+parsePackagesDirectory = eitherReader $ mapLeft show . fmap MkPackagesDirectory . parseRelDir++defaultPackagesDirectory :: PackagesDirectory+defaultPackagesDirectory = MkPackagesDirectory $(mkRelDir "pkgs")++packagesDirectoryOption :: Parser PackagesDirectory+packagesDirectoryOption =+ option parsePackagesDirectory+ ( long "packages-dir"+ <> metavar "PACKAGES_DIR"+ <> value defaultPackagesDirectory+ <> help "The directory to put haskell packages."+ )+++parsePackageSetFile :: ReadM PackageSetFile+parsePackageSetFile = eitherReader $ mapLeft show . fmap MkPackageSetFile . parseRelFile++defaultPackageSetFile :: PackageSetFile+defaultPackageSetFile = MkPackageSetFile $(mkRelFile "initial-packages.nix")++packageSetFileOption :: Parser PackageSetFile+packageSetFileOption =+ option parsePackageSetFile+ ( long "packageset-file"+ <> metavar "PACKAGESET_FILE"+ <> value defaultPackageSetFile+ <> help "The name of the package set file"+ )+++parseOverlayFile :: ReadM OverlayFile+parseOverlayFile = eitherReader $ mapLeft show . fmap MkOverlayFile . parseRelFile++defaultOverlayFile :: OverlayFile+defaultOverlayFile = MkOverlayFile $(mkRelFile "overlay.nix")++overlayFileOption :: Parser OverlayFile+overlayFileOption =+ option parseOverlayFile+ ( long "overlay-file"+ <> metavar "OVERLAY_FILE"+ <> value defaultOverlayFile+ <> help "The name of the overlay file."+ )++type InputFile :: Type+newtype InputFile where+ MkInputFile :: { fromInputFile :: Path Rel File }+ -> InputFile+ deriving stock (Eq, Show)++parseInputFile :: ReadM InputFile+parseInputFile = eitherReader $ mapLeft show . fmap MkInputFile . parseRelFile++defaultInputFile :: InputFile+defaultInputFile = MkInputFile $(mkRelFile "horizon.dhall")++inputFileOption :: Parser InputFile+inputFileOption =+ option parseInputFile+ ( long "input-file"+ <> metavar "INPUT_FILE"+ <> value defaultInputFile+ <> help "The name of the input file."+ )+++type MakePackageSetOptions :: Type+data MakePackageSetOptions where+ MkMakePackageSetOptions :: { optPackageSetFile :: PackageSetFile }+ -> MakePackageSetOptions+ deriving stock (Eq, Show)++makePackageSetOptions :: Parser MakePackageSetOptions+makePackageSetOptions =+ MkMakePackageSetOptions+ <$> packageSetFileOption++defaultMakePackageSetOptions :: MakePackageSetOptions+defaultMakePackageSetOptions = MkMakePackageSetOptions defaultPackageSetFile+++type OverlayOptions :: Type+data OverlayOptions where+ MkOverlayOptions :: { optOverlayFile :: OverlayFile}+ -> OverlayOptions+ deriving stock (Eq, Show)++overlayOptions :: Parser OverlayOptions+overlayOptions =+ MkOverlayOptions+ <$> overlayFileOption++defaultOverlayOptions :: OverlayOptions+defaultOverlayOptions = MkOverlayOptions defaultOverlayFile+++type HorizonCommand :: Type+data HorizonCommand where+ MakePackageSetCommand :: MakePackageSetOptions -> HorizonCommand+ OverlayCommand :: OverlayOptions -> HorizonCommand+ deriving stock (Eq, Show)++type HorizonOptions :: Type+data HorizonOptions where+ MkHorizonOptions :: { optInputFile :: InputFile+ , optPackagesDirectory :: PackagesDirectory+ , optHorizonCommand :: HorizonCommand} -> HorizonOptions+ deriving stock (Eq, Show)++makePackageSetCommand :: Parser HorizonCommand+makePackageSetCommand = fmap MakePackageSetCommand makePackageSetOptions++overlayCommand :: Parser HorizonCommand+overlayCommand = fmap OverlayCommand overlayOptions++horizonCommandOpts :: Parser HorizonCommand+horizonCommandOpts = subparser+ ( command "make-package-set" (info (helper <*> makePackageSetCommand) ( progDesc "Run in MakePackageSet Mode" ))+ <> command "overlay" (info (helper <*> overlayCommand) ( progDesc "Run in Overlay Mode" ))+ )++horizonGenNixOpts :: Parser HorizonOptions+horizonGenNixOpts =+ MkHorizonOptions+ <$> inputFileOption+ <*> packagesDirectoryOption+ <*> horizonCommandOpts++horizonGenNixOptsInfo :: ParserInfo HorizonOptions+horizonGenNixOptsInfo = info (helper <*> horizonGenNixOpts) mempty
+ src/Horizon/Gen/Nix/Pretty.hs view
@@ -0,0 +1,125 @@+module Horizon.Gen.Nix.Pretty (prettyDerivation) where++import Distribution.Nixpkgs.Haskell.Derivation (Derivation,+ benchmarkDepends,+ cabalFlags,+ configureFlags,+ dependencies,+ doBenchmark,+ doCheck,+ editedCabalFile,+ enableExecutableProfiling,+ enableLibraryProfiling,+ enableSeparateDataOutput,+ executableDepends,+ extraAttributes,+ extraFunctionArgs,+ hyperlinkSource,+ isExecutable,+ isLibrary,+ jailbreak,+ libraryDepends,+ metaSection,+ phaseOverrides,+ pkgid, revision,+ runHaddock,+ setupDepends,+ src, subpath,+ testDepends,+ testTarget)+import Language.Nix (ident, localName)+import Language.Nix.PrettyPrinting (Doc, attr, char,+ doubleQuotes,+ empty, funargs,+ int, lbrace,+ listattr, nest,+ onlyIf, pPrint,+ rbrace, string,+ text, vcat, ($$),+ (<+>))+import qualified Language.Nix.PrettyPrinting as P ((<>))++import Control.Lens (each, folded,+ view, (^.))+import Data.List (isPrefixOf)+import qualified Data.Map as Map+import Data.Set (Set, toAscList)+import qualified Data.Set as Set+import Data.Set.Lens (setOf)+import Distribution.Nixpkgs.Fetch (derivKind,+ derivKindFunction,+ derivUrl)+import Distribution.Nixpkgs.Haskell.BuildInfo (pPrintBuildInfo)+import Distribution.Nixpkgs.Haskell.OrphanInstances ()+import Distribution.Nixpkgs.Meta (Meta, broken,+ description,+ homepage,+ license)+import Distribution.Package (packageName,+ packageVersion)+import Distribution.PackageDescription (unFlagAssignment,+ unFlagName)++bool :: Bool -> Doc+bool True = text "true"+bool False = text "false"+++boolattr :: String -> Bool -> Doc+boolattr n v = attr n (bool v)++prettyMeta :: Meta -> Doc+prettyMeta meta = vcat+ [ onlyIf (not (null $ meta ^. homepage)) $ attr "homepage" $ string (meta ^. homepage)+ , onlyIf (not (null $ meta ^. description)) $ attr "description" $ string (meta ^. description)+ , attr "license" $ pPrint $ meta ^. license+ , boolattr "broken" (meta ^. broken)+ ]++prettyDerivation :: Derivation -> Doc+prettyDerivation drv = funargs (map text ("mkDerivation" : toAscList inputs)) $$ vcat+ [ text "mkDerivation" <+> lbrace+ , nest 2 $ vcat+ [ attr "pname" $ doubleQuotes $ pPrint (packageName $ drv ^. pkgid)+ , attr "version" $ doubleQuotes $ pPrint (packageVersion $ drv ^. pkgid)+ , pPrint $ drv ^. src+ , onlyIf (drv ^. subpath /= ".") $ attr "postUnpack" postUnpack+ , onlyIf (drv ^. revision > 0) $ attr "revision" $ doubleQuotes $ int $ drv ^. revision+ , onlyIf (not (null (drv ^. editedCabalFile)) && (drv ^. revision) > 0) $ attr "editedCabalFile" $ string (drv ^. editedCabalFile)+ , listattr "configureFlags" empty (map (show . show) renderedFlags)+ , boolattr "isLibrary" $ drv ^. isLibrary+ , boolattr "isExecutable" $ drv ^. isExecutable+ , boolattr "enableSeparateDataOutput" $ drv ^. enableSeparateDataOutput+ , onlyIf (drv ^. setupDepends /= mempty) $ pPrintBuildInfo "setup" $ drv ^. setupDepends+ , onlyIf (drv ^. libraryDepends /= mempty) $ pPrintBuildInfo "library" $ drv ^. libraryDepends+ , onlyIf (drv ^. executableDepends /= mempty) $ pPrintBuildInfo "executable" $ drv ^. executableDepends+ , onlyIf (drv ^. testDepends /= mempty) $ pPrintBuildInfo "test" $ drv ^. testDepends+ , onlyIf (drv ^. benchmarkDepends /= mempty) $ pPrintBuildInfo "benchmark" $ drv ^. benchmarkDepends+ , boolattr "enableLibraryProfiling" $ drv ^. enableLibraryProfiling+ , boolattr "enableExecutableProfiling" $ drv ^. enableExecutableProfiling+ , boolattr "doHaddock" $ drv ^. runHaddock+ , boolattr "jailbreak" $ drv ^. jailbreak+ , boolattr "doCheck" $ drv ^. doCheck+ , boolattr "doBenchmark" $ drv ^. doBenchmark+ , onlyIf (not (null $ drv ^. testTarget)) $ attr "testTarget" $ string $ drv ^. testTarget+ , boolattr "hyperlinkSource" $ drv ^. hyperlinkSource+ , onlyIf (not (null $ drv ^. phaseOverrides)) $ vcat ((map text . lines) $ drv ^. phaseOverrides)+ , prettyMeta $ drv ^. metaSection+ , vcat [ attr k (text v) | (k,v) <- Map.toList $ drv ^. extraAttributes ]+ ]+ , rbrace+ ]+ where+ inputs :: Set String+ inputs = Set.unions [ Set.map (view (localName . ident)) $ drv ^. extraFunctionArgs+ , setOf (dependencies . each . folded . localName . ident) drv+ , case derivKind (drv ^. src) of+ Nothing -> mempty+ Just derivKind' -> Set.fromList [derivKindFunction derivKind' | not isHackagePackage]+ ]++ renderedFlags = [ text "-f" P.<> (if enable then empty else char '-') P.<> text (unFlagName f) | (f, enable) <- unFlagAssignment $ drv ^. cabalFlags ]+ ++ map text (toAscList (drv ^. configureFlags))+ isHackagePackage = "mirror://hackage/" `isPrefixOf` derivUrl (drv ^. src)++ postUnpack = string $ "sourceRoot+=/" ++ drv ^. subpath ++ "; echo source root reset to $sourceRoot"
+ src/Horizon/Gen/Nix/Types/HorizonMode.hs view
@@ -0,0 +1,9 @@+module Horizon.Gen.Nix.Types.HorizonMode (HorizonMode (MakePackageSetMode, OverlayMode)) where++import Data.Kind (Type)++type HorizonMode :: Type+data HorizonMode where+ MakePackageSetMode :: HorizonMode+ OverlayMode :: HorizonMode+ deriving stock (Show, Eq)
+ src/Horizon/Gen/Nix/Types/OverlayFile.hs view
@@ -0,0 +1,10 @@+module Horizon.Gen.Nix.Types.OverlayFile (OverlayFile(MkOverlayFile), fromOverlayFile) where++import Data.Kind (Type)+import Path (File, Path, Rel)+++type OverlayFile :: Type+data OverlayFile where+ MkOverlayFile :: { fromOverlayFile :: Path Rel File } -> OverlayFile+ deriving stock (Show, Eq)
+ src/Horizon/Gen/Nix/Types/PackageSetFile.hs view
@@ -0,0 +1,10 @@+module Horizon.Gen.Nix.Types.PackageSetFile (PackageSetFile(MkPackageSetFile), fromPackageSetFile) where++import Data.Kind (Type)+import Path (File, Path, Rel)+++type PackageSetFile :: Type+data PackageSetFile where+ MkPackageSetFile :: { fromPackageSetFile :: Path Rel File } -> PackageSetFile+ deriving stock (Show, Eq)
+ src/Horizon/Gen/Nix/Types/PackagesDirectory.hs view
@@ -0,0 +1,10 @@+module Horizon.Gen.Nix.Types.PackagesDirectory (PackagesDirectory(MkPackagesDirectory), fromPackagesDirectory) where++import Data.Kind (Type)+import Path (Dir, Path, Rel)+++type PackagesDirectory :: Type+data PackagesDirectory where+ MkPackagesDirectory :: { fromPackagesDirectory :: Path Rel Dir } -> PackagesDirectory+ deriving stock (Show, Eq)
+ src/Horizon/Gen/Nix/Writers.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE OverloadedStrings #-}+module Horizon.Gen.Nix.Writers (+ writeOverlay+ , writeMakePackageSet+ , writeCabal2Nix+ , writeDerivation+ , writeHaskellPackage+ , writeHaskellPackages+ ) where++import Cabal2nix (Options, cabal2nix')+import Control.Monad (forM_, unless, (>=>))+import Data.Map (keys, toList)+import qualified Data.Text as T+import Distribution.Nixpkgs.Haskell.Derivation (Derivation)+import Horizon.Gen.Nix.Cabal2Nix.Options (haskellPackageToCabal2NixOptions)+import Horizon.Gen.Nix.Pretty (prettyDerivation)+import Horizon.Gen.Nix.Types.OverlayFile (OverlayFile (MkOverlayFile))+import Horizon.Gen.Nix.Types.PackagesDirectory (PackagesDirectory (MkPackagesDirectory))+import Horizon.Gen.Nix.Types.PackageSetFile (PackageSetFile (MkPackageSetFile))+import Horizon.Spec (HaskellPackage,+ Name (MkName),+ Overlay (MkOverlay),+ PackageList (MkPackageList),+ PackageSet (MkPackageSet),+ fromName,+ fromPackageList)+import Path (File, Path,+ parseRelFile,+ toFilePath, (</>))+import Path.Dhall ()+import System.Directory (createDirectoryIfMissing,+ doesFileExist)+import System.IO (IOMode (WriteMode),+ hPutStrLn, stderr,+ stdout, withFile)+import System.IO.Silently (hSilence)+import Text.PrettyPrint.HughesPJClass (render)+++writeOverlay :: PackagesDirectory -> OverlayFile -> Overlay -> IO ()+writeOverlay (MkPackagesDirectory d) (MkOverlayFile f) (MkOverlay (MkPackageList xs)) = withFile (toFilePath f) WriteMode $ \h -> do+ hPutStrLn h "{ pkgs, ... }:"+ hPutStrLn h ""+ hPutStrLn h "final: prev: with pkgs.haskell.lib; {"+ forM_ (keys xs) $ \x -> do+ y <- parseRelFile $ T.unpack $ fromName x <> ".nix"+ hPutStrLn h (" " <> T.unpack (fromName x) <> " = final.callPackage (./" <> toFilePath (d </> y) <> ") { };")+ hPutStrLn h ""+ hPutStrLn h "}"++writeMakePackageSet :: PackagesDirectory -> PackageSetFile -> PackageSet -> IO ()+writeMakePackageSet (MkPackagesDirectory d) (MkPackageSetFile f) (MkPackageSet _ (MkPackageList xs)) = withFile (toFilePath f) WriteMode $ \h -> do+ hPutStrLn h "{ pkgs, lib, callPackage, ... }:"+ hPutStrLn h ""+ hPutStrLn h "self: with pkgs.haskell.lib; {"+ forM_ (keys xs) $ \x -> do+ y <- parseRelFile $ T.unpack $ fromName x <> ".nix"+ hPutStrLn h (" " <> T.unpack (fromName x) <> " = self.callPackage (./" <> toFilePath (d </> y) <> ") { };")+ hPutStrLn h ""+ hPutStrLn h "}"++writeCabal2Nix :: Path b File -> Options -> IO ()+writeCabal2Nix f = hSilence [stdout, stderr] . cabal2nix' >=> either (print . render) (writeDerivation f)++writeDerivation :: Path b File -> Derivation -> IO ()+writeDerivation f = writeFile (toFilePath f) . show . prettyDerivation++writeHaskellPackage :: PackagesDirectory -> Name -> HaskellPackage -> IO ()+writeHaskellPackage (MkPackagesDirectory d) (MkName f) y = do+ let o = haskellPackageToCabal2NixOptions y+ createDirectoryIfMissing True (toFilePath d)+ f' <- parseRelFile $ T.unpack (f <> ".nix")+ let j = d </> f'+ q <- doesFileExist $ toFilePath j+ unless q $ writeCabal2Nix j o++writeHaskellPackages :: PackagesDirectory -> PackageList -> IO ()+writeHaskellPackages d = mapM_ (uncurry $ writeHaskellPackage d) . toList . fromPackageList
+ test/Spec.hs view
@@ -0,0 +1,121 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+module Main ( main ) where++import Control.Exception (bracket_)+import Horizon.Gen.Nix (horizonGenNix)+import Horizon.Gen.Nix.Options (HorizonCommand (MakePackageSetCommand, OverlayCommand),+ HorizonOptions (MkHorizonOptions),+ InputFile (MkInputFile),+ defaultMakePackageSetOptions,+ defaultOverlayOptions,+ defaultPackagesDirectory,+ optHorizonCommand, optInputFile,+ optPackagesDirectory)+import Path (mkRelFile)+import System.Directory (removeFile)+import Test.Syd (Spec, aroundAll_, describe,+ doNotRandomiseExecutionOrder,+ goldenStringFile, it, sequential,+ sydTest)++main :: IO ()+main = sydTest $ doNotRandomiseExecutionOrder $ sequential $ do+ samplePackageSet+ modifiedPackageSet+ sampleOverlay+ modifiedOverlay++withSamplePackageSet :: IO a -> IO a+withSamplePackageSet = bracket_ runSamplePackageSet cleanupPackageSet+++withModifiedPackageSet :: IO a -> IO a+withModifiedPackageSet = bracket_ runModifiedPackageSet cleanupPackageSet+++withSampleOverlay :: IO a -> IO a+withSampleOverlay = bracket_ runSampleOverlay cleanupOverlay+++withModifiedOverlay :: IO a -> IO a+withModifiedOverlay = bracket_ runModifiedOverlay cleanupOverlay+++runSamplePackageSet :: IO ()+runSamplePackageSet = do+ let opts = MkHorizonOptions+ { optHorizonCommand = MakePackageSetCommand defaultMakePackageSetOptions+ , optPackagesDirectory = defaultPackagesDirectory+ , optInputFile = MkInputFile $(mkRelFile "test/data/sample-package-set/input.dhall")+ }+ horizonGenNix opts+++runModifiedPackageSet :: IO ()+runModifiedPackageSet = do+ let opts = MkHorizonOptions+ { optHorizonCommand = MakePackageSetCommand defaultMakePackageSetOptions+ , optPackagesDirectory = defaultPackagesDirectory+ , optInputFile = MkInputFile $(mkRelFile "test/data/modified-package-set/input.dhall")+ }+ horizonGenNix opts++runSampleOverlay :: IO ()+runSampleOverlay = do+ let opts = MkHorizonOptions+ { optHorizonCommand = OverlayCommand defaultOverlayOptions+ , optPackagesDirectory = defaultPackagesDirectory+ , optInputFile = MkInputFile $(mkRelFile "test/data/sample-overlay/input.dhall")+ }+ horizonGenNix opts+++runModifiedOverlay :: IO ()+runModifiedOverlay = do+ let opts = MkHorizonOptions+ { optHorizonCommand = OverlayCommand defaultOverlayOptions+ , optPackagesDirectory = defaultPackagesDirectory+ , optInputFile = MkInputFile $(mkRelFile "test/data/modified-overlay/input.dhall")+ }+ horizonGenNix opts++cleanupPackageSet :: IO ()+cleanupPackageSet = mapM_ removeFile ["initial-packages.nix", "pkgs/lens.nix", "pkgs/Cabal-syntax.nix" ]+++cleanupOverlay :: IO ()+cleanupOverlay = mapM_ removeFile ["overlay.nix", "pkgs/lens.nix", "pkgs/Cabal-syntax.nix" ]++++samplePackageSet :: Spec+samplePackageSet = describe "sample package set" $ sequential $+ aroundAll_ withSamplePackageSet $ do+ it "initial-packages.nix" $ goldenStringFile "test/data/sample-package-set/output/initial-packages.nix.golden" (readFile "initial-packages.nix")+ it "pkgs/lens.nix" $ goldenStringFile "test/data/sample-package-set/output/pkgs/lens.nix.golden" (readFile "pkgs/lens.nix")+ it "pkgs/Cabal-syntax.nix" $ goldenStringFile "test/data/sample-package-set/output/pkgs/Cabal-syntax.nix.golden" (readFile "pkgs/Cabal-syntax.nix")+++modifiedPackageSet :: Spec+modifiedPackageSet = describe "modified package set" $ sequential $+ aroundAll_ withModifiedPackageSet $ do+ it "initial-packages.nix" $ goldenStringFile "test/data/modified-package-set/output/initial-packages.nix.golden" (readFile "initial-packages.nix")+ it "pkgs/lens.nix" $ goldenStringFile "test/data/modified-package-set/output/pkgs/lens.nix.golden" (readFile "pkgs/lens.nix")+ it "pkgs/Cabal-syntax.nix" $ goldenStringFile "test/data/modified-package-set/output/pkgs/Cabal-syntax.nix.golden" (readFile "pkgs/Cabal-syntax.nix")+++sampleOverlay :: Spec+sampleOverlay = describe "sample overlay" $ sequential $+ aroundAll_ withSampleOverlay $ do+ it "overlay.nix" $ goldenStringFile "test/data/sample-overlay/output/overlay.nix.golden" (readFile "overlay.nix")+ it "pkgs/lens.nix" $ goldenStringFile "test/data/sample-overlay/output/pkgs/lens.nix.golden" (readFile "pkgs/lens.nix")+ it "pkgs/Cabal-syntax.nix" $ goldenStringFile "test/data/sample-overlay/output/pkgs/Cabal-syntax.nix.golden" (readFile "pkgs/Cabal-syntax.nix")+++modifiedOverlay :: Spec+modifiedOverlay = describe "modified overlay" $ sequential $+ aroundAll_ withModifiedOverlay $ do+ it "overlay.nix" $ goldenStringFile "test/data/modified-overlay/output/overlay.nix.golden" (readFile "overlay.nix")+ it "pkgs/lens.nix" $ goldenStringFile "test/data/modified-overlay/output/pkgs/lens.nix.golden" (readFile "pkgs/lens.nix")+ it "pkgs/Cabal-syntax.nix" $ goldenStringFile "test/data/modified-overlay/output/pkgs/Cabal-syntax.nix.golden" (readFile "pkgs/Cabal-syntax.nix")