packages feed

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 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")