diff --git a/Cabal/src/Distribution/Simple.hs b/Cabal/src/Distribution/Simple.hs index 3e9e06a7169..6e54ad45017 100644 --- a/Cabal/src/Distribution/Simple.hs +++ b/Cabal/src/Distribution/Simple.hs @@ -153,6 +153,15 @@ defaultMainWithSetupHooksArgs setupHooks = , hscolourHook = setup_hscolourHook } where + preBuildHook = + case SetupHooks.preBuildComponentRules (SetupHooks.buildHooks setupHooks) of + Nothing -> const $ return [] + Just pbcRules -> \pbci -> runPreBuildHooks pbci pbcRules + postBuildHook = + case SetupHooks.postBuildComponentHook (SetupHooks.buildHooks setupHooks) of + Nothing -> const $ return () + Just hk -> hk + setup_confHook :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags @@ -168,12 +177,13 @@ defaultMainWithSetupHooksArgs setupHooks = -> BuildFlags -> IO () setup_buildHook pkg_descr lbi hooks flags = - build_setupHooks - (SetupHooks.buildHooks setupHooks) - pkg_descr - lbi - flags - (allSuffixHandlers hooks) + void $ + build_setupHooks + (preBuildHook, postBuildHook) + pkg_descr + lbi + flags + (allSuffixHandlers hooks) setup_copyHook :: PackageDescription @@ -207,7 +217,7 @@ defaultMainWithSetupHooksArgs setupHooks = -> IO () setup_replHook pkg_descr lbi hooks flags args = repl_setupHooks - (SetupHooks.buildHooks setupHooks) + preBuildHook pkg_descr lbi flags @@ -221,12 +231,13 @@ defaultMainWithSetupHooksArgs setupHooks = -> HaddockFlags -> IO () setup_haddockHook pkg_descr lbi hooks flags = - haddock_setupHooks - (SetupHooks.buildHooks setupHooks) - pkg_descr - lbi - (allSuffixHandlers hooks) - flags + void $ + haddock_setupHooks + preBuildHook + pkg_descr + lbi + (allSuffixHandlers hooks) + flags setup_hscolourHook :: PackageDescription @@ -236,7 +247,7 @@ defaultMainWithSetupHooksArgs setupHooks = -> IO () setup_hscolourHook pkg_descr lbi hooks flags = hscolour_setupHooks - (SetupHooks.buildHooks setupHooks) + preBuildHook pkg_descr lbi (allSuffixHandlers hooks) diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 8926682ce84..44f9e24d43e 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -25,6 +25,8 @@ module Distribution.Simple.Build ( -- * Build build , build_setupHooks + , buildComponent + , runPostBuildHooks -- * Repl , repl @@ -33,6 +35,7 @@ module Distribution.Simple.Build -- * Build preparation , preBuildComponent + , runPreBuildHooks , AutogenFile (..) , AutogenFileContents , writeBuiltinAutogenFiles @@ -91,6 +94,7 @@ import Distribution.Simple.BuildPaths import Distribution.Simple.BuildTarget import Distribution.Simple.BuildToolDepends import Distribution.Simple.Configure +import Distribution.Simple.Errors import Distribution.Simple.Flag import Distribution.Simple.LocalBuildInfo import Distribution.Simple.PreProcess @@ -104,9 +108,8 @@ import Distribution.Simple.Setup.Common import Distribution.Simple.Setup.Config import Distribution.Simple.Setup.Repl import Distribution.Simple.SetupHooks.Internal - ( BuildHooks (..) - , BuildingWhat (..) - , noBuildHooks + ( BuildingWhat (..) + , buildingWhatVerbosity ) import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks import qualified Distribution.Simple.SetupHooks.Rule as SetupHooks @@ -126,7 +129,6 @@ import Distribution.Compat.Graph (IsNode (..)) import Control.Monad import qualified Data.ByteString.Lazy as LBS import qualified Data.Map as Map -import Distribution.Simple.Errors import System.Directory (doesFileExist, removeFile) import System.FilePath (takeDirectory) @@ -143,10 +145,16 @@ build -> [PPSuffixHandler] -- ^ preprocessors to run before compiling -> IO () -build = build_setupHooks noBuildHooks +build pkg lbi flags suffixHandlers = + void $ build_setupHooks noHooks pkg lbi flags suffixHandlers + where + noHooks = (const $ return [], const $ return ()) build_setupHooks - :: BuildHooks + :: ( SetupHooks.PreBuildComponentInputs -> IO [SetupHooks.MonitorFilePath] + , SetupHooks.PostBuildComponentInputs -> IO () + ) + -- ^ build hooks -> PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo @@ -155,13 +163,15 @@ build_setupHooks -- ^ Flags that the user passed to build -> [PPSuffixHandler] -- ^ preprocessors to run before compiling - -> IO () + -> IO [SetupHooks.MonitorFilePath] build_setupHooks - (BuildHooks{preBuildComponentRules = mbPbcRules, postBuildComponentHook = mbPostBuild}) + (preBuildHook, postBuildHook) pkg_descr lbi flags suffixHandlers = do + let verbosity = fromFlag $ buildVerbosity flags + distPref = fromFlag $ buildDistPref flags checkSemaphoreSupport verbosity (compiler lbi) flags targets <- readTargetInfos verbosity pkg_descr lbi (buildTargets flags) let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) @@ -188,7 +198,7 @@ build_setupHooks curDir <- absoluteWorkingDirLBI lbi -- Now do the actual building - (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do + (mons, _) <- (\f -> foldM f ([], installedPkgs lbi) componentsToBuild) $ \(monsAcc, index) target -> do let comp = targetComponent target clbi = targetCLBI target bi = componentBuildInfo comp @@ -200,18 +210,8 @@ build_setupHooks , withPackageDB = withPackageDB lbi ++ [internalPackageDB] , installedPkgs = index } - runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO () - runPreBuildHooks lbi2 tgt = - let inputs = - SetupHooks.PreBuildComponentInputs - { SetupHooks.buildingWhat = BuildNormal flags - , SetupHooks.localBuildInfo = lbi2 - , SetupHooks.targetInfo = tgt - } - in for_ mbPbcRules $ \pbcRules -> do - (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules - SetupHooks.executeRules verbosity lbi2 tgt ruleFromId - preBuildComponent runPreBuildHooks verbosity lbi' target + pbci = SetupHooks.PreBuildComponentInputs (BuildNormal flags) lbi' target + mons <- preBuildComponent (preBuildHook pbci) verbosity lbi' target let numJobs = buildNumJobs flags par_strat <- toFlag <$> case buildUseSemaphore flags of @@ -239,13 +239,40 @@ build_setupHooks , SetupHooks.localBuildInfo = lbi' , SetupHooks.targetInfo = target } - for_ mbPostBuild ($ postBuildInputs) - return (maybe index (Index.insert `flip` index) mb_ipi) + postBuildHook postBuildInputs + return (monsAcc ++ mons, maybe index (Index.insert `flip` index) mb_ipi) + return mons + +runPreBuildHooks + :: SetupHooks.PreBuildComponentInputs + -> SetupHooks.Rules SetupHooks.PreBuildComponentInputs + -> IO [SetupHooks.MonitorFilePath] +runPreBuildHooks + pbci@SetupHooks.PreBuildComponentInputs + { SetupHooks.buildingWhat = what + , SetupHooks.localBuildInfo = lbi + , SetupHooks.targetInfo = tgt + } + pbRules = do + let verbosity = buildingWhatVerbosity what + (rules, monitors) <- SetupHooks.computeRules verbosity pbci pbRules + SetupHooks.executeRules verbosity lbi tgt rules + return monitors - return () - where - distPref = fromFlag (buildDistPref flags) - verbosity = fromFlag (buildVerbosity flags) +runPostBuildHooks + :: BuildFlags + -> LocalBuildInfo + -> TargetInfo + -> (SetupHooks.PostBuildComponentInputs -> IO ()) + -> IO () +runPostBuildHooks flags lbi tgt postBuild = + let inputs = + SetupHooks.PostBuildComponentInputs + { SetupHooks.buildFlags = flags + , SetupHooks.localBuildInfo = lbi + , SetupHooks.targetInfo = tgt + } + in postBuild inputs -- | Check for conditions that would prevent the build from succeeding. checkSemaphoreSupport @@ -329,11 +356,11 @@ repl -- ^ preprocessors to run before compiling -> [String] -> IO () -repl = repl_setupHooks noBuildHooks +repl = repl_setupHooks (const $ return []) repl_setupHooks - :: BuildHooks - -- ^ build hook + :: (SetupHooks.PreBuildComponentInputs -> IO [SetupHooks.MonitorFilePath]) + -- ^ pre-build hook -> PackageDescription -- ^ Mostly information from the .cabal file -> LocalBuildInfo @@ -345,7 +372,7 @@ repl_setupHooks -> [String] -> IO () repl_setupHooks - (BuildHooks{preBuildComponentRules = mbPbcRules}) + preBuildHook pkg_descr lbi flags @@ -388,17 +415,7 @@ repl_setupHooks (componentBuildInfo comp) (withPrograms lbi') } - runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO () - runPreBuildHooks lbi2 tgt = - let inputs = - SetupHooks.PreBuildComponentInputs - { SetupHooks.buildingWhat = BuildRepl flags - , SetupHooks.localBuildInfo = lbi2 - , SetupHooks.targetInfo = tgt - } - in for_ mbPbcRules $ \pbcRules -> do - (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules - SetupHooks.executeRules verbosity lbi2 tgt ruleFromId + pbci lbi' tgt = SetupHooks.PreBuildComponentInputs (BuildRepl flags) lbi' tgt -- build any dependent components sequence_ @@ -406,7 +423,8 @@ repl_setupHooks let clbi = targetCLBI subtarget comp = targetComponent subtarget lbi' <- lbiForComponent comp lbi - preBuildComponent runPreBuildHooks verbosity lbi' subtarget + _monitors <- + preBuildComponent (preBuildHook (pbci lbi' subtarget)) verbosity lbi' subtarget buildComponent (mempty{buildCommonFlags = mempty{setupVerbosity = toFlag verbosity}}) NoFlag @@ -423,7 +441,8 @@ repl_setupHooks let clbi = targetCLBI target comp = targetComponent target lbi' <- lbiForComponent comp lbi - preBuildComponent runPreBuildHooks verbosity lbi' target + _monitors <- + preBuildComponent (preBuildHook (pbci lbi' target)) verbosity lbi' target replComponent flags verbosity pkg_descr lbi' suffixHandlers comp clbi distPref -- | Start an interpreter without loading any package files. @@ -1118,20 +1137,20 @@ componentInitialBuildSteps _distPref pkg_descr lbi clbi verbosity = do -- | Creates the autogenerated files for a particular configured component, -- and runs the pre-build hook. preBuildComponent - :: (LocalBuildInfo -> TargetInfo -> IO ()) + :: IO r -- ^ pre-build hook -> Verbosity -> LocalBuildInfo -- ^ Configuration information -> TargetInfo - -> IO () + -> IO r preBuildComponent preBuildHook verbosity lbi tgt = do let pkg_descr = localPkgDescr lbi clbi = targetCLBI tgt compBuildDir = interpretSymbolicPathLBI lbi $ componentBuildDir lbi clbi createDirectoryIfMissingVerbose verbosity True compBuildDir writeBuiltinAutogenFiles verbosity pkg_descr lbi clbi - preBuildHook lbi tgt + preBuildHook -- | Generate and write to disk all built-in autogenerated files -- for the specified component. These files will be put in the diff --git a/Cabal/src/Distribution/Simple/Compiler.hs b/Cabal/src/Distribution/Simple/Compiler.hs index 346c4c82125..6c81b50f891 100644 --- a/Cabal/src/Distribution/Simple/Compiler.hs +++ b/Cabal/src/Distribution/Simple/Compiler.hs @@ -509,11 +509,13 @@ profilingVanillaSupported comp = waySupported "p" comp -- | Is the compiler distributed with profiling dynamic libraries profilingDynamicSupported :: Compiler -> Maybe Bool -profilingDynamicSupported comp = - -- Certainly not before this version, as it was not implemented yet. - if compilerVersion comp <= mkVersion [9, 11, 0] - then Just False - else waySupported "p_dyn" comp +profilingDynamicSupported comp + | GHC <- compilerFlavor comp + , -- Certainly not before 9.11, as prof+dyn was not implemented yet. + compilerVersion comp <= mkVersion [9, 11, 0] = + Just False + | otherwise = + waySupported "p_dyn" comp -- | Either profiling dynamic is definitely supported or we don't know (so assume -- it is) diff --git a/Cabal/src/Distribution/Simple/Configure.hs b/Cabal/src/Distribution/Simple/Configure.hs index 942211e34e0..07f2113d870 100644 --- a/Cabal/src/Distribution/Simple/Configure.hs +++ b/Cabal/src/Distribution/Simple/Configure.hs @@ -32,6 +32,18 @@ module Distribution.Simple.Configure ( configure , configure_setupHooks + , computePackageInfo + , configureFinal + , runPreConfPackageHook + , runPostConfPackageHook + , runPreConfComponentHook + , configurePackage + , PackageInfo (..) + , mkProgramDb + , finalCheckPackage + , configureComponents + , mkPromisedDepsSet + , combinedConstraints , writePersistBuildConfig , getConfigStateFile , getPersistBuildConfig @@ -48,8 +60,10 @@ module Distribution.Simple.Configure , getInstalledPackagesMonitorFiles , getInstalledPackagesById , getPackageDBContents + , configCompiler , configCompilerEx , configCompilerAuxEx + , configCompilerProgDb , computeEffectiveProfiling , ccLdOptionsBuildInfo , checkForeignDeps @@ -456,91 +470,203 @@ configure_setupHooks -> ConfigFlags -> IO LocalBuildInfo configure_setupHooks - (ConfigureHooks{preConfPackageHook, postConfPackageHook, preConfComponentHook}) + confHooks@(ConfigureHooks{preConfPackageHook}) (g_pkg_descr, hookedBuildInfo) cfg = do -- Cabal pre-configure - let verbosity = fromFlag (configVerbosity cfg) - distPref = fromFlag $ configDistPref cfg - mbWorkDir = flagToMaybe $ configWorkingDir cfg (lbc0, comp, platform, enabledComps) <- preConfigurePackage cfg g_pkg_descr -- Package-wide pre-configure hook lbc1 <- - case preConfPackageHook of - Nothing -> return lbc0 - Just pre_conf -> do - let programDb0 = LBC.withPrograms lbc0 - programDb0' = programDb0{unconfiguredProgs = Map.empty} - input = - SetupHooks.PreConfPackageInputs - { SetupHooks.configFlags = cfg - , SetupHooks.localBuildConfig = lbc0{LBC.withPrograms = programDb0'} - , -- Unconfigured programs are not supplied to the hook, - -- as these cannot be passed over a serialisation boundary - -- (see the "Binary ProgramDb" instance). - SetupHooks.compiler = comp - , SetupHooks.platform = platform - } - SetupHooks.PreConfPackageOutputs - { SetupHooks.buildOptions = opts1 - , SetupHooks.extraConfiguredProgs = progs1 - } <- - pre_conf input - -- The package-wide pre-configure hook returns BuildOptions that - -- overrides the one it was passed in, as well as an update to - -- the ProgramDb in the form of new configured programs to add - -- to the program database. - return $ - lbc0 - { LBC.withBuildOptions = opts1 - , LBC.withPrograms = - updateConfiguredProgs - (`Map.union` progs1) - programDb0 - } + maybe + (return lbc0) + (runPreConfPackageHook cfg comp platform lbc0) + preConfPackageHook -- Cabal package-wide configure - (lbc2, pbd2, pkg_info) <- - finalizeAndConfigurePackage cfg lbc1 g_pkg_descr comp platform enabledComps - - -- Package-wide post-configure hook - for_ postConfPackageHook $ \postConfPkg -> do - let input = - SetupHooks.PostConfPackageInputs - { SetupHooks.localBuildConfig = lbc2 - , SetupHooks.packageBuildDescr = pbd2 - } - postConfPkg input + (allConstraints, pkgInfo) <- + computePackageInfo cfg lbc1 g_pkg_descr comp + (packageDbs, pkg_descr0, flags) <- + finalizePackageDescription + cfg + g_pkg_descr + comp + platform + enabledComps + allConstraints + pkgInfo - -- Per-component pre-configure hook - pkg_descr <- do - let pkg_descr2 = LBC.localPkgDescr pbd2 - applyComponentDiffs - verbosity - ( \c -> for preConfComponentHook $ \computeDiff -> do - let input = - SetupHooks.PreConfComponentInputs - { SetupHooks.localBuildConfig = lbc2 - , SetupHooks.packageBuildDescr = pbd2 - , SetupHooks.component = c - } - SetupHooks.PreConfComponentOutputs - { SetupHooks.componentDiff = diff - } <- - computeDiff input - return diff - ) - pkg_descr2 - let pbd3 = pbd2{LBC.localPkgDescr = pkg_descr} + configureFinal + confHooks + hookedBuildInfo + cfg + lbc1 + (g_pkg_descr, pkg_descr0) + flags + enabledComps + comp + platform + packageDbs + pkgInfo + +configureFinal + :: ConfigureHooks + -> HookedBuildInfo + -> ConfigFlags + -> LBC.LocalBuildConfig + -> (GenericPackageDescription, PackageDescription) + -> FlagAssignment + -> ComponentRequestedSpec + -> Compiler + -> Platform + -> PackageDBStack + -> PackageInfo + -> IO LocalBuildInfo +configureFinal + (ConfigureHooks{postConfPackageHook, preConfComponentHook}) + hookedBuildInfo + cfg + lbc1 + (gpkgDescr, pkgDescr0) + flags + enabledComps + comp + platform + packageDbs + pkgInfo@PackageInfo + { installedPackageSet = installedPkgSet + , promisedDepsSet = promisedDeps + } = + do + let verbosity = fromFlag (configVerbosity cfg) + distPref = fromFlag $ configDistPref cfg + mbWorkDir = flagToMaybe $ configWorkingDir cfg + + (lbc2, pbd2) <- + configurePackage cfg lbc1 pkgDescr0 flags enabledComps comp platform packageDbs + + -- Package-wide post-configure hook + for_ postConfPackageHook $ runPostConfPackageHook lbc2 pbd2 + + -- Per-component pre-configure hook + pkgDescr <- do + let pkgDescr2 = LBC.localPkgDescr pbd2 + applyComponentDiffs + verbosity + (for preConfComponentHook . runPreConfComponentHook lbc2 pbd2) + pkgDescr2 + let pbd3 = pbd2{LBC.localPkgDescr = pkgDescr} + + -- Cabal per-component configure + finalCheckPackage gpkgDescr pbd3 hookedBuildInfo + + let + use_external_internal_deps = + case enabledComps of + OneComponentRequestedSpec{} -> True + ComponentRequestedSpec{} -> False + -- The list of 'InstalledPackageInfo' recording the selected + -- dependencies on external packages. + -- + -- Invariant: For any package name, there is at most one package + -- in externalPackageDeps which has that name. + -- + -- NB: The dependency selection is global over ALL components + -- in the package (similar to how allConstraints and + -- requiredDepsMap are global over all components). In particular, + -- if *any* component (post-flag resolution) has an unsatisfiable + -- dependency, we will fail. This can sometimes be undesirable + -- for users, see #1786 (benchmark conflicts with executable), + -- + -- In the presence of Backpack, these package dependencies are + -- NOT complete: they only ever include the INDEFINITE + -- dependencies. After we apply an instantiation, we'll get + -- definite references which constitute extra dependencies. + -- (Why not have cabal-install pass these in explicitly? + -- For one it's deterministic; for two, we need to associate + -- them with renamings which would require a far more complicated + -- input scheme than what we have today.) + externalPkgDeps <- + selectDependencies + verbosity + use_external_internal_deps + pkgInfo + pkgDescr + enabledComps + lbi <- configureComponents lbc2 pbd3 installedPkgSet promisedDeps externalPkgDeps + writePersistBuildConfig mbWorkDir distPref lbi - -- Cabal per-component configure - externalPkgDeps <- finalCheckPackage g_pkg_descr pbd3 hookedBuildInfo pkg_info - lbi <- configureComponents lbc2 pbd3 pkg_info externalPkgDeps + return lbi - writePersistBuildConfig mbWorkDir distPref lbi +runPreConfPackageHook + :: ConfigFlags + -> Compiler + -> Platform + -> LBC.LocalBuildConfig + -> (SetupHooks.PreConfPackageInputs -> IO SetupHooks.PreConfPackageOutputs) + -> IO LBC.LocalBuildConfig +runPreConfPackageHook cfg comp platform lbc0 pre_conf = do + let programDb0 = LBC.withPrograms lbc0 + programDb0' = programDb0{unconfiguredProgs = Map.empty} + input = + SetupHooks.PreConfPackageInputs + { SetupHooks.configFlags = cfg + , SetupHooks.localBuildConfig = lbc0{LBC.withPrograms = programDb0'} + , -- Unconfigured programs are not supplied to the hook, + -- as these cannot be passed over a serialisation boundary + -- (see the "Binary ProgramDb" instance). + SetupHooks.compiler = comp + , SetupHooks.platform = platform + } + SetupHooks.PreConfPackageOutputs + { SetupHooks.buildOptions = opts1 + , SetupHooks.extraConfiguredProgs = progs1 + } <- + pre_conf input + -- The package-wide pre-configure hook returns BuildOptions that + -- overrides the one it was passed in, as well as an update to + -- the ProgramDb in the form of new configured programs to add + -- to the program database. + return $ + lbc0 + { LBC.withBuildOptions = opts1 + , LBC.withPrograms = + updateConfiguredProgs + (`Map.union` progs1) + programDb0 + } - return lbi +runPostConfPackageHook + :: LBC.LocalBuildConfig + -> LBC.PackageBuildDescr + -> (SetupHooks.PostConfPackageInputs -> IO ()) + -> IO () +runPostConfPackageHook lbc2 pbd2 postConfPkg = + let input = + SetupHooks.PostConfPackageInputs + { SetupHooks.localBuildConfig = lbc2 + , SetupHooks.packageBuildDescr = pbd2 + } + in postConfPkg input + +runPreConfComponentHook + :: LBC.LocalBuildConfig + -> LBC.PackageBuildDescr + -> Component + -> (SetupHooks.PreConfComponentInputs -> IO SetupHooks.PreConfComponentOutputs) + -> IO SetupHooks.ComponentDiff +runPreConfComponentHook lbc pbd c hook = do + let input = + SetupHooks.PreConfComponentInputs + { SetupHooks.localBuildConfig = lbc + , SetupHooks.packageBuildDescr = pbd + , SetupHooks.component = c + } + SetupHooks.PreConfComponentOutputs + { SetupHooks.componentDiff = diff + } <- + hook input + return diff preConfigurePackage :: ConfigFlags @@ -818,18 +944,25 @@ computeLocalBuildConfig cfg comp programDb = do return $ LBC.LocalBuildConfig - { extraConfigArgs = [] -- Currently configure does not - -- take extra args, but if it - -- did they would go here. - , withPrograms = programDb + { extraConfigArgs = [] + , -- Currently configure does not + -- take extra args, but if it + -- did they would go here. + withPrograms = programDb , withBuildOptions = buildOptions } data PackageInfo = PackageInfo { internalPackageSet :: Set LibraryName + -- ^ Libraries internal to the package , promisedDepsSet :: Map (PackageName, ComponentName) PromisedComponent + -- ^ Collection of components that are promised, i.e. are not installed already. + -- + -- See 'PromisedDependency' for more details. , installedPackageSet :: InstalledPackageIndex + -- ^ Installed packages , requiredDepsMap :: Map (PackageName, ComponentName) InstalledPackageInfo + -- ^ Packages for which we have been given specific deps to use } configurePackage @@ -840,12 +973,11 @@ configurePackage -> ComponentRequestedSpec -> Compiler -> Platform - -> ProgramDb -> PackageDBStack -> IO (LBC.LocalBuildConfig, LBC.PackageBuildDescr) -configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform programDb0 packageDbs = do - let common = configCommonFlags cfg - verbosity = fromFlag $ setupVerbosity common +configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform packageDbs = do + let verbosity = fromFlag (configVerbosity cfg) + programDb0 = LBC.withPrograms lbc0 -- add extra include/lib dirs as specified in cfg pkg_descr0 = addExtraIncludeLibDirsFromConfigFlags pkg_descr00 cfg @@ -905,7 +1037,7 @@ configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform programDb0 pac defaultInstallDirs' use_external_internal_deps (compilerFlavor comp) - (fromFlag (configUserInstall cfg)) + (fromFlagOrDefault True (configUserInstall cfg)) (hasLibs pkg_descr2) let installDirs = @@ -934,15 +1066,13 @@ configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform programDb0 pac return (lbc, pbd) -finalizeAndConfigurePackage +computePackageInfo :: ConfigFlags -> LBC.LocalBuildConfig -> GenericPackageDescription -> Compiler - -> Platform - -> ComponentRequestedSpec - -> IO (LBC.LocalBuildConfig, LBC.PackageBuildDescr, PackageInfo) -finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do + -> IO ([PackageVersionConstraint], PackageInfo) +computePackageInfo cfg lbc0 g_pkg_descr comp = do let common = configCommonFlags cfg verbosity = fromFlag $ setupVerbosity common mbWorkDir = flagToMaybe $ setupWorkingDir common @@ -952,7 +1082,7 @@ finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do packageDbs :: PackageDBStack packageDbs = interpretPackageDbFlags - (fromFlag (configUserInstall cfg)) + (fromFlagOrDefault True (configUserInstall cfg)) (configPackageDBs cfg) -- The InstalledPackageIndex of all installed packages @@ -997,13 +1127,35 @@ finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do let promisedDepsSet = mkPromisedDepsSet (configPromisedDependencies cfg) - pkg_info = - PackageInfo + return $ + ( allConstraints + , PackageInfo { internalPackageSet , promisedDepsSet , installedPackageSet , requiredDepsMap } + ) + +finalizePackageDescription + :: ConfigFlags + -> GenericPackageDescription + -> Compiler + -> Platform + -> ComponentRequestedSpec + -> [PackageVersionConstraint] + -> PackageInfo + -> IO (PackageDBStack, PackageDescription, FlagAssignment) +finalizePackageDescription cfg g_pkg_descr comp platform enabled allConstraints pkgInfo = do + let common = configCommonFlags cfg + verbosity = fromFlag $ setupVerbosity common + + -- What package database(s) to use + let packageDbs :: PackageDBStack + packageDbs = + interpretPackageDbFlags + (fromFlagOrDefault True (configUserInstall cfg)) + (configPackageDBs cfg) -- pkg_descr: The resolved package description, that does not contain any -- conditionals, because we have an assignment for @@ -1026,7 +1178,7 @@ finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do ( pkg_descr0 :: PackageDescription , flags :: FlagAssignment ) <- - configureFinalizedPackage + finalizePackageDescription2 verbosity cfg enabled @@ -1036,27 +1188,12 @@ finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do (fromFlagOrDefault False (configExactConfiguration cfg)) (fromFlagOrDefault False (configAllowDependingOnPrivateLibs cfg)) (packageName g_pkg_descr) - installedPackageSet - internalPackageSet - promisedDepsSet - requiredDepsMap + pkgInfo ) comp platform g_pkg_descr - - (lbc, pbd) <- - configurePackage - cfg - lbc0 - pkg_descr0 - flags - enabled - comp - platform - programDb0 - packageDbs - return (lbc, pbd, pkg_info) + return (packageDbs, pkg_descr0, flags) addExtraIncludeLibDirsFromConfigFlags :: PackageDescription -> ConfigFlags -> PackageDescription @@ -1111,8 +1248,7 @@ finalCheckPackage :: GenericPackageDescription -> LBC.PackageBuildDescr -> HookedBuildInfo - -> PackageInfo - -> IO ([PreExistingComponent], [ConfiguredPromisedComponent]) + -> IO () finalCheckPackage g_pkg_descr ( LBC.PackageBuildDescr @@ -1123,16 +1259,11 @@ finalCheckPackage , componentEnabledSpec = enabled } ) - hookedBuildInfo - (PackageInfo{internalPackageSet, promisedDepsSet, installedPackageSet, requiredDepsMap}) = + hookedBuildInfo = do let common = configCommonFlags cfg verbosity = fromFlag $ setupVerbosity common cabalFileDir = packageRoot common - use_external_internal_deps = - case enabled of - OneComponentRequestedSpec{} -> True - ComponentRequestedSpec{} -> False checkCompilerProblems verbosity comp pkg_descr enabled checkPackageProblems @@ -1156,7 +1287,7 @@ finalCheckPackage let langs = unsupportedLanguages comp langlist when (not (null langs)) $ dieWithException verbosity $ - UnsupportedLanguages (packageId g_pkg_descr) (compilerId comp) (map prettyShow langs) + UnsupportedLanguages (packageId pkg_descr) (compilerId comp) (map prettyShow langs) let extlist = nub $ concatMap @@ -1165,7 +1296,7 @@ finalCheckPackage let exts = unsupportedExtensions comp extlist when (not (null exts)) $ dieWithException verbosity $ - UnsupportedLanguageExtension (packageId g_pkg_descr) (compilerId comp) (map prettyShow exts) + UnsupportedLanguageExtension (packageId pkg_descr) (compilerId comp) (map prettyShow exts) -- Check foreign library build requirements let flibs = [flib | CFLib flib <- enabledComponents pkg_descr enabled] @@ -1174,41 +1305,11 @@ finalCheckPackage dieWithException verbosity $ CantFindForeignLibraries unsupportedFLibs - -- The list of 'InstalledPackageInfo' recording the selected - -- dependencies on external packages. - -- - -- Invariant: For any package name, there is at most one package - -- in externalPackageDeps which has that name. - -- - -- NB: The dependency selection is global over ALL components - -- in the package (similar to how allConstraints and - -- requiredDepsMap are global over all components). In particular, - -- if *any* component (post-flag resolution) has an unsatisfiable - -- dependency, we will fail. This can sometimes be undesirable - -- for users, see #1786 (benchmark conflicts with executable), - -- - -- In the presence of Backpack, these package dependencies are - -- NOT complete: they only ever include the INDEFINITE - -- dependencies. After we apply an instantiation, we'll get - -- definite references which constitute extra dependencies. - -- (Why not have cabal-install pass these in explicitly? - -- For one it's deterministic; for two, we need to associate - -- them with renamings which would require a far more complicated - -- input scheme than what we have today.) - configureDependencies - verbosity - use_external_internal_deps - internalPackageSet - promisedDepsSet - installedPackageSet - requiredDepsMap - pkg_descr - enabled - configureComponents :: LBC.LocalBuildConfig -> LBC.PackageBuildDescr - -> PackageInfo + -> InstalledPackageIndex + -> Map (PackageName, ComponentName) PromisedComponent -> ([PreExistingComponent], [ConfiguredPromisedComponent]) -> IO LocalBuildInfo configureComponents @@ -1220,7 +1321,8 @@ configureComponents , componentEnabledSpec = enabled } ) - (PackageInfo{promisedDepsSet, installedPackageSet}) + installedPackageSet + promisedDepsSet externalPkgDeps = do let common = configCommonFlags cfg @@ -1472,23 +1574,19 @@ dependencySatisfiable -> Bool -- ^ allow depending on private libs? -> PackageName - -> InstalledPackageIndex - -- ^ installed set - -> Set LibraryName - -- ^ library components - -> Map (PackageName, ComponentName) PromisedComponent - -> Map (PackageName, ComponentName) InstalledPackageInfo - -- ^ required dependencies + -> PackageInfo -> (Dependency -> DependencySatisfaction) dependencySatisfiable use_external_internal_deps exact_config allow_private_deps pn - installedPackageSet - packageLibraries - promisedDeps - requiredDepsMap + PackageInfo + { internalPackageSet = packageLibraries + , promisedDepsSet = promisedDeps + , installedPackageSet + , requiredDepsMap + } (Dependency depName vr sublibs) | exact_config = -- When we're given '--exact-configuration', we assume that all @@ -1583,7 +1681,7 @@ dependencySatisfiable -- | Finalize a generic package description. -- -- The workhorse is 'finalizePD'. -configureFinalizedPackage +finalizePackageDescription2 :: Verbosity -> ConfigFlags -> ComponentRequestedSpec @@ -1595,7 +1693,7 @@ configureFinalizedPackage -> Platform -> GenericPackageDescription -> IO (PackageDescription, FlagAssignment) -configureFinalizedPackage +finalizePackageDescription2 verbosity cfg enabled @@ -1651,25 +1749,17 @@ checkCompilerProblems verbosity comp pkg_descr enabled = do $ dieWithException verbosity CompilerDoesn'tSupportBackpack -- | Select dependencies for the package. -configureDependencies +selectDependencies :: Verbosity -> UseExternalInternalDeps - -> Set LibraryName - -> Map (PackageName, ComponentName) PromisedComponent - -> InstalledPackageIndex - -- ^ installed packages - -> Map (PackageName, ComponentName) InstalledPackageInfo - -- ^ required deps + -> PackageInfo -> PackageDescription -> ComponentRequestedSpec -> IO ([PreExistingComponent], [ConfiguredPromisedComponent]) -configureDependencies +selectDependencies verbosity use_external_internal_deps - packageLibraries - promisedDeps - installedPackageSet - requiredDepsMap + pkgInfo pkg_descr enableSpec = do let failedDeps :: [FailedDependency] @@ -1682,10 +1772,7 @@ configureDependencies , let status = selectDependency (package pkg_descr) - packageLibraries - promisedDeps - installedPackageSet - requiredDepsMap + pkgInfo use_external_internal_deps dep ] @@ -1939,15 +2026,7 @@ data DependencyResolution selectDependency :: PackageId -- ^ Package id of current package - -> Set LibraryName - -- ^ package libraries - -> Map (PackageName, ComponentName) PromisedComponent - -- ^ Set of components that are promised, i.e. are not installed already. See 'PromisedDependency' for more details. - -> InstalledPackageIndex - -- ^ Installed packages - -> Map (PackageName, ComponentName) InstalledPackageInfo - -- ^ Packages for which we have been given specific deps to - -- use + -> PackageInfo -> UseExternalInternalDeps -- ^ Are we configuring a -- single component? @@ -1955,10 +2034,13 @@ selectDependency -> [Either FailedDependency DependencyResolution] selectDependency pkgid - internalIndex - promisedIndex - installedIndex - requiredDepsMap + ( PackageInfo + { internalPackageSet = internalIndex + , promisedDepsSet = promisedIndex + , installedPackageSet = installedIndex + , requiredDepsMap + } + ) use_external_internal_deps (Dependency dep_pkgname vr libs) = -- If the dependency specification matches anything in the internal package @@ -2484,10 +2566,14 @@ configCompilerAuxEx cfg = do programDb verbosity +-- | Configure the compiler and associated programs such as @hc-pkg@, @haddock@ +-- and toolchain program such as @ar@, @ld@. configCompilerEx :: Maybe CompilerFlavor -> Maybe FilePath + -- ^ user-specified @hc@ path (optional) -> Maybe FilePath + -- ^ user-specified @hc-pkg@ path (optional) -> ProgramDb -> Verbosity -> IO (Compiler, Platform, ProgramDb) @@ -2496,10 +2582,46 @@ configCompilerEx (Just hcFlavor) hcPath hcPkg progdb verbosity = do (comp, maybePlatform, programDb) <- case hcFlavor of GHC -> GHC.configure verbosity hcPath hcPkg progdb GHCJS -> GHCJS.configure verbosity hcPath hcPkg progdb - UHC -> UHC.configure verbosity hcPath hcPkg progdb + UHC -> UHC.configure verbosity hcPath progdb _ -> dieWithException verbosity UnknownCompilerException return (comp, fromMaybe buildPlatform maybePlatform, programDb) +-- | Configure the compiler ONLY. +configCompiler + :: Maybe CompilerFlavor + -> Maybe FilePath + -- ^ user-specified @hc@ path (optional) + -> ProgramDb + -> Verbosity + -> IO (Compiler, Platform, ProgramDb) +configCompiler mbFlavor hcPath progdb verbosity = do + (comp, maybePlatform, programDb) <- + case mbFlavor of + Nothing -> dieWithException verbosity UnknownCompilerException + Just hcFlavor -> + case hcFlavor of + GHC -> GHC.configureCompiler verbosity hcPath progdb + GHCJS -> GHCJS.configureCompiler verbosity hcPath progdb + UHC -> UHC.configure verbosity hcPath progdb + _ -> dieWithException verbosity UnknownCompilerException + return (comp, fromMaybe buildPlatform maybePlatform, programDb) + +-- | Configure programs associated to the compiler, such as @hc-pkg@, @haddock@ +-- and toolchain program such as @ar@, @ld@. +configCompilerProgDb + :: Verbosity + -> Compiler + -> ProgramDb + -- ^ program database containing the compiler + -> Maybe FilePath + -- ^ user-specified @hc-pkg@ path (optional) + -> IO ProgramDb +configCompilerProgDb verbosity comp hcProgDb hcPkgPath = do + case compilerFlavor comp of + GHC -> GHC.compilerProgramDb verbosity comp hcProgDb hcPkgPath + GHCJS -> GHCJS.compilerProgramDb verbosity comp hcProgDb hcPkgPath + _ -> return hcProgDb + -- ----------------------------------------------------------------------------- -- Testing C lib and header dependencies diff --git a/Cabal/src/Distribution/Simple/GHC.hs b/Cabal/src/Distribution/Simple/GHC.hs index 62415e7ea8e..b6ec7b10dc0 100644 --- a/Cabal/src/Distribution/Simple/GHC.hs +++ b/Cabal/src/Distribution/Simple/GHC.hs @@ -41,6 +41,8 @@ module Distribution.Simple.GHC ( getGhcInfo , configure + , configureCompiler + , compilerProgramDb , getInstalledPackages , getInstalledPackagesMonitorFiles , getPackageDBContents @@ -86,6 +88,7 @@ import Prelude () import Control.Arrow ((***)) import Control.Monad (forM_) import qualified Data.Map as Map +import Data.Maybe (fromJust) import Distribution.CabalSpecVersion import Distribution.InstalledPackageInfo (InstalledPackageInfo) import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo @@ -96,6 +99,7 @@ import Distribution.Simple.Build.Inputs (PreBuildComponentInputs (..)) import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler import Distribution.Simple.Errors +import Distribution.Simple.Flag import qualified Distribution.Simple.GHC.Build as GHC import Distribution.Simple.GHC.Build.Modules (BuildWay (..)) import Distribution.Simple.GHC.Build.Utils @@ -152,20 +156,35 @@ import Distribution.Simple.Setup.Build -- ----------------------------------------------------------------------------- -- Configuring +-- | Configure GHC, and then auxiliary programs such as @ghc-pkg@, @haddock@ +-- as well as toolchain programs such as @ar@, @ld. configure :: Verbosity -> Maybe FilePath + -- ^ user-specified @ghc@ path (optional) -> Maybe FilePath + -- ^ user-specified @ghc-pkg@ path (optional) -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb) configure verbosity hcPath hcPkgPath conf0 = do + (comp, compPlatform, progdb1) <- configureCompiler verbosity hcPath conf0 + compProgDb <- compilerProgramDb verbosity comp progdb1 hcPkgPath + return (comp, compPlatform, compProgDb) + +-- | Configure GHC. +configureCompiler + :: Verbosity + -> Maybe FilePath + -- ^ user-specified @ghc@ path (optional) + -> ProgramDb + -> IO (Compiler, Maybe Platform, ProgramDb) +configureCompiler verbosity hcPath conf0 = do (ghcProg, ghcVersion, progdb1) <- requireProgramVersion verbosity ghcProgram (orLaterVersion (mkVersion [7, 0, 1])) (userMaybeSpecifyPath "ghc" hcPath conf0) - let implInfo = ghcVersionImplInfo ghcVersion -- Cabal currently supports GHC less than `maxGhcVersion` let maxGhcVersion = mkVersion [9, 14] @@ -181,48 +200,12 @@ configure verbosity hcPath hcPkgPath conf0 = do ++ " is version " ++ prettyShow ghcVersion - -- This is slightly tricky, we have to configure ghc first, then we use the - -- location of ghc to help find ghc-pkg in the case that the user did not - -- specify the location of ghc-pkg directly: - (ghcPkgProg, ghcPkgVersion, progdb2) <- - requireProgramVersion - verbosity - ghcPkgProgram - { programFindLocation = guessGhcPkgFromGhcPath ghcProg - } - anyVersion - (userMaybeSpecifyPath "ghc-pkg" hcPkgPath progdb1) - - when (ghcVersion /= ghcPkgVersion) $ - dieWithException verbosity $ - VersionMismatchGHC (programPath ghcProg) ghcVersion (programPath ghcPkgProg) ghcPkgVersion - -- Likewise we try to find the matching hsc2hs and haddock programs. - let hsc2hsProgram' = - hsc2hsProgram - { programFindLocation = guessHsc2hsFromGhcPath ghcProg - } - haddockProgram' = - haddockProgram - { programFindLocation = guessHaddockFromGhcPath ghcProg - } - hpcProgram' = - hpcProgram - { programFindLocation = guessHpcFromGhcPath ghcProg - } - runghcProgram' = - runghcProgram - { programFindLocation = guessRunghcFromGhcPath ghcProg - } - progdb3 = - addKnownProgram haddockProgram' $ - addKnownProgram hsc2hsProgram' $ - addKnownProgram hpcProgram' $ - addKnownProgram runghcProgram' progdb2 - + let implInfo = ghcVersionImplInfo ghcVersion languages <- Internal.getLanguages verbosity implInfo ghcProg extensions0 <- Internal.getExtensions verbosity implInfo ghcProg ghcInfo <- Internal.getGhcInfo verbosity implInfo ghcProg + let ghcInfoMap = Map.fromList ghcInfo filterJS = if ghcVersion < mkVersion [9, 8] then filterExt JavaScriptFFI else id extensions = @@ -254,7 +237,13 @@ configure verbosity hcPath hcPkgPath conf0 = do -- So, we need to be careful to only strip the /common/ prefix. -- In this example, @AbiTag@ is "inplace". compilerAbiTag :: AbiTag - compilerAbiTag = maybe NoAbiTag AbiTag (dropWhile (== '-') . stripCommonPrefix (prettyShow compilerId) <$> Map.lookup "Project Unit Id" ghcInfoMap) + compilerAbiTag = + maybe + NoAbiTag + AbiTag + ( dropWhile (== '-') . stripCommonPrefix (prettyShow compilerId) + <$> Map.lookup "Project Unit Id" ghcInfoMap + ) let comp = Compiler @@ -266,9 +255,73 @@ configure verbosity hcPath hcPkgPath conf0 = do , compilerProperties = ghcInfoMap } compPlatform = Internal.targetPlatform ghcInfo - -- configure gcc and ld - progdb4 = Internal.configureToolchain implInfo ghcProg ghcInfoMap progdb3 - return (comp, compPlatform, progdb4) + return (comp, compPlatform, progdb1) + +-- | Given a configured @ghc@ program, configure auxiliary programs such +-- as @ghc-pkg@ or @haddock@, as well as toolchain programs such as @ar@, @ld@, +-- based on: +-- +-- - the location of the @ghc@ executable, +-- - toolchain information in the GHC settings file. +compilerProgramDb + :: Verbosity + -> Compiler + -> ProgramDb + -> Maybe FilePath + -- ^ user-specified @ghc-pkg@ path (optional) + -> IO ProgramDb +compilerProgramDb verbosity comp progdb1 hcPkgPath = do + let + ghcProg = fromJust $ lookupProgram ghcProgram progdb1 + ghcVersion = compilerVersion comp + + -- This is slightly tricky, we have to configure ghc first, then we use the + -- location of ghc to help find ghc-pkg in the case that the user did not + -- specify the location of ghc-pkg directly: + (ghcPkgProg, ghcPkgVersion, progdb2) <- + requireProgramVersion + verbosity + ghcPkgProgram + { programFindLocation = guessGhcPkgFromGhcPath ghcProg + } + anyVersion + (userMaybeSpecifyPath "ghc-pkg" hcPkgPath progdb1) + + when (ghcVersion /= ghcPkgVersion) $ + dieWithException verbosity $ + VersionMismatchGHC (programPath ghcProg) ghcVersion (programPath ghcPkgProg) ghcPkgVersion + -- Likewise we try to find the matching hsc2hs and haddock programs. + let hsc2hsProgram' = + hsc2hsProgram + { programFindLocation = guessHsc2hsFromGhcPath ghcProg + } + haddockProgram' = + haddockProgram + { programFindLocation = guessHaddockFromGhcPath ghcProg + } + hpcProgram' = + hpcProgram + { programFindLocation = guessHpcFromGhcPath ghcProg + } + runghcProgram' = + runghcProgram + { programFindLocation = guessRunghcFromGhcPath ghcProg + } + progdb3 = + addKnownProgram haddockProgram' $ + addKnownProgram hsc2hsProgram' $ + addKnownProgram hpcProgram' $ + addKnownProgram runghcProgram' progdb2 + + -- configure gcc, ld, ar etc... based on the paths stored + -- in the GHC settings file + progdb4 = + Internal.configureToolchain + (ghcVersionImplInfo ghcVersion) + ghcProg + (compilerProperties comp) + progdb3 + return progdb4 -- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find -- the corresponding tool; e.g. if the tool is ghc-pkg, we try looking diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs index ca71857828e..56a4b120b63 100644 --- a/Cabal/src/Distribution/Simple/GHCJS.hs +++ b/Cabal/src/Distribution/Simple/GHCJS.hs @@ -5,6 +5,8 @@ module Distribution.Simple.GHCJS ( getGhcInfo , configure + , configureCompiler + , compilerProgramDb , getInstalledPackages , getInstalledPackagesMonitorFiles , getPackageDBContents @@ -87,6 +89,7 @@ import Control.Arrow ((***)) import Control.Monad (msum) import Data.Char (isLower) import qualified Data.Map as Map +import Data.Maybe (fromJust) import System.Directory ( canonicalizePath , createDirectoryIfMissing @@ -106,13 +109,29 @@ import qualified System.Info -- ----------------------------------------------------------------------------- -- Configuring +-- | Configure GHCJS, and then auxiliary programs such as @ghc-pkg@, @haddock@ +-- as well as toolchain programs such as @ar@, @ld. configure :: Verbosity -> Maybe FilePath + -- ^ user-specified @ghcjs@ path (optional) -> Maybe FilePath + -- ^ user-specified @ghcjs-pkg@ path (optional) -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb) configure verbosity hcPath hcPkgPath conf0 = do + (comp, compPlatform, progdb1) <- configureCompiler verbosity hcPath conf0 + compProgDb <- compilerProgramDb verbosity comp progdb1 hcPkgPath + return (comp, compPlatform, compProgDb) + +-- | Configure GHCJS. +configureCompiler + :: Verbosity + -> Maybe FilePath + -- ^ user-specified @ghc@ path (optional) + -> ProgramDb + -> IO (Compiler, Maybe Platform, ProgramDb) +configureCompiler verbosity hcPath conf0 = do (ghcjsProg, ghcjsVersion, progdb1) <- requireProgramVersion verbosity @@ -133,6 +152,43 @@ configure verbosity hcPath hcPkgPath conf0 = do let implInfo = ghcjsVersionImplInfo ghcjsVersion ghcjsGhcVersion + languages <- Internal.getLanguages verbosity implInfo ghcjsProg + extensions <- Internal.getExtensions verbosity implInfo ghcjsProg + + ghcjsInfo <- Internal.getGhcInfo verbosity implInfo ghcjsProg + let ghcInfoMap = Map.fromList ghcjsInfo + + let comp = + Compiler + { compilerId = CompilerId GHCJS ghcjsVersion + , compilerAbiTag = + AbiTag $ + "ghc" ++ intercalate "_" (map show . versionNumbers $ ghcjsGhcVersion) + , compilerCompat = [CompilerId GHC ghcjsGhcVersion] + , compilerLanguages = languages + , compilerExtensions = extensions + , compilerProperties = ghcInfoMap + } + compPlatform = Internal.targetPlatform ghcjsInfo + return (comp, compPlatform, progdb1) + +-- | Given a configured @ghcjs@ program, configure auxiliary programs such +-- as @ghcjs-pkg@ or @haddock@, based on the location of the @ghcjs@ executable. +compilerProgramDb + :: Verbosity + -> Compiler + -> ProgramDb + -> Maybe FilePath + -- ^ user-specified @ghc-pkg@ path (optional) + -> IO ProgramDb +compilerProgramDb verbosity comp progdb1 hcPkgPath = do + let + ghcjsProg = fromJust $ lookupProgram ghcjsProgram progdb1 + ghcjsVersion = compilerVersion comp + ghcjsGhcVersion = case compilerCompat comp of + [CompilerId GHC ghcjsGhcVer] -> ghcjsGhcVer + compat -> error $ "could not parse ghcjsGhcVersion:" ++ show compat + -- This is slightly tricky, we have to configure ghc first, then we use the -- location of ghc to help find ghc-pkg in the case that the user did not -- specify the location of ghc-pkg directly: @@ -187,25 +243,7 @@ configure verbosity hcPath hcPkgPath conf0 = do addKnownProgram hpcProgram' $ {- addKnownProgram runghcProgram' -} progdb2 - languages <- Internal.getLanguages verbosity implInfo ghcjsProg - extensions <- Internal.getExtensions verbosity implInfo ghcjsProg - - ghcjsInfo <- Internal.getGhcInfo verbosity implInfo ghcjsProg - let ghcInfoMap = Map.fromList ghcjsInfo - - let comp = - Compiler - { compilerId = CompilerId GHCJS ghcjsVersion - , compilerAbiTag = - AbiTag $ - "ghc" ++ intercalate "_" (map show . versionNumbers $ ghcjsGhcVersion) - , compilerCompat = [CompilerId GHC ghcjsGhcVersion] - , compilerLanguages = languages - , compilerExtensions = extensions - , compilerProperties = ghcInfoMap - } - compPlatform = Internal.targetPlatform ghcjsInfo - return (comp, compPlatform, progdb3) + return progdb3 guessGhcjsPkgFromGhcjsPath :: ConfiguredProgram diff --git a/Cabal/src/Distribution/Simple/Haddock.hs b/Cabal/src/Distribution/Simple/Haddock.hs index 29d5f80e1ec..90e2e9e8a7a 100644 --- a/Cabal/src/Distribution/Simple/Haddock.hs +++ b/Cabal/src/Distribution/Simple/Haddock.hs @@ -55,6 +55,9 @@ import Distribution.Simple.BuildPaths import Distribution.Simple.BuildTarget import Distribution.Simple.Compiler import Distribution.Simple.Errors +import Distribution.Simple.FileMonitor.Types + ( MonitorFilePath + ) import Distribution.Simple.Flag import Distribution.Simple.Glob (matchDirFileGlob) import Distribution.Simple.InstallDirs @@ -67,12 +70,9 @@ import qualified Distribution.Simple.Program.HcPkg as HcPkg import Distribution.Simple.Program.ResponseFile import Distribution.Simple.Register import Distribution.Simple.Setup -import Distribution.Simple.SetupHooks.Internal - ( BuildHooks (..) - , noBuildHooks - ) import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks -import qualified Distribution.Simple.SetupHooks.Rule as SetupHooks + ( PreBuildComponentInputs (..) + ) import Distribution.Simple.Utils import Distribution.System import Distribution.Types.ComponentLocalBuildInfo @@ -87,7 +87,6 @@ import qualified Distribution.Utils.ShortText as ShortText import Distribution.Verbosity import Distribution.Version -import Control.Monad import Data.Bool (bool) import Data.Either (rights) import System.Directory (doesDirectoryExist, doesFileExist) @@ -227,15 +226,17 @@ haddock -> [PPSuffixHandler] -> HaddockFlags -> IO () -haddock = haddock_setupHooks noBuildHooks +haddock pkg lbi suffixHandlers flags = + void $ haddock_setupHooks (const $ return []) pkg lbi suffixHandlers flags haddock_setupHooks - :: BuildHooks + :: (SetupHooks.PreBuildComponentInputs -> IO [MonitorFilePath]) + -- ^ pre-build hook -> PackageDescription -> LocalBuildInfo -> [PPSuffixHandler] -> HaddockFlags - -> IO () + -> IO [MonitorFilePath] haddock_setupHooks _ pkg_descr @@ -246,13 +247,14 @@ haddock_setupHooks && not (fromFlag $ haddockExecutables haddockFlags) && not (fromFlag $ haddockTestSuites haddockFlags) && not (fromFlag $ haddockBenchmarks haddockFlags) - && not (fromFlag $ haddockForeignLibs haddockFlags) = - warn (fromFlag $ setupVerbosity $ haddockCommonFlags haddockFlags) $ + && not (fromFlag $ haddockForeignLibs haddockFlags) = do + warn (fromFlag $ haddockVerbosity haddockFlags) $ "No documentation was generated as this package does not contain " ++ "a library. Perhaps you want to use the --executables, --tests," ++ " --benchmarks or --foreign-libraries flags." + return [] haddock_setupHooks - (BuildHooks{preBuildComponentRules = mbPbcRules}) + preBuildHook pkg_descr lbi suffixes @@ -308,7 +310,7 @@ haddock_setupHooks let using_hscolour = flag haddockLinkedSource && version < mkVersion [2, 17] when using_hscolour $ hscolour' - noBuildHooks + (const $ return []) -- NB: we are not passing the user BuildHooks here, -- because we are already running the pre/post build hooks -- for Haddock. @@ -330,7 +332,7 @@ haddock_setupHooks internalPackageDB <- createInternalPackageDB verbosity lbi (flag $ setupDistPref . haddockCommonFlags) - (\f -> foldM_ f (installedPkgs lbi) targets') $ \index target -> do + (mons, _mbIPI) <- (\f -> foldM f ([], installedPkgs lbi) targets') $ \(monsAcc, index) target -> do curDir <- absoluteWorkingDirLBI lbi let component = targetComponent target @@ -345,21 +347,11 @@ haddock_setupHooks , installedPkgs = index } - runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO () - runPreBuildHooks lbi2 tgt = - let inputs = - SetupHooks.PreBuildComponentInputs - { SetupHooks.buildingWhat = BuildHaddock flags - , SetupHooks.localBuildInfo = lbi2 - , SetupHooks.targetInfo = tgt - } - in for_ mbPbcRules $ \pbcRules -> do - (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules - SetupHooks.executeRules verbosity lbi2 tgt ruleFromId + pbci = SetupHooks.PreBuildComponentInputs (BuildHaddock flags) lbi' target -- See Note [Hi Haddock Recompilation Avoidance] reusingGHCCompilationArtifacts verbosity tmpFileOpts mbWorkDir lbi bi clbi version $ \haddockArtifactsDirs -> do - preBuildComponent runPreBuildHooks verbosity lbi' target + mons <- preBuildComponent (preBuildHook pbci) verbosity lbi' target preprocessComponent pkg_descr component lbi' clbi False verbosity suffixes let doExe com = case (compToExe com) of @@ -529,7 +521,7 @@ haddock_setupHooks benchArgs return index - return ipi + return (monsAcc ++ mons, ipi) for_ (extraDocFiles pkg_descr) $ \fpath -> do files <- matchDirFileGlob verbosity (specVersion pkg_descr) mbWorkDir fpath @@ -537,6 +529,8 @@ haddock_setupHooks for_ files $ copyFileToCwd verbosity mbWorkDir (unDir targetDir) + return mons + -- | Execute 'Haddock' configured with 'HaddocksFlags'. It is used to build -- index and contents for documentation of multiple packages. createHaddockIndex @@ -1071,7 +1065,13 @@ reusingGHCCompilationArtifacts verbosity tmpFileOpts mbWorkDir lbi bi clbi versi let vanillaOpts = componentGhcOptions normal lbi bi clbi (buildDir lbi) i = interpretSymbolicPath mbWorkDir - copyDir ghcDir tmpDir = copyDirectoryRecursive verbosity (i $ fromFlag $ ghcDir vanillaOpts) (i tmpDir) + copyDir getGhcDir tmpDir = do + let ghcDir = i $ fromFlag $ getGhcDir vanillaOpts + ghcDirExists <- doesDirectoryExist ghcDir + -- Don't try to copy artifacts if they don't exist, e.g. if + -- we have not yet run the 'build' command. + when ghcDirExists $ + copyDirectoryRecursive verbosity ghcDir (i tmpDir) copyDir ghcOptObjDir tmpObjDir copyDir ghcOptHiDir tmpHiDir -- copyDir ghcOptStubDir tmpStubDir -- (see W.1 in Note [Hi Haddock Recompilation Avoidance]) @@ -1469,20 +1469,22 @@ hscolour -> [PPSuffixHandler] -> HscolourFlags -> IO () -hscolour = hscolour_setupHooks noBuildHooks +hscolour = hscolour_setupHooks (const $ return []) hscolour_setupHooks - :: BuildHooks + :: (SetupHooks.PreBuildComponentInputs -> IO [MonitorFilePath]) + -- ^ pre-build hook -> PackageDescription -> LocalBuildInfo -> [PPSuffixHandler] -> HscolourFlags -> IO () -hscolour_setupHooks setupHooks = - hscolour' setupHooks dieNoVerbosity ForDevelopment +hscolour_setupHooks preBuildHook = + hscolour' preBuildHook dieNoVerbosity ForDevelopment hscolour' - :: BuildHooks + :: (SetupHooks.PreBuildComponentInputs -> IO [MonitorFilePath]) + -- ^ pre-build hook -> (String -> IO ()) -- ^ Called when the 'hscolour' exe is not found. -> HaddockTarget @@ -1492,7 +1494,7 @@ hscolour' -> HscolourFlags -> IO () hscolour' - (BuildHooks{preBuildComponentRules = mbPbcRules}) + preBuildHook onNoHsColour haddockTarget pkg_descr @@ -1527,19 +1529,10 @@ hscolour' hscolourPref haddockTarget distPref pkg_descr withAllComponentsInBuildOrder pkg_descr lbi $ \comp clbi -> do - let tgt = TargetInfo clbi comp - runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO () - runPreBuildHooks lbi2 target = - let inputs = - SetupHooks.PreBuildComponentInputs - { SetupHooks.buildingWhat = BuildHscolour flags - , SetupHooks.localBuildInfo = lbi2 - , SetupHooks.targetInfo = target - } - in for_ mbPbcRules $ \pbcRules -> do - (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules - SetupHooks.executeRules verbosity lbi2 tgt ruleFromId - preBuildComponent runPreBuildHooks verbosity lbi tgt + let + target = TargetInfo clbi comp + pbci = SetupHooks.PreBuildComponentInputs (BuildHscolour flags) lbi target + _monitors <- preBuildComponent (preBuildHook pbci) verbosity lbi target preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes let doExe com = case (compToExe com) of diff --git a/Cabal/src/Distribution/Simple/PreProcess.hs b/Cabal/src/Distribution/Simple/PreProcess.hs index 2484bcf8ac2..697a0e490dd 100644 --- a/Cabal/src/Distribution/Simple/PreProcess.hs +++ b/Cabal/src/Distribution/Simple/PreProcess.hs @@ -345,8 +345,8 @@ preprocessFile mbWorkDir searchLoc buildLoc forSDist baseFile verbosity builtinS createDirectoryIfMissingVerbose verbosity True destDir runPreProcessorWithHsBootHack pp - (getSymbolicPath $ psrcLoc, getSymbolicPath $ psrcRelFile) - (getSymbolicPath $ buildLoc, srcStem <.> "hs") + (psrcLoc, getSymbolicPath $ psrcRelFile) + (buildLoc, srcStem <.> "hs") where i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path buildAsSrcLoc :: SymbolicPath Pkg (Dir Source) @@ -361,20 +361,25 @@ preprocessFile mbWorkDir searchLoc buildLoc forSDist baseFile verbosity builtinS pp (inBaseDir, inRelativeFile) (outBaseDir, outRelativeFile) = do + -- Preprocessors are expected to take into account the working + -- directory, e.g. using runProgramCwd with a working directory + -- computed with mbWorkDirLBI. + -- Hence the use of 'getSymbolicPath' here. runPreProcessor pp - (inBaseDir, inRelativeFile) - (outBaseDir, outRelativeFile) + (getSymbolicPath $ inBaseDir, inRelativeFile) + (getSymbolicPath $ outBaseDir, outRelativeFile) verbosity - exists <- doesFileExist inBoot - when exists $ copyFileVerbose verbosity inBoot outBoot - where + -- Here we interact directly with the file system, so we must + -- interpret symbolic paths with respect to the working directory. + let + inFile = normalise (i inBaseDir inRelativeFile) + outFile = normalise (i outBaseDir outRelativeFile) inBoot = replaceExtension inFile "hs-boot" outBoot = replaceExtension outFile "hs-boot" - - inFile = normalise (inBaseDir inRelativeFile) - outFile = normalise (outBaseDir outRelativeFile) + exists <- doesFileExist inBoot + when exists $ copyFileVerbose verbosity inBoot outBoot -- ------------------------------------------------------------ diff --git a/Cabal/src/Distribution/Simple/Program/Db.hs b/Cabal/src/Distribution/Simple/Program/Db.hs index c76b38e9923..929b3dd6372 100644 --- a/Cabal/src/Distribution/Simple/Program/Db.hs +++ b/Cabal/src/Distribution/Simple/Program/Db.hs @@ -67,6 +67,7 @@ module Distribution.Simple.Program.Db , ConfiguredProgs , updateUnconfiguredProgs , updateConfiguredProgs + , updatePathProgDb ) where import Distribution.Compat.Prelude @@ -483,6 +484,45 @@ reconfigurePrograms verbosity paths argss progdb = do where progs = catMaybes [lookupKnownProgram name progdb | (name, _) <- paths] +-- | Update the PATH and environment variables of already-configured programs +-- in the program database. +-- +-- This is a somewhat sketchy operation, but it handles the following situation: +-- +-- - we add a build-tool-depends executable to the program database, with its +-- associated data directory environment variables; +-- - we want invocations of GHC (an already configured program) to be able to +-- find this program (e.g. if the build-tool-depends executable is used +-- in a Template Haskell splice). +-- +-- In this case, we want to add the build tool to the PATH of GHC, even though +-- GHC is already configured which in theory means we shouldn't touch it any +-- more. +updatePathProgDb :: Verbosity -> ProgramDb -> IO ProgramDb +updatePathProgDb verbosity progdb = + updatePathProgs verbosity progs progdb + where + progs = Map.elems $ configuredProgs progdb + +-- | See 'updatePathProgDb' +updatePathProgs :: Verbosity -> [ConfiguredProgram] -> ProgramDb -> IO ProgramDb +updatePathProgs verbosity progs progdb = + foldM (flip (updatePathProg verbosity)) progdb progs + +-- | See 'updatePathProgDb'. +updatePathProg :: Verbosity -> ConfiguredProgram -> ProgramDb -> IO ProgramDb +updatePathProg _verbosity prog progdb = do + newPath <- programSearchPathAsPATHVar (progSearchPath progdb) + let envOverrides = progOverrideEnv progdb + progOverrides = programOverrideEnv prog + prog' = + prog + { programOverrideEnv = + [("PATH", Just newPath)] + ++ filter ((/= "PATH") . fst) (envOverrides ++ progOverrides) + } + return $ updateProgram prog' progdb + -- | Check that a program is configured and available to be run. -- -- It raises an exception if the program could not be configured, otherwise diff --git a/Cabal/src/Distribution/Simple/Register.hs b/Cabal/src/Distribution/Simple/Register.hs index 48962782728..949768d9102 100644 --- a/Cabal/src/Distribution/Simple/Register.hs +++ b/Cabal/src/Distribution/Simple/Register.hs @@ -169,7 +169,7 @@ registerAll -> IO () registerAll pkg lbi regFlags ipis = do - when (fromFlag (regPrintId regFlags)) $ do + when (Just True == flagToMaybe (regPrintId regFlags)) $ do for_ ipis $ \installedPkgInfo -> -- Only print the public library's IPI when diff --git a/Cabal/src/Distribution/Simple/SetupHooks/Errors.hs b/Cabal/src/Distribution/Simple/SetupHooks/Errors.hs index 22b070e5d41..55dcdb8d7a5 100644 --- a/Cabal/src/Distribution/Simple/SetupHooks/Errors.hs +++ b/Cabal/src/Distribution/Simple/SetupHooks/Errors.hs @@ -28,9 +28,6 @@ import qualified Distribution.Simple.SetupHooks.Rule as Rule import Distribution.Types.Component import qualified Data.Graph as Graph -import Data.List - ( intercalate - ) import qualified Data.List.NonEmpty as NE import qualified Data.Tree as Tree @@ -129,7 +126,7 @@ rulesExceptionMessage = \case showCycle (r, rs) = unlines . map (" " ++) . lines $ Tree.drawTree $ - fmap showRule $ + fmap show $ Tree.Node r rs CantFindSourceForRuleDependencies _r deps -> unlines $ @@ -172,22 +169,9 @@ rulesExceptionMessage = \case DuplicateRuleId rId r1 r2 -> unlines $ [ "Duplicate pre-build rule (" <> show rId <> ")" - , " - " <> showRule (ruleBinary r1) - , " - " <> showRule (ruleBinary r2) + , " - " <> show (ruleBinary r1) + , " - " <> show (ruleBinary r2) ] - where - showRule :: RuleBinary -> String - showRule (Rule{staticDependencies = deps, results = reslts}) = - "Rule: " ++ showDeps deps ++ " --> " ++ show (NE.toList reslts) - -showDeps :: [Rule.Dependency] -> String -showDeps deps = "[" ++ intercalate ", " (map showDep deps) ++ "]" - -showDep :: Rule.Dependency -> String -showDep = \case - RuleDependency (RuleOutput{outputOfRule = rId, outputIndex = i}) -> - "(" ++ show rId ++ ")[" ++ show i ++ "]" - FileDependency loc -> show loc cannotApplyComponentDiffCode :: CannotApplyComponentDiffReason -> Int cannotApplyComponentDiffCode = \case diff --git a/Cabal/src/Distribution/Simple/SetupHooks/Internal.hs b/Cabal/src/Distribution/Simple/SetupHooks/Internal.hs index 0057bbee9a1..a2cb607a417 100644 --- a/Cabal/src/Distribution/Simple/SetupHooks/Internal.hs +++ b/Cabal/src/Distribution/Simple/SetupHooks/Internal.hs @@ -5,6 +5,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} @@ -77,6 +78,7 @@ module Distribution.Simple.SetupHooks.Internal -- ** Executing build rules , executeRules + , executeRulesUserOrSystem -- ** HookedBuildInfo compatibility code , hookedBuildInfoComponents @@ -120,6 +122,7 @@ import Data.Coerce (coerce) import qualified Data.Graph as Graph import qualified Data.List.NonEmpty as NE import qualified Data.Map as Map +import Data.Monoid (Ap (..)) import qualified Data.Set as Set import System.Directory (doesFileExist) @@ -789,8 +792,8 @@ applyComponentDiffs verbosity f = traverseComponents apply_diff Just diff -> applyComponentDiff verbosity c diff Nothing -> return c -forComponents_ :: PackageDescription -> (Component -> IO ()) -> IO () -forComponents_ pd f = getConst $ traverseComponents (Const . f) pd +forComponents_ :: Applicative m => PackageDescription -> (Component -> m ()) -> m () +forComponents_ pd f = getAp . getConst $ traverseComponents (Const . Ap . f) pd applyComponentDiff :: Verbosity diff --git a/Cabal/src/Distribution/Simple/SetupHooks/Rule.hs b/Cabal/src/Distribution/Simple/SetupHooks/Rule.hs index 70b188cf437..7d6fc7e8dfd 100644 --- a/Cabal/src/Distribution/Simple/SetupHooks/Rule.hs +++ b/Cabal/src/Distribution/Simple/SetupHooks/Rule.hs @@ -272,6 +272,8 @@ deriving stock instance Eq (RuleData User) deriving stock instance Eq (RuleData System) deriving anyclass instance Binary (RuleData User) deriving anyclass instance Binary (RuleData System) +deriving anyclass instance Structured (RuleData User) +deriving anyclass instance Structured (RuleData System) -- | Trimmed down 'Show' instance, mostly for error messages. instance Show RuleBinary where @@ -678,6 +680,10 @@ data } -> RuleCommands scope deps ruleCmd +-- NB: whenever you change this datatype, you **must** also update its +-- 'Structured' instance. The structure hash is used as a handshake when +-- communicating with an external hooks executable. + {- Note [Hooks Binary instances] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Hooks API is strongly typed: users can declare rule commands with varying @@ -1086,6 +1092,35 @@ instance -- that involve existential quantification. data family Tok (arg :: Symbol) :: k +instance + (Typeable scope, Typeable ruleCmd, Typeable deps) + => Structured (RuleCommands scope deps ruleCmd) + where + structure _ = + Structure + tr + 0 + (show tr) + [ + ( "StaticRuleCommand" + , + [ nominalStructure $ Proxy @(ruleCmd scope (Tok "arg") (IO ())) + , nominalStructure $ Proxy @(Typeable.TypeRep (Tok "arg" :: Hs.Type)) + ] + ) + , + ( "DynamicRuleCommands" + , + [ nominalStructure $ Proxy @(Static scope (Dict (Binary (Tok "depsRes"), Show (Tok "depsRes"), Eq (Tok "depsRes")))) + , nominalStructure $ Proxy @(deps scope (Tok "depsArg") (Tok "depsRes")) + , nominalStructure $ Proxy @(ruleCmd scope (Tok "arg") (Tok "depsRes" -> IO ())) + , nominalStructure $ Proxy @(Typeable.TypeRep (Tok "depsArg", Tok "depsRes", Tok "arg")) + ] + ) + ] + where + tr = Typeable.SomeTypeRep $ Typeable.typeRep @(RuleCommands scope deps ruleCmd) + instance ( forall res. Binary (ruleCmd System LBS.ByteString res) , Binary (deps System LBS.ByteString LBS.ByteString) diff --git a/Cabal/src/Distribution/Simple/Test/ExeV10.hs b/Cabal/src/Distribution/Simple/Test/ExeV10.hs index 3ad112af2bb..37cf440270d 100644 --- a/Cabal/src/Distribution/Simple/Test/ExeV10.hs +++ b/Cabal/src/Distribution/Simple/Test/ExeV10.hs @@ -20,6 +20,7 @@ import qualified Distribution.Simple.LocalBuildInfo as LBI , buildDir , depLibraryPaths ) + import Distribution.Simple.Program.Db import Distribution.Simple.Program.Find import Distribution.Simple.Program.Run @@ -27,7 +28,7 @@ import Distribution.Simple.Setup.Common import Distribution.Simple.Setup.Test import Distribution.Simple.Test.Log import Distribution.Simple.Utils -import Distribution.System +import Distribution.System (Platform (Platform)) import Distribution.TestSuite import qualified Distribution.Types.LocalBuildInfo as LBI ( LocalBuildInfo (..) diff --git a/Cabal/src/Distribution/Simple/UHC.hs b/Cabal/src/Distribution/Simple/UHC.hs index aa41388c6d0..0016c93d4a8 100644 --- a/Cabal/src/Distribution/Simple/UHC.hs +++ b/Cabal/src/Distribution/Simple/UHC.hs @@ -59,10 +59,9 @@ import System.FilePath (pathSeparator) configure :: Verbosity -> Maybe FilePath - -> Maybe FilePath -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb) -configure verbosity hcPath _hcPkgPath progdb = do +configure verbosity hcPath progdb = do (_uhcProg, uhcVersion, progdb') <- requireProgramVersion verbosity diff --git a/bootstrap/bootstrap.py b/bootstrap/bootstrap.py index 47ce691abfc..eaba6fcac8f 100755 --- a/bootstrap/bootstrap.py +++ b/bootstrap/bootstrap.py @@ -84,12 +84,10 @@ class PackageSource(Enum): local_packages: List[PackageName] = [ "Cabal-syntax" , "Cabal" , "Cabal-hooks" - , "Cabal-QuickCheck" - , "Cabal-described" - , "Cabal-tests" - , "Cabal-tree-diff" , "cabal-install-solver" - , "cabal-install" ] + , "cabal-install" + , "hooks-exe" + ] # Value passed to setup build -j {jobs_amount} # 1 is not set by default. diff --git a/bootstrap/linux-9.10.1.json b/bootstrap/linux-9.10.1.json index dd3fcca7d14..f0fc1080fdb 100644 --- a/bootstrap/linux-9.10.1.json +++ b/bootstrap/linux-9.10.1.json @@ -223,7 +223,7 @@ "version": "2.6.4.2" }, { - "cabal_sha256": "75ada03bd2d2b747319e38877a55bf8be529db4520a07d4e5ffbd24c5e850dcb", + "cabal_sha256": "ad36c6a1b3bc203b02751c8bffae8a684cc755661a2a567362cd4a0da1193c5e", "component": "lib:HTTP", "flags": [ "-conduit10", @@ -232,7 +232,7 @@ "-warp-tests" ], "package": "HTTP", - "revision": 5, + "revision": 6, "source": "hackage", "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", "version": "4000.4.1" @@ -452,7 +452,17 @@ "version": "0.6.2.6" }, { - "cabal_sha256": "5b6ade9c872ff68b1c1c2ac7cd51441ae1ab7557020e744a3f60413938b9121c", + "cabal_sha256": null, + "component": "lib:hooks-exe", + "flags": [], + "package": "hooks-exe", + "revision": null, + "source": "local", + "src_sha256": null, + "version": "0.1" + }, + { + "cabal_sha256": "a5effff3d14a0bbfde51dd62e72cff069b56be4298f16a78db7d3cc0c678c859", "component": "lib:open-browser", "flags": [ "-example" @@ -460,8 +470,8 @@ "package": "open-browser", "revision": 0, "source": "hackage", - "src_sha256": "c2ab01c3238e31b1560114bf8311a70a6c3538e643d3035286a166d137957804", - "version": "0.2.1.1" + "src_sha256": "deff01d066a027bfb609522465e8e0580d8b56004cebb5b1f3e0f05f79cbf85d", + "version": "0.4.0.0" }, { "cabal_sha256": "d6c0c6d1136f5046207a331114ff4130e70640452096de7719bf03e3fceb7c7b", diff --git a/bootstrap/linux-9.12.2.json b/bootstrap/linux-9.12.2.json index 969b9a40b33..4c8511bee7c 100644 --- a/bootstrap/linux-9.12.2.json +++ b/bootstrap/linux-9.12.2.json @@ -197,7 +197,7 @@ "version": "2.6.4.2" }, { - "cabal_sha256": "75ada03bd2d2b747319e38877a55bf8be529db4520a07d4e5ffbd24c5e850dcb", + "cabal_sha256": "ad36c6a1b3bc203b02751c8bffae8a684cc755661a2a567362cd4a0da1193c5e", "component": "lib:HTTP", "flags": [ "-conduit10", @@ -206,7 +206,7 @@ "-warp-tests" ], "package": "HTTP", - "revision": 5, + "revision": 6, "source": "hackage", "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", "version": "4000.4.1" @@ -426,7 +426,17 @@ "version": "0.6.2.6" }, { - "cabal_sha256": "5b6ade9c872ff68b1c1c2ac7cd51441ae1ab7557020e744a3f60413938b9121c", + "cabal_sha256": null, + "component": "lib:hooks-exe", + "flags": [], + "package": "hooks-exe", + "revision": null, + "source": "local", + "src_sha256": null, + "version": "0.1" + }, + { + "cabal_sha256": "a5effff3d14a0bbfde51dd62e72cff069b56be4298f16a78db7d3cc0c678c859", "component": "lib:open-browser", "flags": [ "-example" @@ -434,8 +444,8 @@ "package": "open-browser", "revision": 0, "source": "hackage", - "src_sha256": "c2ab01c3238e31b1560114bf8311a70a6c3538e643d3035286a166d137957804", - "version": "0.2.1.1" + "src_sha256": "deff01d066a027bfb609522465e8e0580d8b56004cebb5b1f3e0f05f79cbf85d", + "version": "0.4.0.0" }, { "cabal_sha256": "d6c0c6d1136f5046207a331114ff4130e70640452096de7719bf03e3fceb7c7b", diff --git a/bootstrap/linux-9.2.8.json b/bootstrap/linux-9.2.8.json index 551976a71ee..9dac67c750e 100644 --- a/bootstrap/linux-9.2.8.json +++ b/bootstrap/linux-9.2.8.json @@ -233,7 +233,7 @@ "version": "2.6.4.2" }, { - "cabal_sha256": "75ada03bd2d2b747319e38877a55bf8be529db4520a07d4e5ffbd24c5e850dcb", + "cabal_sha256": "ad36c6a1b3bc203b02751c8bffae8a684cc755661a2a567362cd4a0da1193c5e", "component": "lib:HTTP", "flags": [ "-conduit10", @@ -242,7 +242,7 @@ "-warp-tests" ], "package": "HTTP", - "revision": 5, + "revision": 6, "source": "hackage", "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", "version": "4000.4.1" @@ -473,7 +473,17 @@ "version": "0.6.2.6" }, { - "cabal_sha256": "5b6ade9c872ff68b1c1c2ac7cd51441ae1ab7557020e744a3f60413938b9121c", + "cabal_sha256": null, + "component": "lib:hooks-exe", + "flags": [], + "package": "hooks-exe", + "revision": null, + "source": "local", + "src_sha256": null, + "version": "0.1" + }, + { + "cabal_sha256": "a5effff3d14a0bbfde51dd62e72cff069b56be4298f16a78db7d3cc0c678c859", "component": "lib:open-browser", "flags": [ "-example" @@ -481,8 +491,8 @@ "package": "open-browser", "revision": 0, "source": "hackage", - "src_sha256": "c2ab01c3238e31b1560114bf8311a70a6c3538e643d3035286a166d137957804", - "version": "0.2.1.1" + "src_sha256": "deff01d066a027bfb609522465e8e0580d8b56004cebb5b1f3e0f05f79cbf85d", + "version": "0.4.0.0" }, { "cabal_sha256": "d6c0c6d1136f5046207a331114ff4130e70640452096de7719bf03e3fceb7c7b", diff --git a/bootstrap/linux-9.4.8.json b/bootstrap/linux-9.4.8.json index 8d66decacac..b14466fb2c6 100644 --- a/bootstrap/linux-9.4.8.json +++ b/bootstrap/linux-9.4.8.json @@ -233,7 +233,7 @@ "version": "2.6.4.2" }, { - "cabal_sha256": "75ada03bd2d2b747319e38877a55bf8be529db4520a07d4e5ffbd24c5e850dcb", + "cabal_sha256": "ad36c6a1b3bc203b02751c8bffae8a684cc755661a2a567362cd4a0da1193c5e", "component": "lib:HTTP", "flags": [ "-conduit10", @@ -242,7 +242,7 @@ "-warp-tests" ], "package": "HTTP", - "revision": 5, + "revision": 6, "source": "hackage", "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", "version": "4000.4.1" @@ -463,7 +463,17 @@ "version": "0.6.2.6" }, { - "cabal_sha256": "5b6ade9c872ff68b1c1c2ac7cd51441ae1ab7557020e744a3f60413938b9121c", + "cabal_sha256": null, + "component": "lib:hooks-exe", + "flags": [], + "package": "hooks-exe", + "revision": null, + "source": "local", + "src_sha256": null, + "version": "0.1" + }, + { + "cabal_sha256": "a5effff3d14a0bbfde51dd62e72cff069b56be4298f16a78db7d3cc0c678c859", "component": "lib:open-browser", "flags": [ "-example" @@ -471,8 +481,8 @@ "package": "open-browser", "revision": 0, "source": "hackage", - "src_sha256": "c2ab01c3238e31b1560114bf8311a70a6c3538e643d3035286a166d137957804", - "version": "0.2.1.1" + "src_sha256": "deff01d066a027bfb609522465e8e0580d8b56004cebb5b1f3e0f05f79cbf85d", + "version": "0.4.0.0" }, { "cabal_sha256": "d6c0c6d1136f5046207a331114ff4130e70640452096de7719bf03e3fceb7c7b", diff --git a/bootstrap/linux-9.6.6.json b/bootstrap/linux-9.6.6.json deleted file mode 100644 index c94c6580dc5..00000000000 --- a/bootstrap/linux-9.6.6.json +++ /dev/null @@ -1,532 +0,0 @@ -{ - "builtin": [ - { - "package": "rts", - "version": "1.0.2" - }, - { - "package": "ghc-prim", - "version": "0.10.0" - }, - { - "package": "ghc-bignum", - "version": "1.3" - }, - { - "package": "base", - "version": "4.18.2.1" - }, - { - "package": "array", - "version": "0.5.6.0" - }, - { - "package": "deepseq", - "version": "1.4.8.1" - }, - { - "package": "ghc-boot-th", - "version": "9.6.6" - }, - { - "package": "pretty", - "version": "1.1.3.6" - }, - { - "package": "template-haskell", - "version": "2.20.0.0" - }, - { - "package": "containers", - "version": "0.6.7" - }, - { - "package": "bytestring", - "version": "0.11.5.3" - }, - { - "package": "transformers", - "version": "0.6.1.0" - }, - { - "package": "mtl", - "version": "2.3.1" - }, - { - "package": "stm", - "version": "2.5.1.0" - }, - { - "package": "exceptions", - "version": "0.10.7" - }, - { - "package": "filepath", - "version": "1.4.300.1" - }, - { - "package": "time", - "version": "1.12.2" - }, - { - "package": "binary", - "version": "0.8.9.1" - }, - { - "package": "text", - "version": "2.0.2" - }, - { - "package": "parsec", - "version": "3.1.16.1" - } - ], - "dependencies": [ - { - "cabal_sha256": "5b7f8afd7a879c3c8c3c636fd3c7543cdd5e0b514b7da90e76907ccd11434031", - "component": "lib:unix", - "flags": [ - "-os-string" - ], - "package": "unix", - "revision": 1, - "source": "hackage", - "src_sha256": "8117599bb3e4aa1d4656710afbc85aef2a75483eddfac5338f8cc88fb505eea2", - "version": "2.8.6.0" - }, - { - "cabal_sha256": "e3e1866eab82cb28f6a5f28507643da3987008b737e66a3c7398f39f16d824dc", - "component": "lib:file-io", - "flags": [ - "-os-string" - ], - "package": "file-io", - "revision": 0, - "source": "hackage", - "src_sha256": "e3d9113a015c57e3d8c2294550c41544f84a265291fed96cca697f91b6e86f52", - "version": "0.1.4" - }, - { - "cabal_sha256": "2490137bb7738bd79392959458ef5f276219ea5ba8a9a56d3e0b06315c1bb917", - "component": "lib:directory", - "flags": [ - "-os-string" - ], - "package": "directory", - "revision": 1, - "source": "hackage", - "src_sha256": "20a24846117fc5f8751d974b7de07210a161989410467e9adca525381b8e64cc", - "version": "1.3.9.0" - }, - { - "cabal_sha256": "de553eefe0b6548a560e9d8100486310548470a403c1fa21108dd03713da5fc7", - "component": "exe:alex", - "flags": [], - "package": "alex", - "revision": 0, - "source": "hackage", - "src_sha256": "c92efe86f8eb959ee03be6c04ee57ebc7e4abc75a6c4b26551215d7443e92a07", - "version": "3.5.1.0" - }, - { - "cabal_sha256": null, - "component": "lib:Cabal-syntax", - "flags": [], - "package": "Cabal-syntax", - "revision": null, - "source": "local", - "src_sha256": null, - "version": "3.15.0.0" - }, - { - "cabal_sha256": "9a0b2ef8096517fa0e0c7a5e9a5c2ae5744ed824c3331005f9408245810df345", - "component": "lib:process", - "flags": [], - "package": "process", - "revision": 0, - "source": "hackage", - "src_sha256": "496fe0566c3915b112e9772ac9c967dfeb8d5ca04895e54ae0160522bee76e65", - "version": "1.6.25.0" - }, - { - "cabal_sha256": null, - "component": "lib:Cabal", - "flags": [ - "-git-rev" - ], - "package": "Cabal", - "revision": null, - "source": "local", - "src_sha256": null, - "version": "3.15.0.0" - }, - { - "cabal_sha256": null, - "component": "lib:Cabal-hooks", - "flags": [], - "package": "Cabal-hooks", - "revision": null, - "source": "local", - "src_sha256": null, - "version": "3.16" - }, - { - "cabal_sha256": "276325277350cd2c2c88916ed3ae5cd35b2b4f494ec594fbd9534081eb7fb759", - "component": "exe:hsc2hs", - "flags": [ - "-in-ghc-tree" - ], - "package": "hsc2hs", - "revision": 3, - "source": "hackage", - "src_sha256": "6f4e34d788fe2ca7091ee0a10307ee8a7c060a1ba890f2bffad16a7d4d5cef76", - "version": "0.68.10" - }, - { - "cabal_sha256": "b0fafb2834530084f6406017500ae619f9e5e2049787a6750c68e0d331fd62dc", - "component": "lib:network", - "flags": [ - "-devel" - ], - "package": "network", - "revision": 0, - "source": "hackage", - "src_sha256": "dbd8a10456908294eb5ab9c522bf2da75444d958429a643a821464213698523e", - "version": "3.2.6.0" - }, - { - "cabal_sha256": "129a59ba3ccfcd06192fd6da899e2711ae276a466915a047bd6727e4a0321d2e", - "component": "lib:th-compat", - "flags": [], - "package": "th-compat", - "revision": 2, - "source": "hackage", - "src_sha256": "81f55fafc7afad7763c09cb8b7b4165ca3765edcf70ffa42c7393043a1382a1e", - "version": "0.1.5" - }, - { - "cabal_sha256": "6fffb57373962b5651a2db8b0af732098b3bf029a7ced76a9855615de2026588", - "component": "lib:network-uri", - "flags": [], - "package": "network-uri", - "revision": 1, - "source": "hackage", - "src_sha256": "9c188973126e893250b881f20e8811dca06c223c23402b06f7a1f2e995797228", - "version": "2.6.4.2" - }, - { - "cabal_sha256": "b90ce97917703f6613ed5a8cfe1a51525b990244f5610509baa15c8499eadca3", - "component": "lib:HTTP", - "flags": [ - "-conduit10", - "+network-uri", - "-warn-as-error", - "-warp-tests" - ], - "package": "HTTP", - "revision": 4, - "source": "hackage", - "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", - "version": "4000.4.1" - }, - { - "cabal_sha256": "2efc549644dd418bad537d1601fdd437c440d807265016bd993b6996c679ad2f", - "component": "lib:os-string", - "flags": [], - "package": "os-string", - "revision": 0, - "source": "hackage", - "src_sha256": "339c35fd3a290522f23de4e33528423cfd0b0a8f22946b0b9816a817b926cba0", - "version": "2.0.7" - }, - { - "cabal_sha256": "2f23146cbe0325029927b221647695a4c7d6e97548ff731110979e34361f58ef", - "component": "lib:hashable", - "flags": [ - "-arch-native", - "-random-initial-seed" - ], - "package": "hashable", - "revision": 1, - "source": "hackage", - "src_sha256": "e58b3a8e18da5f6cd7e937e5fd683e500bb1f8276b3768269759119ca0cddb6a", - "version": "1.5.0.0" - }, - { - "cabal_sha256": "b7648c6165729a973d95cb328f9fd874813a81c727707e8b2552b4f03399763b", - "component": "lib:async", - "flags": [ - "-bench" - ], - "package": "async", - "revision": 3, - "source": "hackage", - "src_sha256": "1818473ebab9212afad2ed76297aefde5fae8b5d4404daf36939aece6a8f16f7", - "version": "2.2.5" - }, - { - "cabal_sha256": "a694e88f9ec9fc79f0b03f233d3fea592b68f70a34aac2ddb5bcaecb6562e2fd", - "component": "lib:base16-bytestring", - "flags": [], - "package": "base16-bytestring", - "revision": 1, - "source": "hackage", - "src_sha256": "1d5a91143ef0e22157536093ec8e59d226a68220ec89378d5dcaeea86472c784", - "version": "1.0.2.0" - }, - { - "cabal_sha256": "45305ccf8914c66d385b518721472c7b8c858f1986945377f74f85c1e0d49803", - "component": "lib:base64-bytestring", - "flags": [], - "package": "base64-bytestring", - "revision": 1, - "source": "hackage", - "src_sha256": "fbf8ed30edde271eb605352021431d8f1b055f95a56af31fe2eacf6bdfdc49c9", - "version": "1.2.1.0" - }, - { - "cabal_sha256": "caa9b4a92abf1496c7f6a3c0f4e357426a54880077cb9f04e260a8bfa034b77b", - "component": "lib:splitmix", - "flags": [ - "-optimised-mixer" - ], - "package": "splitmix", - "revision": 1, - "source": "hackage", - "src_sha256": "9df07a9611ef45f1b1258a0b412f4d02c920248f69d2e2ce8ccda328f7e13002", - "version": "0.1.0.5" - }, - { - "cabal_sha256": "32397de181e20ccaacf806ec70de9308cf044f089a2be37c936f3f8967bde867", - "component": "lib:random", - "flags": [], - "package": "random", - "revision": 0, - "source": "hackage", - "src_sha256": "790f4dc2d2327c453ff6aac7bf15399fd123d55e927935f68f84b5df42d9a4b4", - "version": "1.2.1.2" - }, - { - "cabal_sha256": "4d33a49cd383d50af090f1b888642d10116e43809f9da6023d9fc6f67d2656ee", - "component": "lib:edit-distance", - "flags": [], - "package": "edit-distance", - "revision": 1, - "source": "hackage", - "src_sha256": "3e8885ee2f56ad4da940f043ae8f981ee2fe336b5e8e4ba3f7436cff4f526c4a", - "version": "0.2.2.1" - }, - { - "cabal_sha256": null, - "component": "lib:cabal-install-solver", - "flags": [ - "-debug-expensive-assertions", - "-debug-tracetree" - ], - "package": "cabal-install-solver", - "revision": null, - "source": "local", - "src_sha256": null, - "version": "3.15.0.0" - }, - { - "cabal_sha256": "acb64f2af52d81b0bb92c266f11d43def726a7a7b74a2c23d219e160b54edec7", - "component": "lib:cryptohash-sha256", - "flags": [ - "-exe", - "+use-cbits" - ], - "package": "cryptohash-sha256", - "revision": 5, - "source": "hackage", - "src_sha256": "73a7dc7163871a80837495039a099967b11f5c4fe70a118277842f7a713c6bf6", - "version": "0.11.102.1" - }, - { - "cabal_sha256": "ccce771562c49a2b29a52046ca68c62179e97e8fbeacdae32ca84a85445e8f42", - "component": "lib:echo", - "flags": [ - "-example" - ], - "package": "echo", - "revision": 0, - "source": "hackage", - "src_sha256": "c9fe1bf2904825a65b667251ec644f197b71dc5c209d2d254be5de3d496b0e43", - "version": "0.1.4" - }, - { - "cabal_sha256": "48383789821af5cc624498f3ee1d0939a070cda9468c0bfe63c951736be81c75", - "component": "lib:ed25519", - "flags": [ - "+no-donna", - "+test-doctests", - "+test-hlint", - "+test-properties" - ], - "package": "ed25519", - "revision": 8, - "source": "hackage", - "src_sha256": "d8a5958ebfa9309790efade64275dc5c441b568645c45ceed1b0c6ff36d6156d", - "version": "0.0.5.0" - }, - { - "cabal_sha256": "8a3004c2de2a0b5ef0634d3da6eae62ba8d8a734bab9ed8c6cfd749e7ca08997", - "component": "lib:lukko", - "flags": [ - "+ofd-locking" - ], - "package": "lukko", - "revision": 0, - "source": "hackage", - "src_sha256": "72d86f8aa625b461f4397f737346f78a1700a7ffbff55cf6375c5e18916e986d", - "version": "0.1.2" - }, - { - "cabal_sha256": "e9f151d9999be8953443e730524b2792e9c0a4fb5b1463097fa1a8230870fd8a", - "component": "lib:tar-internal", - "flags": [], - "package": "tar", - "revision": 1, - "source": "hackage", - "src_sha256": "50bb660feec8a524416d6934251b996eaa7e39d49ae107ad505ab700d43f6814", - "version": "0.6.3.0" - }, - { - "cabal_sha256": "e9f151d9999be8953443e730524b2792e9c0a4fb5b1463097fa1a8230870fd8a", - "component": "lib:tar", - "flags": [], - "package": "tar", - "revision": 1, - "source": "hackage", - "src_sha256": "50bb660feec8a524416d6934251b996eaa7e39d49ae107ad505ab700d43f6814", - "version": "0.6.3.0" - }, - { - "cabal_sha256": "85e64a75c0b490506a7edaa2d54950c668e66b65758bb08bb14cd31faf53a206", - "component": "lib:zlib", - "flags": [ - "-bundled-c-zlib", - "+non-blocking-ffi", - "+pkg-config" - ], - "package": "zlib", - "revision": 2, - "source": "hackage", - "src_sha256": "6edd38b6b81df8d274952aa85affa6968ae86b2231e1d429ce8bc9083e6a55bc", - "version": "0.7.1.0" - }, - { - "cabal_sha256": "a7311a70ce2cc820ee430c389f57f82a082f148230b37526c34eac72b7b3ff34", - "component": "lib:hackage-security", - "flags": [ - "+cabal-syntax", - "+lukko" - ], - "package": "hackage-security", - "revision": 4, - "source": "hackage", - "src_sha256": "2e4261576b3e11b9f5175392947f56a638cc1a3584b8acbb962b809d7c69db69", - "version": "0.6.2.6" - }, - { - "cabal_sha256": "e4be4a206f5ab6ddb5ae4fbb39101529196e20af5670c5d33326fea6eff886fd", - "component": "lib:open-browser", - "flags": [], - "package": "open-browser", - "revision": 0, - "source": "hackage", - "src_sha256": "0bed2e63800f738e78a4803ed22902accb50ac02068b96c17ce83a267244ca66", - "version": "0.2.1.0" - }, - { - "cabal_sha256": "0322b2fcd1358f3355e0c8608efa60d27b14d1c9d476451dbcb9181363bd8b27", - "component": "lib:regex-base", - "flags": [], - "package": "regex-base", - "revision": 4, - "source": "hackage", - "src_sha256": "7b99408f580f5bb67a1c413e0bc735886608251331ad36322020f2169aea2ef1", - "version": "0.94.0.2" - }, - { - "cabal_sha256": "816d6acc560cb86672f347a7bef8129578dde26ed760f9e79b4976ed9bd7b9fd", - "component": "lib:regex-posix", - "flags": [ - "-_regex-posix-clib" - ], - "package": "regex-posix", - "revision": 3, - "source": "hackage", - "src_sha256": "c7827c391919227711e1cff0a762b1678fd8739f9c902fc183041ff34f59259c", - "version": "0.96.0.1" - }, - { - "cabal_sha256": "3e196e1362e4d0ec9dfcd7f8d58b24fac91beafaa1c8ee34dc9dee489c362377", - "component": "lib:resolv", - "flags": [], - "package": "resolv", - "revision": 4, - "source": "hackage", - "src_sha256": "880d283df9132a7375fa28670f71e86480a4f49972256dc2a204c648274ae74b", - "version": "0.2.0.2" - }, - { - "cabal_sha256": "8bb7261bd54bd58acfcb154be6a161fb6d0d31a1852aadc8e927d2ad2d7651d1", - "component": "lib:safe-exceptions", - "flags": [], - "package": "safe-exceptions", - "revision": 1, - "source": "hackage", - "src_sha256": "3c51d8d50c9b60ff8bf94f942fd92e3bea9e62c5afa778dfc9f707b79da41ef6", - "version": "0.1.7.4" - }, - { - "cabal_sha256": "2de5218cef72b8ef090bd7d0fd930ffa143242a120c62e013b5cf039858f1855", - "component": "lib:semaphore-compat", - "flags": [], - "package": "semaphore-compat", - "revision": 3, - "source": "hackage", - "src_sha256": "1c6e6fab021c2ccee5d86112fb1c0bd016d15e0cf70c489dae5fb5ec156ed9e2", - "version": "1.0.0" - }, - { - "cabal_sha256": null, - "component": "lib:cabal-install", - "flags": [ - "-git-rev", - "+lukko", - "+native-dns" - ], - "package": "cabal-install", - "revision": null, - "source": "local", - "src_sha256": null, - "version": "3.15.0.0" - }, - { - "cabal_sha256": null, - "component": "exe:cabal", - "flags": [ - "-git-rev", - "+lukko", - "+native-dns" - ], - "package": "cabal-install", - "revision": null, - "source": "local", - "src_sha256": null, - "version": "3.15.0.0" - }, - { - "cabal_sha256": "e4be4a206f5ab6ddb5ae4fbb39101529196e20af5670c5d33326fea6eff886fd", - "component": "exe:example", - "flags": [], - "package": "open-browser", - "revision": 0, - "source": "hackage", - "src_sha256": "0bed2e63800f738e78a4803ed22902accb50ac02068b96c17ce83a267244ca66", - "version": "0.2.1.0" - } - ] -} diff --git a/bootstrap/linux-9.6.7.json b/bootstrap/linux-9.6.7.json index e32d157fb24..b4809db7153 100644 --- a/bootstrap/linux-9.6.7.json +++ b/bootstrap/linux-9.6.7.json @@ -87,10 +87,6 @@ { "package": "parsec", "version": "3.1.16.1" - }, - { - "package": "process", - "version": "1.6.19.0" } ], "dependencies": [ @@ -114,6 +110,16 @@ "src_sha256": null, "version": "3.15.0.0" }, + { + "cabal_sha256": "092ab61596e914d21983aa2e9206a74c4faa38a5a636446b5c954305821cb496", + "component": "lib:process", + "flags": [], + "package": "process", + "revision": 1, + "source": "hackage", + "src_sha256": "496fe0566c3915b112e9772ac9c967dfeb8d5ca04895e54ae0160522bee76e65", + "version": "1.6.25.0" + }, { "cabal_sha256": null, "component": "lib:Cabal", @@ -181,7 +187,7 @@ "version": "2.6.4.2" }, { - "cabal_sha256": "75ada03bd2d2b747319e38877a55bf8be529db4520a07d4e5ffbd24c5e850dcb", + "cabal_sha256": "ad36c6a1b3bc203b02751c8bffae8a684cc755661a2a567362cd4a0da1193c5e", "component": "lib:HTTP", "flags": [ "-conduit10", @@ -190,7 +196,7 @@ "-warp-tests" ], "package": "HTTP", - "revision": 5, + "revision": 6, "source": "hackage", "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", "version": "4000.4.1" @@ -432,7 +438,17 @@ "version": "0.6.2.6" }, { - "cabal_sha256": "5b6ade9c872ff68b1c1c2ac7cd51441ae1ab7557020e744a3f60413938b9121c", + "cabal_sha256": null, + "component": "lib:hooks-exe", + "flags": [], + "package": "hooks-exe", + "revision": null, + "source": "local", + "src_sha256": null, + "version": "0.1" + }, + { + "cabal_sha256": "a5effff3d14a0bbfde51dd62e72cff069b56be4298f16a78db7d3cc0c678c859", "component": "lib:open-browser", "flags": [ "-example" @@ -440,8 +456,8 @@ "package": "open-browser", "revision": 0, "source": "hackage", - "src_sha256": "c2ab01c3238e31b1560114bf8311a70a6c3538e643d3035286a166d137957804", - "version": "0.2.1.1" + "src_sha256": "deff01d066a027bfb609522465e8e0580d8b56004cebb5b1f3e0f05f79cbf85d", + "version": "0.4.0.0" }, { "cabal_sha256": "d6c0c6d1136f5046207a331114ff4130e70640452096de7719bf03e3fceb7c7b", diff --git a/bootstrap/linux-9.8.4.json b/bootstrap/linux-9.8.4.json index fa49c7f3281..581f7ac6ce7 100644 --- a/bootstrap/linux-9.8.4.json +++ b/bootstrap/linux-9.8.4.json @@ -185,7 +185,7 @@ "version": "2.6.4.2" }, { - "cabal_sha256": "75ada03bd2d2b747319e38877a55bf8be529db4520a07d4e5ffbd24c5e850dcb", + "cabal_sha256": "ad36c6a1b3bc203b02751c8bffae8a684cc755661a2a567362cd4a0da1193c5e", "component": "lib:HTTP", "flags": [ "-conduit10", @@ -194,7 +194,7 @@ "-warp-tests" ], "package": "HTTP", - "revision": 5, + "revision": 6, "source": "hackage", "src_sha256": "df31d8efec775124dab856d7177ddcba31be9f9e0836ebdab03d94392f2dd453", "version": "4000.4.1" @@ -436,7 +436,17 @@ "version": "0.6.2.6" }, { - "cabal_sha256": "5b6ade9c872ff68b1c1c2ac7cd51441ae1ab7557020e744a3f60413938b9121c", + "cabal_sha256": null, + "component": "lib:hooks-exe", + "flags": [], + "package": "hooks-exe", + "revision": null, + "source": "local", + "src_sha256": null, + "version": "0.1" + }, + { + "cabal_sha256": "a5effff3d14a0bbfde51dd62e72cff069b56be4298f16a78db7d3cc0c678c859", "component": "lib:open-browser", "flags": [ "-example" @@ -444,8 +454,8 @@ "package": "open-browser", "revision": 0, "source": "hackage", - "src_sha256": "c2ab01c3238e31b1560114bf8311a70a6c3538e643d3035286a166d137957804", - "version": "0.2.1.1" + "src_sha256": "deff01d066a027bfb609522465e8e0580d8b56004cebb5b1f3e0f05f79cbf85d", + "version": "0.4.0.0" }, { "cabal_sha256": "d6c0c6d1136f5046207a331114ff4130e70640452096de7719bf03e3fceb7c7b", diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index eac396f6d0e..fcc5c1fb979 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -160,6 +160,7 @@ library Distribution.Client.Init.Simple Distribution.Client.Init.Types Distribution.Client.Init.Utils + Distribution.Client.InLibrary Distribution.Client.Install Distribution.Client.InstallPlan Distribution.Client.InstallSymlink @@ -243,6 +244,7 @@ library , edit-distance >= 0.2.2 && < 0.3 , exceptions >= 0.10.4 && < 0.11 , filepath >= 1.4.0.0 && < 1.6 + , hooks-exe ^>= 0.1 , HTTP >= 4000.1.5 && < 4000.5 , mtl >= 2.0 && < 2.4 , network-uri >= 2.6.0.2 && < 2.7 diff --git a/cabal-install/src/Distribution/Client/CmdLegacy.hs b/cabal-install/src/Distribution/Client/CmdLegacy.hs index 61ae0f7458b..b15375b6147 100644 --- a/cabal-install/src/Distribution/Client/CmdLegacy.hs +++ b/cabal-install/src/Distribution/Client/CmdLegacy.hs @@ -14,7 +14,8 @@ import Distribution.Client.Sandbox ) import qualified Distribution.Client.Setup as Client import Distribution.Client.SetupWrapper - ( SetupScriptOptions (..) + ( SetupRunnerArgs (NotInLibrary) + , SetupScriptOptions (..) , defaultSetupScriptOptions , setupWrapper ) @@ -79,6 +80,7 @@ wrapperAction command getCommonFlags = getCommonFlags (const (return flags)) (const extraArgs) + NotInLibrary -- diff --git a/cabal-install/src/Distribution/Client/CmdRun.hs b/cabal-install/src/Distribution/Client/CmdRun.hs index 6f3d4123af7..b8ab864cd44 100644 --- a/cabal-install/src/Distribution/Client/CmdRun.hs +++ b/cabal-install/src/Distribution/Client/CmdRun.hs @@ -62,7 +62,6 @@ import Distribution.Client.ProjectPlanning.Types , dataDirsEnvironmentForPlan , elabExeDependencyPaths ) - import Distribution.Client.ScriptUtils ( AcceptNoTargets (..) , TargetContext (..) diff --git a/cabal-install/src/Distribution/Client/Configure.hs b/cabal-install/src/Distribution/Client/Configure.hs index bf0b7fdec27..39fd74852ff 100644 --- a/cabal-install/src/Distribution/Client/Configure.hs +++ b/cabal-install/src/Distribution/Client/Configure.hs @@ -42,7 +42,8 @@ import Distribution.Client.Setup , filterConfigureFlags ) import Distribution.Client.SetupWrapper - ( SetupScriptOptions (..) + ( SetupRunnerArgs (NotInLibrary) + , SetupScriptOptions (..) , defaultSetupScriptOptions , setupWrapper ) @@ -203,6 +204,7 @@ configure configCommonFlags (const (return configFlags)) (const extraArgs) + NotInLibrary Right installPlan0 -> let installPlan = InstallPlan.configureInstallPlan configFlags installPlan0 in case fst (InstallPlan.ready installPlan) of @@ -246,7 +248,6 @@ configure (flagToMaybe (configCabalVersion configExFlags)) ) Nothing - False logMsg message rest = debug verbosity message >> rest @@ -258,7 +259,6 @@ configureSetupScript -> SymbolicPath Pkg (Dir Dist) -> VersionRange -> Maybe Lock - -> Bool -> InstalledPackageIndex -> Maybe ReadyPackage -> SetupScriptOptions @@ -270,7 +270,6 @@ configureSetupScript distPref cabalVersion lock - forceExternal index mpkg = SetupScriptOptions @@ -288,7 +287,6 @@ configureSetupScript , useExtraEnvOverrides = [] , setupCacheLock = lock , useWin32CleanHack = False - , forceExternalSetupMethod = forceExternal , -- If we have explicit setup dependencies, list them; otherwise, we give -- the empty list of dependencies; ideally, we would fix the version of -- Cabal here, so that we no longer need the special case for that in @@ -506,6 +504,7 @@ configurePackage configCommonFlags (return . configureFlags) (const extraArgs) + NotInLibrary where gpkg :: PkgDesc.GenericPackageDescription gpkg = srcpkgDescription spkg diff --git a/cabal-install/src/Distribution/Client/Dependency.hs b/cabal-install/src/Distribution/Client/Dependency.hs index d59bc611c44..108f0ac2c3e 100644 --- a/cabal-install/src/Distribution/Client/Dependency.hs +++ b/cabal-install/src/Distribution/Client/Dependency.hs @@ -65,6 +65,8 @@ module Distribution.Client.Dependency , addSetupCabalMinVersionConstraint , addSetupCabalMaxVersionConstraint , addSetupCabalProfiledDynamic + , setImplicitSetupInfo + , extendSetupInfoDeps ) where import Distribution.Client.Compat.Prelude @@ -596,49 +598,77 @@ removeBound RelaxUpper RelaxDepModNone = removeUpperBound removeBound RelaxLower RelaxDepModCaret = transformCaretLower removeBound RelaxUpper RelaxDepModCaret = transformCaretUpper --- | Supply defaults for packages without explicit Setup dependencies +-- | Supply defaults for packages without explicit Setup dependencies. +-- It also serves to add the implicit dependency on @hooks-exe@ needed to +-- compile the @Setup.hs@ executable produced from 'SetupHooks' when +-- @build-type: Hooks@. The first argument function determines which implicit +-- dependencies are needed (including the one on @hooks-exe@). -- -- Note: It's important to apply 'addDefaultSetupDepends' after -- 'addSourcePackages'. Otherwise, the packages inserted by -- 'addSourcePackages' won't have upper bounds in dependencies relaxed. addDefaultSetupDependencies - :: (UnresolvedSourcePackage -> Maybe [Dependency]) + :: (Maybe [Dependency] -> PD.BuildType -> Maybe PD.SetupBuildInfo -> Maybe PD.SetupBuildInfo) + -- ^ Function to update the SetupBuildInfo of the package using those dependencies + -> (UnresolvedSourcePackage -> Maybe [Dependency]) + -- ^ Function to determine extra setup dependencies -> DepResolverParams -> DepResolverParams -addDefaultSetupDependencies defaultSetupDeps params = +addDefaultSetupDependencies applyDefaultSetupDeps defaultSetupDeps params = params { depResolverSourcePkgIndex = - fmap applyDefaultSetupDeps (depResolverSourcePkgIndex params) + fmap go (depResolverSourcePkgIndex params) } where - applyDefaultSetupDeps :: UnresolvedSourcePackage -> UnresolvedSourcePackage - applyDefaultSetupDeps srcpkg = + go :: UnresolvedSourcePackage -> UnresolvedSourcePackage + go srcpkg = srcpkg { srcpkgDescription = gpkgdesc { PD.packageDescription = pkgdesc { PD.setupBuildInfo = - case PD.setupBuildInfo pkgdesc of - Just sbi -> Just sbi - Nothing -> case defaultSetupDeps srcpkg of - Nothing -> Nothing - Just deps - | isCustom -> - Just - PD.SetupBuildInfo - { PD.defaultSetupDepends = True - , PD.setupDepends = deps - } - | otherwise -> Nothing + applyDefaultSetupDeps + (defaultSetupDeps srcpkg) + (PD.buildType pkgdesc) + (PD.setupBuildInfo pkgdesc) } } } where - isCustom = PD.buildType pkgdesc == PD.Custom || PD.buildType pkgdesc == PD.Hooks gpkgdesc = srcpkgDescription srcpkg pkgdesc = PD.packageDescription gpkgdesc +setImplicitSetupInfo + :: Maybe [Dependency] + -> PD.BuildType + -> Maybe PD.SetupBuildInfo + -> Maybe PD.SetupBuildInfo +setImplicitSetupInfo mdeps buildty msetupinfo = + case msetupinfo of + Just sbi -> Just sbi + Nothing -> case mdeps of + Nothing -> Nothing + Just deps + | isCustom -> + Just + PD.SetupBuildInfo + { PD.defaultSetupDepends = True + , PD.setupDepends = deps + } + | otherwise -> Nothing + where + isCustom = buildty == PD.Custom || buildty == PD.Hooks + +extendSetupInfoDeps :: Maybe [Dependency] -> PD.BuildType -> Maybe PD.SetupBuildInfo -> Maybe PD.SetupBuildInfo +extendSetupInfoDeps mDeps buildTy mSetupInfo + | Nothing <- mSetupInfo = + assert + (buildTy /= PD.Hooks) -- Hooks needs explicit setup-depends + Nothing + | Just setupInfo <- mSetupInfo = + Just setupInfo{PD.setupDepends = PD.setupDepends setupInfo ++ fromMaybe [] mDeps} + -- | If a package has a custom setup then we need to add a setup-depends -- on Cabal. addSetupCabalMinVersionConstraint @@ -734,7 +764,7 @@ standardInstallPolicy -> [PackageSpecifier UnresolvedSourcePackage] -> DepResolverParams standardInstallPolicy installedPkgIndex sourcePkgDb pkgSpecifiers = - addDefaultSetupDependencies mkDefaultSetupDeps $ + addDefaultSetupDependencies setImplicitSetupInfo mkDefaultSetupDeps $ basicInstallPolicy installedPkgIndex sourcePkgDb diff --git a/cabal-install/src/Distribution/Client/InLibrary.hs b/cabal-install/src/Distribution/Client/InLibrary.hs new file mode 100644 index 00000000000..ab5daf7dfe9 --- /dev/null +++ b/cabal-install/src/Distribution/Client/InLibrary.hs @@ -0,0 +1,327 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} + +module Distribution.Client.InLibrary + ( libraryConfigureInputsFromElabPackage + , configure + , build + , haddock + , copy + , register + , repl + , test + , bench + ) +where + +import Distribution.Client.Compat.Prelude +import Prelude () + +import Distribution.Backpack.DescribeUnitId (setupMessage') +import Distribution.Client.ProjectPlanning.Types +import Distribution.Client.RebuildMonad +import qualified Distribution.Client.SetupHooks.CallHooksExe as ExternalHooksExe + ( buildTypePreBuildHooks + , buildTypeSetupHooks + ) +import Distribution.Client.Types + +import qualified Distribution.PackageDescription as PD +import Distribution.Simple (Compiler, PackageDBStackCWD) +import qualified Distribution.Simple.Bench as Cabal +import Distribution.Simple.Build (build_setupHooks, repl_setupHooks) +import qualified Distribution.Simple.Configure as Cabal +import Distribution.Simple.Haddock (haddock_setupHooks) +import Distribution.Simple.Install (install_setupHooks) +import Distribution.Simple.LocalBuildInfo (mbWorkDirLBI) +import qualified Distribution.Simple.PreProcess as Cabal +import Distribution.Simple.Program.Db +import qualified Distribution.Simple.Register as Cabal +import qualified Distribution.Simple.Setup as Cabal +import Distribution.Simple.SetupHooks.Internal +import qualified Distribution.Simple.Test as Cabal +import Distribution.Simple.Utils +import Distribution.System (Platform) +import Distribution.Types.BuildType +import Distribution.Types.ComponentRequestedSpec +import qualified Distribution.Types.LocalBuildConfig as LBC +import Distribution.Types.LocalBuildInfo +import Distribution.Utils.Path + ( makeSymbolicPath + , relativeSymbolicPath + ) + +import Distribution.Types.HookedBuildInfo (emptyHookedBuildInfo) +import System.Directory (canonicalizePath) + +-------------------------------------------------------------------------------- +-- Configure + +data LibraryConfigureInputs = LibraryConfigureInputs + { compiler :: Compiler + , platform :: Platform + , buildType :: BuildType + , compRequested :: Maybe PD.ComponentName + , localBuildConfig :: LBC.LocalBuildConfig + , packageDBStack :: PackageDBStackCWD + , packageDescription :: PD.PackageDescription + , gPackageDescription :: PD.GenericPackageDescription + , flagAssignment :: PD.FlagAssignment + } + +libraryConfigureInputsFromElabPackage + :: BuildType + -> ProgramDb + -> ElaboratedSharedConfig + -> ElaboratedReadyPackage + -> [String] + -- ^ targets + -> LibraryConfigureInputs +libraryConfigureInputsFromElabPackage + bt + progDb + -- NB: don't use the ProgramDb from the ElaboratedSharedConfig; + -- that one is only for the compiler itself and not for the package. + ElaboratedSharedConfig + { pkgConfigPlatform = plat + , pkgConfigCompiler = compil + } + (ReadyPackage pkg) + userTargets = + LibraryConfigureInputs + { compiler = compil + , platform = plat + , buildType = + -- NB: don't get the build-type from 'pkgDescr', + -- because for Configure build-type we rewrite the build-type + -- to Simple for components that are neither the main library + -- nor an executable. + -- + -- See also 'isMainLibOrExeComponent'. + bt + , compRequested = + case elabPkgOrComp pkg of + ElabComponent elabComp + | Just elabCompNm <- compComponentName elabComp -> + Just elabCompNm + _ -> Nothing + , localBuildConfig = + LBC.LocalBuildConfig + { LBC.extraConfigArgs = userTargets + , LBC.withPrograms = progDb + , LBC.withBuildOptions = elabBuildOptions pkg + } + , packageDBStack = elabBuildPackageDBStack pkg + , packageDescription = pkgDescr + , gPackageDescription = gpkgDescr + , flagAssignment = elabFlagAssignment pkg + } + where + pkgDescr = elabPkgDescription pkg + gpkgDescr = elabGPkgDescription pkg + +configure + :: LibraryConfigureInputs + -> Cabal.ConfigFlags + -> IO LocalBuildInfo +configure + LibraryConfigureInputs + { platform = plat + , compiler = compil + , buildType = bt + , compRequested = mbComp + , localBuildConfig = lbc0 + , packageDBStack = packageDBs + , packageDescription = pkgDescr + , gPackageDescription = gpkgDescr + , flagAssignment = flagAssgn + } + cfg = do + -- Here, we essentially want to call the Cabal library 'configure' function, + -- but skipping over all the steps we don't need such as rediscovering the + -- compiler or re-resolving the conditionals in the package, as we have done + -- all of that already. + -- + -- To achieve this, we call the Cabal 'configureFinal' function which skips + -- these preparatory steps. + -- This code can still be improved, as it seems like 'configureFinal' still + -- does a fair bit of redundant work. In the end, it would be ideal if the + -- entirety of this function body was a single call to a function in the + -- Cabal library that gets called within the Cabal configure function. + let verbosity = Cabal.fromFlag $ Cabal.configVerbosity cfg + mbWorkDir = Cabal.flagToMaybe $ Cabal.configWorkingDir cfg + distPref = Cabal.fromFlag $ Cabal.configDistPref cfg + confHooks = configureHooks $ ExternalHooksExe.buildTypeSetupHooks mbWorkDir distPref bt + + -- cabal-install uses paths relative to the current working directory, + -- while the Cabal library expects symbolic paths. Perform the conversion here + -- by making the paths absolute. + packageDBs' <- traverse (traverse $ fmap makeSymbolicPath . canonicalizePath) packageDBs + + -- Configure package + let pkgId :: PD.PackageIdentifier + pkgId = PD.package pkgDescr + case mbComp of + Nothing -> setupMessage verbosity "Configuring" pkgId + Just cname -> + setupMessage' + verbosity + "Configuring" + pkgId + cname + (Just (Cabal.configInstantiateWith cfg)) + + -- TODO: we should avoid re-doing package-wide things over and over + -- in the per-component world, e.g. + -- > cabal build comp1 && cabal build comp2 + -- should only run the per-package configuration (including hooks) a single time. + -- + -- This seemingly requires a rethinking of + -- Distribution.Client.ProjectBuilding.UnpackedPackage.buildAndRegisterUnpackedPackage + -- to allow more granular recompilation checking, at the level of components. + lbc1 <- case preConfPackageHook confHooks of + Nothing -> return lbc0 + Just hk -> Cabal.runPreConfPackageHook cfg compil plat lbc0 hk + let compRequestedSpec = case mbComp of + Just compName -> OneComponentRequestedSpec compName + Nothing -> + ComponentRequestedSpec + { testsRequested = Cabal.fromFlag (Cabal.configTests cfg) + , benchmarksRequested = Cabal.fromFlag (Cabal.configBenchmarks cfg) + } + (_allConstraints, pkgInfo) <- + Cabal.computePackageInfo cfg lbc1 gpkgDescr compil + -- NB: no need to re-apply "allConstraints", as we already have a + -- finalized package description in hand. + + -- Post-configure hooks & per-component configure + lbi1 <- + Cabal.configureFinal + confHooks + emptyHookedBuildInfo + cfg + lbc1 + (gpkgDescr, pkgDescr) + flagAssgn + compRequestedSpec + compil + plat + packageDBs' + pkgInfo + + -- Remember the .cabal filename if we know it. + pkgDescrFilePath <- + case Cabal.flagToMaybe $ Cabal.configCabalFilePath cfg of + Just pkgFile -> return pkgFile + Nothing -> relativeSymbolicPath <$> tryFindPackageDesc verbosity mbWorkDir + return $ lbi1{pkgDescrFile = Just pkgDescrFilePath} + +-------------------------------------------------------------------------------- +-- Build + +build + :: Cabal.BuildFlags + -> LocalBuildInfo + -> [String] + -> IO [MonitorFilePath] +build flags lbi _args = + build_setupHooks (preBuildHook, postBuildHook) pkgDescr lbi flags Cabal.knownSuffixHandlers + where + hooks = ExternalHooksExe.buildTypeSetupHooks mbWorkDir distPref bt + -- (Recall that pre-build hooks are treated specially; + -- see the 'buildTypeSetupHooks' and 'buildTypePreBuildHooks' functions.) + preBuildHook = ExternalHooksExe.buildTypePreBuildHooks mbWorkDir distPref bt + postBuildHook + | Just postBuild <- postBuildComponentHook $ buildHooks hooks = + postBuild + | otherwise = + const $ return () + pkgDescr = localPkgDescr lbi + bt = PD.buildType pkgDescr + mbWorkDir = mbWorkDirLBI lbi + distPref = Cabal.fromFlag $ Cabal.buildDistPref flags + +-------------------------------------------------------------------------------- +-- Haddock + +haddock + :: Cabal.HaddockFlags + -> LocalBuildInfo + -> [String] + -> IO [MonitorFilePath] +haddock flags lbi _args = + haddock_setupHooks preBuildHook pkgDescr lbi Cabal.knownSuffixHandlers flags + where + preBuildHook = ExternalHooksExe.buildTypePreBuildHooks mbWorkDir distPref bt + pkgDescr = localPkgDescr lbi + bt = PD.buildType pkgDescr + mbWorkDir = mbWorkDirLBI lbi + distPref = Cabal.fromFlag $ Cabal.haddockDistPref flags + +-------------------------------------------------------------------------------- +-- Repl + +repl + :: Cabal.ReplFlags + -> LocalBuildInfo + -> [String] + -> IO () +repl flags lbi _args = + repl_setupHooks preBuildHook pkgDescr lbi flags Cabal.knownSuffixHandlers [] + where + preBuildHook = ExternalHooksExe.buildTypePreBuildHooks mbWorkDir distPref bt + pkgDescr = localPkgDescr lbi + bt = PD.buildType pkgDescr + mbWorkDir = mbWorkDirLBI lbi + distPref = Cabal.fromFlag $ Cabal.replDistPref flags + +-------------------------------------------------------------------------------- +-- Copy + +copy + :: Cabal.CopyFlags + -> LocalBuildInfo + -> [String] + -> IO () +copy flags lbi _args = + install_setupHooks hooks pkgDescr lbi flags + where + hooks = installHooks $ ExternalHooksExe.buildTypeSetupHooks mbWorkDir distPref bt + pkgDescr = localPkgDescr lbi + bt = PD.buildType pkgDescr + mbWorkDir = mbWorkDirLBI lbi + distPref = Cabal.fromFlag $ Cabal.copyDistPref flags + +-------------------------------------------------------------------------------- +-- Test, bench, register. +-- +-- NB: no hooks into these phases. + +test + :: Cabal.TestFlags + -> LocalBuildInfo + -> [String] + -> IO () +test flags lbi args = + Cabal.test args pkgDescr lbi flags + where + pkgDescr = localPkgDescr lbi + +bench + :: Cabal.BenchmarkFlags + -> LocalBuildInfo + -> [String] + -> IO () +bench flags lbi args = + Cabal.bench args pkgDescr lbi flags + where + pkgDescr = localPkgDescr lbi + +register + :: Cabal.RegisterFlags + -> LocalBuildInfo + -> [String] + -> IO () +register flags lbi _args = Cabal.register pkgDescr lbi flags + where + pkgDescr = localPkgDescr lbi diff --git a/cabal-install/src/Distribution/Client/Install.hs b/cabal-install/src/Distribution/Client/Install.hs index 635cd7e1689..d2d4c19dfec 100644 --- a/cabal-install/src/Distribution/Client/Install.hs +++ b/cabal-install/src/Distribution/Client/Install.hs @@ -116,7 +116,8 @@ import Distribution.Client.Setup , filterTestFlags ) import Distribution.Client.SetupWrapper - ( SetupScriptOptions (..) + ( SetupRunnerArgs (NotInLibrary) + , SetupScriptOptions (..) , defaultSetupScriptOptions , setupWrapper ) @@ -339,7 +340,7 @@ install ++ "see https://github.com/haskell/cabal/issues/3353" ++ " (if you didn't type --root-cmd, comment out root-cmd" ++ " in your ~/.config/cabal/config file)" - let userOrSandbox = fromFlag (configUserInstall configFlags) + let userOrSandbox = fromFlagOrDefault defaultUserInstall (configUserInstall configFlags) unless userOrSandbox $ warn verbosity $ "the --global flag is deprecated -- " @@ -1249,7 +1250,7 @@ regenerateHaddockIndex defaultDirs <- InstallDirs.defaultInstallDirs (compilerFlavor comp) - (fromFlag (configUserInstall configFlags)) + (fromFlagOrDefault defaultUserInstall (configUserInstall configFlags)) True let indexFileTemplate = fromFlag (installHaddockIndex installFlags) indexFile = substHaddockIndexFileName defaultDirs indexFileTemplate @@ -1503,7 +1504,6 @@ performInstallations distPref (chooseCabalVersion configExFlags (libVersion miscOptions)) (Just lock) - parallelInstall index (Just rpkg) @@ -1968,7 +1968,7 @@ installUnpackedPackage _ -> ipkgs let packageDBs = interpretPackageDbFlags - (fromFlag (configUserInstall configFlags)) + (fromFlagOrDefault defaultUserInstall (configUserInstall configFlags)) (configPackageDBs configFlags) for_ ipkgs' $ \ipkg' -> registerPackage @@ -2090,6 +2090,7 @@ installUnpackedPackage getCommonFlags flags (const []) + NotInLibrary ) -- helper @@ -2124,7 +2125,7 @@ withWin32SelfUpgrade verbosity uid configFlags cinfo platform pkg action = do defaultDirs <- InstallDirs.defaultInstallDirs compFlavor - (fromFlag (configUserInstall configFlags)) + (fromFlagOrDefault defaultUserInstall (configUserInstall configFlags)) (PackageDescription.hasLibs pkg) Win32SelfUpgrade.possibleSelfUpgrade diff --git a/cabal-install/src/Distribution/Client/InstallSymlink.hs b/cabal-install/src/Distribution/Client/InstallSymlink.hs index 506c957a4e8..a7b69fd2def 100644 --- a/cabal-install/src/Distribution/Client/InstallSymlink.hs +++ b/cabal-install/src/Distribution/Client/InstallSymlink.hs @@ -61,7 +61,6 @@ import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.Setup ( ConfigFlags (..) , flagToMaybe - , fromFlag , fromFlagOrDefault ) import Distribution.Simple.Utils (info, withTempDirectory) @@ -95,6 +94,7 @@ import System.IO.Error ) import Distribution.Client.Compat.Directory (createFileLink, getSymbolicLinkTarget, pathIsSymbolicLink) +import Distribution.Client.Config (defaultUserInstall) import Distribution.Client.Init.Prompt (promptYesNo) import Distribution.Client.Init.Types (DefaultPrompt (MandatoryPrompt), runPromptIO) import Distribution.Client.Types.OverwritePolicy @@ -217,7 +217,7 @@ symlinkBinaries defaultDirs <- InstallDirs.defaultInstallDirs compilerFlavor - (fromFlag (configUserInstall configFlags)) + (fromFlagOrDefault defaultUserInstall (configUserInstall configFlags)) (PackageDescription.hasLibs pkg) let templateDirs = InstallDirs.combineInstallDirs diff --git a/cabal-install/src/Distribution/Client/Main.hs b/cabal-install/src/Distribution/Client/Main.hs index e67704637b5..34d993dfc90 100644 --- a/cabal-install/src/Distribution/Client/Main.hs +++ b/cabal-install/src/Distribution/Client/Main.hs @@ -99,6 +99,7 @@ import Distribution.Client.Config ( SavedConfig (..) , createDefaultConfigFile , defaultConfigFile + , defaultUserInstall , getConfigFilePath , loadConfig , userConfigDiff @@ -109,7 +110,8 @@ import qualified Distribution.Client.List as List , list ) import Distribution.Client.SetupWrapper - ( SetupScriptOptions (..) + ( SetupRunnerArgs (NotInLibrary) + , SetupScriptOptions (..) , defaultSetupScriptOptions , setupWrapper ) @@ -541,6 +543,7 @@ wrapperAction command getCommonFlags = getCommonFlags (const (return flags)) (const extraArgs) + NotInLibrary configureAction :: (ConfigFlags, ConfigExFlags) @@ -566,7 +569,7 @@ configureAction (configFlags, configExFlags) extraArgs globalFlags = do let packageDBs :: PackageDBStack packageDBs = interpretPackageDbFlags - (fromFlag (configUserInstall configFlags')) + (fromFlagOrDefault defaultUserInstall (configUserInstall configFlags')) (configPackageDBs configFlags') withRepoContext verbosity globalFlags' $ \repoContext -> @@ -650,6 +653,7 @@ build verbosity config distPref buildFlags extraArgs = buildCommonFlags (return . mkBuildFlags) (const extraArgs) + NotInLibrary where progDb = defaultProgramDb setupOptions = defaultSetupScriptOptions{useDistPref = distPref} @@ -743,6 +747,7 @@ replAction replFlags extraArgs globalFlags = do Cabal.replCommonFlags (const (return replFlags')) (const extraArgs) + NotInLibrary -- No .cabal file in the current directory: just start the REPL (possibly -- using the sandbox package DB). @@ -790,6 +795,7 @@ installAction (configFlags, _, installFlags, _, _, _) _ globalFlags (const common) (const (return (mempty, mempty, mempty, mempty, mempty, mempty))) (const []) + NotInLibrary installAction ( configFlags , configExFlags @@ -957,6 +963,7 @@ testAction (buildFlags, testFlags) extraArgs globalFlags = do Cabal.testCommonFlags (const (return testFlags')) (const extraArgs') + NotInLibrary data ComponentNames = ComponentNamesUnknown @@ -1078,6 +1085,7 @@ benchmarkAction Cabal.benchmarkCommonFlags (const (return benchmarkFlags')) (const extraArgs') + NotInLibrary haddockAction :: HaddockFlags -> [String] -> Action haddockAction haddockFlags extraArgs globalFlags = do @@ -1118,6 +1126,7 @@ haddockAction haddockFlags extraArgs globalFlags = do haddockCommonFlags (const (return haddockFlags')) (const extraArgs) + NotInLibrary when (haddockForHackage haddockFlags == Flag ForHackage) $ do pkg <- fmap LBI.localPkgDescr (getPersistBuildConfig mbWorkDir distPref) let dest = getSymbolicPath distPref name <.> "tar.gz" @@ -1153,6 +1162,7 @@ cleanAction cleanFlags extraArgs globalFlags = do cleanCommonFlags (const (return cleanFlags')) (const extraArgs) + NotInLibrary listAction :: ListFlags -> [String] -> Action listAction listFlags extraArgs globalFlags = do diff --git a/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs b/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs index e19c52157c0..6b496d77770 100644 --- a/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs +++ b/cabal-install/src/Distribution/Client/ProjectBuilding/UnpackedPackage.hs @@ -1,7 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -80,17 +82,21 @@ import Distribution.Simple.Compiler ( PackageDBStackCWD , coercePackageDBStack ) +import qualified Distribution.Simple.Configure as Cabal import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.LocalBuildInfo ( ComponentName (..) , LibraryName (..) ) +import qualified Distribution.Simple.LocalBuildInfo as Cabal import Distribution.Simple.Program import qualified Distribution.Simple.Register as Cabal import qualified Distribution.Simple.Setup as Cabal import Distribution.Types.BuildType import Distribution.Types.PackageDescription.Lens (componentModules) +import Distribution.Client.Errors +import Distribution.Compat.Directory (listDirectory) import Distribution.Simple.Utils import Distribution.System (Platform (..)) import Distribution.Utils.Path hiding @@ -99,6 +105,8 @@ import Distribution.Utils.Path hiding ) import Distribution.Version +import Distribution.Client.ProjectBuilding.PackageFileMonitor + import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy.Char8 as LBS.Char8 @@ -110,13 +118,9 @@ import System.FilePath (dropDrive, normalise, takeDirectory, (<.>), ()) import System.IO (Handle, IOMode (AppendMode), withFile) import System.Semaphore (SemaphoreName (..)) +import GHC.Stack import Web.Browser (openBrowser) -import Distribution.Client.Errors -import Distribution.Compat.Directory (listDirectory) - -import Distribution.Client.ProjectBuilding.PackageFileMonitor - -- | Each unpacked package is processed in the following phases: -- -- * Configure phase @@ -131,20 +135,21 @@ import Distribution.Client.ProjectBuilding.PackageFileMonitor -- Depending on whether we are installing the package or building it inplace, -- the phases will be carried out differently. For example, when installing, -- the test, benchmark, and repl phase are ignored. -data PackageBuildingPhase - = PBConfigurePhase {runConfigure :: IO ()} - | PBBuildPhase {runBuild :: IO ()} - | PBHaddockPhase {runHaddock :: IO ()} - | PBInstallPhase - { runCopy :: FilePath -> IO () - , runRegister +data PackageBuildingPhase r where + PBConfigurePhase :: {runConfigure :: IO InLibraryLBI} -> PackageBuildingPhase InLibraryLBI + PBBuildPhase :: {runBuild :: IO [MonitorFilePath]} -> PackageBuildingPhase () + PBHaddockPhase :: {runHaddock :: IO [MonitorFilePath]} -> PackageBuildingPhase () + PBInstallPhase + :: { runCopy :: FilePath -> IO () + , runRegister :: PackageDBStackCWD -> Cabal.RegisterOptions -> IO InstalledPackageInfo - } - | PBTestPhase {runTest :: IO ()} - | PBBenchPhase {runBench :: IO ()} - | PBReplPhase {runRepl :: IO ()} + } + -> PackageBuildingPhase () + PBTestPhase :: {runTest :: IO ()} -> PackageBuildingPhase () + PBBenchPhase :: {runBench :: IO ()} -> PackageBuildingPhase () + PBReplPhase :: {runRepl :: IO ()} -> PackageBuildingPhase () -- | Structures the phases of building and registering a package amongst others -- (see t'PackageBuildingPhase'). Delegates logic specific to a certain @@ -167,13 +172,13 @@ buildAndRegisterUnpackedPackage -> SymbolicPath Pkg (Dir Dist) -> Maybe FilePath -- ^ The path to an /initialized/ log file - -> (PackageBuildingPhase -> IO ()) + -> (forall r. PackageBuildingPhase r -> IO r) -> IO () buildAndRegisterUnpackedPackage verbosity distDirLayout@DistDirLayout{distTempDirectory} maybe_semaphore - buildTimeSettings@BuildTimeSettings{buildSettingNumJobs, buildSettingKeepTempFiles} + buildTimeSettings@BuildTimeSettings{buildSettingKeepTempFiles} registerLock cacheLock pkgshared@ElaboratedSharedConfig @@ -187,36 +192,57 @@ buildAndRegisterUnpackedPackage mlogFile delegate = do -- Configure phase - delegate $ - PBConfigurePhase $ - annotateFailure mlogFile ConfigureFailed $ - setup configureCommand Cabal.configCommonFlags configureFlags configureArgs + mbLBI <- + delegate $ + PBConfigurePhase $ + annotateFailure mlogFile ConfigureFailed $ + setup + configureCommand + Cabal.configCommonFlags + configureFlags + configureArgs + (InLibraryArgs $ InLibraryConfigureArgs pkgshared rpkg) -- Build phase delegate $ PBBuildPhase $ annotateFailure mlogFile BuildFailed $ do - setup buildCommand Cabal.buildCommonFlags (return . buildFlags) buildArgs + setup + buildCommand + Cabal.buildCommonFlags + (return . buildFlags) + buildArgs + (InLibraryArgs $ InLibraryPostConfigureArgs SBuildPhase mbLBI) -- Haddock phase whenHaddock $ delegate $ PBHaddockPhase $ annotateFailure mlogFile HaddocksFailed $ do - setup haddockCommand Cabal.haddockCommonFlags (return . haddockFlags) haddockArgs + setup + haddockCommand + Cabal.haddockCommonFlags + (return . haddockFlags) + haddockArgs + (InLibraryArgs $ InLibraryPostConfigureArgs SHaddockPhase mbLBI) -- Install phase delegate $ PBInstallPhase { runCopy = \destdir -> annotateFailure mlogFile InstallFailed $ - setup Cabal.copyCommand Cabal.copyCommonFlags (return . copyFlags destdir) copyArgs + setup + Cabal.copyCommand + Cabal.copyCommonFlags + (return . copyFlags destdir) + copyArgs + (InLibraryArgs $ InLibraryPostConfigureArgs SCopyPhase mbLBI) , runRegister = \pkgDBStack registerOpts -> annotateFailure mlogFile InstallFailed $ do -- We register ourselves rather than via Setup.hs. We need to -- grab and modify the InstalledPackageInfo. We decide what -- the installed package id is, not the build system. - ipkg0 <- generateInstalledPackageInfo + ipkg0 <- generateInstalledPackageInfo mbLBI let ipkg = ipkg0{Installed.installedUnitId = uid} criticalSection registerLock $ Cabal.registerPackage @@ -235,21 +261,36 @@ buildAndRegisterUnpackedPackage delegate $ PBTestPhase $ annotateFailure mlogFile TestsFailed $ - setup testCommand Cabal.testCommonFlags (return . testFlags) testArgs + setup + testCommand + Cabal.testCommonFlags + (return . testFlags) + testArgs + (InLibraryArgs $ InLibraryPostConfigureArgs STestPhase mbLBI) -- Bench phase whenBench $ delegate $ PBBenchPhase $ annotateFailure mlogFile BenchFailed $ - setup benchCommand Cabal.benchmarkCommonFlags (return . benchFlags) benchArgs + setup + benchCommand + Cabal.benchmarkCommonFlags + (return . benchFlags) + benchArgs + (InLibraryArgs $ InLibraryPostConfigureArgs SBenchPhase mbLBI) -- Repl phase whenRepl $ delegate $ PBReplPhase $ annotateFailure mlogFile ReplFailed $ - setupInteractive replCommand Cabal.replCommonFlags replFlags replArgs + setupInteractive + replCommand + Cabal.replCommonFlags + (return . replFlags) + replArgs + (InLibraryArgs $ InLibraryPostConfigureArgs SReplPhase mbLBI) return () where @@ -276,7 +317,8 @@ buildAndRegisterUnpackedPackage | otherwise = return () mbWorkDir = useWorkingDir scriptOptions - commonFlags = setupHsCommonFlags verbosity mbWorkDir builddir buildSettingKeepTempFiles + commonFlags targets = + setupHsCommonFlags verbosity mbWorkDir builddir targets buildSettingKeepTempFiles configureCommand = Cabal.configureCommand defaultProgramDb configureFlags v = @@ -286,7 +328,7 @@ buildAndRegisterUnpackedPackage plan rpkg pkgshared - commonFlags + (commonFlags $ configureArgs v) configureArgs _ = setupHsConfigureArgs pkg buildCommand = Cabal.buildCommand defaultProgramDb @@ -296,7 +338,7 @@ buildAndRegisterUnpackedPackage comp_par_strat pkg pkgshared - commonFlags + (commonFlags $ buildArgs v) buildArgs _ = setupHsBuildArgs pkg copyFlags destdir v = @@ -304,7 +346,7 @@ buildAndRegisterUnpackedPackage setupHsCopyFlags pkg pkgshared - commonFlags + (commonFlags $ buildArgs v) destdir -- In theory, we could want to copy less things than those that were -- built, but instead, we simply copy the targets that were built. @@ -315,7 +357,7 @@ buildAndRegisterUnpackedPackage flip filterTestFlags v $ setupHsTestFlags pkg - commonFlags + (commonFlags $ testArgs v) testArgs _ = setupHsTestArgs pkg benchCommand = Cabal.benchmarkCommand @@ -324,7 +366,7 @@ buildAndRegisterUnpackedPackage setupHsBenchFlags pkg pkgshared - commonFlags + (commonFlags $ benchArgs v) benchArgs _ = setupHsBenchArgs pkg replCommand = Cabal.replCommand defaultProgramDb @@ -333,7 +375,7 @@ buildAndRegisterUnpackedPackage setupHsReplFlags pkg pkgshared - commonFlags + (commonFlags $ replArgs v) replArgs _ = setupHsReplArgs pkg haddockCommand = Cabal.haddockCommand @@ -343,7 +385,7 @@ buildAndRegisterUnpackedPackage pkg pkgshared buildTimeSettings - commonFlags + (commonFlags $ haddockArgs v) haddockArgs v = flip filterHaddockArgs v $ setupHsHaddockArgs pkg @@ -356,17 +398,18 @@ buildAndRegisterUnpackedPackage distDirLayout srcdir builddir - (isParallelBuild buildSettingNumJobs) cacheLock setup - :: CommandUI flags + :: (HasCallStack, RightFlagsForPhase flags setupSpec) + => CommandUI flags -> (flags -> CommonSetupFlags) -> (Version -> IO flags) -> (Version -> [String]) - -> IO () - setup cmd getCommonFlags flags args = - withLogging $ \mLogFileHandle -> do + -> SetupRunnerArgs setupSpec + -> IO (SetupRunnerRes setupSpec) + setup cmd getCommonFlags flags args wrapperArgs = + withLogging $ \mLogFileHandle -> setupWrapper verbosity scriptOptions @@ -381,25 +424,24 @@ buildAndRegisterUnpackedPackage getCommonFlags flags args + wrapperArgs setupInteractive - :: CommandUI flags + :: RightFlagsForPhase flags setupSpec + => CommandUI flags -> (flags -> CommonSetupFlags) - -> (Version -> flags) + -> (Version -> IO flags) -> (Version -> [String]) - -> IO () - setupInteractive cmd getCommonFlags flags args = + -> SetupRunnerArgs setupSpec + -> IO (SetupRunnerRes setupSpec) + setupInteractive = setupWrapper verbosity scriptOptions{isInteractive = True} (Just (elabPkgDescription pkg)) - cmd - getCommonFlags - (\v -> return (flags v)) - args - generateInstalledPackageInfo :: IO InstalledPackageInfo - generateInstalledPackageInfo = + generateInstalledPackageInfo :: InLibraryLBI -> IO InstalledPackageInfo + generateInstalledPackageInfo mbLBI = withTempInstalledPackageInfoFile verbosity distTempDirectory @@ -409,9 +451,14 @@ buildAndRegisterUnpackedPackage setupHsRegisterFlags pkg pkgshared - commonFlags + (commonFlags []) pkgConfDest - setup (Cabal.registerCommand) Cabal.registerCommonFlags (\v -> return (registerFlags v)) (const []) + setup + (Cabal.registerCommand) + Cabal.registerCommonFlags + (return . registerFlags) + (const []) + (InLibraryArgs $ InLibraryPostConfigureArgs SRegisterPhase mbLBI) withLogging :: (Maybe Handle -> IO r) -> IO r withLogging action = @@ -485,20 +532,21 @@ buildInplaceUnpackedPackage builddir Nothing -- no log file for inplace builds! $ \case - PBConfigurePhase{runConfigure} -> do - whenReConfigure $ do - runConfigure + PBConfigurePhase{runConfigure} -> + whenReconfigure $ do + mbLBI <- runConfigure invalidatePackageRegFileMonitor packageFileMonitor updatePackageConfigFileMonitor packageFileMonitor (getSymbolicPath srcdir) pkg + return mbLBI PBBuildPhase{runBuild} -> do whenRebuild $ do timestamp <- beginUpdateFileMonitor - runBuild - -- Be sure to invalidate the cache if building throws an exception! - -- If not, we'll abort execution with a stale recompilation cache. - -- See ghc#24926 for an example of how this can go wrong. - `onException` invalidatePackageRegFileMonitor packageFileMonitor - + monitors' <- + runBuild + -- Be sure to invalidate the cache if building throws an exception! + -- If not, we'll abort execution with a stale recompilation cache. + -- See ghc#24926 for an example of how this can go wrong. + `onException` invalidatePackageRegFileMonitor packageFileMonitor let listSimple = execRebuild (getSymbolicPath srcdir) (needElaboratedConfiguredPackage pkg) listSdist = @@ -509,6 +557,7 @@ buildInplaceUnpackedPackage if null xs then m' else return xs monitors <- case PD.buildType (elabPkgDescription pkg) of Simple -> listSimple + Hooks -> listSdist `ifNullThen` listSimple -- If a Custom setup was used, AND the Cabal is recent -- enough to have sdist --list-sources, use that to -- determine the files that we need to track. This can @@ -540,10 +589,10 @@ buildInplaceUnpackedPackage timestamp pkg buildStatus - (monitors ++ dep_monitors) + (monitors ++ monitors' ++ dep_monitors) buildResult PBHaddockPhase{runHaddock} -> do - runHaddock + _monitors <- runHaddock let haddockTarget = elabHaddockForHackage pkg when (haddockTarget == Cabal.ForHackage) $ do let dest = distDirectory name <.> "tar.gz" @@ -599,10 +648,24 @@ buildInplaceUnpackedPackage packageFileMonitor = newPackageFileMonitor pkgshared distDirLayout dparams - whenReConfigure action = case buildStatus of - BuildStatusConfigure _ -> action - _ -> return () - + whenReconfigure :: IO InLibraryLBI -> IO InLibraryLBI + whenReconfigure action = + case buildStatus of + BuildStatusConfigure _ -> action + _ -> do + lbi_wo_programs <- Cabal.getPersistBuildConfig (Just srcdir) builddir + -- Restore info about unconfigured programs, since it is not serialized + -- TODO: copied from Distribution.Simple.getBuildConfig. + let lbi = + lbi_wo_programs + { Cabal.withPrograms = + restoreProgramDb + builtinPrograms + (Cabal.withPrograms lbi_wo_programs) + } + return $ InLibraryLBI lbi + + whenRebuild, whenReRegister :: IO () -> IO () whenRebuild action | null (elabBuildTargets pkg) , -- NB: we have to build the test/bench suite! @@ -697,10 +760,12 @@ buildAndInstallUnpackedPackage runConfigure PBBuildPhase{runBuild} -> do noticeProgress ProgressBuilding - runBuild + _monitors <- runBuild + return () PBHaddockPhase{runHaddock} -> do noticeProgress ProgressHaddock - runHaddock + _monitors <- runHaddock + return () PBInstallPhase{runCopy, runRegister} -> do noticeProgress ProgressInstalling diff --git a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs index 9b12f570252..550c986b596 100644 --- a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs +++ b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs @@ -1189,6 +1189,7 @@ printPlan verbosity Nothing -- omit working directory (makeSymbolicPath "$builddir") + (setupHsConfigureArgs elab) buildSettingKeepTempFiles fullConfigureFlags = runIdentity $ diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 246585ca096..18afc373b37 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -126,6 +126,7 @@ import Distribution.Client.ProjectPlanOutput import Distribution.Client.ProjectPlanning.SetupPolicy ( NonSetupLibDepSolverPlanPackage (..) , mkDefaultSetupDeps + , mkHooksSetupImplicitDeps , packageSetupScriptSpecVersion , packageSetupScriptStyle ) @@ -488,48 +489,48 @@ configureCompiler progsearchpath <- liftIO $ getSystemSearchPath - rerunIfChanged - verbosity - fileMonitorCompiler - ( hcFlavor - , hcPath - , hcPkg - , progsearchpath - , packageConfigProgramPaths - , packageConfigProgramPathExtra - ) - $ do - liftIO $ info verbosity "Compiler settings changed, reconfiguring..." - progdb <- - liftIO $ - -- Add paths in the global config - prependProgramSearchPath verbosity (fromNubList projectConfigProgPathExtra) [] defaultProgramDb - -- Add paths in the local config - >>= prependProgramSearchPath verbosity (fromNubList packageConfigProgramPathExtra) [] - >>= pure . userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) - result@(_, _, progdb') <- - liftIO $ - Cabal.configCompilerEx - hcFlavor - hcPath - hcPkg - progdb - verbosity - -- Note that we added the user-supplied program locations and args - -- for /all/ programs, not just those for the compiler prog and - -- compiler-related utils. In principle we don't know which programs - -- the compiler will configure (and it does vary between compilers). - -- We do know however that the compiler will only configure the - -- programs it cares about, and those are the ones we monitor here. - monitorFiles (programsMonitorFiles progdb') - - -- Note: There is currently a bug here: we are dropping unconfigured - -- programs from the 'ProgramDb' when we re-use the cache created by - -- 'rerunIfChanged'. - -- - -- See Note [Caching the result of configuring the compiler] - - return result + (hc, plat, hcProgDb) <- + rerunIfChanged + verbosity + fileMonitorCompiler + ( hcFlavor + , hcPath + , hcPkg + , progsearchpath + , packageConfigProgramPaths + , packageConfigProgramPathExtra + ) + $ do + liftIO $ info verbosity "Compiler settings changed, reconfiguring..." + progdb <- + liftIO $ + -- Add paths in the global config + prependProgramSearchPath verbosity (fromNubList projectConfigProgPathExtra) [] defaultProgramDb + -- Add paths in the local config + >>= prependProgramSearchPath verbosity (fromNubList packageConfigProgramPathExtra) [] + >>= pure . userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) + result@(_, _, progdb') <- + liftIO $ + Cabal.configCompiler + hcFlavor + hcPath + progdb + verbosity + -- Note that we added the user-supplied program locations and args + -- for /all/ programs, not just those for the compiler prog and + -- compiler-related utils. In principle we don't know which programs + -- the compiler will configure (and it does vary between compilers). + -- We do know however that the compiler will only configure the + -- programs it cares about, and those are the ones we monitor here. + monitorFiles (programsMonitorFiles progdb') + return result + + -- Now, **outside** of the caching logic of 'rerunIfChanged', add on + -- auxiliary unconfigured programs to the ProgramDb (e.g. hc-pkg, haddock, ar, ld...). + -- + -- See Note [Caching the result of configuring the compiler] + finalProgDb <- liftIO $ Cabal.configCompilerProgDb verbosity hc hcProgDb hcPkg + return (hc, plat, finalProgDb) where hcFlavor = flagToMaybe projectConfigHcFlavor hcPath = flagToMaybe projectConfigHcPath @@ -547,18 +548,19 @@ contains a 'ProgramDb'): - On the first run, we will obtain a 'ProgramDb' which may contain several unconfigured programs. In particular, configuring GHC will add tools such as `ar` and `ld` as unconfigured programs to the 'ProgramDb', with custom - logic for finding their location based on the location of the GHC binary. + logic for finding their location based on the location of the GHC binary + and its associated settings file. - On subsequent runs, if we use the cache created by 'rerunIfChanged', we will deserialise the 'ProgramDb' from disk, which means it won't include any unconfigured programs, which might mean we are unable to find 'ar' or 'ld'. -This is not currently a huge problem because, in the Cabal library, we eagerly -re-run the configureCompiler step (thus recovering any lost information), but -this is wasted work that we should stop doing in Cabal, given that cabal-install -has already figured out all the necessary information about the compiler. +To solve this, we cache the ProgramDb containing the compiler (which will be +a configured program, hence properly serialised/deserialised), and then +re-compute any attendant unconfigured programs (such as hc-pkg, haddock or build +tools such as ar, ld) using 'configCompilerProgDb'. -To fix this bug, we can't simply eagerly configure all unconfigured programs, -as was originally attempted, for a couple of reasons: +Another idea would be to simply eagerly configure all unconfigured programs, +as was originally attempted. But this doesn't work, for a couple of reasons: - it does more work than necessary, by configuring programs that we may not end up needing, @@ -568,8 +570,8 @@ as was originally attempted, for a couple of reasons: or by package-level `extra-prog-path` arguments. This lead to bug reports #10633 and #10692. -See #9840 for more information about the problems surrounding the lossly -Binary ProgramDb instance. +See #9840 for more information about the problems surrounding the lossy +'Binary ProgramDb' instance. -} ------------------------------------------------------------------------------ @@ -1328,6 +1330,13 @@ planPackages . removeLowerBounds solverSettingAllowOlder . removeUpperBounds solverSettingAllowNewer . addDefaultSetupDependencies + extendSetupInfoDeps + ( mkHooksSetupImplicitDeps + . PD.packageDescription + . srcpkgDescription + ) + . addDefaultSetupDependencies + setImplicitSetupInfo ( mkDefaultSetupDeps comp platform . PD.packageDescription . srcpkgDescription @@ -1717,6 +1726,7 @@ elaborateInstallPlan -- new 'ElabSetup' type, and teach all of the code paths how to -- handle it. -- Once you've implemented this, swap it for the code below. + -- (See #9986 for more information about this task.) cuz_buildtype = case bt of PD.Configure -> [] @@ -1724,9 +1734,12 @@ elaborateInstallPlan -- main library in cabal. Other components will need to depend -- on the main library for configured data. PD.Custom -> [CuzBuildType CuzCustomBuildType] - PD.Hooks -> [CuzBuildType CuzHooksBuildType] PD.Make -> [CuzBuildType CuzMakeBuildType] PD.Simple -> [] + -- TODO: remove the following, once we make Setup a separate + -- component (task tracked at #9986). + PD.Hooks -> [CuzBuildType CuzHooksBuildType] + -- cabal-format versions prior to 1.8 have different build-depends semantics -- for now it's easier to just fallback to legacy-mode when specVersion < 1.8 -- see, https://github.com/haskell/cabal/issues/4121 @@ -2194,6 +2207,7 @@ elaborateInstallPlan gdesc of Right (desc, _) -> desc Left _ -> error "Failed to finalizePD in elaborateSolverToCommon" + elabGPkgDescription = gdesc elabFlagAssignment = flags elabFlagDefaults = PD.mkFlagAssignment @@ -2279,10 +2293,15 @@ elaborateInstallPlan { withVanillaLib = perPkgOptionFlag pkgid True packageConfigVanillaLib -- TODO: [required feature]: also needs to be handled recursively , withSharedLib = canBuildSharedLibs && pkgid `Set.member` pkgsUseSharedLibrary , withStaticLib = perPkgOptionFlag pkgid False packageConfigStaticLib - , withDynExe = perPkgOptionFlag pkgid False packageConfigDynExe + , withDynExe = + perPkgOptionFlag pkgid False packageConfigDynExe + -- We can't produce a dynamic executable if the user + -- wants to enable executable profiling but the + -- compiler doesn't support prof+dyn. + && (okProfDyn || not profExe) , withFullyStaticExe = perPkgOptionFlag pkgid False packageConfigFullyStaticExe , withGHCiLib = perPkgOptionFlag pkgid False packageConfigGHCiLib -- TODO: [required feature] needs to default to enabled on windows still - , withProfExe = perPkgOptionFlag pkgid False packageConfigProf + , withProfExe = profExe , withProfLib = canBuildProfilingLibs && pkgid `Set.member` pkgsUseProfilingLibrary , withProfLibShared = canBuildProfilingSharedLibs && pkgid `Set.member` pkgsUseProfilingLibraryShared , exeCoverage = perPkgOptionFlag pkgid False packageConfigCoverage @@ -2297,6 +2316,8 @@ elaborateInstallPlan , withProfLibDetail = elabProfExeDetail , withProfExeDetail = elabProfLibDetail } + okProfDyn = profilingDynamicSupportedOrUnknown compiler + profExe = perPkgOptionFlag pkgid False packageConfigProf ( elabProfExeDetail , elabProfLibDetail @@ -2321,16 +2342,30 @@ elaborateInstallPlan ] <> perPkgOptionMapLast pkgid packageConfigProgramPaths elabProgramArgs = - Map.unionWith - (++) - ( Map.fromList - [ (programId prog, args) - | prog <- configuredPrograms compilerprogdb - , let args = programOverrideArgs $ addHaddockIfDocumentationEnabled prog - , not (null args) - ] - ) - (perPkgOptionMapMappend pkgid packageConfigProgramArgs) + -- Workaround for + -- + -- It turns out, that even with Cabal 2.0, there's still cases such as e.g. + -- custom Setup.hs scripts calling out to GHC even when going via + -- @runProgram ghcProgram@, as e.g. happy does in its + -- + -- (see also ) + -- + -- So for now, let's pass the rather harmless and idempotent + -- `-hide-all-packages` flag to all invocations (which has + -- the benefit that every GHC invocation starts with a + -- consistently well-defined clean slate) until we find a + -- better way. + Map.insertWith (++) "ghc" ["-hide-all-packages"] $ + Map.unionWith + (++) + ( Map.fromList + [ (programId prog, args) + | prog <- configuredPrograms compilerprogdb + , let args = programOverrideArgs $ addHaddockIfDocumentationEnabled prog + , not (null args) + ] + ) + (perPkgOptionMapMappend pkgid packageConfigProgramArgs) elabProgramPathExtra = perPkgOptionNubList pkgid packageConfigProgramPathExtra elabConfiguredPrograms = configuredPrograms compilerprogdb elabConfigureScriptArgs = perPkgOptionList pkgid packageConfigConfigureArgs @@ -3829,11 +3864,10 @@ setupHsScriptOptions -> DistDirLayout -> SymbolicPath CWD (Dir Pkg) -> SymbolicPath Pkg (Dir Dist) - -> Bool -> Lock -> SetupScriptOptions -- TODO: Fix this so custom is a separate component. Custom can ALWAYS --- be a separate component!!! +-- be a separate component!!! See #9986. setupHsScriptOptions (ReadyPackage elab@ElaboratedConfiguredPackage{..}) plan @@ -3841,7 +3875,6 @@ setupHsScriptOptions distdir srcdir builddir - isParallelBuild cacheLock = SetupScriptOptions { useCabalVersion = thisVersion elabSetupScriptCliVersion @@ -3874,7 +3907,6 @@ setupHsScriptOptions -- for build-tools-depends. useExtraEnvOverrides = dataDirsEnvironmentForPlan distdir plan , useWin32CleanHack = False -- TODO: [required eventually] - , forceExternalSetupMethod = isParallelBuild , setupCacheLock = Just cacheLock , isInteractive = False , isMainLibOrExeComponent = case elabPkgOrComp of @@ -4013,7 +4045,7 @@ setupHsConfigureFlags , configDynExe , configFullyStaticExe , configGHCiLib - , -- , configProfExe -- overridden + , -- configProfExe -- overridden configProfLib , configProfShared , -- , configProf -- overridden @@ -4043,27 +4075,7 @@ setupHsConfigureFlags ElabComponent _ -> toFlag elabComponentId configProgramPaths = Map.toList elabProgramPaths - configProgramArgs - | {- elabSetupScriptCliVersion < mkVersion [1,24,3] -} True = - -- workaround for - -- - -- It turns out, that even with Cabal 2.0, there's still cases such as e.g. - -- custom Setup.hs scripts calling out to GHC even when going via - -- @runProgram ghcProgram@, as e.g. happy does in its - -- - -- (see also ) - -- - -- So for now, let's pass the rather harmless and idempotent - -- `-hide-all-packages` flag to all invocations (which has - -- the benefit that every GHC invocation starts with a - -- consistently well-defined clean slate) until we find a - -- better way. - Map.toList $ - Map.insertWith - (++) - "ghc" - ["-hide-all-packages"] - elabProgramArgs + configProgramArgs = Map.toList elabProgramArgs configProgramPathExtra = toNubList elabProgramPathExtra configHcFlavor = toFlag (compilerFlavor pkgConfigCompiler) configHcPath = mempty -- we use configProgramPaths instead @@ -4076,8 +4088,8 @@ setupHsConfigureFlags configExtraLibDirsStatic = fmap makeSymbolicPath $ elabExtraLibDirsStatic configExtraFrameworkDirs = fmap makeSymbolicPath $ elabExtraFrameworkDirs configExtraIncludeDirs = fmap makeSymbolicPath $ elabExtraIncludeDirs - configProgPrefix = maybe mempty toFlag elabProgPrefix - configProgSuffix = maybe mempty toFlag elabProgSuffix + configProgPrefix = maybe (Flag (Cabal.toPathTemplate "")) toFlag elabProgPrefix + configProgSuffix = maybe (Flag (Cabal.toPathTemplate "")) toFlag elabProgSuffix configInstallDirs = fmap @@ -4161,15 +4173,16 @@ setupHsCommonFlags :: Verbosity -> Maybe (SymbolicPath CWD (Dir Pkg)) -> SymbolicPath Pkg (Dir Dist) + -> [String] -> Bool -> Cabal.CommonSetupFlags -setupHsCommonFlags verbosity mbWorkDir builddir keepTempFiles = +setupHsCommonFlags verbosity mbWorkDir builddir targets keepTempFiles = Cabal.CommonSetupFlags { setupDistPref = toFlag builddir , setupVerbosity = toFlag verbosity , setupCabalFilePath = mempty , setupWorkingDir = maybeToFlag mbWorkDir - , setupTargets = [] + , setupTargets = targets , setupKeepTempFiles = toFlag keepTempFiles } @@ -4209,11 +4222,11 @@ setupHsTestFlags setupHsTestFlags (ElaboratedConfiguredPackage{..}) common = Cabal.TestFlags { testCommonFlags = common - , testMachineLog = maybe mempty toFlag elabTestMachineLog - , testHumanLog = maybe mempty toFlag elabTestHumanLog + , testMachineLog = maybeToFlag elabTestMachineLog + , testHumanLog = maybeToFlag elabTestHumanLog , testShowDetails = maybe (Flag Cabal.Always) toFlag elabTestShowDetails , testKeepTix = toFlag elabTestKeepTix - , testWrapper = maybe mempty toFlag elabTestWrapper + , testWrapper = maybeToFlag elabTestWrapper , testFailWhenNoTestSuites = toFlag elabTestFailWhenNoTestSuites , testOptions = elabTestTestOptions } @@ -4315,18 +4328,18 @@ setupHsHaddockFlags , haddockProgramArgs = mempty -- unused, set at configure time , haddockHoogle = toFlag elabHaddockHoogle , haddockHtml = toFlag elabHaddockHtml - , haddockHtmlLocation = maybe mempty toFlag elabHaddockHtmlLocation + , haddockHtmlLocation = maybeToFlag elabHaddockHtmlLocation , haddockForHackage = toFlag elabHaddockForHackage , haddockForeignLibs = toFlag elabHaddockForeignLibs , haddockExecutables = toFlag elabHaddockExecutables , haddockTestSuites = toFlag elabHaddockTestSuites , haddockBenchmarks = toFlag elabHaddockBenchmarks , haddockInternal = toFlag elabHaddockInternal - , haddockCss = maybe mempty toFlag elabHaddockCss + , haddockCss = maybeToFlag elabHaddockCss , haddockLinkedSource = toFlag elabHaddockLinkedSource , haddockQuickJump = toFlag elabHaddockQuickJump - , haddockHscolourCss = maybe mempty toFlag elabHaddockHscolourCss - , haddockContents = maybe mempty toFlag elabHaddockContents + , haddockHscolourCss = maybeToFlag elabHaddockHscolourCss + , haddockContents = maybeToFlag elabHaddockContents , haddockIndex = maybe mempty toFlag elabHaddockIndex , haddockBaseUrl = maybe mempty toFlag elabHaddockBaseUrl , haddockResourcesDir = maybe mempty toFlag elabHaddockResourcesDir diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning/SetupPolicy.hs b/cabal-install/src/Distribution/Client/ProjectPlanning/SetupPolicy.hs index a510ea2bff6..a6eb0fd465c 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning/SetupPolicy.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning/SetupPolicy.hs @@ -22,9 +22,16 @@ -- In cases 1 and 2 we obviously have to build an external Setup.hs script, -- while in case 4 we can use the internal library API. -- +-- Since @3.14.0.0@ we must also consider the @Setup.hs@ scripts constructed +-- from 'SetupHooks' values, because these generated @Setup.hs@ scripts depend +-- on the @hooks-exe@ package (which creates an executable from 'SetupHooks'). +-- Therefore, 'SetupPolicy' is also concerned with augmenting the setup +-- dependencies with @hooks-exe@ when @build-type: Hooks@. +-- -- @since 3.12.0.0 module Distribution.Client.ProjectPlanning.SetupPolicy ( mkDefaultSetupDeps + , mkHooksSetupImplicitDeps , packageSetupScriptStyle , packageSetupScriptSpecVersion , NonSetupLibDepSolverPlanPackage (..) @@ -157,6 +164,30 @@ mkDefaultSetupDeps compiler platform pkg = csvToVersion :: CabalSpecVersion -> Version csvToVersion = mkVersion . cabalSpecMinimumLibraryVersion +-- | Returns an implicit dependency on @hooks-exe@ needed to create a +-- @Setup.hs@ executable from a 'SetupHooks' value, if @build-type: Hooks@, +-- as well as a dependency on @Cabal@ if there isn't one already. +-- +-- @since 3.14.0.0 +mkHooksSetupImplicitDeps + :: PackageDescription + -> Maybe [Dependency] +mkHooksSetupImplicitDeps pkg + | Hooks <- buildType pkg = + Just $ + [Dependency hooksExePkgname anyVersion mainLibSet] + -- Add a dependency on Cabal if there isn't one, so that we can compile: + -- module Main where + -- import Distribution.Simple (defaultMainWithSetupHooks) + -- import SetupHooks (setupHooks) + -- main = defaultMainWithSetupHooks setupHooks + ++ [ Dependency cabalPkgname (orLaterVersion $ mkVersion [3, 13, 0]) mainLibSet + | setupBI <- maybeToList $ setupBuildInfo pkg + , not $ any ((== cabalPkgname) . depPkgName) $ setupDepends setupBI + ] + | otherwise = + Nothing + -- | A newtype for 'SolverPlanPackage' for which the -- dependency graph considers only dependencies on libraries which are -- NOT from setup dependencies. Used to compute the set @@ -216,8 +247,9 @@ packageSetupScriptSpecVersion _ pkg libDepGraph deps = fromMaybe [] $ Graph.closure libDepGraph (CD.setupDeps deps) -cabalPkgname :: PackageName +cabalPkgname, hooksExePkgname :: PackageName cabalPkgname = mkPackageName "Cabal" +hooksExePkgname = mkPackageName "hooks-exe" legacyCustomSetupPkgs :: Compiler -> Platform -> [PackageName] legacyCustomSetupPkgs compiler (Platform _ os) = diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs index 6aa1065d20e..ba92122293e 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs @@ -223,6 +223,8 @@ data ElaboratedConfiguredPackage = ElaboratedConfiguredPackage , elabFlagDefaults :: Cabal.FlagAssignment -- ^ The original default flag assignment, used only for reporting. , elabPkgDescription :: Cabal.PackageDescription + , elabGPkgDescription :: Cabal.GenericPackageDescription + -- ^ Original 'GenericPackageDescription' (just used to report errors/warnings) , elabPkgSourceLocation :: PackageLocation (Maybe FilePath) -- ^ Where the package comes from, e.g. tarball, local dir etc. This -- is not the same as where it may be unpacked to for the build. diff --git a/cabal-install/src/Distribution/Client/SetupWrapper.hs b/cabal-install/src/Distribution/Client/SetupWrapper.hs index 69c8f888698..15da999a39f 100644 --- a/cabal-install/src/Distribution/Client/SetupWrapper.hs +++ b/cabal-install/src/Distribution/Client/SetupWrapper.hs @@ -1,8 +1,11 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} {- FOURMOLU_DISABLE -} ----------------------------------------------------------------------------- @@ -25,9 +28,16 @@ module Distribution.Client.SetupWrapper ( getSetup , runSetup , runSetupCommand + , SetupRunnerArgs(..) + , SPostConfigurePhase(..) + , InLibraryArgs(..) + , SetupRunnerRes + , InLibraryLBI(..) + , RightFlagsForPhase , setupWrapper , SetupScriptOptions (..) , defaultSetupScriptOptions + , externalSetupMethod ) where import Distribution.Client.Compat.Prelude @@ -35,7 +45,6 @@ import Prelude () import qualified Distribution.Backpack as Backpack import Distribution.CabalSpecVersion (cabalSpecMinimumLibraryVersion) -import qualified Distribution.Make as Make import Distribution.Package ( ComponentId , PackageId @@ -58,13 +67,11 @@ import Distribution.Simple.Build.Macros ( generatePackageVersionMacros ) import Distribution.Simple.BuildPaths - ( defaultDistPref - , exeExtension + ( exeExtension ) import Distribution.Simple.Compiler import Distribution.Simple.Configure - ( configCompilerEx - ) + hiding ( getInstalledPackages ) import Distribution.Simple.PackageDescription ( readGenericPackageDescription ) @@ -73,19 +80,7 @@ import Distribution.Simple.PreProcess , runSimplePreProcessor ) import Distribution.Simple.Program - ( ProgramDb - , emptyProgramDb - , getDbProgramOutputCwd - , getProgramSearchPath - , ghcProgram - , ghcjsProgram - , runDbProgramCwd - ) import Distribution.Simple.Program.Db - ( configureAllKnownPrograms - , prependProgramSearchPath - , progOverrideEnv - ) import Distribution.Simple.Program.Find ( programSearchPathAsPATHVar ) @@ -108,6 +103,8 @@ import Distribution.Version import Distribution.Client.Config ( defaultCacheDir ) +import Distribution.Client.FileMonitor + ( MonitorFilePath ) import Distribution.Client.IndexUtils ( getInstalledPackages ) @@ -124,9 +121,6 @@ import Distribution.Client.Utils #endif , moreRecentFile , tryCanonicalizePath - , withEnv - , withEnvOverrides - , withExtraPathEnv ) import Distribution.Utils.Path hiding ( (), (<.>) ) @@ -143,12 +137,6 @@ import Distribution.Simple.Program.GHC , GhcOptions (..) , renderGhcOptions ) -import Distribution.Simple.Setup - ( CommonSetupFlags (..) - , pattern Flag - , GlobalFlags (..) - , globalCommand - ) import Distribution.Simple.Utils ( cabalVersion , copyFileVerbose @@ -171,16 +159,28 @@ import Distribution.Utils.Generic import Distribution.Compat.Stack import Distribution.ReadE +import Distribution.Simple.Setup +import Distribution.Client.Compat.ExecutablePath (getExecutablePath) +import Distribution.Compat.Process (proc) import Distribution.System (Platform (..), buildPlatform) import Distribution.Utils.NubList ( toNubListR ) +import Distribution.Types.LocalBuildInfo ( LocalBuildInfo ) +import qualified Distribution.Types.LocalBuildInfo as LBI import Distribution.Verbosity - +import Distribution.Client.Errors +import qualified Distribution.Client.InLibrary as InLibrary +import Distribution.Client.ProjectPlanning.Types +import Distribution.Client.SetupHooks.Version + ( hooksVersion ) +import Distribution.Client.SetupHooks.CallHooksExe + ( externalSetupHooksABI, hooksProgFilePath ) import Data.List (foldl1') +import Data.Kind (Type, Constraint) import qualified Data.Map.Lazy as Map -import Distribution.Client.Compat.ExecutablePath (getExecutablePath) -import Distribution.Compat.Process (proc) +import Data.Type.Equality ( type (==) ) +import Data.Type.Bool ( If ) import System.Directory (doesFileExist) import System.FilePath ((<.>), ()) import System.IO (Handle, hPutStr) @@ -188,38 +188,104 @@ import System.Process (StdStream (..)) import qualified System.Process as Process import qualified Data.ByteString.Lazy as BS -import Distribution.Client.Errors #ifdef mingw32_HOST_OS import Distribution.Simple.Utils ( withTempDirectory ) import Control.Exception ( bracket ) -import System.FilePath ( equalFilePath, takeDirectory ) import System.Directory ( doesDirectoryExist ) +import System.FilePath ( equalFilePath, takeDirectory, takeFileName ) import qualified System.Win32 as Win32 #endif +data AllowInLibrary + = AllowInLibrary + | Don'tAllowInLibrary + deriving Eq + +data SetupKind + = InLibrary + | GeneralSetup + +-- | If we end up using the in-library method, we use the v'InLibraryLBI' +-- constructor. If not, we use the 'NotInLibraryNoLBI' constructor. +-- +-- NB: we don't know ahead of time whether we can use the in-library method; +-- e.g. for a package with Hooks build-type, it depends on whether the Cabal +-- version used by the package matches with the Cabal version that cabal-install +-- was built against. +data InLibraryLBI + = InLibraryLBI LocalBuildInfo + | NotInLibraryNoLBI + +data SPostConfigurePhase (flags :: Type) where + SBuildPhase :: SPostConfigurePhase BuildFlags + SHaddockPhase :: SPostConfigurePhase HaddockFlags + SReplPhase :: SPostConfigurePhase ReplFlags + SCopyPhase :: SPostConfigurePhase CopyFlags + SRegisterPhase :: SPostConfigurePhase RegisterFlags + STestPhase :: SPostConfigurePhase TestFlags + SBenchPhase :: SPostConfigurePhase BenchmarkFlags + +data SetupWrapperSpec + = TryInLibrary Type + | UseGeneralSetup + +type family RightFlagsForPhase (flags :: Type) (setupSpec :: SetupWrapperSpec) :: Constraint where + RightFlagsForPhase flags UseGeneralSetup = () + RightFlagsForPhase flags (TryInLibrary flags') = flags ~ flags' + +data SetupRunnerArgs (spec :: SetupWrapperSpec) where + NotInLibrary + :: SetupRunnerArgs UseGeneralSetup + InLibraryArgs + :: InLibraryArgs flags + -> SetupRunnerArgs (TryInLibrary flags) + +data InLibraryArgs (flags :: Type) where + InLibraryConfigureArgs + :: ElaboratedSharedConfig + -> ElaboratedReadyPackage + -> InLibraryArgs ConfigFlags + InLibraryPostConfigureArgs + :: SPostConfigurePhase flags + -> InLibraryLBI + -> InLibraryArgs flags + +type family SetupRunnerRes (spec :: SetupWrapperSpec) where + SetupRunnerRes UseGeneralSetup = () + SetupRunnerRes (TryInLibrary phase) = InLibraryPhaseRes phase + +type family InLibraryPhaseRes flags where + InLibraryPhaseRes ConfigFlags = InLibraryLBI + InLibraryPhaseRes BuildFlags = [MonitorFilePath] + InLibraryPhaseRes HaddockFlags = [MonitorFilePath] + InLibraryPhaseRes ReplFlags = () + InLibraryPhaseRes _ = () + -- | @Setup@ encapsulates the outcome of configuring a setup method to build a -- particular package. -data Setup = Setup - { setupMethod :: SetupMethod +data Setup kind = Setup + { setupMethod :: SetupMethod kind , setupScriptOptions :: SetupScriptOptions , setupVersion :: Version , setupBuildType :: BuildType , setupPackage :: PackageDescription } +data ASetup = forall kind. ASetup ( Setup kind ) + -- | @SetupMethod@ represents one of the methods used to run Cabal commands. -data SetupMethod - = -- | run Cabal commands through \"cabal\" in the - -- current process - InternalMethod - | -- | run Cabal commands through \"cabal\" as a - -- child process - SelfExecMethod - | -- | run Cabal commands through a custom \"Setup\" executable - ExternalMethod FilePath +data SetupMethod (kind :: SetupKind) where + -- | Directly use Cabal library functions, bypassing the Setup + -- mechanism entirely. + LibraryMethod :: SetupMethod InLibrary + -- | run Cabal commands through @cabal@ as a child process, + -- using @cabal --act-as-setup@ + SelfExecMethod :: SetupMethod GeneralSetup + -- | run Cabal commands through a custom \"Setup\" executable + ExternalMethod :: FilePath -> SetupMethod GeneralSetup -- TODO: The 'setupWrapper' and 'SetupScriptOptions' should be split into two -- parts: one that has no policy and just does as it's told with all the @@ -266,7 +332,6 @@ data SetupScriptOptions = SetupScriptOptions -- -- * @'Just' v@ means \"set the environment variable's value to @v@\". -- * 'Nothing' means \"unset the environment variable\". - , forceExternalSetupMethod :: Bool , useDependencies :: [(ComponentId, PackageId)] -- ^ List of dependencies to use when building Setup.hs. , useDependenciesExclusive :: Bool @@ -342,7 +407,6 @@ defaultSetupScriptOptions = , useExtraPathEnv = [] , useExtraEnvOverrides = [] , useWin32CleanHack = False - , forceExternalSetupMethod = False , setupCacheLock = Nothing , isInteractive = False , isMainLibOrExeComponent = True @@ -357,12 +421,13 @@ workingDir options = case useWorkingDir options of _ -> "." -- | A @SetupRunner@ implements a 'SetupMethod'. -type SetupRunner = +type SetupRunner kind = Verbosity -> SetupScriptOptions -> BuildType -> [String] - -> IO () + -> SetupRunnerArgs kind + -> IO (SetupRunnerRes kind) -- | Prepare to build a package by configuring a 'SetupMethod'. The returned -- 'Setup' object identifies the method. The 'SetupScriptOptions' may be changed @@ -372,8 +437,9 @@ getSetup :: Verbosity -> SetupScriptOptions -> Maybe PackageDescription - -> IO Setup -getSetup verbosity options mpkg = do + -> AllowInLibrary + -> IO ASetup +getSetup verbosity options mpkg allowInLibrary = do pkg <- maybe getPkg return mpkg let options' = options @@ -390,16 +456,15 @@ getSetup verbosity options mpkg = do buildType' = case (buildType pkg, isMainLibOrExeComponent options) of (Configure, False) -> Simple (bt, _) -> bt - (version, method, options'') <- - getSetupMethod verbosity options' pkg buildType' - return - Setup - { setupMethod = method - , setupScriptOptions = options'' - , setupVersion = version - , setupBuildType = buildType' - , setupPackage = pkg - } + withSetupMethod verbosity options' pkg buildType' allowInLibrary $ + \ (version, method, options'') -> + ASetup $ Setup + { setupMethod = method + , setupScriptOptions = options'' + , setupVersion = version + , setupBuildType = buildType' + , setupPackage = pkg + } where mbWorkDir = useWorkingDir options getPkg = @@ -410,38 +475,65 @@ getSetup verbosity options mpkg = do -- | Decide if we're going to be able to do a direct internal call to the -- entry point in the Cabal library or if we're going to have to compile -- and execute an external Setup.hs script. -getSetupMethod +withSetupMethod :: Verbosity -> SetupScriptOptions -> PackageDescription -> BuildType - -> IO (Version, SetupMethod, SetupScriptOptions) -getSetupMethod verbosity options pkg buildType' + -> AllowInLibrary + -> ( forall kind. (Version, SetupMethod kind, SetupScriptOptions ) -> r ) + -> IO r +withSetupMethod verbosity options pkg buildType' allowInLibrary with | buildType' == Custom - || buildType' == Hooks + || (buildType' == Hooks && isJust (useLoggingHandle options)) || maybe False (cabalVersion /=) (useCabalSpecVersion options) - || not (cabalVersion `withinRange` useCabalVersion options) = - getExternalSetupMethod verbosity options pkg buildType' - | isJust (useLoggingHandle options) - -- Forcing is done to use an external process e.g. due to parallel - -- build concerns. - || forceExternalSetupMethod options = - return (cabalVersion, SelfExecMethod, options) - | otherwise = return (cabalVersion, InternalMethod, options) - -runSetupMethod :: WithCallStack (SetupMethod -> SetupRunner) -runSetupMethod InternalMethod = internalSetupMethod + || not (cabalVersion `withinRange` useCabalVersion options) + || allowInLibrary == Don'tAllowInLibrary = + withExternalSetupMethod + | -- TODO: once we refactor the Cabal library to be able to take a logging + -- handle as an argument, we will be able to get rid of the self-exec method. + -- Tracking ticket: #9987. + isJust (useLoggingHandle options) = + return $ with (cabalVersion, SelfExecMethod, options) + | otherwise + = do + abiOK <- + if buildType' == Hooks + then do + -- Compile the hooks executable + compileExternalExe verbosity options pkg buildType' WantHooks + externalHooksABI <- externalSetupHooksABI $ hooksProgFilePath (useWorkingDir options) (useDistPref options) + let internalHooksABI = hooksVersion + return $ externalHooksABI == internalHooksABI + else return True + if abiOK + then do + debug verbosity $ "Using in-library setup method with build-type " ++ show buildType' + return $ with (cabalVersion, LibraryMethod, options) + else do + debug verbosity $ "Hooks ABI mismatch; falling back to external setup method." + withExternalSetupMethod + where + withExternalSetupMethod = do + debug verbosity $ "Using external setup method with build-type " ++ show buildType' + debug verbosity $ + "Using explicit dependencies: " + ++ show (useDependenciesExclusive options) + with <$> compileExternalExe verbosity options pkg buildType' WantSetup + +runSetupMethod :: WithCallStack (SetupMethod GeneralSetup -> SetupRunner UseGeneralSetup) runSetupMethod (ExternalMethod path) = externalSetupMethod path runSetupMethod SelfExecMethod = selfExecSetupMethod -- | Run a configured 'Setup' with specific arguments. runSetup :: Verbosity - -> Setup + -> Setup GeneralSetup -> [String] -- ^ command-line arguments - -> IO () -runSetup verbosity setup args0 = do + -> SetupRunnerArgs UseGeneralSetup + -> IO (SetupRunnerRes UseGeneralSetup) +runSetup verbosity setup args0 setupArgs = do let method = setupMethod setup options = setupScriptOptions setup bt = setupBuildType setup @@ -455,7 +547,7 @@ runSetup verbosity setup args0 = do ++ " After: " ++ show args ++ "\n" - runSetupMethod method verbosity options bt args + runSetupMethod method verbosity options bt args setupArgs -- | This is a horrible hack to make sure passing fancy verbosity -- flags (e.g., @-v'info +callstack'@) doesn't break horribly on @@ -494,7 +586,7 @@ verbosityHack ver args0 -- | Run a command through a configured 'Setup'. runSetupCommand :: Verbosity - -> Setup + -> Setup GeneralSetup -> CommandUI flags -- ^ command definition -> (flags -> CommonSetupFlags) @@ -502,20 +594,23 @@ runSetupCommand -- ^ command flags -> [String] -- ^ extra command-line arguments - -> IO () -runSetupCommand verbosity setup cmd getCommonFlags flags extraArgs = + -> SetupRunnerArgs UseGeneralSetup + -> IO (SetupRunnerRes UseGeneralSetup) +runSetupCommand verbosity setup cmd getCommonFlags flags extraArgs setupArgs = -- The 'setupWorkingDir' flag corresponds to a global argument which needs to -- be passed before the individual command (e.g. 'configure' or 'build'). let common = getCommonFlags flags globalFlags = mempty { globalWorkingDir = setupWorkingDir common } args = commandShowOptions (globalCommand []) globalFlags ++ (commandName cmd : commandShowOptions cmd flags ++ extraArgs) - in runSetup verbosity setup args + in runSetup verbosity setup args setupArgs -- | Configure a 'Setup' and run a command in one step. The command flags -- may depend on the Cabal library version in use. setupWrapper - :: Verbosity + :: forall setupSpec flags + . RightFlagsForPhase flags setupSpec + => Verbosity -> SetupScriptOptions -> Maybe PackageDescription -> CommandUI flags @@ -523,56 +618,134 @@ setupWrapper -> (Version -> IO flags) -- ^ produce command flags given the Cabal library version -> (Version -> [String]) - -> IO () -setupWrapper verbosity options mpkg cmd getCommonFlags getFlags getExtraArgs = do - setup <- getSetup verbosity options mpkg + -> SetupRunnerArgs setupSpec + -> IO (SetupRunnerRes setupSpec) +setupWrapper verbosity options mpkg cmd getCommonFlags getFlags getExtraArgs wrapperArgs = do + let allowInLibrary = case wrapperArgs of + NotInLibrary -> Don'tAllowInLibrary + InLibraryArgs {} -> AllowInLibrary + ASetup (setup :: Setup kind) <- getSetup verbosity options mpkg allowInLibrary let version = setupVersion setup - extraArgs = getExtraArgs version flags <- getFlags version - runSetupCommand - verbosity - setup - cmd - getCommonFlags - flags - extraArgs + let extraArgs = getExtraArgs version + notInLibraryMethod :: kind ~ GeneralSetup => IO (SetupRunnerRes setupSpec) + notInLibraryMethod = do + runSetupCommand verbosity setup cmd getCommonFlags flags extraArgs NotInLibrary + return $ case wrapperArgs of + NotInLibrary -> () + InLibraryArgs libArgs -> + case libArgs of + InLibraryConfigureArgs {} -> NotInLibraryNoLBI + InLibraryPostConfigureArgs sPhase _ -> + case sPhase of + SBuildPhase -> [] + SHaddockPhase -> [] + SReplPhase -> () + SCopyPhase -> () + SRegisterPhase -> () + STestPhase -> () + SBenchPhase -> () + case setupMethod setup of + LibraryMethod -> + case wrapperArgs of + InLibraryArgs libArgs -> + case libArgs of + InLibraryConfigureArgs elabSharedConfig elabReadyPkg -> do + -- See (1)(a) in Note [Constructing the ProgramDb] + baseProgDb <- + prependProgramSearchPath verbosity + (useExtraPathEnv options) + (useExtraEnvOverrides options) =<< + mkProgramDb flags -- Passes user-supplied arguments to e.g. GHC + (restoreProgramDb builtinPrograms $ + useProgramDb options) -- Recall that 'useProgramDb' is set to 'pkgConfigCompilerProgs' + -- See (2) in Note [Constructing the ProgramDb] + setupProgDb <- + configCompilerProgDb + verbosity + (pkgConfigCompiler elabSharedConfig) + baseProgDb + Nothing -- we use configProgramPaths instead + lbi0 <- + InLibrary.configure + (InLibrary.libraryConfigureInputsFromElabPackage + (setupBuildType setup) + setupProgDb + elabSharedConfig + elabReadyPkg + extraArgs + ) + flags + let progs0 = LBI.withPrograms lbi0 + -- See (1)(b) in Note [Constructing the ProgramDb] + progs1 <- updatePathProgDb verbosity progs0 + let + lbi = + lbi0 + { LBI.withPrograms = progs1 + } + mbWorkDir = useWorkingDir options + distPref = useDistPref options + -- Write the LocalBuildInfo to disk. This is needed, for instance, if we + -- skip re-configuring; we retrieve the LocalBuildInfo stored on disk from + -- the previous invocation of 'configure' and pass it to 'build'. + writePersistBuildConfig mbWorkDir distPref lbi + return (InLibraryLBI lbi) + InLibraryPostConfigureArgs sPhase mbLBI -> + case mbLBI of + NotInLibraryNoLBI -> + error "internal error: in-library post-conf but no LBI" + -- To avoid running into the above error, we must ensure that + -- when we skip re-configuring, we retrieve the cached + -- LocalBuildInfo (see "whenReconfigure" + -- in Distribution.Client.ProjectBuilding.UnpackedPackage). + InLibraryLBI lbi -> + case sPhase of + SBuildPhase -> InLibrary.build flags lbi extraArgs + SHaddockPhase -> InLibrary.haddock flags lbi extraArgs + SReplPhase -> InLibrary.repl flags lbi extraArgs + SCopyPhase -> InLibrary.copy flags lbi extraArgs + STestPhase -> InLibrary.test flags lbi extraArgs + SBenchPhase -> InLibrary.bench flags lbi extraArgs + SRegisterPhase -> InLibrary.register flags lbi extraArgs + NotInLibrary -> + error "internal error: NotInLibrary argument but getSetup chose InLibrary" + ExternalMethod {} -> notInLibraryMethod + SelfExecMethod -> notInLibraryMethod + +{- Note [Constructing the ProgramDb] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When using the in-library method for configuring a package, we want to start off +with the information cabal-install already has in hand, such as the compiler. +Specifically, we skip 'Cabal.Distribution.Simple.preConfigurePackage', which +includes the call to 'configCompilerEx'. + +To obtain a program database with all the required information, we do a few +things: + + (1) + (a) When building a package with internal build tools, we must ensure that + these build tools are available in PATH, with appropriate environment + variable overrides for their data directory. To do this, we call + 'prependProgramSearchPath'. + + (b) Moreover, these programs must be available in the search paths for the + compiler itself, in case they are run at compile-time (e.g. with a Template + Haskell splice). We achieve this using 'updatePathProgDb'. + + (2) Given the compiler, we must compute the ProgramDb of programs that are + specified alongside the compiler, such as ghc-pkg, haddock, and toolchain + programs such as ar, ld. + + We do this using the function 'configCompilerProgDb'. +-} -- ------------------------------------------------------------ --- * Internal SetupMethod +-- * 'invoke' function -- ------------------------------------------------------------ --- | Run a Setup script by directly invoking the @Cabal@ library. -internalSetupMethod :: SetupRunner -internalSetupMethod verbosity options bt args = do - info verbosity $ - "Using internal setup method with build-type " - ++ show bt - ++ " and args:\n " - ++ show args - -- NB: we do not set the working directory of the process here, because - -- we will instead pass the -working-dir flag when invoking the Setup script. - -- Note that the Setup script is guaranteed to support this flag, because - -- the logic in 'getSetupMethod' guarantees we have an up-to-date Cabal version. - -- - -- In the future, it would be desirable to also stop relying on the following - -- pieces of process-global state, as this would allow us to use this internal - -- setup method in concurrent contexts. - withEnv "HASKELL_DIST_DIR" (getSymbolicPath $ useDistPref options) $ - withExtraPathEnv (useExtraPathEnv options) $ - withEnvOverrides (useExtraEnvOverrides options) $ - buildTypeAction bt args - -buildTypeAction :: BuildType -> ([String] -> IO ()) -buildTypeAction Simple = Simple.defaultMainArgs -buildTypeAction Configure = - Simple.defaultMainWithSetupHooksArgs - Simple.autoconfSetupHooks -buildTypeAction Make = Make.defaultMainArgs -buildTypeAction Hooks = error "buildTypeAction Hooks" -buildTypeAction Custom = error "buildTypeAction Custom" - invoke :: Verbosity -> FilePath -> [String] -> SetupScriptOptions -> IO () invoke verbosity path args options = do info verbosity $ unwords (path : args) @@ -611,8 +784,8 @@ invoke verbosity path args options = do -- ------------------------------------------------------------ -selfExecSetupMethod :: SetupRunner -selfExecSetupMethod verbosity options bt args0 = do +selfExecSetupMethod :: SetupRunner UseGeneralSetup +selfExecSetupMethod verbosity options bt args0 NotInLibrary = do let args = [ "act-as-setup" , "--build-type=" ++ prettyShow bt @@ -633,8 +806,8 @@ selfExecSetupMethod verbosity options bt args0 = do -- ------------------------------------------------------------ -externalSetupMethod :: WithCallStack (FilePath -> SetupRunner) -externalSetupMethod path verbosity options _ args = +externalSetupMethod :: WithCallStack (FilePath -> SetupRunner UseGeneralSetup) +externalSetupMethod path verbosity options _ args NotInLibrary = #ifndef mingw32_HOST_OS invoke verbosity @@ -659,7 +832,7 @@ externalSetupMethod path verbosity options _ args = (\tmpPath -> invoke' tmpPath) moveOutOfTheWay tmpDir origPath = do - let tmpPath = tmpDir "setup" <.> exeExtension buildPlatform + let tmpPath = tmpDir takeFileName origPath Win32.moveFile origPath tmpPath return tmpPath @@ -672,29 +845,40 @@ externalSetupMethod path verbosity options _ args = #endif -getExternalSetupMethod +data ExternalExe = HooksExe | SetupExe +data WantedExternalExe (meth :: ExternalExe) where + WantHooks :: WantedExternalExe HooksExe + WantSetup :: WantedExternalExe SetupExe + +compileExternalExe :: Verbosity -> SetupScriptOptions -> PackageDescription -> BuildType - -> IO (Version, SetupMethod, SetupScriptOptions) -getExternalSetupMethod verbosity options pkg bt = do - debug verbosity $ "Using external setup method with build-type " ++ show bt - debug verbosity $ - "Using explicit dependencies: " - ++ show (useDependenciesExclusive options) - createDirectoryIfMissingVerbose verbosity True $ i setupDir + -> WantedExternalExe exe + -> IO (If (exe == HooksExe) () (Version, SetupMethod GeneralSetup, SetupScriptOptions)) +compileExternalExe verbosity options pkg bt wantedMeth = do + createDirectoryIfMissingVerbose verbosity True $ i (setupDir options) (cabalLibVersion, mCabalLibInstalledPkgId, options') <- cabalLibVersionToUse debug verbosity $ "Using Cabal library version " ++ prettyShow cabalLibVersion - path <- + exePath <- if useCachedSetupExecutable then getCachedSetupExecutable + verbosity + platform + (package pkg) + bt options' cabalLibVersion mCabalLibInstalledPkgId else - compileSetupExecutable + compileExe + verbosity + platform + (package pkg) + bt + wantedMeth options' cabalLibVersion mCabalLibInstalledPkgId @@ -704,52 +888,34 @@ getExternalSetupMethod verbosity options pkg bt = do -- be turned into an absolute path. On some systems, runProcess' will take -- path as relative to the new working directory instead of the current -- working directory. - path' <- tryCanonicalizePath path + exePath' <- tryCanonicalizePath exePath -- See 'Note: win32 clean hack' above. #ifdef mingw32_HOST_OS -- setupProgFile may not exist if we're using a cached program - setupProgFile' <- canonicalizePathNoThrow $ i setupProgFile + setupProgFile' <- canonicalizePathNoThrow $ i (setupProgFile options) let win32CleanHackNeeded = (useWin32CleanHack options) -- Skip when a cached setup script is used. - && setupProgFile' `equalFilePath` path' + && setupProgFile' `equalFilePath` exePath' #else let win32CleanHackNeeded = False #endif let options'' = options'{useWin32CleanHack = win32CleanHackNeeded} - return (cabalLibVersion, ExternalMethod path', options'') + case wantedMeth of + WantHooks -> return () + WantSetup -> return (cabalLibVersion, ExternalMethod exePath', options'') where mbWorkDir = useWorkingDir options -- See Note [Symbolic paths] in Distribution.Utils.Path + i :: SymbolicPathX allowAbs Pkg to -> FilePath i = interpretSymbolicPath mbWorkDir - setupDir = useDistPref options Cabal.Path. makeRelativePathEx "setup" - setupVersionFile = setupDir Cabal.Path. makeRelativePathEx ("setup" <.> "version") - setupHs = setupDir Cabal.Path. makeRelativePathEx ("setup" <.> "hs") - setupHooks = setupDir Cabal.Path. makeRelativePathEx ("SetupHooks" <.> "hs") - setupProgFile = setupDir Cabal.Path. makeRelativePathEx ("setup" <.> exeExtension buildPlatform) - platform = fromMaybe buildPlatform (usePlatform options) useCachedSetupExecutable = bt == Simple || bt == Configure || bt == Make - maybeGetInstalledPackages - :: SetupScriptOptions - -> Compiler - -> ProgramDb - -> IO InstalledPackageIndex - maybeGetInstalledPackages options' comp progdb = - case usePackageIndex options' of - Just index -> return index - Nothing -> - getInstalledPackages - verbosity - comp - (usePackageDB options') - progdb - -- Choose the version of Cabal to use if the setup script has a dependency on -- Cabal, and possibly update the setup script options. The version also -- determines how to filter the flags to Setup. @@ -760,7 +926,7 @@ getExternalSetupMethod verbosity options pkg bt = do -- checking 'useCabalSpecVersion', then the saved version, and finally the -- versions available in the index. -- - -- The version chosen here must match the one used in 'compileSetupExecutable' + -- The version chosen here must match the one used in 'compileExe' -- (See issue #3433). cabalLibVersionToUse :: IO @@ -796,23 +962,23 @@ getExternalSetupMethod verbosity options pkg bt = do _ -> installedVersion where -- This check duplicates the checks in 'getCachedSetupExecutable' / - -- 'compileSetupExecutable'. Unfortunately, we have to perform it twice + -- 'compileExe'. Unfortunately, we have to perform it twice -- because the selected Cabal version may change as a result of this -- check. canUseExistingSetup :: Version -> IO Bool canUseExistingSetup version = if useCachedSetupExecutable then do - (_, cachedSetupProgFile) <- cachedSetupDirAndProg options version + (_, cachedSetupProgFile) <- cachedSetupDirAndProg platform bt options version doesFileExist cachedSetupProgFile else (&&) - <$> i setupProgFile `existsAndIsMoreRecentThan` i setupHs - <*> i setupProgFile `existsAndIsMoreRecentThan` i setupVersionFile + <$> i (setupProgFile options) `existsAndIsMoreRecentThan` i (setupHs options) + <*> i (setupProgFile options) `existsAndIsMoreRecentThan` i (setupVersionFile options) writeSetupVersionFile :: Version -> IO () writeSetupVersionFile version = - writeFile (i setupVersionFile) (show version ++ "\n") + writeFile (i (setupVersionFile options)) (show version ++ "\n") installedVersion :: IO @@ -821,9 +987,12 @@ getExternalSetupMethod verbosity options pkg bt = do , SetupScriptOptions ) installedVersion = do - (comp, progdb, options') <- configureCompiler options + (comp, progdb, options') <- configureCompiler verbosity options (version, mipkgid, options'') <- installedCabalVersion + verbosity + pkg + bt options' comp progdb @@ -833,7 +1002,7 @@ getExternalSetupMethod verbosity options pkg bt = do savedVersion :: IO (Maybe Version) savedVersion = do - versionString <- readFile (i setupVersionFile) `catchIO` \_ -> return "" + versionString <- readFile (i (setupVersionFile options)) `catchIO` \_ -> return "" case reads versionString of [(version, s)] | all isSpace s -> return (Just version) _ -> return Nothing @@ -846,325 +1015,441 @@ getExternalSetupMethod verbosity options pkg bt = do unless (useHs || useLhs) $ dieWithException verbosity UpdateSetupScript let src = (if useHs then customSetupHs else customSetupLhs) - srcNewer <- src `moreRecentFile` i setupHs + srcNewer <- src `moreRecentFile` i (setupHs options) when srcNewer $ if useHs - then copyFileVerbose verbosity src (i setupHs) - else runSimplePreProcessor ppUnlit src (i setupHs) verbosity + then copyFileVerbose verbosity src (i (setupHs options)) + else runSimplePreProcessor ppUnlit src (i (setupHs options)) verbosity where customSetupHs = workingDir options "Setup.hs" customSetupLhs = workingDir options "Setup.lhs" updateSetupScript cabalLibVersion Hooks = do - let customSetupHooks = workingDir options "SetupHooks.hs" useHs <- doesFileExist customSetupHooks - unless (useHs) $ + unless useHs $ die' verbosity "Using 'build-type: Hooks' but there is no SetupHooks.hs file." - copyFileVerbose verbosity customSetupHooks (i setupHooks) - rewriteFileLBS verbosity (i setupHs) (buildTypeScript cabalLibVersion) --- rewriteFileLBS verbosity hooksHs hooksScript - updateSetupScript cabalLibVersion _ = - rewriteFileLBS verbosity (i setupHs) (buildTypeScript cabalLibVersion) - - buildTypeScript :: Version -> BS.ByteString - buildTypeScript cabalLibVersion = "{-# LANGUAGE NoImplicitPrelude #-}\n" <> case bt of - Simple -> "import Distribution.Simple; main = defaultMain\n" - Configure - | cabalLibVersion >= mkVersion [3, 13, 0] - -> "import Distribution.Simple; main = defaultMainWithSetupHooks autoconfSetupHooks\n" - | cabalLibVersion >= mkVersion [1, 3, 10] - -> "import Distribution.Simple; main = defaultMainWithHooks autoconfUserHooks\n" - | otherwise - -> "import Distribution.Simple; main = defaultMainWithHooks defaultUserHooks\n" - Make -> "import Distribution.Make; main = defaultMain\n" - Hooks - | cabalLibVersion >= mkVersion [3, 13, 0] - -> "import Distribution.Simple; import SetupHooks; main = defaultMainWithSetupHooks setupHooks\n" - | otherwise - -> error "buildTypeScript Hooks with Cabal < 3.13" - Custom -> error "buildTypeScript Custom" - - installedCabalVersion - :: SetupScriptOptions - -> Compiler - -> ProgramDb - -> IO - ( Version - , Maybe InstalledPackageId - , SetupScriptOptions - ) - installedCabalVersion options' _ _ - | packageName pkg == mkPackageName "Cabal" - && bt == Custom = - return (packageVersion pkg, Nothing, options') - installedCabalVersion options' compiler progdb = do - index <- maybeGetInstalledPackages options' compiler progdb - let cabalDepName = mkPackageName "Cabal" - cabalDepVersion = useCabalVersion options' - options'' = options'{usePackageIndex = Just index} - case PackageIndex.lookupDependency index cabalDepName cabalDepVersion of - [] -> - dieWithException verbosity $ InstalledCabalVersion (packageName pkg) (useCabalVersion options) - pkgs -> - let ipkginfo = fromMaybe err $ safeHead . snd . bestVersion fst $ pkgs - err = error "Distribution.Client.installedCabalVersion: empty version list" - in return - ( packageVersion ipkginfo - , Just . IPI.installedComponentId $ ipkginfo - , options'' - ) - - bestVersion :: (a -> Version) -> [a] -> a - bestVersion f = firstMaximumBy (comparing (preference . f)) + copyFileVerbose verbosity customSetupHooks (i (setupHooks options)) + rewriteFileLBS verbosity (i (setupHs options)) (buildTypeScript Hooks cabalLibVersion) + rewriteFileLBS verbosity (i (hooksHs options)) hooksExeScript + updateSetupScript cabalLibVersion bt' = + rewriteFileLBS verbosity (i (setupHs options)) (buildTypeScript bt' cabalLibVersion) + +-- | The source code for a non-Custom 'Setup' executable. +buildTypeScript :: BuildType -> Version -> BS.ByteString +buildTypeScript bt cabalLibVersion = "{-# LANGUAGE NoImplicitPrelude #-}\n" <> case bt of + Simple -> "import Distribution.Simple; main = defaultMain\n" + Configure + | cabalLibVersion >= mkVersion [3, 13, 0] + -> "import Distribution.Simple; main = defaultMainWithSetupHooks autoconfSetupHooks\n" + | cabalLibVersion >= mkVersion [1, 3, 10] + -> "import Distribution.Simple; main = defaultMainWithHooks autoconfUserHooks\n" + | otherwise + -> "import Distribution.Simple; main = defaultMainWithHooks defaultUserHooks\n" + Make -> "import Distribution.Make; main = defaultMain\n" + Hooks + | cabalLibVersion >= mkVersion [3, 13, 0] + -> "import Distribution.Simple; import SetupHooks; main = defaultMainWithSetupHooks setupHooks\n" + | otherwise + -> error "buildTypeScript Hooks with Cabal < 3.13" + Custom -> error "buildTypeScript Custom" + +-- | The source code for an external hooks executable, using the 'hooks-exe' library. +hooksExeScript :: BS.ByteString +hooksExeScript = + "{-# LANGUAGE NoImplicitPrelude #-}\nimport Distribution.Client.SetupHooks.HooksExe (hooksMain); import SetupHooks; main = hooksMain setupHooks\n" + +installedCabalVersion + :: Verbosity + -> PackageDescription + -> BuildType + -> SetupScriptOptions + -> Compiler + -> ProgramDb + -> IO + ( Version + , Maybe InstalledPackageId + , SetupScriptOptions + ) +installedCabalVersion _verbosity pkg bt options' _ _ + | packageName pkg == mkPackageName "Cabal" + && bt == Custom = + return (packageVersion pkg, Nothing, options') +installedCabalVersion verbosity pkg _bt options' compiler progdb = do + index <- maybeGetInstalledPackages verbosity options' compiler progdb + let cabalDepName = mkPackageName "Cabal" + cabalDepVersion = useCabalVersion options' + options'' = options'{usePackageIndex = Just index} + case PackageIndex.lookupDependency index cabalDepName cabalDepVersion of + [] -> + dieWithException verbosity $ InstalledCabalVersion (packageName pkg) (useCabalVersion options') + pkgs -> + let ipkginfo = fromMaybe err $ safeHead . snd . bestVersion fst $ pkgs + err = error "Distribution.Client.installedCabalVersion: empty version list" + in return + ( packageVersion ipkginfo + , Just . IPI.installedComponentId $ ipkginfo + , options'' + ) + +bestVersion :: (a -> Version) -> [a] -> a +bestVersion f = firstMaximumBy (comparing (preference . f)) + where + -- Like maximumBy, but picks the first maximum element instead of the + -- last. In general, we expect the preferred version to go first in the + -- list. For the default case, this has the effect of choosing the version + -- installed in the user package DB instead of the global one. See #1463. + -- + -- Note: firstMaximumBy could be written as just + -- `maximumBy cmp . reverse`, but the problem is that the behaviour of + -- maximumBy is not fully specified in the case when there is not a single + -- greatest element. + firstMaximumBy :: (a -> a -> Ordering) -> [a] -> a + firstMaximumBy _ [] = + error "Distribution.Client.firstMaximumBy: empty list" + firstMaximumBy cmp xs = foldl1' maxBy xs where - -- Like maximumBy, but picks the first maximum element instead of the - -- last. In general, we expect the preferred version to go first in the - -- list. For the default case, this has the effect of choosing the version - -- installed in the user package DB instead of the global one. See #1463. - -- - -- Note: firstMaximumBy could be written as just - -- `maximumBy cmp . reverse`, but the problem is that the behaviour of - -- maximumBy is not fully specified in the case when there is not a single - -- greatest element. - firstMaximumBy :: (a -> a -> Ordering) -> [a] -> a - firstMaximumBy _ [] = - error "Distribution.Client.firstMaximumBy: empty list" - firstMaximumBy cmp xs = foldl1' maxBy xs - where - maxBy x y = case cmp x y of GT -> x; EQ -> x; LT -> y - - preference version = - ( sameVersion - , sameMajorVersion - , stableVersion - , latestVersion - ) - where - sameVersion = version == cabalVersion - sameMajorVersion = majorVersion version == majorVersion cabalVersion - majorVersion = take 2 . versionNumbers - stableVersion = case versionNumbers version of - (_ : x : _) -> even x - _ -> False - latestVersion = version - - configureCompiler - :: SetupScriptOptions - -> IO (Compiler, ProgramDb, SetupScriptOptions) - configureCompiler options' = do - (comp, progdb) <- case useCompiler options' of - Just comp -> return (comp, useProgramDb options') - Nothing -> do - (comp, _, progdb) <- - configCompilerEx - (Just GHC) - Nothing - Nothing - (useProgramDb options') - verbosity - return (comp, progdb) - -- Whenever we need to call configureCompiler, we also need to access the - -- package index, so let's cache it in SetupScriptOptions. - index <- maybeGetInstalledPackages options' comp progdb - return - ( comp - , progdb - , options' - { useCompiler = Just comp - , usePackageIndex = Just index - , useProgramDb = progdb - } - ) - - -- \| Path to the setup exe cache directory and path to the cached setup - -- executable. - cachedSetupDirAndProg - :: SetupScriptOptions - -> Version - -> IO (FilePath, FilePath) - cachedSetupDirAndProg options' cabalLibVersion = do - cacheDir <- defaultCacheDir - let setupCacheDir = cacheDir "setup-exe-cache" - cachedSetupProgFile = - setupCacheDir - ( "setup-" - ++ buildTypeString - ++ "-" - ++ cabalVersionString - ++ "-" - ++ platformString - ++ "-" - ++ compilerVersionString - ) - <.> exeExtension buildPlatform - return (setupCacheDir, cachedSetupProgFile) + maxBy x y = case cmp x y of GT -> x; EQ -> x; LT -> y + + preference version = + ( sameVersion + , sameMajorVersion + , stableVersion + , latestVersion + ) where - buildTypeString = show bt - cabalVersionString = "Cabal-" ++ prettyShow cabalLibVersion - compilerVersionString = - prettyShow $ - maybe buildCompilerId compilerId $ - useCompiler options' - platformString = prettyShow platform - - -- \| Look up the setup executable in the cache; update the cache if the setup - -- executable is not found. - getCachedSetupExecutable - :: SetupScriptOptions - -> Version - -> Maybe InstalledPackageId - -> IO FilePath - getCachedSetupExecutable - options' - cabalLibVersion - maybeCabalLibInstalledPkgId = do - (setupCacheDir, cachedSetupProgFile) <- - cachedSetupDirAndProg options' cabalLibVersion - cachedSetupExists <- doesFileExist cachedSetupProgFile - if cachedSetupExists + sameVersion = version == cabalVersion + sameMajorVersion = majorVersion version == majorVersion cabalVersion + majorVersion = take 2 . versionNumbers + stableVersion = case versionNumbers version of + (_ : x : _) -> even x + _ -> False + latestVersion = version + +configureCompiler + :: Verbosity + -> SetupScriptOptions + -> IO (Compiler, ProgramDb, SetupScriptOptions) +configureCompiler verbosity options' = do + (comp, progdb) <- case useCompiler options' of + Just comp -> return (comp, useProgramDb options') + Nothing -> do + (comp, _, progdb) <- + configCompilerEx + (Just GHC) + Nothing + Nothing + (useProgramDb options') + verbosity + return (comp, progdb) + -- Whenever we need to call configureCompiler, we also need to access the + -- package index, so let's cache it in SetupScriptOptions. + index <- maybeGetInstalledPackages verbosity options' comp progdb + return + ( comp + , progdb + , options' + { useCompiler = Just comp + , usePackageIndex = Just index + , useProgramDb = progdb + } + ) + +maybeGetInstalledPackages + :: Verbosity + -> SetupScriptOptions + -> Compiler + -> ProgramDb + -> IO InstalledPackageIndex +maybeGetInstalledPackages verbosity options' comp progdb = + case usePackageIndex options' of + Just index -> return index + Nothing -> + getInstalledPackages + verbosity + comp + (usePackageDB options') + progdb + +-- | Path to the setup exe cache directory and path to the cached setup +-- executable. +cachedSetupDirAndProg + :: Platform + -> BuildType + -> SetupScriptOptions + -> Version + -> IO (FilePath, FilePath) +cachedSetupDirAndProg platform bt options' cabalLibVersion = do + cacheDir <- defaultCacheDir + let setupCacheDir = cacheDir "setup-exe-cache" + cachedSetupProgFile = + setupCacheDir + ( "setup-" + ++ buildTypeString + ++ "-" + ++ cabalVersionString + ++ "-" + ++ platformString + ++ "-" + ++ compilerVersionString + ) + <.> exeExtension buildPlatform + return (setupCacheDir, cachedSetupProgFile) + where + buildTypeString = show bt + cabalVersionString = "Cabal-" ++ prettyShow cabalLibVersion + compilerVersionString = + prettyShow $ + maybe buildCompilerId compilerId $ + useCompiler options' + platformString = prettyShow platform + +-- | Look up the executable in the cache; update the cache if the executable +-- is not found. +getCachedSetupExecutable + :: Verbosity + -> Platform + -> PackageIdentifier + -> BuildType + -> SetupScriptOptions + -> Version + -> Maybe InstalledPackageId + -> IO FilePath +getCachedSetupExecutable + verbosity + platform + pkgId + bt + options' + cabalLibVersion + maybeCabalLibInstalledPkgId = do + (setupCacheDir, cachedSetupProgFile) <- + cachedSetupDirAndProg platform bt options' cabalLibVersion + cachedSetupExists <- doesFileExist cachedSetupProgFile + if cachedSetupExists + then + debug verbosity $ + "Found cached setup executable: " ++ cachedSetupProgFile + else criticalSection' $ do + -- The cache may have been populated while we were waiting. + cachedSetupExists' <- doesFileExist cachedSetupProgFile + if cachedSetupExists' then debug verbosity $ "Found cached setup executable: " ++ cachedSetupProgFile - else criticalSection' $ do - -- The cache may have been populated while we were waiting. - cachedSetupExists' <- doesFileExist cachedSetupProgFile - if cachedSetupExists' - then - debug verbosity $ - "Found cached setup executable: " ++ cachedSetupProgFile - else do - debug verbosity $ "Setup executable not found in the cache." - src <- - compileSetupExecutable - options' - cabalLibVersion - maybeCabalLibInstalledPkgId - True - createDirectoryIfMissingVerbose verbosity True setupCacheDir - installExecutableFile verbosity src cachedSetupProgFile - -- Do not strip if we're using GHCJS, since the result may be a script - when (maybe True ((/= GHCJS) . compilerFlavor) $ useCompiler options') $ do - -- Add the relevant PATH overrides for the package to the - -- program database. - setupProgDb - <- prependProgramSearchPath verbosity - (useExtraPathEnv options) - (useExtraEnvOverrides options) - (useProgramDb options') - >>= configureAllKnownPrograms verbosity - Strip.stripExe - verbosity - platform - setupProgDb - cachedSetupProgFile - return cachedSetupProgFile - where - criticalSection' = maybe id criticalSection $ setupCacheLock options' - - -- \| If the Setup.hs is out of date wrt the executable then recompile it. - -- Currently this is GHC/GHCJS only. It should really be generalised. - compileSetupExecutable - :: SetupScriptOptions - -> Version - -> Maybe ComponentId - -> Bool - -> IO FilePath - compileSetupExecutable - options' - cabalLibVersion - maybeCabalLibInstalledPkgId - forceCompile = do - setupHsNewer <- i setupHs `moreRecentFile` i setupProgFile - cabalVersionNewer <- i setupVersionFile `moreRecentFile` i setupProgFile - let outOfDate = setupHsNewer || cabalVersionNewer - when (outOfDate || forceCompile) $ do - debug verbosity "Setup executable needs to be updated, compiling..." - (compiler, progdb, options'') <- configureCompiler options' - pkgDbs <- traverse (traverse (makeRelativeToDirS mbWorkDir)) (coercePackageDBStack (usePackageDB options'')) - let cabalPkgid = PackageIdentifier (mkPackageName "Cabal") cabalLibVersion - (program, extraOpts) = - case compilerFlavor compiler of - GHCJS -> (ghcjsProgram, ["-build-runner"]) - _ -> (ghcProgram, ["-threaded"]) - cabalDep = - maybe - [] - (\ipkgid -> [(ipkgid, cabalPkgid)]) - maybeCabalLibInstalledPkgId - - -- With 'useDependenciesExclusive' and Custom build type, - -- we enforce the deps specified, so only the given ones can be used. - -- Otherwise we add on a dep on the Cabal library - -- (unless 'useDependencies' already contains one). - selectedDeps - | (useDependenciesExclusive options' && (bt /= Hooks)) - -- NB: to compile build-type: Hooks packages, we need Cabal - -- in order to compile @main = defaultMainWithSetupHooks setupHooks@. - || any (isCabalPkgId . snd) (useDependencies options') - = useDependencies options' - | otherwise = - useDependencies options' ++ cabalDep - addRenaming (ipid, _) = - -- Assert 'DefUnitId' invariant - ( Backpack.DefiniteUnitId (unsafeMkDefUnitId (newSimpleUnitId ipid)) - , defaultRenaming - ) - cppMacrosFile = setupDir Cabal.Path. makeRelativePathEx "setup_macros.h" - ghcOptions = - mempty - { -- Respect -v0, but don't crank up verbosity on GHC if - -- Cabal verbosity is requested. For that, use - -- --ghc-option=-v instead! - ghcOptVerbosity = Flag (min verbosity normal) - , ghcOptMode = Flag GhcModeMake - , ghcOptInputFiles = toNubListR [setupHs] - , ghcOptOutputFile = Flag $ setupProgFile - , ghcOptObjDir = Flag $ setupDir - , ghcOptHiDir = Flag $ setupDir - , ghcOptSourcePathClear = Flag True - , ghcOptSourcePath = case bt of - Custom -> toNubListR [sameDirectory] - Hooks -> toNubListR [sameDirectory] - _ -> mempty - , ghcOptPackageDBs = pkgDbs - , ghcOptHideAllPackages = Flag (useDependenciesExclusive options') - , ghcOptCabal = Flag (useDependenciesExclusive options') - , ghcOptPackages = toNubListR $ map addRenaming selectedDeps - -- With 'useVersionMacros', use a version CPP macros .h file. - , ghcOptCppIncludes = - toNubListR - [ cppMacrosFile - | useVersionMacros options' - ] - , ghcOptExtra = extraOpts - , ghcOptExtensions = toNubListR $ - if bt == Custom || any (isBasePkgId . snd) selectedDeps - then [] - else [ Simple.DisableExtension Simple.ImplicitPrelude ] - -- Pass -WNoImplicitPrelude to avoid depending on base - -- when compiling a Simple Setup.hs file. - , ghcOptExtensionMap = Map.fromList . Simple.compilerExtensions $ compiler - } - let ghcCmdLine = renderGhcOptions compiler platform ghcOptions - when (useVersionMacros options') $ - rewriteFileEx verbosity (i cppMacrosFile) $ - generatePackageVersionMacros (pkgVersion $ package pkg) (map snd selectedDeps) - case useLoggingHandle options of - Nothing -> runDbProgramCwd verbosity mbWorkDir program progdb ghcCmdLine - -- If build logging is enabled, redirect compiler output to - -- the log file. - Just logHandle -> do - output <- - getDbProgramOutputCwd - verbosity - mbWorkDir - program - progdb - ghcCmdLine - hPutStr logHandle output - return $ i setupProgFile + else do + debug verbosity $ "Setup executable not found in the cache." + src <- + compileExe + verbosity + platform + pkgId + bt + WantSetup + options' + cabalLibVersion + maybeCabalLibInstalledPkgId + True + createDirectoryIfMissingVerbose verbosity True setupCacheDir + installExecutableFile verbosity src cachedSetupProgFile + -- Do not strip if we're using GHCJS, since the result may be a script + when (maybe True ((/= GHCJS) . compilerFlavor) $ useCompiler options') $ do + -- Add the relevant PATH overrides for the package to the + -- program database. + setupProgDb + <- prependProgramSearchPath verbosity + (useExtraPathEnv options') + (useExtraEnvOverrides options') + (useProgramDb options') + >>= configureAllKnownPrograms verbosity + Strip.stripExe + verbosity + platform + setupProgDb + cachedSetupProgFile + return cachedSetupProgFile + where + criticalSection' = maybe id criticalSection $ setupCacheLock options' + +-- | If the Setup.hs is out of date wrt the executable then recompile it. +-- Currently this is GHC/GHCJS only. It should really be generalised. +compileExe + :: Verbosity + -> Platform + -> PackageIdentifier + -> BuildType + -> WantedExternalExe exe + -> SetupScriptOptions + -> Version + -> Maybe ComponentId + -> Bool + -> IO FilePath +compileExe verbosity platform pkgId bt wantedMeth opts ver mbCompId forceCompile = + case wantedMeth of + WantHooks -> + compileHooksScript verbosity platform pkgId opts ver mbCompId forceCompile + WantSetup -> + compileSetupScript verbosity platform pkgId bt opts ver mbCompId forceCompile + +compileSetupScript + :: Verbosity + -> Platform + -> PackageIdentifier + -> BuildType + -> SetupScriptOptions + -> Version + -> Maybe ComponentId + -> Bool + -> IO FilePath +compileSetupScript verbosity platform pkgId bt opts ver mbCompId forceCompile = + compileSetupX "Setup" + [setupHs opts] (setupProgFile opts) + verbosity platform pkgId bt opts ver mbCompId forceCompile + +compileHooksScript + :: Verbosity + -> Platform + -> PackageIdentifier + -> SetupScriptOptions + -> Version + -> Maybe ComponentId + -> Bool + -> IO FilePath +compileHooksScript verbosity platform pkgId opts ver mbCompId forceCompile = + compileSetupX "SetupHooks" + [setupHooks opts, hooksHs opts] (hooksProgFile opts) + verbosity platform pkgId Hooks opts ver mbCompId forceCompile + +setupDir :: SetupScriptOptions -> SymbolicPath Pkg (Dir setup) +setupDir opts = useDistPref opts Cabal.Path. makeRelativePathEx "setup" +setupVersionFile :: SetupScriptOptions -> SymbolicPath Pkg File +setupVersionFile opts = setupDir opts Cabal.Path. makeRelativePathEx ( "setup" <.> "version" ) +setupHs, hooksHs, setupHooks, setupProgFile, hooksProgFile :: SetupScriptOptions -> SymbolicPath Pkg File +setupHs opts = setupDir opts Cabal.Path. makeRelativePathEx ( "setup" <.> "hs" ) +hooksHs opts = setupDir opts Cabal.Path. makeRelativePathEx ( "hooks" <.> "hs" ) +setupHooks opts = setupDir opts Cabal.Path. makeRelativePathEx ( "SetupHooks" <.> "hs" ) +setupProgFile opts = setupDir opts Cabal.Path. makeRelativePathEx ( "setup" <.> exeExtension buildPlatform ) +hooksProgFile opts = setupDir opts Cabal.Path. makeRelativePathEx ( "hooks" <.> exeExtension buildPlatform ) + +compileSetupX + :: String + -> [SymbolicPath Pkg File] -- input files + -> SymbolicPath Pkg File -- output file + -> Verbosity + -> Platform + -> PackageIdentifier + -> BuildType + -> SetupScriptOptions + -> Version + -> Maybe ComponentId + -> Bool + -> IO FilePath +compileSetupX + what + inPaths outPath + verbosity + platform + pkgId + bt + options' + cabalLibVersion + maybeCabalLibInstalledPkgId + forceCompile = do + setupXHsNewer <- fmap or $ sequenceA $ fmap ( \ inPath -> i inPath `moreRecentFile` i outPath ) inPaths + cabalVersionNewer <- i (setupVersionFile options') `moreRecentFile` i (setupProgFile options') + let outOfDate = setupXHsNewer || cabalVersionNewer + when (outOfDate || forceCompile) $ do + debug verbosity $ what ++ " executable needs to be updated, compiling..." + (compiler, progdb, options'') <- configureCompiler verbosity options' + pkgDbs <- traverse (traverse (makeRelativeToDirS mbWorkDir)) (coercePackageDBStack (usePackageDB options'')) + let cabalPkgid = PackageIdentifier (mkPackageName "Cabal") cabalLibVersion + (program, extraOpts) = + case compilerFlavor compiler of + GHCJS -> (ghcjsProgram, ["-build-runner"]) + _ -> (ghcProgram, ["-threaded"]) + cabalDep = + maybe + [] + (\ipkgid -> [(ipkgid, cabalPkgid)]) + maybeCabalLibInstalledPkgId + + -- With 'useDependenciesExclusive' and Custom build type, + -- we enforce the deps specified, so only the given ones can be used. + -- Otherwise we add on a dep on the Cabal library + -- (unless 'useDependencies' already contains one). + selectedDeps + | (useDependenciesExclusive options' && (bt /= Hooks)) + -- NB: to compile build-type: Hooks packages, we need Cabal + -- in order to compile @main = defaultMainWithSetupHooks setupHooks@. + || any (isCabalPkgId . snd) (useDependencies options') + = useDependencies options' + | otherwise = + useDependencies options' ++ cabalDep + addRenaming (ipid, _) = + -- Assert 'DefUnitId' invariant + ( Backpack.DefiniteUnitId (unsafeMkDefUnitId (newSimpleUnitId ipid)) + , defaultRenaming + ) + cppMacrosFile = setupDir options' Cabal.Path. makeRelativePathEx "setup_macros.h" + ghcOptions = + mempty + { -- Respect -v0, but don't crank up verbosity on GHC if + -- Cabal verbosity is requested. For that, use + -- --ghc-option=-v instead! + ghcOptVerbosity = Flag (min verbosity normal) + , ghcOptMode = Flag GhcModeMake + , ghcOptInputFiles = toNubListR inPaths + , ghcOptOutputFile = Flag outPath + , ghcOptObjDir = Flag (setupDir options') + , ghcOptHiDir = Flag (setupDir options') + , ghcOptSourcePathClear = Flag True + , ghcOptSourcePath = case bt of + Custom -> toNubListR [sameDirectory] + Hooks -> toNubListR [sameDirectory] + _ -> mempty + , ghcOptPackageDBs = pkgDbs + , ghcOptHideAllPackages = Flag (useDependenciesExclusive options') + , ghcOptCabal = Flag (useDependenciesExclusive options') + , ghcOptPackages = toNubListR $ map addRenaming selectedDeps + -- With 'useVersionMacros', use a version CPP macros .h file. + , ghcOptCppIncludes = + toNubListR + [ cppMacrosFile + | useVersionMacros options' + ] + , ghcOptExtra = extraOpts + , ghcOptExtensions = toNubListR $ + if bt == Custom || any (isBasePkgId . snd) selectedDeps + then [] + else [ Simple.DisableExtension Simple.ImplicitPrelude ] + -- Pass -WNoImplicitPrelude to avoid depending on base + -- when compiling a Simple Setup.hs file. + , ghcOptExtensionMap = Map.fromList . Simple.compilerExtensions $ compiler + } + let ghcCmdLine = renderGhcOptions compiler platform ghcOptions + when (useVersionMacros options') $ + rewriteFileEx verbosity (i cppMacrosFile) $ + generatePackageVersionMacros (pkgVersion pkgId) (map snd selectedDeps) + case useLoggingHandle options' of + Nothing -> runDbProgramCwd verbosity mbWorkDir program progdb ghcCmdLine + -- If build logging is enabled, redirect compiler output to + -- the log file. + Just logHandle -> do + output <- + getDbProgramOutputCwd + verbosity + mbWorkDir + program + progdb + ghcCmdLine + hPutStr logHandle output + return $ i outPath + where + mbWorkDir = useWorkingDir options' + -- See Note [Symbolic paths] in Distribution.Utils.Path + i :: SymbolicPathX allowAbs Pkg to -> FilePath + i = interpretSymbolicPath mbWorkDir isCabalPkgId, isBasePkgId :: PackageIdentifier -> Bool isCabalPkgId (PackageIdentifier pname _) = pname == mkPackageName "Cabal" diff --git a/cabal-install/src/Distribution/Client/SourceFiles.hs b/cabal-install/src/Distribution/Client/SourceFiles.hs index 1166f333f3c..9e97af9a50b 100644 --- a/cabal-install/src/Distribution/Client/SourceFiles.hs +++ b/cabal-install/src/Distribution/Client/SourceFiles.hs @@ -81,7 +81,9 @@ needComponent pkg_descr comp = CBench bench -> needBenchmark pkg_descr bench needSetup :: Rebuild () -needSetup = findFirstFileMonitored id ["Setup.hs", "Setup.lhs"] >> return () +needSetup = do + void $ findFirstFileMonitored id ["Setup.hs", "Setup.lhs"] + void $ findFirstFileMonitored id ["SetupHooks.hs", "SetupHooks.lhs"] needLibrary :: PackageDescription -> Library -> Rebuild () needLibrary diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs index 69c4134f142..ae8aeb1f6ea 100644 --- a/cabal-install/tests/IntegrationTests2.hs +++ b/cabal-install/tests/IntegrationTests2.hs @@ -2128,9 +2128,21 @@ getProgArgs :: [ElaboratedConfiguredPackage] -> String -> Maybe [String] getProgArgs [] _ = Nothing getProgArgs (elab : pkgs) name | pkgName (elabPkgSourceId elab) == mkPackageName name = - Map.lookup "ghc" (elabProgramArgs elab) + removeHideAllPackages $ Map.lookup "ghc" (elabProgramArgs elab) | otherwise = getProgArgs pkgs name + where + removeHideAllPackages mbArgs = + -- Filter out "-hide-all-packages", as we pass that by default + -- to GHC invocations in order to avoid it picking up environment files. + -- See https://github.com/haskell/cabal/issues/4010 + case filter (/= "-hide-all-packages") <$> mbArgs of + Just args' + | null args' -> + Nothing + | otherwise -> + Just args' + Nothing -> Nothing --------------------------------- -- Test utils to plan and build diff --git a/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.out b/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.out index 8224b23be46..e69de29bb2d 100644 --- a/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.out +++ b/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.out @@ -1,12 +0,0 @@ -# cabal v2-build -Configuration is affected by the following files: -- cabal.project -Resolving dependencies... -Build profile: -w ghc- -O1 -In order, the following will be built: - - pkg-0 (lib) (first run) -Configuring library for pkg-0... -Error: [Cabal-5559] -[autogen-guard] To use the autogenerated module PackageInfo_* you need to specify `cabal-version: 3.12` or higher. -Error: [Cabal-7125] -Failed to build pkg-0-inplace. The failure occurred during the configure step. diff --git a/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.test.hs b/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.test.hs index 0711dcccfe1..5bdb7ead190 100644 --- a/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.test.hs +++ b/cabal-testsuite/PackageTests/BuildAutogenPackageGuard/cabal.test.hs @@ -4,5 +4,6 @@ import Test.Cabal.Prelude -- build failure. main = cabalTest $ do withProjectFile "cabal.project" $ do - fails $ cabal "v2-build" ["pkg"] + res <- recordMode DoNotRecord $ fails $ cabal' "v2-build" ["pkg"] + assertOutputContains "[autogen-guard]" res diff --git a/cabal-testsuite/PackageTests/HaddockArtifacts/a.cabal b/cabal-testsuite/PackageTests/HaddockArtifacts/a.cabal new file mode 100644 index 00000000000..f00f3c6011c --- /dev/null +++ b/cabal-testsuite/PackageTests/HaddockArtifacts/a.cabal @@ -0,0 +1,10 @@ +name: a +version: 0.1.0.0 +build-type: Simple +cabal-version: >= 1.10 + +library + exposed-modules: MyLib + build-depends: base + hs-source-dirs: src + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/HaddockArtifacts/setup.test.hs b/cabal-testsuite/PackageTests/HaddockArtifacts/setup.test.hs new file mode 100644 index 00000000000..4b3beb7b001 --- /dev/null +++ b/cabal-testsuite/PackageTests/HaddockArtifacts/setup.test.hs @@ -0,0 +1,11 @@ +import Test.Cabal.Prelude + +import System.Directory +import System.FilePath + +main = setupTest . recordMode DoNotRecord $ do + workDir <- fmap testWorkDir getTestEnv + setup "configure" [] + setup "build" [] + liftIO $ removeDirectoryRecursive $ workDir "work" "dist" "build" + setup "haddock" [] diff --git a/cabal-testsuite/PackageTests/HaddockArtifacts/src/MyLib.hs b/cabal-testsuite/PackageTests/HaddockArtifacts/src/MyLib.hs new file mode 100644 index 00000000000..4ec2b88a213 --- /dev/null +++ b/cabal-testsuite/PackageTests/HaddockArtifacts/src/MyLib.hs @@ -0,0 +1,8 @@ +module MyLib where + +-- | Some docs +foo :: Int +foo = 3 + +-- | More docs +data A = A Int -- ^ field diff --git a/cabal-testsuite/PackageTests/HsBootHack/dep/dep.cabal b/cabal-testsuite/PackageTests/HsBootHack/dep/dep.cabal new file mode 100644 index 00000000000..26d4d345e7e --- /dev/null +++ b/cabal-testsuite/PackageTests/HsBootHack/dep/dep.cabal @@ -0,0 +1,15 @@ +cabal-version: 3.0 +name: dep +version: 0.1.0.0 +license: BSD-3-Clause +author: sheaf +maintainer: sheaf +category: Testing +build-type: Simple +description: Testing the preprocessor hs-boot hack + +library + hs-source-dirs: src + exposed-modules: DepA, DepB, DepC + build-depends: base + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepA.hs b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepA.hs new file mode 100644 index 00000000000..8c35f67bc65 --- /dev/null +++ b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepA.hs @@ -0,0 +1,5 @@ +module DepA where + +import {-# SOURCE #-} DepB + +data A = A B diff --git a/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepB.hs-boot b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepB.hs-boot new file mode 100644 index 00000000000..dc58e7a489f --- /dev/null +++ b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepB.hs-boot @@ -0,0 +1,3 @@ +module DepB where + +data B diff --git a/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepB.hsc b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepB.hsc new file mode 100644 index 00000000000..b1be18210cb --- /dev/null +++ b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepB.hsc @@ -0,0 +1,5 @@ +module DepB where + +import DepA + +data B = B A diff --git a/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepC.hs b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepC.hs new file mode 100644 index 00000000000..96bb37628cf --- /dev/null +++ b/cabal-testsuite/PackageTests/HsBootHack/dep/src/DepC.hs @@ -0,0 +1,5 @@ +module DepC where + +import DepB + +data C = C B diff --git a/cabal-testsuite/PackageTests/HsBootHack/setup.test.hs b/cabal-testsuite/PackageTests/HsBootHack/setup.test.hs new file mode 100644 index 00000000000..7a267e1ae53 --- /dev/null +++ b/cabal-testsuite/PackageTests/HsBootHack/setup.test.hs @@ -0,0 +1,6 @@ +import Test.Cabal.Prelude + +-- Test the hs-boot hack respects working directory +main = setupTest . recordMode DoNotRecord $ withDirectory "dep" $ do + void $ setup' "configure" [] + void $ setup' "build" [] diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.out b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.out index 5ebd9d91f5c..e69de29bb2d 100644 --- a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.out +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.out @@ -1,10 +0,0 @@ -# cabal v2-run -Warning: The package description file ./script.cabal has warnings: script.cabal:0:0: A package using 'cabal-version: >=1.10' must use section syntax. See the Cabal user guide for details. -Configuration is affected by the following files: -- cabal.project -Resolving dependencies... -Build profile: -w ghc- -O1 -In order, the following will be built: - - fake-package-0 (exe:script-script.lhs) (first run) -Configuring executable 'script-script.lhs' for fake-package-0... -Building executable 'script-script.lhs' for fake-package-0... diff --git a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs index 64c858e8d0d..e7ae8bfc4c6 100644 --- a/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs +++ b/cabal-testsuite/PackageTests/NewBuild/CmdRun/ScriptLiterate/cabal.test.hs @@ -1,5 +1,5 @@ import Test.Cabal.Prelude -main = cabalTest $ do +main = cabalTest $ recordMode DoNotRecord $ do res <- cabal' "v2-run" ["script.lhs"] assertOutputContains "Hello World" res diff --git a/cabal-testsuite/PackageTests/NoOSSupport/DynExe/cabal.out b/cabal-testsuite/PackageTests/NoOSSupport/DynExe/cabal.out index 53ccefe2347..e8998267995 100644 --- a/cabal-testsuite/PackageTests/NoOSSupport/DynExe/cabal.out +++ b/cabal-testsuite/PackageTests/NoOSSupport/DynExe/cabal.out @@ -1,12 +1,14 @@ -# cabal build -Resolving dependencies... -Build profile: -w ghc- -O1 -In order, the following will be built: - - aa-0.1.0.0 (exe:a) (first run) -Configuring executable 'a' for aa-0.1.0.0... -Warning: Executables will use dynamic linking, but a shared library is not -being built. Linking will fail if any executables depend on the library. -Error: [Cabal-3339] -Operating system: windows, does not support shared executables -Error: [Cabal-7125] -Failed to build aa-0.1.0.0-inplace-a. The failure occurred during the configure step. +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - aa-0.1.0.0 (exe:a) (first run) +Configuring executable 'a' for aa-0.1.0.0... +Error: [Cabal-7125] +Failed to build aa-0.1.0.0-inplace-a. The failure occurred during the configure step. The exception was: + -----BEGIN CABAL OUTPUT----- +Error: [Cabal-3339] +Operating system: windows, does not support shared executables +CallStack (from HasCallStack): + dieWithException, called at src/Distribution/Simple/Configure.hs:2867:5 in Cabal-3.15.0.0-inplace:Distribution.Simple.Configure + diff --git a/cabal-testsuite/PackageTests/NoOSSupport/RelocatableExe/cabal.out b/cabal-testsuite/PackageTests/NoOSSupport/RelocatableExe/cabal.out index f59d29e6b17..9e9a4c6c35f 100644 --- a/cabal-testsuite/PackageTests/NoOSSupport/RelocatableExe/cabal.out +++ b/cabal-testsuite/PackageTests/NoOSSupport/RelocatableExe/cabal.out @@ -1,10 +1,14 @@ -# cabal build -Resolving dependencies... -Build profile: -w ghc- -O1 -In order, the following will be built: - - aa-0.1.0.0 (exe:a) (first run) -Configuring executable 'a' for aa-0.1.0.0... -Error: [Cabal-3339] -Operating system: windows, does not support relocatable builds -Error: [Cabal-7125] -Failed to build aa-0.1.0.0-inplace-a. The failure occurred during the configure step. +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - aa-0.1.0.0 (exe:a) (first run) +Configuring executable 'a' for aa-0.1.0.0... +Error: [Cabal-7125] +Failed to build aa-0.1.0.0-inplace-a. The failure occurred during the configure step. The exception was: + -----BEGIN CABAL OUTPUT----- +Error: [Cabal-3339] +Operating system: windows, does not support relocatable builds +CallStack (from HasCallStack): + dieWithException, called at src/Distribution/Simple/Configure.hs:2893:9 in Cabal-3.15.0.0-inplace:Distribution.Simple.Configure + diff --git a/cabal-testsuite/PackageTests/Project/CoverageProject/cabal.out b/cabal-testsuite/PackageTests/Project/CoverageProject/cabal.out index 3b7c1d4b788..d1e7c397eae 100644 --- a/cabal-testsuite/PackageTests/Project/CoverageProject/cabal.out +++ b/cabal-testsuite/PackageTests/Project/CoverageProject/cabal.out @@ -7,11 +7,9 @@ In order, the following will be built: - pkg-a-0.1 (lib) (first run) - pkg-a-0.1 (test:testing) (first run) Configuring library for pkg-a-0.1... -Warning: [no-default-language] Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' must specify the 'default-language' field for each component (e.g. Haskell98 or Haskell2010). If a component uses different languages in different modules then list the other ones in the 'other-languages' field. Preprocessing library for pkg-a-0.1... Building library for pkg-a-0.1... Configuring test suite 'testing' for pkg-a-0.1... -Warning: [no-default-language] Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' must specify the 'default-language' field for each component (e.g. Haskell98 or Haskell2010). If a component uses different languages in different modules then list the other ones in the 'other-languages' field. Preprocessing test suite 'testing' for pkg-a-0.1... Building test suite 'testing' for pkg-a-0.1... Running 1 test suites... diff --git a/cabal-testsuite/PackageTests/Project/CoverageProject/pkg-a/pkg-a.cabal b/cabal-testsuite/PackageTests/Project/CoverageProject/pkg-a/pkg-a.cabal index 4a064d3389c..c6c44432201 100644 --- a/cabal-testsuite/PackageTests/Project/CoverageProject/pkg-a/pkg-a.cabal +++ b/cabal-testsuite/PackageTests/Project/CoverageProject/pkg-a/pkg-a.cabal @@ -18,6 +18,7 @@ library test-suite testing type: exitcode-stdio-1.0 build-depends: base, pkg-a + default-language: Haskell2010 main-is: Main.hs hs-source-dirs: test diff --git a/cabal-testsuite/PackageTests/Regression/T5318/install.out b/cabal-testsuite/PackageTests/Regression/T5318/install.out index 9c47fdc6b50..e69de29bb2d 100644 --- a/cabal-testsuite/PackageTests/Regression/T5318/install.out +++ b/cabal-testsuite/PackageTests/Regression/T5318/install.out @@ -1,8 +0,0 @@ -# cabal v1-install -Resolving dependencies... -Configuring empty-data-dir-0... -Preprocessing executable 'foo' for empty-data-dir-0... -Building executable 'foo' for empty-data-dir-0... -Installing executable foo in -Warning: The directory /install.dist/home/.cabal/bin is not in the system search path. -Completed empty-data-dir-0 diff --git a/cabal-testsuite/PackageTests/Regression/T5318/install.test.hs b/cabal-testsuite/PackageTests/Regression/T5318/install.test.hs index 6fd409c2704..3efaca5c05a 100644 --- a/cabal-testsuite/PackageTests/Regression/T5318/install.test.hs +++ b/cabal-testsuite/PackageTests/Regression/T5318/install.test.hs @@ -1,3 +1,3 @@ import Test.Cabal.Prelude -main = cabalTest $ +main = cabalTest $ recordMode DoNotRecord $ cabal "v1-install" [] diff --git a/cabal-testsuite/PackageTests/Regression/T6440/cabal.out b/cabal-testsuite/PackageTests/Regression/T6440/cabal.out index 8de48ba2a8d..f621b184ee1 100644 --- a/cabal-testsuite/PackageTests/Regression/T6440/cabal.out +++ b/cabal-testsuite/PackageTests/Regression/T6440/cabal.out @@ -8,15 +8,12 @@ In order, the following will be built: - cabal6440-0.1 (lib) (first run) - cabal6440-0.1 (test:tests) (first run) Configuring library 'intern6440' for cabal6440-0.1... -Warning: [no-default-language] Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' must specify the 'default-language' field for each component (e.g. Haskell98 or Haskell2010). If a component uses different languages in different modules then list the other ones in the 'other-languages' field. Preprocessing library 'intern6440' for cabal6440-0.1... Building library 'intern6440' for cabal6440-0.1... Configuring library for cabal6440-0.1... -Warning: [no-default-language] Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' must specify the 'default-language' field for each component (e.g. Haskell98 or Haskell2010). If a component uses different languages in different modules then list the other ones in the 'other-languages' field. Preprocessing library for cabal6440-0.1... Building library for cabal6440-0.1... Configuring test suite 'tests' for cabal6440-0.1... -Warning: [no-default-language] Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' must specify the 'default-language' field for each component (e.g. Haskell98 or Haskell2010). If a component uses different languages in different modules then list the other ones in the 'other-languages' field. Preprocessing test suite 'tests' for cabal6440-0.1... Building test suite 'tests' for cabal6440-0.1... Running 1 test suites... diff --git a/cabal-testsuite/PackageTests/Regression/T6440/cabal6440.cabal b/cabal-testsuite/PackageTests/Regression/T6440/cabal6440.cabal index 42192a71672..1af78b1545b 100644 --- a/cabal-testsuite/PackageTests/Regression/T6440/cabal6440.cabal +++ b/cabal-testsuite/PackageTests/Regression/T6440/cabal6440.cabal @@ -13,7 +13,7 @@ library intern6440 exposed-modules: Inn build-depends: base hs-source-dirs: srcint - + default-language: Haskell2010 test-suite tests main-is: Main.hs diff --git a/cabal-testsuite/PackageTests/Regression/T7234/Success/cabal.out b/cabal-testsuite/PackageTests/Regression/T7234/Success/cabal.out index 2598efd4917..75e26db7a4b 100644 --- a/cabal-testsuite/PackageTests/Regression/T7234/Success/cabal.out +++ b/cabal-testsuite/PackageTests/Regression/T7234/Success/cabal.out @@ -6,7 +6,6 @@ Resolving dependencies... Build profile: -w ghc- -O1 In order, the following will be built: - issue7234-0 (lib) (first run) -Warning: issue7234.cabal:14:3: The field "other-extensions" is available only since the Cabal specification version 1.10. Configuring library for issue7234-0... Preprocessing library for issue7234-0... Building library for issue7234-0... diff --git a/cabal-testsuite/PackageTests/Regression/T9640/cabal.out b/cabal-testsuite/PackageTests/Regression/T9640/cabal.out index a2b4d7da6f0..b345280fbf3 100644 --- a/cabal-testsuite/PackageTests/Regression/T9640/cabal.out +++ b/cabal-testsuite/PackageTests/Regression/T9640/cabal.out @@ -13,7 +13,6 @@ Configuring one-custom-0.1.0.0... Preprocessing library for one-custom-0.1.0.0... Building library for one-custom-0.1.0.0... Installing library in -Warning: depend-on-custom-with-exe.cabal:16:1: Ignoring trailing fields after sections: "ghc-options" Configuring library for depend-on-custom-with-exe-0.1.0.0... Preprocessing library for depend-on-custom-with-exe-0.1.0.0... Building library for depend-on-custom-with-exe-0.1.0.0... diff --git a/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/SetupHooks.hs b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/SetupHooks.hs new file mode 100644 index 00000000000..ab5e0c64ba6 --- /dev/null +++ b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/SetupHooks.hs @@ -0,0 +1,6 @@ +module SetupHooks where + +import Distribution.Simple.SetupHooks + +setupHooks = noSetupHooks + diff --git a/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/SetupHooksRecompilation.cabal b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/SetupHooksRecompilation.cabal new file mode 100644 index 00000000000..f469abdb9e2 --- /dev/null +++ b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/SetupHooksRecompilation.cabal @@ -0,0 +1,17 @@ +cabal-version: 3.14 +name: SetupHooksRecompilation +version: 0.1.0.0 +license: NONE +author: Rodrigo Mesquita +maintainer: rodrigo.m.mesquita@gmail.com +build-type: Hooks +extra-doc-files: CHANGELOG.md + +custom-setup + setup-depends: base, Cabal, Cabal-syntax, Cabal-hooks + +library + exposed-modules: MyLib + build-depends: base >= 4.12 && < 5.0 + hs-source-dirs: src + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/cabal.project b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/cabal.project new file mode 100644 index 00000000000..e6fdbadb439 --- /dev/null +++ b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/cabal.project @@ -0,0 +1 @@ +packages: . diff --git a/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/cabal.test.hs b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/cabal.test.hs new file mode 100644 index 00000000000..d91478dc30d --- /dev/null +++ b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/cabal.test.hs @@ -0,0 +1,17 @@ +import Test.Cabal.Prelude + +import System.Directory ( doesFileExist ) + +main = cabalTest $ do + env <- getTestEnv + case testPackageDbPath env of + Nothing -> skip "Cabal-hooks library unavailable." + Just _pkgdb -> recordMode DoNotRecord $ do + cabal "v2-build" [] + let setupHooksPath = testCurrentDir env "SetupHooks.hs" + setupHooksExists <- liftIO $ doesFileExist setupHooksPath + unless setupHooksExists $ + error "Broken test: tried to write to a SetupHooks.hs file that doesn't exist." + liftIO $ appendFile setupHooksPath "this should fail to compile!" + -- If this doesn't fail, it's because we didn't re-build. + fails $ cabal "v2-build" [] diff --git a/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/src/MyLib.hs b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/src/MyLib.hs new file mode 100644 index 00000000000..e657c4403f6 --- /dev/null +++ b/cabal-testsuite/PackageTests/SetupHooks/SetupHooksRecompilation/src/MyLib.hs @@ -0,0 +1,4 @@ +module MyLib (someFunc) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/cabal-testsuite/main/cabal-tests.hs b/cabal-testsuite/main/cabal-tests.hs index 05871ab7190..4ffc8eafd3e 100644 --- a/cabal-testsuite/main/cabal-tests.hs +++ b/cabal-testsuite/main/cabal-tests.hs @@ -159,7 +159,7 @@ buildCabalLibsProject projString verb mbGhc dir = do , "--project-file=" ++ dir "cabal.project-test" , "build" , "-w", programPath ghc - , "Cabal", "Cabal-syntax", "Cabal-hooks" + , "Cabal", "Cabal-syntax", "Cabal-hooks", "hooks-exe" ] ) { progInvokeCwd = Just dir }) -- Determine the path to the packagedb in the store for this ghc version @@ -192,7 +192,8 @@ buildCabalLibsSpecific ver verb mbGhc builddir_rel = do buildCabalLibsIntree :: String -> Verbosity -> Maybe FilePath -> FilePath -> IO [FilePath] buildCabalLibsIntree root verb mbGhc builddir_rel = do dir <- canonicalizePath (builddir_rel "intree") - buildCabalLibsProject ("packages: " ++ root "Cabal" ++ " " ++ root "Cabal-syntax" ++ " " ++ root "Cabal-hooks") verb mbGhc dir + let libs = [ "Cabal", "Cabal-syntax", "Cabal-hooks", "hooks-exe" ] + buildCabalLibsProject ("packages: " ++ unwords ( map ( root ) libs ) ) verb mbGhc dir main :: IO () main = do diff --git a/cabal.bootstrap.project b/cabal.bootstrap.project index efbc4d4258d..2abe26dc713 100644 --- a/cabal.bootstrap.project +++ b/cabal.bootstrap.project @@ -4,6 +4,7 @@ packages: , Cabal-hooks , cabal-install , cabal-install-solver + , hooks-exe -- Don't include tests or benchmarks for bootstrapping tests: False diff --git a/changelog.d/pr-10991.md b/changelog.d/pr-10991.md new file mode 100644 index 00000000000..19bb10d99ed --- /dev/null +++ b/changelog.d/pr-10991.md @@ -0,0 +1,15 @@ +--- +synopsis: "Take --working-dir into account in runPreProcessorWithHsBootHack" +packages: [Cabal] +prs: 10991 +issues: [11000] +--- + +The preprocessor hs-boot hack handles the situation in which a file to be +preprocessed is supplied alongside an hs-boot file, e.g. Foo.x and Foo.hs-boot +are given together. + +This code now respects the --working-dir Cabal global argument. This fixes +build failures of the form: + + attempting to use module `Foo` (Foo.hs-boot) which is not loaded diff --git a/changelog.d/pr-10992.md b/changelog.d/pr-10992.md new file mode 100644 index 00000000000..de3a89df3bf --- /dev/null +++ b/changelog.d/pr-10992.md @@ -0,0 +1,11 @@ +--- +synopsis: "Haddock: don't try to copy build dir if it doesn't exist" +packages: [Cabal] +prs: 10992 +issues: [11001] +--- + +This small patch fixes a little oversight in 'reusingGHCCompilationArtifacts', +which would unconditionally attempt to copy over the GHC build artifacts to be +re-used by Haddock, even when those artifacts did not exist (which caused +an error). diff --git a/hooks-exe/Setup.hs b/hooks-exe/Setup.hs new file mode 100644 index 00000000000..021805cb81a --- /dev/null +++ b/hooks-exe/Setup.hs @@ -0,0 +1,6 @@ +module Main where + +import Distribution.Simple + +main :: IO () +main = defaultMain diff --git a/hooks-exe/changelog.md b/hooks-exe/changelog.md new file mode 100644 index 00000000000..0248669336a --- /dev/null +++ b/hooks-exe/changelog.md @@ -0,0 +1,6 @@ +# Changelog for `Cabal-hooks` + +## 0.1 – January 2024 + + * Initial release of `Hooks` integration for `cabal-install`. + diff --git a/hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe.hs b/hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe.hs new file mode 100644 index 00000000000..f6425a1cfc0 --- /dev/null +++ b/hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe.hs @@ -0,0 +1,224 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} + +{- HLINT ignore "Use curry" -} + +module Distribution.Client.SetupHooks.CallHooksExe + ( callHooksExe + , externalSetupHooks + , externalSetupHooksABI + , buildTypeSetupHooks + , buildTypePreBuildHooks + , runExternalPreBuildRules + , hooksProgFilePath + ) where + +-- base +import GHC.Stack + +-- bytestring +import Data.ByteString.Lazy as LBS + ( hGetContents + , hPut + , null + ) + +-- process +import qualified System.Process as P +import System.Process.CommunicationHandle + ( readCreateProcessWithExitCodeCommunicationHandle ) + +-- filepath +import System.FilePath + ( (), (<.>) ) + +-- Cabal +import Distribution.Compat.Prelude +import qualified Distribution.Compat.Binary as Binary +import Distribution.Simple + ( autoconfSetupHooks ) +import Distribution.Simple.BuildPaths + ( exeExtension ) +import Distribution.Simple.SetupHooks.Internal +import Distribution.Simple.SetupHooks.Rule +import Distribution.Simple.Utils + ( dieWithException ) +import Distribution.System + ( buildPlatform ) +import Distribution.Types.BuildType + ( BuildType(..) ) +import Distribution.Utils.Path + ( CWD + , Dist + , Pkg + , SymbolicPath + , FileOrDir(..) + , interpretSymbolicPath + ) +import qualified Distribution.Verbosity as Verbosity + +-- hooks-cli +import Distribution.Client.SetupHooks.CallHooksExe.Errors +import Distribution.Client.SetupHooks.Version + ( HooksVersion ) + +-------------------------------------------------------------------------------- + +type HookIO inputs outputs = + ( HasCallStack + , Typeable inputs, Typeable outputs + , Binary inputs, Binary outputs + ) + +-- | Call an external hooks executable in order to execute a Cabal Setup hook. +callHooksExe + :: forall inputs outputs + . HookIO inputs outputs + => FilePath -- ^ path to hooks executable + -> String -- ^ name of the hook to run + -> inputs -- ^ argument to the hook + -> IO outputs +callHooksExe hooksExe hookName input = do + (ex, output) <- + -- The arguments to the external hooks executable are: + -- + -- 1. Input handle, from which the hooks executable receives its input. + -- 2. Output handle, to which the hooks executable writes its output. + -- 3. The hook type to run. + -- + -- The hooks executable will read input from the input handle, decode it, + -- run the necessary hook, producing a result which it encodes and writes + -- to the output handle. + readCreateProcessWithExitCodeCommunicationHandle + ( \(theyRead, theyWrite) -> P.proc hooksExe [show theyRead, show theyWrite, hookName] ) + ( \ hWeRead -> hGetContents hWeRead ) + ( \ hWeWrite -> do + let i = Binary.encode input + unless (LBS.null i) $ + hPut hWeWrite i + ) + case ex of + ExitFailure exitCode -> + dieWithException Verbosity.normal $ + HookFailed hookName $ + HookException exitCode + ExitSuccess -> do + let mbOutput = Binary.decodeOrFail output + case mbOutput of + Left (_, offset, err) -> do + dieWithException Verbosity.normal $ + HookFailed hookName $ + CouldNotDecodeOutput output offset err + Right (_, _, res) -> return res + +-- | Construct a 'SetupHooks' that runs the hooks of the external hooks executable +-- at the given path through the CLI. +-- +-- This should only be used at the final step of compiling a package, when we +-- have all the hooks in hand. The SetupHooks that are returned by this function +-- cannot be combined with any other SetupHooks; they must directly be used to +-- build the package. +externalSetupHooks :: FilePath -> SetupHooks +externalSetupHooks hooksExe = + SetupHooks + { configureHooks = + ConfigureHooks + { preConfPackageHook = Just $ hook "preConfPackage" + , postConfPackageHook = Just $ hook "postConfPackage" + , preConfComponentHook = Just $ hook "preConfComponent" + } + , buildHooks = + BuildHooks + { -- NB: external pre-build rules are special, due to the StaticPtr machinery. + -- To invoke them, we must separately call 'runExternalPreBuildRules'. + preBuildComponentRules = Nothing + , postBuildComponentHook = Just $ hook "postBuildComponent" + } + , installHooks = + InstallHooks + { installComponentHook = Just $ hook "installComponent" + } + } + where + hook :: HookIO inputs outputs => String -> inputs -> IO outputs + hook = callHooksExe hooksExe + +-- | The ABI of an external hooks executable. +-- +-- This information is used to handshake before further communication, +-- in order to avoid a cascade of errors with mismatched 'Binary' instances. +externalSetupHooksABI :: FilePath -> IO HooksVersion +externalSetupHooksABI hooksExe = + callHooksExe hooksExe "version" () + +-- | The 'SetupHooks' associated to a particular 'BuildType'. +-- +-- **Warning:** for @build-type: Hooks@, this does not include the pre-build +-- hooks. Those can be retrieved with 'buildTypePreBuildHooks'. +buildTypeSetupHooks + :: Maybe (SymbolicPath CWD (Dir Pkg)) + -> SymbolicPath Pkg (Dir Dist) + -> BuildType + -> SetupHooks +buildTypeSetupHooks mbWorkDir distPref = \case + Hooks -> externalSetupHooks $ hooksProgFilePath mbWorkDir distPref + Configure -> autoconfSetupHooks + _ -> noSetupHooks + -- SetupHooks TODO: if any built-in functionality is implemented using SetupHooks, + -- we would also need to include those. + +-- | The pre-build hooks obtained by communication with an external hooks executable. +buildTypePreBuildHooks + :: Maybe (SymbolicPath CWD (Dir Pkg)) + -> SymbolicPath Pkg (Dir Dist) + -> BuildType + -> ( PreBuildComponentInputs -> IO [MonitorFilePath] ) +buildTypePreBuildHooks mbWorkDir distPref = \ case + Hooks -> runExternalPreBuildRules $ hooksProgFilePath mbWorkDir distPref + _ -> \ _pbci -> return [] + -- SetupHooks TODO: if any built-in functionality is implemented using pre-build hooks, + -- we would also need to include those (for example, pre-processors such as hsc2hs). + +-- | Run all pre-build rules coming from an external hooks executable at the +-- given filepath. +-- +-- TODO: in the future, we will want to keep track of the dependency graph ourselves, +-- and when re-building, only re-build what we need (instead of re-running all rules). +runExternalPreBuildRules :: FilePath -> PreBuildComponentInputs -> IO [MonitorFilePath] +runExternalPreBuildRules hooksExe + pbci@PreBuildComponentInputs + { buildingWhat = what + , localBuildInfo = lbi + , targetInfo = tgt } = do + let verbosity = buildingWhatVerbosity what + -- Here we make sure to use 'RuleBinary' (@'Scope' == 'System'@) + -- to avoid looking up static pointer keys from the hooks executable + -- from the outside (e.g. from within cabal-install). + (rulesMap :: Map RuleId RuleBinary, monitors) <- hook "preBuildRules" pbci + executeRulesUserOrSystem + SSystem + ( \ rId cmd -> case cmd of + StaticRuleCommand {} -> return Nothing + DynamicRuleCommands {} -> hook "runPreBuildRuleDeps" (rId, cmd) + ) + ( \ rId cmd -> hook "runPreBuildRule" (rId, cmd) ) + verbosity lbi tgt rulesMap + return monitors + where + hook :: HookIO inputs outputs => String -> inputs -> IO outputs + hook = callHooksExe hooksExe + +-- | The path to the external hooks executable. +hooksProgFilePath + :: Maybe (SymbolicPath CWD (Dir Pkg)) + -> SymbolicPath Pkg (Dir Dist) + -> FilePath +hooksProgFilePath mbWorkDir distPref = + interpretSymbolicPath mbWorkDir distPref + "setup" + "hooks" + <.> exeExtension buildPlatform diff --git a/hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe/Errors.hs b/hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe/Errors.hs new file mode 100644 index 00000000000..a890b09d802 --- /dev/null +++ b/hooks-exe/cli/Distribution/Client/SetupHooks/CallHooksExe/Errors.hs @@ -0,0 +1,92 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} + +module Distribution.Client.SetupHooks.CallHooksExe.Errors + ( HookInput(..) + , SetupHooksCallExeException (..) + , HookFailedReason(..) + , setupHooksCallExeExceptionCode + , setupHooksCallExeExceptionMessage + ) where + +-- Cabal +import Distribution.Compat.Binary + ( Binary ) +import Distribution.Simple.Utils + +-- base +import GHC.Exception +import Data.Typeable + ( Typeable ) +import GHC.Int + ( Int64 ) + +-- bytestring +import Data.ByteString.Lazy + ( ByteString ) + +-------------------------------------------------------------------------------- + +data HookInput where + HookInput :: (Binary input, Typeable input, Show input) + => input -> HookInput +instance Show HookInput where + show (HookInput input) = show input + +data SetupHooksCallExeException + = HookFailed + String + -- ^ hook name + HookFailedReason + -- ^ why did the hook fail? + deriving Show + +data HookFailedReason + -- | The hooks executable terminated with non-zero exit code. + = HookException + Int -- ^ exit code + -- | We failed to decode the output of the hooks executable. + | CouldNotDecodeOutput + ByteString + -- ^ hook output that we failed to decode + Int64 + -- ^ byte offset at which the decoding error took place + String + -- ^ info about the decoding error + deriving Show + +setupHooksCallExeExceptionCode :: SetupHooksCallExeException -> Int +setupHooksCallExeExceptionCode = \case + HookFailed _ reason -> setupHooksCallExeFailedExceptionCode reason + +setupHooksCallExeFailedExceptionCode :: HookFailedReason -> Int +setupHooksCallExeFailedExceptionCode = \case + HookException {} -> 7717 + CouldNotDecodeOutput {} -> 5412 + +setupHooksCallExeExceptionMessage :: SetupHooksCallExeException -> String +setupHooksCallExeExceptionMessage = \case + HookFailed hookName reason -> + setupHooksCallExeFailedMessage hookName reason + +setupHooksCallExeFailedMessage :: String -> HookFailedReason -> String +setupHooksCallExeFailedMessage hookName = \case + HookException {} -> + "An exception occurred when running the " ++ hookName ++ " hook." + CouldNotDecodeOutput _bytes offset err -> + "Failed to decode the output of the " ++ hookName ++ " hook.\n\ + \Decoding failed at position " ++ show offset ++ " with error: " ++ err ++ ".\n\ + \This could be due to a mismatch between the Cabal version of cabal-install and of the hooks executable." + +instance Exception (VerboseException SetupHooksCallExeException) where + displayException (VerboseException stack timestamp verb err) = + withOutputMarker + verb + ( concat + [ "Error: [Cabal-" + , show (setupHooksCallExeExceptionCode err) + , "]\n" + ] + ) + ++ exceptionWithMetadata stack timestamp verb (setupHooksCallExeExceptionMessage err) diff --git a/hooks-exe/exe/Distribution/Client/SetupHooks/Errors.hs b/hooks-exe/exe/Distribution/Client/SetupHooks/Errors.hs new file mode 100644 index 00000000000..7ddbe2a58e9 --- /dev/null +++ b/hooks-exe/exe/Distribution/Client/SetupHooks/Errors.hs @@ -0,0 +1,104 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE InstanceSigs #-} + +module Distribution.Client.SetupHooks.HooksExe.Errors + ( SetupHooksExeException (..) + , BadHooksExecutableArgs (..) + , setupHooksExeExceptionCode + , setupHooksExeExceptionMessage + ) where + +import Distribution.Simple.SetupHooks.Rule (RuleId (..)) +import Distribution.Simple.Utils +import GHC.Exception + +import Data.ByteString.Lazy (ByteString) + +data SetupHooksExeException + = -- | Missing hook type. + NoHookType + | -- | Could not parse communication handle. + NoHandle (Maybe String) + | -- | Incorrect arguments passed to the hooks executable. + BadHooksExeArgs + String + -- ^ hook name + BadHooksExecutableArgs + deriving (Show) + +-- | An error describing an invalid argument passed to an external +-- hooks executable compiled from the @SetupHooks@ module of a package with +-- Hooks build-type. +data BadHooksExecutableArgs + = -- | User queried the external hooks executable with an unknown hook type. + UnknownHookType + { knownHookTypes :: [String] } + | -- | The hooks executable failed to decode the input passed to + -- a particular hook. + CouldNotDecode + { couldNotDecodeWhat :: String + -- ^ A description of what it is that we failed to decode. + , couldNotDecodeData :: ByteString + -- ^ The actual data that we failed to decode. + } + | -- | The rule does not have a dynamic dependency computation. + NoDynDepsCmd RuleId + deriving (Show) + +setupHooksExeExceptionCode :: SetupHooksExeException -> Int +setupHooksExeExceptionCode = \case + NoHookType -> 7982 + NoHandle {} -> 8811 + BadHooksExeArgs _ rea -> + badHooksExeArgsCode rea + +setupHooksExeExceptionMessage :: SetupHooksExeException -> String +setupHooksExeExceptionMessage = \case + NoHookType -> + "Missing argument to Hooks executable.\n\ + \Expected three arguments: input and output communication handles, and hook type." + NoHandle Nothing -> + "Missing argument to Hooks executable.\n\ + \Expected three arguments: input and output communication handles, and hook type." + NoHandle (Just h) -> + "Invalid " ++ what ++ " passed to Hooks executable." + BadHooksExeArgs hookName reason -> + badHooksExeArgsMessage hookName reason + +badHooksExeArgsCode :: BadHooksExecutableArgs -> Int +badHooksExeArgsCode = \case + UnknownHookType{} -> 4229 + CouldNotDecode {} -> 9121 + NoDynDepsCmd{} -> 3231 + +badHooksExeArgsMessage :: String -> BadHooksExecutableArgs -> String +badHooksExeArgsMessage hookName = \case + UnknownHookType knownHookNames -> + "Unknown hook type " + ++ hookName + ++ ".\n\ + \Known hook types are: " + ++ show knownHookNames + ++ "." + CouldNotDecode { couldNotDecodeWhat = what } -> + "Failed to decode " ++ what ++ " of " ++ hookName ++ " hook.\n\ + \This could be due to a mismatch between the Cabal version of cabal-install and of the hooks executable." + NoDynDepsCmd rId -> + unlines $ + [ "Unexpected rule " <> show rId <> " in" <> hookName + , "The rule does not have an associated dynamic dependency computation." + ] + +instance Exception (VerboseException SetupHooksExeException) where + displayException :: VerboseException SetupHooksExeException -> String + displayException (VerboseException stack timestamp verb err) = + withOutputMarker + verb + ( concat + [ "Error: [Cabal-" + , show (setupHooksExeExceptionCode err) + , "]\n" + ] + ) + ++ exceptionWithMetadata stack timestamp verb (setupHooksExeExceptionMessage err) diff --git a/hooks-exe/exe/Distribution/Client/SetupHooks/HooksExe.hs b/hooks-exe/exe/Distribution/Client/SetupHooks/HooksExe.hs new file mode 100644 index 00000000000..4010d2fa2ed --- /dev/null +++ b/hooks-exe/exe/Distribution/Client/SetupHooks/HooksExe.hs @@ -0,0 +1,201 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Distribution.Client.SetupHooks.HooksExe + ( hooksMain ) where + +-- base +import System.Environment + ( getArgs ) +import System.IO + ( Handle, hClose, hFlush ) + +-- bytestring +import Data.ByteString.Lazy as LBS + ( hGetContents + , hPutStr + , null + ) + +-- containers +import qualified Data.Map as Map + +-- process +import System.Process.CommunicationHandle + ( openCommunicationHandleRead + , openCommunicationHandleWrite + ) + +-- Cabal +import Distribution.Compat.Prelude +import qualified Distribution.Compat.Binary as Binary +import Distribution.Simple.SetupHooks.Internal +import Distribution.Simple.SetupHooks.Rule +import Distribution.Simple.Utils + ( dieWithException ) +import Distribution.Types.Component + ( componentName ) +import qualified Distribution.Types.LocalBuildConfig as LBC +import qualified Distribution.Verbosity as Verbosity + +-- hooks-exe +import Distribution.Client.SetupHooks.HooksExe.Errors + ( SetupHooksExeException(..) + , BadHooksExecutableArgs(..) + ) +import Distribution.Client.SetupHooks.Version + ( hooksVersion ) + +-------------------------------------------------------------------------------- + +-- | Create a hooks executable given 'SetupHooks': +-- +-- - the first two argument are references to input & output communication +-- handles, +-- - the second argument is the hook type. +-- +-- The hook reads binary data passed to it over the input handle, decodes it, +-- runs the hook, and encodes its result to binary, writing the result to the +-- output handle. +hooksMain :: SetupHooks -> IO () +hooksMain setupHooks = do + args <- getArgs + case args of + -- First two arguments are references to read/write handles the hooks executable should use. + inputFdRef : outputFdRef : hooksExeArgs -> do + hReadMb <- traverse openCommunicationHandleRead $ readMaybe inputFdRef + hWriteMb <- traverse openCommunicationHandleWrite $ readMaybe outputFdRef + case hReadMb of + Nothing -> + dieWithException Verbosity.normal $ + NoHandle (Just $ "hook input communication handle '" ++ inputFdRef ++ "'") + Just hRead -> + case hWriteMb of + Nothing -> + dieWithException Verbosity.normal $ + NoHandle (Just $ "hook output communication handle '" ++ outputFdRef ++ "'") + Just hWrite -> + -- Third argument is the hook to run. + case hooksExeArgs of + hookName : _ -> + case lookup hookName allHookHandlers of + Just handleAction -> + handleAction (hRead, hWrite) setupHooks + Nothing -> + dieWithException Verbosity.normal $ + BadHooksExeArgs hookName $ + UnknownHookType + { knownHookTypes = map fst allHookHandlers + } + _ -> dieWithException Verbosity.normal NoHookType + _ -> dieWithException Verbosity.normal $ + NoHandle Nothing + where + allHookHandlers = + [ (nm, action) + | HookHandler + { hookName = nm + , hookHandler = action + } <- + hookHandlers + ] + +-- | Implementation of a particular hook in a separate hooks executable, +-- which communicates through the given 'Handle's. +runHookHandle + :: forall inputs outputs + . (Binary inputs, Binary outputs) + => (Handle, Handle) + -- ^ Input/output communication handles + -> String + -- ^ Hook name + -> (inputs -> IO outputs) + -- ^ Hook to run + -- + -- Inputs are passed via the input handle, and outputs are written to the + -- output handle. + -> IO () +runHookHandle (hRead, hWrite) hookName hook = do + inputsData <- LBS.hGetContents hRead + let mb_inputs = Binary.decodeOrFail inputsData + case mb_inputs of + Left (_, offset, err) -> + dieWithException Verbosity.normal $ + BadHooksExeArgs hookName $ + CouldNotDecodeInput inputsData offset err + Right (_, _, inputs) -> do + output <- hook inputs + let outputData = Binary.encode output + unless (LBS.null outputData) $ + LBS.hPutStr hWrite outputData + hFlush hWrite + hClose hWrite + +data HookHandler = HookHandler + { hookName :: !String + , hookHandler :: (Handle, Handle) -> SetupHooks -> IO () + } + +hookHandlers :: [HookHandler] +hookHandlers = + [ let hookName = "version" + in HookHandler hookName $ \h _ -> + -- Print the API version and ABI hash for the hooks executable. + runHookHandle h hookName $ \ () -> + return $ hooksVersion + , let hookName = "preConfPackage" + noHook (PreConfPackageInputs{localBuildConfig = lbc}) = + return $ + PreConfPackageOutputs + { buildOptions = LBC.withBuildOptions lbc + , extraConfiguredProgs = Map.empty + } + in HookHandler hookName $ \h (SetupHooks{configureHooks = ConfigureHooks{..}}) -> + -- Run the package-wide pre-configure hook. + runHookHandle h hookName $ fromMaybe noHook preConfPackageHook + , let hookName = "postConfPackage" + noHook _ = return () + in HookHandler hookName $ \h (SetupHooks{configureHooks = ConfigureHooks{..}}) -> + -- Run the package-wide post-configure hook. + runHookHandle h hookName $ fromMaybe noHook postConfPackageHook + , let hookName = "preConfComponent" + noHook (PreConfComponentInputs{component = c}) = + return $ PreConfComponentOutputs{componentDiff = emptyComponentDiff $ componentName c} + in HookHandler hookName $ \h (SetupHooks{configureHooks = ConfigureHooks{..}}) -> + -- Run a per-component pre-configure hook; the choice of component + -- is determined by the input passed to the hook. + runHookHandle h hookName $ fromMaybe noHook preConfComponentHook + , let hookName = "preBuildRules" + in HookHandler hookName $ \h (SetupHooks{buildHooks = BuildHooks{..}}) -> + -- Return all pre-build rules. + runHookHandle h hookName $ \preBuildInputs -> + case preBuildComponentRules of + Nothing -> return (Map.empty, []) + Just pbcRules -> + computeRules Verbosity.normal preBuildInputs pbcRules + , let hookName = "runPreBuildRuleDeps" + in HookHandler hookName $ \h _ -> + -- Run the given pre-build rule dependency computation. + runHookHandle h hookName $ \(ruleId, ruleDeps) -> + case runRuleDynDepsCmd ruleDeps of + Nothing -> dieWithException Verbosity.normal $ BadHooksExeArgs hookName $ NoDynDepsCmd ruleId + Just getDeps -> getDeps + , let hookName = "runPreBuildRule" + in HookHandler hookName $ \h _ -> + -- Run the given pre-build rule. + runHookHandle h hookName $ \(_ruleId :: RuleId, rExecCmd) -> + runRuleExecCmd rExecCmd + , let hookName = "postBuildComponent" + noHook _ = return () + in HookHandler hookName $ \h (SetupHooks{buildHooks = BuildHooks{..}}) -> + -- Run the per-component post-build hook. + runHookHandle h hookName $ fromMaybe noHook postBuildComponentHook + , let hookName = "installComponent" + noHook _ = return () + in HookHandler hookName $ \h (SetupHooks{installHooks = InstallHooks{..}}) -> + -- Run the per-component copy/install hook. + runHookHandle h hookName $ fromMaybe noHook installComponentHook + ] diff --git a/hooks-exe/exe/Distribution/Client/SetupHooks/HooksExe/Errors.hs b/hooks-exe/exe/Distribution/Client/SetupHooks/HooksExe/Errors.hs new file mode 100644 index 00000000000..3e257c47185 --- /dev/null +++ b/hooks-exe/exe/Distribution/Client/SetupHooks/HooksExe/Errors.hs @@ -0,0 +1,116 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE InstanceSigs #-} + +module Distribution.Client.SetupHooks.HooksExe.Errors + ( SetupHooksExeException (..) + , BadHooksExecutableArgs (..) + , setupHooksExeExceptionCode + , setupHooksExeExceptionMessage + ) where + +-- Cabal +import Distribution.Simple.SetupHooks.Rule + ( RuleId (..) ) +import Distribution.Simple.Utils + +-- base +import GHC.Exception +import GHC.Int + ( Int64 ) + +-- bytestring +import Data.ByteString.Lazy + ( ByteString ) + +-------------------------------------------------------------------------------- + +data SetupHooksExeException + = -- | Missing hook type. + NoHookType + | -- | Could not parse communication handle. + NoHandle (Maybe String) + | -- | Incorrect arguments passed to the hooks executable. + BadHooksExeArgs + String + -- ^ hook name + BadHooksExecutableArgs + deriving (Show) + +-- | An error describing an invalid argument passed to an external +-- hooks executable compiled from the @SetupHooks@ module of a package with +-- Hooks build-type. +data BadHooksExecutableArgs + = -- | User queried the external hooks executable with an unknown hook type. + UnknownHookType + { knownHookTypes :: [String] } + | -- | The hooks executable failed to decode the input passed to + -- a particular hook. + CouldNotDecodeInput + ByteString + -- ^ hook input that we failed to decode + Int64 + -- ^ byte offset at which the decoding error took place + String + -- ^ info about the decoding error + | -- | The rule does not have a dynamic dependency computation. + NoDynDepsCmd RuleId + deriving (Show) + +setupHooksExeExceptionCode :: SetupHooksExeException -> Int +setupHooksExeExceptionCode = \case + NoHookType -> 7982 + NoHandle {} -> 8811 + BadHooksExeArgs _ rea -> + badHooksExeArgsCode rea + +setupHooksExeExceptionMessage :: SetupHooksExeException -> String +setupHooksExeExceptionMessage = \case + NoHookType -> + "Missing argument to Hooks executable.\n\ + \Expected two arguments: communication handle and hook type." + NoHandle Nothing -> + "Missing argument to Hooks executable.\n\ + \Expected two arguments: communication handle and hook type." + NoHandle (Just h) -> + "Invalid handle reference passed to Hooks executable: '" ++ h ++ "'." + BadHooksExeArgs hookName reason -> + badHooksExeArgsMessage hookName reason + +badHooksExeArgsCode :: BadHooksExecutableArgs -> Int +badHooksExeArgsCode = \case + UnknownHookType{} -> 4229 + CouldNotDecodeInput {} -> 9121 + NoDynDepsCmd{} -> 3231 + +badHooksExeArgsMessage :: String -> BadHooksExecutableArgs -> String +badHooksExeArgsMessage hookName = \case + UnknownHookType knownHookNames -> + "Unknown hook type " + ++ hookName + ++ ".\n\ + \Known hook types are: " + ++ show knownHookNames + ++ "." + CouldNotDecodeInput _bytes offset err -> + "Failed to decode the input to the " ++ hookName ++ " hook.\n\ + \Decoding failed at position " ++ show offset ++ " with error: " ++ err ++ ".\n\ + \This could be due to a mismatch between the Cabal version of cabal-install and of the hooks executable." + NoDynDepsCmd rId -> + unlines $ + [ "Unexpected rule " <> show rId <> " in the " <> hookName <> " hook." + , "The rule does not have an associated dynamic dependency computation." + ] + +instance Exception (VerboseException SetupHooksExeException) where + displayException :: VerboseException SetupHooksExeException -> String + displayException (VerboseException stack timestamp verb err) = + withOutputMarker + verb + ( concat + [ "Error: [Cabal-" + , show (setupHooksExeExceptionCode err) + , "]\n" + ] + ) + ++ exceptionWithMetadata stack timestamp verb (setupHooksExeExceptionMessage err) diff --git a/hooks-exe/hooks-exe.cabal b/hooks-exe/hooks-exe.cabal new file mode 100644 index 00000000000..8871e2cbf12 --- /dev/null +++ b/hooks-exe/hooks-exe.cabal @@ -0,0 +1,70 @@ +cabal-version: 3.0 +name: hooks-exe +version: 0.1 +copyright: 2024, Cabal Development Team +license: BSD-3-Clause +author: Cabal Development Team +maintainer: cabal-devel@haskell.org +homepage: http://www.haskell.org/cabal/ +bug-reports: https://github.com/haskell/cabal/issues +synopsis: cabal-install integration for Hooks build-type +description: + Layer for integrating Hooks build-type with cabal-install +category: Distribution +build-type: Simple + +extra-source-files: + readme.md changelog.md + +common warnings + ghc-options: + -Wall + -Wcompat + -Wnoncanonical-monad-instances -Wincomplete-uni-patterns + -Wincomplete-record-updates + -fno-warn-unticked-promoted-constructors + if impl(ghc < 8.8) + ghc-options: -Wnoncanonical-monadfail-instances + if impl(ghc >=9.0) + -- Warning: even though introduced with GHC 8.10, -Wunused-packages + -- gives false positives with GHC 8.10. + ghc-options: -Wunused-packages + +-- NB: we are not using named sub-libraries, as the cabal-install bootstrap +-- script does not currently support them. +library + import: warnings + hs-source-dirs: + -- Component that defines a hooks version, to ensure compatibility between the + -- hooks executable and the executable it communicates with. + version, + -- Component imported by cabal-install to interface with an external + -- hooks executable. + cli, + -- Component used to create an external hooks executable + -- from a SetupHooks.hs module. + exe + + build-depends: + base + >= 4.10 && < 4.22, + bytestring + >= 0.10.6.0 && < 0.13, + containers + >= 0.5.6.2 && < 0.8 , + filepath + >= 1.4.0.0 && < 1.6 , + process + >= 1.6.20.0 && < 1.8 , + Cabal-syntax, Cabal + + exposed-modules: + Distribution.Client.SetupHooks.CallHooksExe + Distribution.Client.SetupHooks.HooksExe + Distribution.Client.SetupHooks.Version + other-modules: + Distribution.Client.SetupHooks.CallHooksExe.Errors + Distribution.Client.SetupHooks.HooksExe.Errors + + default-language: + Haskell2010 diff --git a/hooks-exe/readme.md b/hooks-exe/readme.md new file mode 100644 index 00000000000..05614591214 --- /dev/null +++ b/hooks-exe/readme.md @@ -0,0 +1,4 @@ +# `hooks-exe` + +This library integrates `Cabal`'s `Hooks` build-type into `cabal-install`. +It is only meant to be used by `cabal-install`, not imported by users. diff --git a/hooks-exe/version/Distribution/Client/SetupHooks/Version.hs b/hooks-exe/version/Distribution/Client/SetupHooks/Version.hs new file mode 100644 index 00000000000..bfcc1db450d --- /dev/null +++ b/hooks-exe/version/Distribution/Client/SetupHooks/Version.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeApplications #-} + +module Distribution.Client.SetupHooks.Version + ( HooksVersion(..), hooksVersion ) + where + +-- base +import Data.Proxy + ( Proxy(Proxy) ) +import GHC.Generics + ( Generic ) + +-- Cabal-syntax +import Distribution.Compat.Binary + ( Binary ) +import Distribution.Types.Version + ( Version ) +import Distribution.Utils.Structured + ( Structured, MD5, structureHash ) + +-- Cabal +import Distribution.Simple.SetupHooks.Rule + ( RuleId, Rule, RuleBinary ) +import Distribution.Simple.SetupHooks.Internal + ( PreConfPackageInputs + , PreConfPackageOutputs, PostConfPackageInputs + , PreConfComponentInputs + , PreConfComponentOutputs + , PreBuildComponentInputs, PostBuildComponentInputs + , InstallComponentInputs + ) +import Distribution.Simple.Utils + ( cabalVersion ) +import Distribution.Types.LocalBuildInfo + ( LocalBuildInfo ) + +-------------------------------------------------------------------------------- + +-- | The version of the Hooks API in use. +-- +-- Used for handshake before beginning inter-process communication. +data HooksVersion = + HooksVersion + { hooksAPIVersion :: !Version + , cabalABIHash :: !MD5 + , hooksABIHash :: !MD5 + } + deriving stock ( Eq, Ord, Show, Generic ) + deriving anyclass Binary + +-- | The version of the Hooks API in use. +-- +-- Used for handshake before beginning inter-process communication. +hooksVersion :: HooksVersion +hooksVersion = HooksVersion + { hooksAPIVersion = cabalVersion + , cabalABIHash = structureHash $ Proxy @CabalABI + , hooksABIHash = structureHash $ Proxy @HooksABI + } + +-------------------------------------------------------------------------------- + +-- | This datatype keeps track of the parts of the Cabal API which are +-- relevant to its binary interface. +data CabalABI + = CabalABI + { cabalLocalBuildInfo :: LocalBuildInfo } + deriving stock Generic +deriving anyclass instance Structured CabalABI + +-- | This datatype keeps track of the parts of the Hooks API which are +-- relevant to its binary interface. +data HooksABI + = HooksABI + { confHooks :: ( ( PreConfPackageInputs, PreConfPackageOutputs ) + , PostConfPackageInputs + , ( PreConfComponentInputs, PreConfComponentOutputs ) ) + , buildHooks :: ( PreBuildComponentInputs, ( RuleId, Rule, RuleBinary ) + , PostBuildComponentInputs ) + , installHooks :: InstallComponentInputs + } + deriving stock Generic +deriving anyclass instance Structured HooksABI diff --git a/project-cabal/pkgs/install.config b/project-cabal/pkgs/install.config index 9010d1f332b..328b95385d4 100644 --- a/project-cabal/pkgs/install.config +++ b/project-cabal/pkgs/install.config @@ -1,3 +1,4 @@ packages: cabal-install , cabal-install-solver + , hooks-exe