diff --git a/nix-tools/cabal.project b/nix-tools/cabal.project index 9cf7f99027..902e02ba31 100644 --- a/nix-tools/cabal.project +++ b/nix-tools/cabal.project @@ -1,4 +1,4 @@ -index-state: 2024-03-28T00:00:00Z +index-state: 2024-10-15T20:31:31Z packages: nix-tools @@ -10,24 +10,22 @@ extra-packages: cabal-install, hpack, Cabal-syntax-json test-show-details: direct allow-newer: + algebraic-graphs:deepseq, hackage-db:base, hackage-db:Cabal, - hpack:Cabal, - hnix:base, - hnix:template-haskell, hnix:aeson, + hnix:base, + hnix:bytestring, + hnix:free, hnix:relude, hnix-store-core:base, - hnix-store-core:memory, + hnix-store-core:bytestring, hnix-store-core:cryptonite, - hnix-store-core:bytestring - -source-repository-package - type: git - location: https://github.com/haskell/cabal.git - tag: c0647bc914928ab6362278c73f17b084ca3ed9ab - subdir: cabal-install - --sha256: sha256-BQs6ciCKWNzsEdUewEvUu4lcyrI5DH7abKzM4035lSc= + hnix-store-core:memory, + hnix:template-haskell, + hpack:Cabal, + lens-family-th:base, + lens-family-th:template-haskell, source-repository-package type: git @@ -38,5 +36,5 @@ source-repository-package source-repository-package type: git location: https://github.com/andreabedini/Cabal-syntax-json.git - tag: bf97be0038489239a11c61653b55afc77356ac1e - --sha256: sha256-i9TEqQqRqFM07q1Lr6wcMlURhBkhkVxHhP1jQjSE+Yg= + tag: b7192832f730d9181a013ef7c77f2ad0b30cca43 + --sha256: sha256-Yw2HQOCmjOvfKHi3xWbSniAZfyrshOvsgmUbqFmDDpU= diff --git a/nix-tools/flake.lock b/nix-tools/flake.lock index 64e554fc5f..06581f1296 100644 --- a/nix-tools/flake.lock +++ b/nix-tools/flake.lock @@ -120,11 +120,11 @@ "hackage": { "flake": false, "locked": { - "lastModified": 1721263615, - "narHash": "sha256-J/VaA4xWMpp43HptVP2tpfLwIYCg+OrBova4Uh5R8C8=", + "lastModified": 1729124899, + "narHash": "sha256-cmb4iMcgk5+jUGMiZGNMzPCAnG17Kz9J6WIitYM17Fc=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "beaee455c56dee413b33f89f6ebd0520ff435295", + "rev": "138edf81c8bcc4209e9706966f7feece70c37a96", "type": "github" }, "original": { @@ -173,11 +173,11 @@ "stackage": "stackage" }, "locked": { - "lastModified": 1721278115, - "narHash": "sha256-Vc3lqax7iBdk65nXo0YoyVs2QmvqK9F+yloB9fB9vHg=", + "lastModified": 1729126246, + "narHash": "sha256-OXGwQF3AMERYaVdImhGLlvJgOE8Zr0yURef4J7zlp6k=", "owner": "input-output-hk", "repo": "haskell.nix", - "rev": "7f87291533c05681bc4735e67705855968739dbe", + "rev": "c2070f089bf688571c14af77744982c86f0c2e21", "type": "github" }, "original": { @@ -342,16 +342,16 @@ "hls-2.9": { "flake": false, "locked": { - "lastModified": 1718469202, - "narHash": "sha256-THXSz+iwB1yQQsr/PY151+2GvtoJnTIB2pIQ4OzfjD4=", + "lastModified": 1720003792, + "narHash": "sha256-qnDx8Pk0UxtoPr7BimEsAZh9g2WuTuMB/kGqnmdryKs=", "owner": "haskell", "repo": "haskell-language-server", - "rev": "40891bccb235ebacce020b598b083eab9dda80f1", + "rev": "0c1817cb2babef0765e4e72dd297c013e8e3d12b", "type": "github" }, "original": { "owner": "haskell", - "ref": "2.9.0.0", + "ref": "2.9.0.1", "repo": "haskell-language-server", "type": "github" } @@ -579,11 +579,11 @@ }, "nixpkgs-2405": { "locked": { - "lastModified": 1720122915, - "narHash": "sha256-Nby8WWxj0elBu1xuRaUcRjPi/rU3xVbkAt2kj4QwX2U=", + "lastModified": 1726447378, + "narHash": "sha256-2yV8nmYE1p9lfmLHhOCbYwQC/W8WYfGQABoGzJOb1JQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "835cf2d3f37989c5db6585a28de967a667a75fb1", + "rev": "086b448a5d54fd117f4dc2dee55c9f0ff461bdc1", "type": "github" }, "original": { @@ -611,11 +611,11 @@ }, "nixpkgs-unstable": { "locked": { - "lastModified": 1720181791, - "narHash": "sha256-i4vJL12/AdyuQuviMMd1Hk2tsGt02hDNhA0Zj1m16N8=", + "lastModified": 1726583932, + "narHash": "sha256-zACxiQx8knB3F8+Ze+1BpiYrI+CbhxyWpcSID9kVhkQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4284c2b73c8bce4b46a6adf23e16d9e2ec8da4bb", + "rev": "658e7223191d2598641d50ee4e898126768fe847", "type": "github" }, "original": { @@ -654,11 +654,11 @@ "stackage": { "flake": false, "locked": { - "lastModified": 1721262189, - "narHash": "sha256-FhQK+UGKGBJCyLo8NBhU65QKm5loHS/APUKno/9jO/U=", + "lastModified": 1729039017, + "narHash": "sha256-fGExfgG+7UNSOV8YfOrWPpOHWrCjA02gQkeSBhaAzjQ=", "owner": "input-output-hk", "repo": "stackage.nix", - "rev": "a55d366b4ab71687ce60d428a775a4ecc824a658", + "rev": "df1d8f0960407551fea7af7af75a9c2f9e18de97", "type": "github" }, "original": { diff --git a/nix-tools/flake.nix b/nix-tools/flake.nix index edca517673..1aba66988c 100644 --- a/nix-tools/flake.nix +++ b/nix-tools/flake.nix @@ -1,8 +1,6 @@ { inputs = { nixpkgs.follows = "haskellNix/nixpkgs"; - # nixpkgs-unstable.url = "github:NixOS/nixpkgs"; - haskellNix.url = "github:input-output-hk/haskell.nix"; }; diff --git a/nix-tools/nix-tools/hackage2nix/Main.hs b/nix-tools/nix-tools/hackage2nix/Main.hs index cf71f09374..a08ac185df 100644 --- a/nix-tools/nix-tools/hackage2nix/Main.hs +++ b/nix-tools/nix-tools/hackage2nix/Main.hs @@ -1,16 +1,20 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE LambdaCase #-} -module Main where +module Main (main) where import Cabal2Nix import Cabal2Nix.Util ( quoted ) +#if !MIN_VERSION_base(4, 17, 0) import Control.Applicative ( liftA2 ) +#endif import Control.Monad.Trans.State.Strict import Crypto.Hash.SHA256 ( hash ) import qualified Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Char8 as BS +import Data.Char ( isUpper ) import Data.Foldable ( toList , for_ ) @@ -50,7 +54,6 @@ import System.Environment ( getArgs ) import System.FilePath ( () , (<.>) ) -import Data.Char (isUpper) -- Avoid issues with case insensitive file systems by escaping upper case -- characters with a leading _ character. @@ -64,27 +67,31 @@ main :: IO () main = do out:rest <- getArgs (inp, src) <- case rest of - [tarball, url, hash] -> return (tarball, Just $ Repo url (Just hash)) + [tarball, url, sha256] -> return (tarball, Just $ Repo url (Just sha256)) [tarball, url] -> return (tarball, Just $ Repo url Nothing) [tarball] -> return (tarball, Nothing) [] -> hackageTarball >>= \tarball -> return (tarball, Nothing) + _ -> error "Usage: hackage2nix [tarball [url [hash]]]" db <- U.readTarball Nothing inp let (nixFiles, cabalFiles) = runState (fmap (toList . (Seq.sortOn fst)) $ foldMapWithKeyA package2nix db) mempty createDirectoryIfMissing False out - writeFile (out "default.nix") $ - "with builtins; mapAttrs (_: mapAttrs (_: data: rec {\n\ - \ inherit (data) sha256;\n\ - \ revisions = data.revisions // {\n\ - \ default = revisions.\"${data.revisions.default}\";\n\ - \ };\n\ - \})) {\n" - -- Import all the per package nix files - <> mconcat (map (\(pname, _) -> - " " <> quoted pname <> " = import ./nix/" <> escapeUpperCase pname <> ".nix;\n") nixFiles) - <> "}\n" + writeFile (out "default.nix") $ unlines [ + "with builtins; mapAttrs (_: mapAttrs (_: data: rec {", + " inherit (data) sha256;", + " revisions = data.revisions // {", + " default = revisions.\"${data.revisions.default}\";", + " };", + "})) {", + -- Import all the per package nix files + unlines [ + " " <> quoted pname <> " = import ./nix/" <> escapeUpperCase pname <> ".nix;" + | (pname, _) <- nixFiles + ], + "}" + ] createDirectoryIfMissing False (out "nix") for_ nixFiles $ \(pname, nix) -> @@ -124,9 +131,9 @@ version2nix pname vnum (U.VersionData { U.cabalFileRevisions, U.metaFile }) = do revisionBindings <- sequenceA $ zipWith (revBindingJson pname vnum) cabalFileRevisions [0 ..] - let hash = decodeUtf8 $ fromString $ P.parseMetaData pname vnum metaFile Map.! "sha256" + let sha256 = decodeUtf8 $ fromString $ P.parseMetaData pname vnum metaFile Map.! "sha256" return $ Seq.singleton (quoted (fromPretty vnum), mkNonRecSet - [ "sha256" $= mkStr hash + [ "sha256" $= mkStr sha256 , "revisions" $= mkNonRecSet ( map (uncurry ($=)) revisionBindings ++ ["default" $= mkStr (fst (last revisionBindings))] diff --git a/nix-tools/nix-tools/lib/Cabal2Nix.hs b/nix-tools/nix-tools/lib-cabal2nix/Cabal2Nix.hs similarity index 100% rename from nix-tools/nix-tools/lib/Cabal2Nix.hs rename to nix-tools/nix-tools/lib-cabal2nix/Cabal2Nix.hs diff --git a/nix-tools/nix-tools/lib/Cabal2Nix/Util.hs b/nix-tools/nix-tools/lib-cabal2nix/Cabal2Nix/Util.hs similarity index 100% rename from nix-tools/nix-tools/lib/Cabal2Nix/Util.hs rename to nix-tools/nix-tools/lib-cabal2nix/Cabal2Nix/Util.hs diff --git a/nix-tools/nix-tools/lib/Cabal2Nix/Plan.hs b/nix-tools/nix-tools/lts2nix/Cabal2Nix/Plan.hs similarity index 100% rename from nix-tools/nix-tools/lib/Cabal2Nix/Plan.hs rename to nix-tools/nix-tools/lts2nix/Cabal2Nix/Plan.hs diff --git a/nix-tools/nix-tools/make-install-plan/MakeInstallPlan.hs b/nix-tools/nix-tools/make-install-plan/MakeInstallPlan.hs index 8c47702799..4eef9e8444 100644 --- a/nix-tools/nix-tools/make-install-plan/MakeInstallPlan.hs +++ b/nix-tools/nix-tools/make-install-plan/MakeInstallPlan.hs @@ -6,15 +6,14 @@ import qualified Cabal2Nix hiding (gpd2nix) import qualified Data.ByteString.Lazy as BSL import Data.Foldable (for_) import qualified Data.Text.Encoding as T -import Distribution.Client.DistDirLayout (DistDirLayout (distDirectory, distProjectCacheFile, distProjectFile)) +import Distribution.Client.DistDirLayout (DistDirLayout (..)) import Distribution.Client.GlobalFlags import Distribution.Client.HashValue (HashValue, showHashValue) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.NixStyleOptions (NixStyleFlags (..), defaultNixStyleFlags, nixStyleOptions) import Distribution.Client.ProjectConfig import Distribution.Client.ProjectOrchestration -import Distribution.Client.ProjectPlanOutput (writePlanExternalRepresentation) -import Distribution.Client.ProjectPlanning (ElaboratedConfiguredPackage (..), rebuildInstallPlan, availableTargets) +import Distribution.Client.ProjectPlanning (ElaboratedConfiguredPackage (..), availableTargets, rebuildInstallPlan) import Distribution.Client.Setup import Distribution.Client.Types.PackageLocation (PackageLocation (..)) import Distribution.Client.Types.Repo (LocalRepo (..), RemoteRepo (..), Repo (..)) @@ -25,7 +24,7 @@ import Distribution.Pretty (prettyShow) import Distribution.Simple.Command import Distribution.Simple.Flag import qualified Distribution.Simple.Utils as Cabal -import Distribution.Types.SourceRepo (KnownRepoType (Git), RepoType (..)) +import Distribution.Types.SourceRepo (KnownRepoType (..), RepoType (..)) import Distribution.Verbosity (Verbosity) import qualified Distribution.Verbosity as Verbosity import Freeze (projectFreezeConfig) @@ -33,6 +32,7 @@ import Nix.Expr import Nix.Pretty (prettyNix) import Prettyprinter (Doc) import Prettyprinter.Render.Text (hPutDoc) +import ProjectPlanOutput (writePlanExternalRepresentation) import System.Environment (getArgs) import System.FilePath import System.IO (IOMode (WriteMode), hClose, openFile) @@ -46,7 +46,7 @@ main = do CommandErrors errs -> putStrLn $ "commandErrors: " ++ show errs CommandReadyToGo (mkflags, _commandParse) -> let globalFlags = defaultGlobalFlags - flags@NixStyleFlags {configFlags} = mkflags (commandDefaultFlags cmdUI) + flags@NixStyleFlags{configFlags} = mkflags (commandDefaultFlags cmdUI) verbosity = fromFlagOrDefault Verbosity.normal (configVerbosity configFlags) cliConfig = commandLineFlagsToProjectConfig globalFlags flags mempty in installPlanAction verbosity cliConfig @@ -54,19 +54,19 @@ main = do cmdUI :: CommandUI (NixStyleFlags ()) cmdUI = CommandUI - { commandName = "", - commandSynopsis = "Makes an install-plan", - commandUsage = ("Usage: " ++), - commandDescription = Nothing, - commandNotes = Nothing, - commandDefaultFlags = defaultNixStyleFlags (), - commandOptions = nixStyleOptions (const []) + { commandName = "" + , commandSynopsis = "Makes an install-plan" + , commandUsage = ("Usage: " ++) + , commandDescription = Nothing + , commandNotes = Nothing + , commandDefaultFlags = defaultNixStyleFlags () + , commandOptions = nixStyleOptions (const []) } -- The following is adapted from cabal-install's Distribution.Client.CmdFreeze installPlanAction :: Verbosity -> ProjectConfig -> IO () installPlanAction verbosity cliConfig = do - ProjectBaseContext {distDirLayout, cabalDirLayout, projectConfig, localPackages} <- + ProjectBaseContext{distDirLayout, cabalDirLayout, projectConfig, localPackages} <- establishProjectBaseContext verbosity cliConfig OtherCommand (_improvedPlan, elaboratedPlan, elaboratedSharedConfig, totalIndexState, activeRepos) <- @@ -74,7 +74,11 @@ installPlanAction verbosity cliConfig = do -- Write plan.json Cabal.notice verbosity $ "Writing plan.json to " ++ distProjectCacheFile distDirLayout "plan.json" - writePlanExternalRepresentation distDirLayout elaboratedPlan elaboratedSharedConfig (availableTargets elaboratedPlan) + writePlanExternalRepresentation + distDirLayout + elaboratedPlan + elaboratedSharedConfig + (availableTargets elaboratedPlan) -- Write cabal.freeze let freezeConfig = projectFreezeConfig elaboratedPlan totalIndexState activeRepos @@ -90,12 +94,12 @@ installPlanAction verbosity cliConfig = do for_ ecps $ \ElaboratedConfiguredPackage - { elabPkgSourceId, - elabPkgSourceLocation, - elabPkgSourceHash, - elabLocalToProject, - elabPkgDescriptionOverride - } -> do + { elabPkgSourceId + , elabPkgSourceLocation + , elabPkgSourceHash + , elabLocalToProject + , elabPkgDescriptionOverride + } -> do let nixFile = cabalFilesDir prettyShow (pkgName elabPkgSourceId) <.> "nix" for_ elabPkgDescriptionOverride $ \pkgTxt -> do -- In the plan we have elabPkgDescription :: PackageDescription which is the cabal file after @@ -123,8 +127,8 @@ packageLocation2Src pkgSrcLoc pkgSrcHash = case pkgSrcLoc of (SourceRepositoryPackage (KnownRepoType Git) location (Just tag) branch subdir []) -> Cabal2Nix.Git location tag branch subdir _otherCases -> error $ "Repository " <> show sourceRepoMaybe <> " not supported" - where - mSrcHash = showHashValue <$> pkgSrcHash + where + mSrcHash = showHashValue <$> pkgSrcHash writeDoc :: FilePath -> Doc ann -> IO () writeDoc file doc = do diff --git a/nix-tools/nix-tools/make-install-plan/ProjectPlanOutput.hs b/nix-tools/nix-tools/make-install-plan/ProjectPlanOutput.hs new file mode 100644 index 0000000000..374dc15e9c --- /dev/null +++ b/nix-tools/nix-tools/make-install-plan/ProjectPlanOutput.hs @@ -0,0 +1,334 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module ProjectPlanOutput ( + -- * Plan output + writePlanExternalRepresentation, +) where + +import Distribution.Client.DistDirLayout +import Distribution.Client.HashValue (hashValue, showHashValue) +import Distribution.Client.ProjectPlanning.Types +import Distribution.Client.Types.ConfiguredId (confInstId) +import Distribution.Client.Types.PackageLocation (PackageLocation (..)) +import Distribution.Client.Types.Repo (RemoteRepo (..), Repo (..)) +import Distribution.Client.Types.SourceRepo (SourceRepoMaybe, SourceRepositoryPackage (..)) +import Distribution.Client.Version (cabalInstallVersion) + +import qualified Distribution.Client.InstallPlan as InstallPlan +import qualified Distribution.Client.Utils.Json as J +import qualified Distribution.Simple.InstallDirs as InstallDirs + +import qualified Distribution.Solver.Types.ComponentDeps as ComponentDeps + +import Distribution.InstalledPackageInfo (InstalledPackageInfo) +import Distribution.Package +import qualified Distribution.PackageDescription as PD +import Distribution.Simple.BuildPaths ( + buildInfoPref, + dllExtension, + exeExtension, + ) +import Distribution.Simple.Compiler ( + showCompilerId, + ) +import Distribution.Simple.Utils +import Distribution.System +import Distribution.Types.ComponentName +import Distribution.Types.Version ( + mkVersion, + ) + +import Distribution.Client.Compat.Prelude +import Prelude () + +import qualified Data.ByteString.Builder as BB +import qualified Data.Map as Map + +import System.FilePath + +import Distribution.Client.ProjectPlanning + +----------------------------------------------------------------------------- +-- Writing plan.json files +-- + +{- | Write out a representation of the elaborated install plan. + +This is for the benefit of debugging and external tools like editors. +-} +writePlanExternalRepresentation :: + DistDirLayout -> + ElaboratedInstallPlan -> + ElaboratedSharedConfig -> + Map (PackageId, ComponentName) [AvailableTarget (UnitId, ComponentName)] -> + IO () +writePlanExternalRepresentation + distDirLayout + elaboratedInstallPlan + elaboratedSharedConfig + targets = + writeFileAtomic (distProjectCacheFile distDirLayout "plan.json") + $ BB.toLazyByteString + . J.encodeToBuilder + $ encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig targets + +{- | Renders a subset of the elaborated install plan in a semi-stable JSON +format. +-} +encodePlanAsJson :: + DistDirLayout -> + ElaboratedInstallPlan -> + ElaboratedSharedConfig -> + Map (PackageId, ComponentName) [AvailableTarget (UnitId, ComponentName)] -> + J.Value +encodePlanAsJson distDirLayout elaboratedInstallPlan elaboratedSharedConfig targets = + -- TODO: [nice to have] include all of the sharedPackageConfig and all of + -- the parts of the elaboratedInstallPlan + J.object + [ "cabal-version" J..= jdisplay cabalInstallVersion + , "cabal-lib-version" J..= jdisplay cabalVersion + , "compiler-id" + J..= (J.String . showCompilerId . pkgConfigCompiler) + elaboratedSharedConfig + , "os" J..= jdisplay os + , "arch" J..= jdisplay arch + , "install-plan" J..= installPlanToJ elaboratedInstallPlan + , "targets" J..= targetsToJ targets + ] + where + plat :: Platform + plat@(Platform arch os) = pkgConfigPlatform elaboratedSharedConfig + + installPlanToJ :: ElaboratedInstallPlan -> [J.Value] + installPlanToJ = map planPackageToJ . InstallPlan.toList + + targetsToJ :: Map (PackageId, ComponentName) [AvailableTarget (UnitId, ComponentName)] -> [J.Value] + targetsToJ = map targetToJ . Map.toList + + targetToJ :: ((PackageId, ComponentName), [AvailableTarget (UnitId, ComponentName)]) -> J.Value + targetToJ ((pkgId, componentName), ts) = + J.object + [ "pkg-name" J..= jdisplay (pkgName pkgId) + , "pkg-version" J..= jdisplay (pkgVersion pkgId) + , "component-name" J..= jdisplay componentName + , "available" J..= map avaialbeTargetToJ ts + ] + + avaialbeTargetToJ :: AvailableTarget (UnitId, ComponentName) -> J.Value + avaialbeTargetToJ target = + case availableTargetStatus target of + TargetDisabledByUser -> J.String "TargetDisabledByUser" + TargetDisabledBySolver -> J.String "TargetDisabledBySolver" + TargetNotBuildable -> J.String "TargetNotBuildable" + TargetNotLocal -> J.String "TargetNotLocal" + TargetBuildable (unitId, componentName) requested -> + J.object + [ "id" J..= jdisplay unitId + , "component-name" J..= jdisplay componentName + , "build-by-default" J..= (requested == TargetRequestedByDefault) + ] + + planPackageToJ :: ElaboratedPlanPackage -> J.Value + planPackageToJ pkg = + case pkg of + InstallPlan.PreExisting ipi -> installedPackageInfoToJ ipi + InstallPlan.Configured elab -> elaboratedPackageToJ False elab + InstallPlan.Installed elab -> elaboratedPackageToJ True elab + -- Note that the plan.json currently only uses the elaborated plan, + -- not the improved plan. So we will not get the Installed state for + -- that case, but the code supports it in case we want to use this + -- later in some use case where we want the status of the build. + + installedPackageInfoToJ :: InstalledPackageInfo -> J.Value + installedPackageInfoToJ ipi = + -- Pre-existing packages lack configuration information such as their flag + -- settings or non-lib components. We only get pre-existing packages for + -- the global/core packages however, so this isn't generally a problem. + -- So these packages are never local to the project. + -- + J.object + [ "type" J..= J.String "pre-existing" + , "id" J..= (jdisplay . installedUnitId) ipi + , "pkg-name" J..= (jdisplay . pkgName . packageId) ipi + , "pkg-version" J..= (jdisplay . pkgVersion . packageId) ipi + , "depends" J..= map jdisplay (installedDepends ipi) + ] + + elaboratedPackageToJ :: Bool -> ElaboratedConfiguredPackage -> J.Value + elaboratedPackageToJ isInstalled elab = + J.object + $ [ "type" + J..= J.String + ( if isInstalled + then "installed" + else "configured" + ) + , "id" J..= (jdisplay . installedUnitId) elab + , "pkg-name" J..= (jdisplay . pkgName . packageId) elab + , "pkg-version" J..= (jdisplay . pkgVersion . packageId) elab + , "flags" + J..= J.object + [ PD.unFlagName fn J..= v + | (fn, v) <- PD.unFlagAssignment (elabFlagAssignment elab) + ] + , "style" J..= J.String (style2str (elabLocalToProject elab) (elabBuildStyle elab)) + , "pkg-src" J..= packageLocationToJ (elabPkgSourceLocation elab) + ] + ++ [ "pkg-cabal-sha256" J..= J.String (showHashValue hash) + | Just hash <- [fmap hashValue (elabPkgDescriptionOverride elab)] + ] + ++ [ "pkg-src-sha256" J..= J.String (showHashValue hash) + | Just hash <- [elabPkgSourceHash elab] + ] + ++ ( case elabBuildStyle elab of + BuildInplaceOnly _ -> + ["dist-dir" J..= J.String dist_dir] ++ [buildInfoFileLocation] + BuildAndInstall -> + -- TODO: install dirs? + [] + ) + ++ case elabPkgOrComp elab of + ElabPackage pkg -> + let components = + J.object + $ [ comp2str c + J..= ( J.object + $ [ "depends" J..= map (jdisplay . confInstId) (map fst ldeps) + , "exe-depends" J..= map (jdisplay . confInstId) edeps + ] + ++ bin_file c + ) + | (c, (ldeps, edeps)) <- + ComponentDeps.toList + $ ComponentDeps.zip + (pkgLibDependencies pkg) + (pkgExeDependencies pkg) + ] + in ["components" J..= components] + ElabComponent comp -> + [ "depends" J..= map (jdisplay . confInstId) (map fst $ elabLibDependencies elab) + , "exe-depends" J..= map jdisplay (elabExeDependencies elab) + , "component-name" J..= J.String (comp2str (compSolverName comp)) + ] + ++ bin_file (compSolverName comp) + where + -- \| Only add build-info file location if the Setup.hs CLI + -- is recent enough to be able to generate build info files. + -- Otherwise, write 'null'. + -- + -- Consumers of `plan.json` can use the nullability of this file location + -- to indicate that the given component uses `build-type: Custom` + -- with an old lib:Cabal version. + buildInfoFileLocation :: J.Pair + buildInfoFileLocation + | elabSetupScriptCliVersion elab < mkVersion [3, 7, 0, 0] = + "build-info" J..= J.Null + | otherwise = + "build-info" J..= J.String (buildInfoPref dist_dir) + + packageLocationToJ :: PackageLocation (Maybe FilePath) -> J.Value + packageLocationToJ pkgloc = + case pkgloc of + LocalUnpackedPackage local -> + J.object + [ "type" J..= J.String "local" + , "path" J..= J.String local + ] + LocalTarballPackage local -> + J.object + [ "type" J..= J.String "local-tar" + , "path" J..= J.String local + ] + RemoteTarballPackage uri _ -> + J.object + [ "type" J..= J.String "remote-tar" + , "uri" J..= J.String (show uri) + ] + RepoTarballPackage repo _ _ -> + J.object + [ "type" J..= J.String "repo-tar" + , "repo" J..= repoToJ repo + ] + RemoteSourceRepoPackage srcRepo _ -> + J.object + [ "type" J..= J.String "source-repo" + , "source-repo" J..= sourceRepoToJ srcRepo + ] + + repoToJ :: Repo -> J.Value + repoToJ repo = + case repo of + RepoLocalNoIndex{..} -> + J.object + [ "type" J..= J.String "local-repo-no-index" + , "path" J..= J.String repoLocalDir + ] + RepoRemote{..} -> + J.object + [ "type" J..= J.String "remote-repo" + , "uri" J..= J.String (show (remoteRepoURI repoRemote)) + ] + RepoSecure{..} -> + J.object + [ "type" J..= J.String "secure-repo" + , "uri" J..= J.String (show (remoteRepoURI repoRemote)) + ] + + sourceRepoToJ :: SourceRepoMaybe -> J.Value + sourceRepoToJ SourceRepositoryPackage{..} = + J.object + $ filter ((/= J.Null) . snd) + $ [ "type" J..= jdisplay srpType + , "location" J..= J.String srpLocation + , "branch" J..= fmap J.String srpBranch + , "tag" J..= fmap J.String srpTag + , "subdir" J..= fmap J.String srpSubdir + ] + + dist_dir :: FilePath + dist_dir = + distBuildDirectory + distDirLayout + (elabDistDirParams elaboratedSharedConfig elab) + + bin_file :: ComponentDeps.Component -> [J.Pair] + bin_file c = case c of + ComponentDeps.ComponentExe s -> bin_file' s + ComponentDeps.ComponentTest s -> bin_file' s + ComponentDeps.ComponentBench s -> bin_file' s + ComponentDeps.ComponentFLib s -> flib_file' s + _ -> [] + bin_file' s = + ["bin-file" J..= J.String bin] + where + bin = + if isInplaceBuildStyle (elabBuildStyle elab) + then dist_dir "build" prettyShow s prettyShow s <.> exeExtension plat + else InstallDirs.bindir (elabInstallDirs elab) prettyShow s <.> exeExtension plat + + flib_file' :: (Pretty a, Show a) => a -> [J.Pair] + flib_file' s = + ["bin-file" J..= J.String bin] + where + bin = + if isInplaceBuildStyle (elabBuildStyle elab) + then dist_dir "build" prettyShow s ("lib" ++ prettyShow s) <.> dllExtension plat + else InstallDirs.bindir (elabInstallDirs elab) ("lib" ++ prettyShow s) <.> dllExtension plat + +comp2str :: ComponentDeps.Component -> String +comp2str = prettyShow + +style2str :: Bool -> BuildStyle -> String +style2str True _ = "local" +style2str False (BuildInplaceOnly OnDisk) = "inplace" +style2str False (BuildInplaceOnly InMemory) = "interactive" +style2str False BuildAndInstall = "global" + +jdisplay :: (Pretty a) => a -> J.Value +jdisplay = J.String . prettyShow diff --git a/nix-tools/nix-tools/nix-tools.cabal b/nix-tools/nix-tools/nix-tools.cabal index 5936f46958..27544a5b2e 100644 --- a/nix-tools/nix-tools/nix-tools.cabal +++ b/nix-tools/nix-tools/nix-tools.cabal @@ -13,45 +13,39 @@ build-type: Simple common warnings ghc-options: -Wall +common cabal-deps + build-depends: + Cabal ^>=3.12, + Cabal-syntax ^>=3.12 + +common cabal-install-deps + import: cabal-deps + build-depends: + cabal-install ^>=3.12, + cabal-install-solver ^>=3.12 + library import: warnings - exposed-modules: Cabal2Nix - , Cabal2Nix.Util - , Cabal2Nix.Plan - , CabalName - , CabalName.CLI - , Distribution.Nixpkgs.Fetch - , StackRepos - , StackRepos.CLI - , Stack2nix - , Stack2nix.Cache - , Stack2nix.CLI - , Stack2nix.External.Resolve - , Stack2nix.Project - , Stack2nix.Stack - build-depends: base >= 4 && <4.20 - , Cabal >= 3.10.3 && <3.11 - , Cabal-syntax >= 3.10 && <3.11 - , aeson >= 2.0 && <2.3 + , cabal-deps + build-depends: base + , aeson , aeson-pretty , base16-bytestring , bytestring - , cryptohash-sha256 , containers + , cryptohash-sha256 , data-fix , deepseq , directory , extra , filepath --- Needs https://github.com/input-output-hk/iohk-nix/commit/6a8c29117eff36ce975e02e01efc8b25d93fcb90#diff-6fb0c6517b547a8baf082d5d2d604842 --- to work with the data-dir issues when building components. --- This commit is included since 0.6.5. - , hnix >= 0.6.5 && <0.18 + , hnix ^>=0.17 , hpack , http-client , http-client-tls , http-types , network-uri + , nix-tools:cabal2nix , optparse-applicative , prettyprinter , process @@ -59,101 +53,132 @@ library , transformers , unordered-containers , yaml - + exposed-modules: , CabalName + , CabalName.CLI + , Distribution.Nixpkgs.Fetch + , StackRepos + , StackRepos.CLI + , Stack2nix + , Stack2nix.Cache + , Stack2nix.CLI + , Stack2nix.External.Resolve + , Stack2nix.Project + , Stack2nix.Stack hs-source-dirs: lib default-language: Haskell2010 +library cabal2nix + import: warnings + , cabal-deps + build-depends: , base + , base16-bytestring + , bytestring + , cryptohash-sha256 + , data-fix + , directory + , filepath + , hnix + , network-uri + , text + exposed-modules: Cabal2Nix + Cabal2Nix.Util + hs-source-dirs: lib-cabal2nix + default-language: Haskell2010 executable cabal-to-nix import: warnings - ghc-options: -Wall main-is: Main.hs build-depends: base - , transformers , bytestring - , hpack + , directory + , filepath , hnix - , text + , hpack , nix-tools - , filepath - , directory + , nix-tools:cabal2nix , prettyprinter + , text + , transformers hs-source-dirs: cabal2nix default-language: Haskell2010 executable hashes-to-nix - ghc-options: -Wall main-is: Main.hs build-depends: base - , hnix - , nix-tools - , data-fix , aeson + , data-fix + , directory + , filepath + , hnix , microlens , microlens-aeson + , nix-tools + , nix-tools:cabal2nix , text - , filepath - , directory hs-source-dirs: hashes2nix default-language: Haskell2010 executable plan-to-nix import: warnings - ghc-options: -Wall - main-is: Main.hs - other-modules: Plan2Nix - , Plan2Nix.Cache - , Plan2Nix.CLI - , Plan2Nix.Project - , Plan2Nix.Plan - build-depends: base - , nix-tools + , cabal-deps + build-depends: , base + , aeson + , bytestring + , directory + , extra + , filepath , hnix - , Cabal - , text , hpack - , unordered-containers - , vector - , aeson , microlens , microlens-aeson + , nix-tools + , nix-tools:cabal2nix , optparse-applicative , prettyprinter - , filepath - , directory - , bytestring + , text , transformers - , extra + , unordered-containers + , vector + main-is: Main.hs + other-modules: Plan2Nix + , Plan2Nix.Cache + , Plan2Nix.CLI + , Plan2Nix.Project + , Plan2Nix.Plan hs-source-dirs: plan2nix default-language: Haskell2010 executable hackage-to-nix import: warnings - main-is: Main.hs - build-depends: base - , nix-tools - , hackage-db - , hnix - , Cabal + , cabal-deps + build-depends: , base , aeson , aeson-pretty - , containers - , bytestring - , text - , cryptohash-sha256 , base16-bytestring , base64-bytestring - , filepath + , bytestring + , Cabal + , containers + , cryptohash-sha256 , directory + , filepath + , hackage-db + , hnix + , nix-tools + , nix-tools:cabal2nix + , text , transformers + main-is: Main.hs hs-source-dirs: hackage2nix default-language: Haskell2010 executable lts-to-nix import: warnings main-is: Main.hs + other-modules: Cabal2Nix.Plan build-depends: base , nix-tools + , nix-tools:cabal2nix , hnix , yaml , aeson @@ -180,11 +205,11 @@ executable truncate-index import: warnings main-is: Main.hs build-depends: base + , bytestring , optparse-applicative - , zlib , tar - , bytestring , time + , zlib hs-source-dirs: truncate-index default-language: Haskell2010 @@ -206,22 +231,20 @@ executable cabal-name executable make-install-plan import: warnings - main-is: MakeInstallPlan.hs - other-modules: Freeze + , cabal-install-deps build-depends: base , bytestring - , Cabal >= 3.10 - , cabal-install >= 3.10 - , cabal-install-solver >= 3.10 - , Cabal-syntax >= 3.10 , containers + , directory , filepath , hnix - , nix-tools + , nix-tools:cabal2nix , prettyprinter , text + main-is: MakeInstallPlan.hs + other-modules: Freeze + ProjectPlanOutput hs-source-dirs: make-install-plan - , plan2nix default-language: Haskell2010 executable default-setup @@ -242,7 +265,7 @@ executable default-setup-ghcjs hs-source-dirs: setup-ghcjs default-language: Haskell2010 -etest-suite tests +test-suite tests import: warnings main-is: Tests.hs build-depends: base diff --git a/nix-tools/nix-tools/setup-ghcjs/Setup.hs b/nix-tools/nix-tools/setup-ghcjs/Setup.hs index 4b2e6db162..c4a20daba6 100644 --- a/nix-tools/nix-tools/setup-ghcjs/Setup.hs +++ b/nix-tools/nix-tools/setup-ghcjs/Setup.hs @@ -1,35 +1,32 @@ {-# language LambdaCase #-} import Distribution.Simple import Distribution.Simple.Setup -import Distribution.Types.PackageDescription +import Distribution.Types.PackageDescription hiding (updatePackageDescription) import Distribution.Types.LocalBuildInfo import Distribution.Simple.PackageIndex import Distribution.Types.InstalledPackageInfo hiding (includeDirs) import qualified Distribution.Types.InstalledPackageInfo as IPI -import Distribution.Types.PackageName import System.FilePath import Control.Monad (filterM, forM_, forM, unless) import System.Directory (doesFileExist) import Distribution.Types.Library (libBuildInfo, Library(..)) -import Distribution.Types.BuildInfo (cSources, jsSources, includeDirs, emptyBuildInfo, options, extraBundledLibs) -import Distribution.Simple.BuildTarget (readBuildTargets, BuildTarget(..), readUserBuildTargets) +import Distribution.Types.BuildInfo (cSources, jsSources, includeDirs, options, extraBundledLibs) +import Distribution.Simple.BuildTarget (readBuildTargets, BuildTarget(..)) import Distribution.Verbosity (silent, verbose) import Distribution.Types.ComponentName import Distribution.Simple.Program.Types (programPath) -import Distribution.Simple.Program.Db (lookupKnownProgram, lookupProgram, knownPrograms) -import Distribution.Simple.Program (Program, gccProgram, arProgram, runDbProgram, simpleProgram, ghcProgram) +import Distribution.Simple.Program.Db (lookupProgram) +import Distribution.Simple.Program (Program, gccProgram, runDbProgram, simpleProgram, ghcProgram) import Distribution.Simple.Utils (createDirectoryIfMissingVerbose) -import Distribution.Types.HookedBuildInfo import Data.List (isPrefixOf, isSuffixOf, intercalate) -import System.Environment (getArgs, getProgName) +import System.Environment (getArgs) import Distribution.Simple.LocalBuildInfo (Component (..), withAllComponentsInBuildOrder, componentBuildDir) import Distribution.Types.TestSuite (TestSuite(..)) import Distribution.Types.TestSuiteInterface (TestSuiteInterface(..) ) import Distribution.Simple.Test.LibV09 (stubName) import Distribution.Types.Executable (exeName, Executable(..)) import Distribution.Types.Benchmark (Benchmark(..)) -import Distribution.Types.TestSuite (TestSuite(..)) -import Distribution.Types.UnqualComponentName (unUnqualComponentName, mkUnqualComponentName) +import Distribution.Types.UnqualComponentName (unUnqualComponentName) emarProgram :: Program @@ -87,7 +84,7 @@ linkCLib libname desc lbi = do libDirs = [ "-L" <> path | path <- concatMap IPI.libraryDirs (topologicalOrder $ installedPkgs lbi) ] let verbosity = verbose - libs <- filterM doesFileExist $ + libs0 <- filterM doesFileExist $ concatMap (\x -> [ libDir "libEMCC" <> (unPackageName . pkgName . sourcePackageId $ x) <> ".js_a" | libDir <- libraryDirs x ]) (topologicalOrder $ installedPkgs lbi) @@ -97,13 +94,13 @@ linkCLib libname desc lbi = do (topologicalOrder $ installedPkgs lbi) print exff exfns <- concat <$> forM exff (fmap words . readFile) - unless (null libs && null exfns) $ do - libs <- case libs of + unless (null libs0 && null exfns) $ do + libs1 <- case libs0 of [] -> do writeFile (buildDir lbi "emcc_linking_dummy.c") "" runDbProgram verbosity gccProgram (withPrograms lbi) $ ["-c", buildDir lbi "emcc_linking_dummy.c", "-o", buildDir lbi "emcc_linking_dummy.o"] return [(buildDir lbi "emcc_linking_dummy.o")] - _ -> return libs + _ -> return libs0 let dst = if libname == "emcc" "lib.js" then buildDir lbi -- who designed this shit in cabal? @@ -123,10 +120,10 @@ linkCLib libname desc lbi = do , "-s", "EXPORTED_RUNTIME_METHODS=['printErr','addFunction','removeFunction','getTempRet0','setTempRet0']" -- , "-s", "EXPORTED_FUNCTIONS=[" <> intercalate ", " (map (\f -> "'" <> f <> "'") exfns) <> "]" - ] ++ libs ++ libDirs ++ extraLibs + ] ++ libs1 ++ libDirs ++ extraLibs postBuildHook :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO () -postBuildHook args flags desc lbi = do +postBuildHook _args flags desc lbi = do case (takeFileName . programPath <$> lookupProgram ghcProgram (withPrograms lbi)) of Just "js-unknown-ghcjs-ghc" -> readBuildTargets silent desc (buildArgs flags) >>= \case @@ -182,11 +179,11 @@ emccBuildHook desc lbi hooks flags = do updateBench :: Benchmark -> Benchmark updateBench bench@Benchmark{ benchmarkBuildInfo = bi } = bench { benchmarkBuildInfo = bi { options = options bi <> extraOpts } } updatePackageDescription :: PackageDescription -> PackageDescription - updatePackageDescription desc = desc - { library = updateLibrary <$> library desc - , executables = updateExe <$> executables desc - , testSuites = updateTest <$> testSuites desc - , benchmarks = updateBench <$> benchmarks desc + updatePackageDescription desc' = desc' + { library = updateLibrary <$> library desc' + , executables = updateExe <$> executables desc' + , testSuites = updateTest <$> testSuites desc' + , benchmarks = updateBench <$> benchmarks desc' } -- @@ -205,12 +202,11 @@ emccCopyHook desc lbi hooks flags = do desc' = updatePackageDescription emccLibs desc copyHook simpleUserHooks desc' lbi' hooks flags where - emccLib = (buildDir lbi) "libEMCC" <> (unPackageName . pkgName . package $ desc) <> ".js_a" -- don't inject it for libraries, only for exe, test, bench. updateLibrary :: [String] -> Library -> Library updateLibrary extraLibs lib@Library{ libBuildInfo = bi } = lib { libBuildInfo = bi { extraBundledLibs = extraBundledLibs bi <> extraLibs } } updatePackageDescription :: [String] -> PackageDescription -> PackageDescription - updatePackageDescription extraLibs desc = desc { library = updateLibrary extraLibs <$> library desc } + updatePackageDescription extraLibs desc' = desc' { library = updateLibrary extraLibs <$> library desc' } emccRegHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO () emccRegHook desc lbi hooks flags = do @@ -222,12 +218,11 @@ emccRegHook desc lbi hooks flags = do desc' = updatePackageDescription emccLibs desc regHook simpleUserHooks desc' lbi' hooks flags where - emccLib = (buildDir lbi) "libEMCC" <> (unPackageName . pkgName . package $ desc) <> ".js_a" -- don't inject it for libraries, only for exe, test, bench. updateLibrary :: [String] -> Library -> Library updateLibrary extraLibs lib@Library{ libBuildInfo = bi } = lib { libBuildInfo = bi { extraBundledLibs = extraBundledLibs bi <> extraLibs } } updatePackageDescription :: [String] -> PackageDescription -> PackageDescription - updatePackageDescription extraLibs desc = desc { library = updateLibrary extraLibs <$> library desc } + updatePackageDescription extraLibs desc' = desc' { library = updateLibrary extraLibs <$> library desc' } emccUnregHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO () emccUnregHook desc lbi hooks flags = do @@ -239,12 +234,11 @@ emccUnregHook desc lbi hooks flags = do desc' = updatePackageDescription emccLibs desc unregHook simpleUserHooks desc' lbi' hooks flags where - emccLib = (buildDir lbi) "libEMCC" <> (unPackageName . pkgName . package $ desc) <> ".js_a" -- don't inject it for libraries, only for exe, test, bench. updateLibrary :: [String] -> Library -> Library updateLibrary extraLibs lib@Library{ libBuildInfo = bi } = lib { libBuildInfo = bi { extraBundledLibs = extraBundledLibs bi <> extraLibs } } updatePackageDescription :: [String] -> PackageDescription -> PackageDescription - updatePackageDescription extraLibs desc = desc { library = updateLibrary extraLibs <$> library desc } + updatePackageDescription extraLibs desc' = desc' { library = updateLibrary extraLibs <$> library desc' } -- -- Main -- diff --git a/nix-tools/nix-tools/truncate-index/Main.hs b/nix-tools/nix-tools/truncate-index/Main.hs index 3041b2480f..9a95207609 100644 --- a/nix-tools/nix-tools/truncate-index/Main.hs +++ b/nix-tools/nix-tools/truncate-index/Main.hs @@ -3,33 +3,27 @@ module Main (main) where -import Data.Maybe import Data.Time.Clock -import Data.Time.Format import Data.Time.Clock.POSIX +import Data.Time.Format.ISO8601 import Codec.Archive.Tar as Tar import Codec.Archive.Tar.Entry as Tar import Data.ByteString.Lazy as BS hiding (filter) import Codec.Compression.GZip as GZip import Options.Applicative hiding (option) -import Data.Semigroup ((<>)) +import Options.Applicative --- | Parse a hackage index state like "2019-01-01T12:00:00Z" -parseIndexState :: String -> Maybe UTCTime -parseIndexState = parseTimeM True defaultTimeLocale (iso8601DateFormat (Just "%T%Z")) - --- | Convert the standard 'UTCTime' type into the 'EpochTime' used by the @tar@ --- library. +-- | Convert the standard 'UTCTime' type into the 'EpochTime' used by the @tar@ library. toEpochTime :: UTCTime -> EpochTime toEpochTime = floor . utcTimeToPOSIXSeconds -- | Filter Haskell Index -filterHaskellIndex :: FilePath -> String -> FilePath -> IO () +filterHaskellIndex :: FilePath -> UTCTime -> FilePath -> IO () filterHaskellIndex orig indexState out = BS.writeFile out . nukeHeaderOS . GZip.compress . Tar.write . f . Tar.read . GZip.decompress =<< BS.readFile orig where f = filter ((<= ts) . Tar.entryTime) . toList - ts = toEpochTime . fromJust . parseIndexState $ indexState + ts = toEpochTime indexState -- gzip headers containt he OS, but we want a stable hash. -- 0xff is unknown OS. http://www.zlib.org/rfc-gzip.html nukeHeaderOS :: BS.ByteString -> BS.ByteString @@ -46,14 +40,14 @@ toList (Fail e) = error (show e) data Args = Args { argOutput :: FilePath , argInput :: FilePath - , argIndexState :: String + , argIndexState :: UTCTime } deriving Show args :: Parser Args args = Args <$> strOption ( long "output" <> short 'o' <> value "00-index.tar.gz" <> showDefault <> metavar "FILE" <> help "The output index" ) <*> strOption ( long "input" <> short 'i' <> value "01-index.tar.gz" <> showDefault <> metavar "FILE" <> help "The input index" ) - <*> strOption ( long "indexState" <> short 's' <> metavar "INDEX" <> help "Index State ( YYYY-MM-DDTHH:MM:SSZ )" ) + <*> option (maybeReader iso8601ParseM) ( long "indexState" <> short 's' <> metavar "INDEX" <> help "Index State ( YYYY-MM-DDTHH:MM:SSZ )" ) main :: IO () main = execParser opts >>= \Args{..} -> filterHaskellIndex argInput argIndexState argOutput diff --git a/nix-tools/overlay.nix b/nix-tools/overlay.nix index dd915b6f48..6035cda448 100644 --- a/nix-tools/overlay.nix +++ b/nix-tools/overlay.nix @@ -1,7 +1,7 @@ final: _prev: let - compiler-nix-name = "ghc96"; + compiler-nix-name = "ghc9101"; nix-tools = nix-tools-set { nix-tools = nix-tools-unchecked;