From 0a1c167a742b8e17fcda2a7154138919f2989dc3 Mon Sep 17 00:00:00 2001 From: Matthew Pickering <matthewtpickering@gmail.com> Date: Wed, 19 Oct 2022 13:30:45 +0100 Subject: [PATCH] Add support for using GHC's -jsem option * The top-level user interface to enable the use of the semaphore is the `--semaphore` flag. If you pass `-j4 --semaphore` then cabal will create a semaphore with 4 slots which is passed to ghc using the `-jsem` option so that all GHC processes coordinate to use 4 capabilities. - The semaphore logic is provided by a new package `semaphore-compat` which provides a cross-platform abstraction for semaphores. * The low level `./Setup.hs build` interface accepts the `--semaphore <SEMAPHORE>` option, which can be used to directly pass the semaphore you require to the Setup script. --- Cabal/Cabal.cabal | 1 + Cabal/src/Distribution/Simple/Build.hs | 32 ++++- Cabal/src/Distribution/Simple/Compiler.hs | 9 ++ Cabal/src/Distribution/Simple/GHC.hs | 17 +-- Cabal/src/Distribution/Simple/GHCJS.hs | 19 +-- Cabal/src/Distribution/Simple/Program/GHC.hs | 12 +- Cabal/src/Distribution/Simple/Setup.hs | 1 - Cabal/src/Distribution/Simple/Setup/Build.hs | 9 ++ Cabal/src/Distribution/Types/ParStrat.hs | 24 ++++ bootstrap/linux-8.10.7.json | 118 +++++++++++------ bootstrap/linux-9.0.2.json | 118 +++++++++++------ bootstrap/linux-9.2.7.json | 63 ++++++--- bootstrap/linux-9.4.4.json | 79 +++++++---- cabal-install/cabal-install.cabal | 3 +- .../src/Distribution/Client/Config.hs | 1 + .../src/Distribution/Client/JobControl.hs | 98 +++++++++++++- .../Distribution/Client/ProjectBuilding.hs | 124 +++++++++++------- .../src/Distribution/Client/ProjectConfig.hs | 13 +- .../Client/ProjectConfig/Legacy.hs | 3 + .../Client/ProjectConfig/Types.hs | 7 +- .../Distribution/Client/ProjectPlanning.hs | 9 +- .../src/Distribution/Client/Setup.hs | 38 +++++- cabal-install/tests/IntegrationTests2.hs | 3 +- .../Distribution/Client/ProjectConfig.hs | 7 +- .../PackageTests/NewBuild/Semaphore/C.hs | 3 + .../NewBuild/Semaphore/Semaphore.cabal | 22 ++++ .../PackageTests/NewBuild/Semaphore/Setup.hs | 2 + .../PackageTests/NewBuild/Semaphore/cabal.out | 0 .../NewBuild/Semaphore/cabal.project | 3 + .../NewBuild/Semaphore/cabal.test.hs | 6 + .../NewBuild/Semaphore/sub-package-A/A.hs | 1 + .../NewBuild/Semaphore/sub-package-A/Setup.hs | 2 + .../sub-package-A/sub-package-A.cabal | 22 ++++ .../NewBuild/Semaphore/sub-package-B/B.hs | 1 + .../NewBuild/Semaphore/sub-package-B/Setup.hs | 2 + .../sub-package-B/sub-package-B.cabal | 22 ++++ changelog.d/pr-8557 | 20 +++ changelog.d/pr-8557-2 | 16 +++ doc/cabal-project.rst | 16 +++ 39 files changed, 736 insertions(+), 210 deletions(-) create mode 100644 Cabal/src/Distribution/Types/ParStrat.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/C.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/Semaphore.cabal create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/Setup.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.out create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.project create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/A.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/Setup.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/sub-package-A.cabal create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/B.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/Setup.hs create mode 100644 cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/sub-package-B.cabal create mode 100644 changelog.d/pr-8557 create mode 100644 changelog.d/pr-8557-2 diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal index 10dafe0050..147d921f0f 100644 --- a/Cabal/Cabal.cabal +++ b/Cabal/Cabal.cabal @@ -148,6 +148,7 @@ library Distribution.Types.LocalBuildInfo Distribution.Types.TargetInfo Distribution.Types.GivenComponent + Distribution.Types.ParStrat Distribution.Utils.Json Distribution.Utils.NubList Distribution.Utils.Progress diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index 25a0af6db3..636c2c298e 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -41,6 +41,7 @@ import Distribution.Types.LocalBuildInfo import Distribution.Types.ModuleRenaming import Distribution.Types.MungedPackageId import Distribution.Types.MungedPackageName +import Distribution.Types.ParStrat import Distribution.Types.TargetInfo import Distribution.Utils.Path @@ -110,6 +111,7 @@ build -- ^ preprocessors to run before compiling -> IO () build pkg_descr lbi flags suffixes = do + checkSemaphoreSupport verbosity (compiler lbi) flags targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags) let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) info verbosity $ @@ -145,10 +147,21 @@ build pkg_descr lbi flags suffixes = do , withPackageDB = withPackageDB lbi ++ [internalPackageDB] , installedPkgs = index } + par_strat <- + toFlag <$> case buildUseSemaphore flags of + Flag sem_name -> case buildNumJobs flags of + Flag{} -> do + warn verbosity $ "Ignoring -j due to --semaphore" + return $ UseSem sem_name + NoFlag -> return $ UseSem sem_name + NoFlag -> return $ case buildNumJobs flags of + Flag n -> NumJobs n + NoFlag -> Serial + mb_ipi <- buildComponent verbosity - (buildNumJobs flags) + par_strat pkg_descr lbi' suffixes @@ -162,6 +175,15 @@ build pkg_descr lbi flags suffixes = do distPref = fromFlag (buildDistPref flags) verbosity = fromFlag (buildVerbosity flags) +-- | Check for conditions that would prevent the build from succeeding. +checkSemaphoreSupport + :: Verbosity -> Compiler -> BuildFlags -> IO () +checkSemaphoreSupport verbosity comp flags = do + unless (jsemSupported comp || (isNothing (flagToMaybe (buildUseSemaphore flags)))) $ + die' verbosity $ + "Your compiler does not support the -jsem flag. " + ++ "To use this feature you must use GHC 9.8 or later." + -- | Write available build information for 'LocalBuildInfo' to disk. -- -- Dumps detailed build information 'build-info.json' to the given directory. @@ -317,7 +339,7 @@ startInterpreter verbosity programDb comp platform packageDBs = buildComponent :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> [PPSuffixHandler] @@ -926,7 +948,7 @@ addInternalBuildTools pkg lbi bi progs = -- multiple libs, e.g. for 'LibTest' library-style test suites buildLib :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -946,7 +968,7 @@ buildLib verbosity numJobs pkg_descr lbi lib clbi = -- foreign library in configure. buildFLib :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib @@ -959,7 +981,7 @@ buildFLib verbosity numJobs pkg_descr lbi flib clbi = buildExe :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable diff --git a/Cabal/src/Distribution/Simple/Compiler.hs b/Cabal/src/Distribution/Simple/Compiler.hs index 074f2f38b5..6aaa0931ee 100644 --- a/Cabal/src/Distribution/Simple/Compiler.hs +++ b/Cabal/src/Distribution/Simple/Compiler.hs @@ -67,6 +67,7 @@ module Distribution.Simple.Compiler , arDashLSupported , libraryDynDirSupported , libraryVisibilitySupported + , jsemSupported -- * Support for profiling detail levels , ProfDetailLevel (..) @@ -363,6 +364,14 @@ unitIdSupported = ghcSupported "Uses unit IDs" backpackSupported :: Compiler -> Bool backpackSupported = ghcSupported "Support Backpack" +-- | Does this compiler support the -jsem option? +jsemSupported :: Compiler -> Bool +jsemSupported comp = case compilerFlavor comp of + GHC -> v >= mkVersion [9, 7] + _ -> False + where + v = compilerVersion comp + -- | Does this compiler support a package database entry with: -- "dynamic-library-dirs"? libraryDynDirSupported :: Compiler -> Bool diff --git a/Cabal/src/Distribution/Simple/GHC.hs b/Cabal/src/Distribution/Simple/GHC.hs index 656a258ca0..501ae75939 100644 --- a/Cabal/src/Distribution/Simple/GHC.hs +++ b/Cabal/src/Distribution/Simple/GHC.hs @@ -114,6 +114,7 @@ import Distribution.Simple.Utils import Distribution.System import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.PackageName.Magic +import Distribution.Types.ParStrat import Distribution.Utils.NubList import Distribution.Utils.Path import Distribution.Verbosity @@ -587,7 +588,7 @@ getInstalledPackagesMonitorFiles verbosity platform progdb = buildLib :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -598,7 +599,7 @@ buildLib = buildOrReplLib Nothing replLib :: ReplOptions -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -609,7 +610,7 @@ replLib = buildOrReplLib . Just buildOrReplLib :: Maybe ReplOptions -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -1173,7 +1174,7 @@ runReplOrWriteFlags verbosity ghcProg comp platform rflags replOpts bi clbi pkg_ -- | Build a foreign library buildFLib :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib @@ -1184,7 +1185,7 @@ buildFLib v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildFLib replFLib :: ReplOptions -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib @@ -1196,7 +1197,7 @@ replFLib replFlags v njobs pkg lbi = -- | Build an executable with GHC. buildExe :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable @@ -1207,7 +1208,7 @@ buildExe v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildExe replExe :: ReplOptions -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable @@ -1526,7 +1527,7 @@ replNoLoad replFlags l -- | Generic build function. See comment for 'GBuildMode'. gbuild :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> GBuildMode diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs index 6c9bf34f0e..2e99e209c6 100644 --- a/Cabal/src/Distribution/Simple/GHCJS.hs +++ b/Cabal/src/Distribution/Simple/GHCJS.hs @@ -76,9 +76,10 @@ import Distribution.Simple.Utils import Distribution.System import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.PackageName.Magic +import Distribution.Types.ParStrat import Distribution.Utils.NubList import Distribution.Utils.Path -import Distribution.Verbosity +import Distribution.Verbosity (Verbosity) import Distribution.Version import Control.Monad (msum) @@ -466,7 +467,7 @@ toJSLibName lib buildLib :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -477,7 +478,7 @@ buildLib = buildOrReplLib Nothing replLib :: [String] -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -488,7 +489,7 @@ replLib = buildOrReplLib . Just buildOrReplLib :: Maybe [String] -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library @@ -889,7 +890,7 @@ startInterpreter verbosity progdb comp platform packageDBs = do -- | Build a foreign library buildFLib :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib @@ -900,7 +901,7 @@ buildFLib v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildFLib replFLib :: [String] -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib @@ -912,7 +913,7 @@ replFLib replFlags v njobs pkg lbi = -- | Build an executable with GHC. buildExe :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable @@ -923,7 +924,7 @@ buildExe v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildExe replExe :: [String] -> Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable @@ -1218,7 +1219,7 @@ isHaskell fp = elem (takeExtension fp) [".hs", ".lhs"] -- | Generic build function. See comment for 'GBuildMode'. gbuild :: Verbosity - -> Flag (Maybe Int) + -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> GBuildMode diff --git a/Cabal/src/Distribution/Simple/Program/GHC.hs b/Cabal/src/Distribution/Simple/Program/GHC.hs index 4b821f5680..1f525fd9f3 100644 --- a/Cabal/src/Distribution/Simple/Program/GHC.hs +++ b/Cabal/src/Distribution/Simple/Program/GHC.hs @@ -41,6 +41,7 @@ import Data.List (stripPrefix) import qualified Data.Map as Map import Data.Monoid (All (..), Any (..), Endo (..)) import qualified Data.Set as Set +import Distribution.Types.ParStrat normaliseGhcArgs :: Maybe Version -> PackageDescription -> [String] -> [String] normaliseGhcArgs (Just ghcVersion) PackageDescription{..} ghcArgs @@ -513,7 +514,7 @@ data GhcOptions = GhcOptions -- ^ Use the \"split sections\" feature; the @ghc -split-sections@ flag. , ghcOptSplitObjs :: Flag Bool -- ^ Use the \"split object files\" feature; the @ghc -split-objs@ flag. - , ghcOptNumJobs :: Flag (Maybe Int) + , ghcOptNumJobs :: Flag ParStrat -- ^ Run N jobs simultaneously (if possible). , ghcOptHPCDir :: Flag FilePath -- ^ Enable coverage analysis; the @ghc -fhpc -hpcdir@ flags. @@ -553,6 +554,8 @@ data GhcOptions = GhcOptions , ghcOptExtraPath :: NubListR FilePath -- ^ Put the extra folders in the PATH environment variable we invoke -- GHC with + -- | Put the extra folders in the PATH environment variable we invoke + -- GHC with , ghcOptCabal :: Flag Bool -- ^ Let GHC know that it is Cabal that's calling it. -- Modifies some of the GHC error messages. @@ -693,7 +696,12 @@ renderGhcOptions comp _platform@(Platform _arch os) opts , if parmakeSupported comp then case ghcOptNumJobs opts of NoFlag -> [] - Flag n -> ["-j" ++ maybe "" show n] + Flag Serial -> [] + Flag (UseSem name) -> + if jsemSupported comp + then ["-jsem " ++ name] + else [] + Flag (NumJobs n) -> ["-j" ++ show n] else [] , -------------------- -- Creating libraries diff --git a/Cabal/src/Distribution/Simple/Setup.hs b/Cabal/src/Distribution/Simple/Setup.hs index b1758d1ee9..b4d55d604b 100644 --- a/Cabal/src/Distribution/Simple/Setup.hs +++ b/Cabal/src/Distribution/Simple/Setup.hs @@ -131,7 +131,6 @@ module Distribution.Simple.Setup , trueArg , falseArg , optionVerbosity - , optionNumJobs ) where import Prelude () diff --git a/Cabal/src/Distribution/Simple/Setup/Build.hs b/Cabal/src/Distribution/Simple/Setup/Build.hs index 6888853285..434558ca54 100644 --- a/Cabal/src/Distribution/Simple/Setup/Build.hs +++ b/Cabal/src/Distribution/Simple/Setup/Build.hs @@ -50,6 +50,7 @@ data BuildFlags = BuildFlags , buildDistPref :: Flag FilePath , buildVerbosity :: Flag Verbosity , buildNumJobs :: Flag (Maybe Int) + , buildUseSemaphore :: Flag String , -- TODO: this one should not be here, it's just that the silly -- UserHooks stop us from passing extra info in other ways buildArgs :: [String] @@ -65,6 +66,7 @@ defaultBuildFlags = , buildDistPref = mempty , buildVerbosity = Flag normal , buildNumJobs = mempty + , buildUseSemaphore = NoFlag , buildArgs = [] , buildCabalFilePath = mempty } @@ -125,6 +127,13 @@ buildOptions progDb showOrParseArgs = [ optionNumJobs buildNumJobs (\v flags -> flags{buildNumJobs = v}) + , option + [] + ["semaphore"] + "semaphore" + buildUseSemaphore + (\v flags -> flags{buildUseSemaphore = v}) + (reqArg' "SEMAPHORE" Flag flagToList) ] ++ programDbPaths progDb diff --git a/Cabal/src/Distribution/Types/ParStrat.hs b/Cabal/src/Distribution/Types/ParStrat.hs new file mode 100644 index 0000000000..59d8beb388 --- /dev/null +++ b/Cabal/src/Distribution/Types/ParStrat.hs @@ -0,0 +1,24 @@ +module Distribution.Types.ParStrat where + +-- | How to control parallelism, e.g. a fixed number of jobs or by using a system semaphore. +data ParStratX sem + = -- | Compile in parallel with the given number of jobs (`-jN` or `-j`). + NumJobs (Maybe Int) + | -- | `--semaphore`: use a system semaphore to control parallelism. + UseSem sem + | -- | No parallelism (neither `-jN` nor `--semaphore`, but could be `-j1`). + Serial + deriving (Show) + +-- | Used by Cabal to indicate that we want to use this specific semaphore (created by cabal-install) +type ParStrat = ParStratX String + +-- | Used by cabal-install to say we want to create a semaphore with N slots. +type ParStratInstall = ParStratX Int + +-- | Determine if the parallelism strategy enables parallel builds. +isParallelBuild :: ParStratX n -> Bool +isParallelBuild Serial = False +isParallelBuild (NumJobs (Just 1)) = False +isParallelBuild (NumJobs _) = True +isParallelBuild UseSem{} = True diff --git a/bootstrap/linux-8.10.7.json b/bootstrap/linux-8.10.7.json index 7c753100da..24dc7fcf5e 100644 --- a/bootstrap/linux-8.10.7.json +++ b/bootstrap/linux-8.10.7.json @@ -25,28 +25,20 @@ "version": "1.4.4.0" }, { - "package": "bytestring", - "version": "0.10.12.0" - }, - { - "package": "containers", - "version": "0.6.5.1" - }, - { - "package": "binary", - "version": "0.8.8.0" + "package": "ghc-boot-th", + "version": "8.10.7" }, { - "package": "filepath", - "version": "1.4.2.1" + "package": "pretty", + "version": "1.1.3.6" }, { - "package": "time", - "version": "1.9.3" + "package": "template-haskell", + "version": "2.16.0.0" }, { - "package": "unix", - "version": "2.7.2.2" + "package": "containers", + "version": "0.6.5.1" }, { "package": "transformers", @@ -57,43 +49,86 @@ "version": "2.2.2" }, { - "package": "ghc-boot-th", - "version": "8.10.7" + "package": "stm", + "version": "2.5.0.1" }, { - "package": "pretty", - "version": "1.1.3.6" + "package": "exceptions", + "version": "0.10.4" }, { - "package": "template-haskell", - "version": "2.16.0.0" - }, + "package": "time", + "version": "1.9.3" + } + ], + "dependencies": [ { - "package": "text", - "version": "1.2.4.1" + "cabal_sha256": "55390b63bbd7846aab6b16b7b255cf5108a3a422798a1e9a3b674eb0c68ac20c", + "flags": [], + "package": "bytestring", + "revision": 0, + "source": "hackage", + "src_sha256": "491aaef7625c693a06c26ae7f097caf23d9e3f9cae14af5ab17e71abb39576d3", + "version": "0.11.4.0" }, { - "package": "parsec", - "version": "3.1.14.0" + "cabal_sha256": "81f468c1c75fd6535152ab69b2d32ac6cfcc03e345267b069abe4da56ec95801", + "flags": [], + "package": "binary", + "revision": 0, + "source": "hackage", + "src_sha256": "ac21ca63452dfc9b0bcab87c57699c531d87f7a9bcb6230ca46fba1b7faeebc0", + "version": "0.8.9.1" }, { - "package": "stm", - "version": "2.5.0.1" + "cabal_sha256": "ab6e3330c42817b0effb14cae9c60d5cdc41d61f7354d04bc529ed031113f27c", + "flags": [ + "-cpphs" + ], + "package": "filepath", + "revision": 0, + "source": "hackage", + "src_sha256": "ed1d11173f5052461cd1df58b5ef4abbfa1803ad2b237da7ddb7c7d64e017de2", + "version": "1.4.100.3" }, { - "package": "exceptions", - "version": "0.10.4" - } - ], - "dependencies": [ + "cabal_sha256": "26eeda14432a2fcc6049045068ecf59525ffff9196616a642b5ab2326a003692", + "flags": [], + "package": "unix", + "revision": 0, + "source": "hackage", + "src_sha256": "5e59d9ca0f8e505d2203af7dba17c67275621fbcd4ed8fcbdd59e80df4d79a7d", + "version": "2.8.1.0" + }, { - "cabal_sha256": "1125a0a4be3aafc8da208940f219d4e4df8a0db87d892cc42bb369071855c590", + "cabal_sha256": "e384a4831b0ac0f8908a1d99d14ce44bf9c5fe2092eec5b2c47ea072d477a493", "flags": [], "package": "directory", "revision": 0, "source": "hackage", - "src_sha256": "dc2785d6548cec2e80700fab007d3e9467f65d3c58ab3efa21b34d9017cf0efd", - "version": "1.3.7.1" + "src_sha256": "bd8253197587d32d4553070d2de89d3817176860932b0e9ab7bb7ba3759d8e9c", + "version": "1.3.8.1" + }, + { + "cabal_sha256": "d495b2a2a53da7e66163477a5837d2109074f8818fc938739b9ecf27d506050a", + "flags": [ + "-developer", + "+simdutf" + ], + "package": "text", + "revision": 0, + "source": "hackage", + "src_sha256": "c735be650a898606ce9f2c8642bc6ac6123eea82871d5e90f92797801f59efad", + "version": "2.0.2" + }, + { + "cabal_sha256": "5769242043b01bf759b07b7efedcb19607837ee79015fcddde34645664136aed", + "flags": [], + "package": "parsec", + "revision": 0, + "source": "hackage", + "src_sha256": "a41962e5d76ea68658876735b8d5b755e0eff336b079d0a2f439c364755d1246", + "version": "3.1.16.1" }, { "cabal_sha256": null, @@ -393,6 +428,15 @@ "src_sha256": "91ce28d8f8a6efd31788d4827ed5cdcb9a546ad4053a86c56f7947c66a30b5bf", "version": "0.1.7.3" }, + { + "cabal_sha256": "868694b09361e7c6756542bcdcfba68a9b8b93410c6798709e475abd3751afe7", + "flags": [], + "package": "semaphore-compat", + "revision": 0, + "source": "hackage", + "src_sha256": "1c6e6fab021c2ccee5d86112fb1c0bd016d15e0cf70c489dae5fb5ec156ed9e2", + "version": "1.0.0" + }, { "cabal_sha256": null, "flags": [ diff --git a/bootstrap/linux-9.0.2.json b/bootstrap/linux-9.0.2.json index 1c28ff534e..5700325f05 100644 --- a/bootstrap/linux-9.0.2.json +++ b/bootstrap/linux-9.0.2.json @@ -25,28 +25,20 @@ "version": "1.4.5.0" }, { - "package": "bytestring", - "version": "0.10.12.1" - }, - { - "package": "containers", - "version": "0.6.4.1" - }, - { - "package": "binary", - "version": "0.8.8.0" + "package": "ghc-boot-th", + "version": "9.0.2" }, { - "package": "filepath", - "version": "1.4.2.1" + "package": "pretty", + "version": "1.1.3.6" }, { - "package": "time", - "version": "1.9.3" + "package": "template-haskell", + "version": "2.17.0.0" }, { - "package": "unix", - "version": "2.7.2.2" + "package": "containers", + "version": "0.6.4.1" }, { "package": "transformers", @@ -57,43 +49,86 @@ "version": "2.2.2" }, { - "package": "ghc-boot-th", - "version": "9.0.2" + "package": "stm", + "version": "2.5.0.0" }, { - "package": "pretty", - "version": "1.1.3.6" + "package": "exceptions", + "version": "0.10.4" }, { - "package": "template-haskell", - "version": "2.17.0.0" - }, + "package": "time", + "version": "1.9.3" + } + ], + "dependencies": [ { - "package": "text", - "version": "1.2.5.0" + "cabal_sha256": "55390b63bbd7846aab6b16b7b255cf5108a3a422798a1e9a3b674eb0c68ac20c", + "flags": [], + "package": "bytestring", + "revision": 0, + "source": "hackage", + "src_sha256": "491aaef7625c693a06c26ae7f097caf23d9e3f9cae14af5ab17e71abb39576d3", + "version": "0.11.4.0" }, { - "package": "parsec", - "version": "3.1.14.0" + "cabal_sha256": "81f468c1c75fd6535152ab69b2d32ac6cfcc03e345267b069abe4da56ec95801", + "flags": [], + "package": "binary", + "revision": 0, + "source": "hackage", + "src_sha256": "ac21ca63452dfc9b0bcab87c57699c531d87f7a9bcb6230ca46fba1b7faeebc0", + "version": "0.8.9.1" }, { - "package": "stm", - "version": "2.5.0.0" + "cabal_sha256": "ab6e3330c42817b0effb14cae9c60d5cdc41d61f7354d04bc529ed031113f27c", + "flags": [ + "-cpphs" + ], + "package": "filepath", + "revision": 0, + "source": "hackage", + "src_sha256": "ed1d11173f5052461cd1df58b5ef4abbfa1803ad2b237da7ddb7c7d64e017de2", + "version": "1.4.100.3" }, { - "package": "exceptions", - "version": "0.10.4" - } - ], - "dependencies": [ + "cabal_sha256": "26eeda14432a2fcc6049045068ecf59525ffff9196616a642b5ab2326a003692", + "flags": [], + "package": "unix", + "revision": 0, + "source": "hackage", + "src_sha256": "5e59d9ca0f8e505d2203af7dba17c67275621fbcd4ed8fcbdd59e80df4d79a7d", + "version": "2.8.1.0" + }, { - "cabal_sha256": "1125a0a4be3aafc8da208940f219d4e4df8a0db87d892cc42bb369071855c590", + "cabal_sha256": "e384a4831b0ac0f8908a1d99d14ce44bf9c5fe2092eec5b2c47ea072d477a493", "flags": [], "package": "directory", "revision": 0, "source": "hackage", - "src_sha256": "dc2785d6548cec2e80700fab007d3e9467f65d3c58ab3efa21b34d9017cf0efd", - "version": "1.3.7.1" + "src_sha256": "bd8253197587d32d4553070d2de89d3817176860932b0e9ab7bb7ba3759d8e9c", + "version": "1.3.8.1" + }, + { + "cabal_sha256": "d495b2a2a53da7e66163477a5837d2109074f8818fc938739b9ecf27d506050a", + "flags": [ + "-developer", + "+simdutf" + ], + "package": "text", + "revision": 0, + "source": "hackage", + "src_sha256": "c735be650a898606ce9f2c8642bc6ac6123eea82871d5e90f92797801f59efad", + "version": "2.0.2" + }, + { + "cabal_sha256": "5769242043b01bf759b07b7efedcb19607837ee79015fcddde34645664136aed", + "flags": [], + "package": "parsec", + "revision": 0, + "source": "hackage", + "src_sha256": "a41962e5d76ea68658876735b8d5b755e0eff336b079d0a2f439c364755d1246", + "version": "3.1.16.1" }, { "cabal_sha256": null, @@ -393,6 +428,15 @@ "src_sha256": "91ce28d8f8a6efd31788d4827ed5cdcb9a546ad4053a86c56f7947c66a30b5bf", "version": "0.1.7.3" }, + { + "cabal_sha256": "868694b09361e7c6756542bcdcfba68a9b8b93410c6798709e475abd3751afe7", + "flags": [], + "package": "semaphore-compat", + "revision": 0, + "source": "hackage", + "src_sha256": "1c6e6fab021c2ccee5d86112fb1c0bd016d15e0cf70c489dae5fb5ec156ed9e2", + "version": "1.0.0" + }, { "cabal_sha256": null, "flags": [ diff --git a/bootstrap/linux-9.2.7.json b/bootstrap/linux-9.2.7.json index ad75816daf..3b8fda58d6 100644 --- a/bootstrap/linux-9.2.7.json +++ b/bootstrap/linux-9.2.7.json @@ -49,24 +49,24 @@ "version": "0.8.9.0" }, { - "package": "filepath", - "version": "1.4.2.2" + "package": "transformers", + "version": "0.5.6.2" }, { - "package": "time", - "version": "1.11.1.1" + "package": "mtl", + "version": "2.2.2" }, { - "package": "unix", - "version": "2.7.2.2" + "package": "stm", + "version": "2.5.0.2" }, { - "package": "transformers", - "version": "0.5.6.2" + "package": "exceptions", + "version": "0.10.4" }, { - "package": "mtl", - "version": "2.2.2" + "package": "time", + "version": "1.11.1.1" }, { "package": "text", @@ -75,25 +75,37 @@ { "package": "parsec", "version": "3.1.15.0" - }, - { - "package": "stm", - "version": "2.5.0.2" - }, - { - "package": "exceptions", - "version": "0.10.4" } ], "dependencies": [ { - "cabal_sha256": "1125a0a4be3aafc8da208940f219d4e4df8a0db87d892cc42bb369071855c590", + "cabal_sha256": "ab6e3330c42817b0effb14cae9c60d5cdc41d61f7354d04bc529ed031113f27c", + "flags": [ + "-cpphs" + ], + "package": "filepath", + "revision": 0, + "source": "hackage", + "src_sha256": "ed1d11173f5052461cd1df58b5ef4abbfa1803ad2b237da7ddb7c7d64e017de2", + "version": "1.4.100.3" + }, + { + "cabal_sha256": "26eeda14432a2fcc6049045068ecf59525ffff9196616a642b5ab2326a003692", + "flags": [], + "package": "unix", + "revision": 0, + "source": "hackage", + "src_sha256": "5e59d9ca0f8e505d2203af7dba17c67275621fbcd4ed8fcbdd59e80df4d79a7d", + "version": "2.8.1.0" + }, + { + "cabal_sha256": "e384a4831b0ac0f8908a1d99d14ce44bf9c5fe2092eec5b2c47ea072d477a493", "flags": [], "package": "directory", "revision": 0, "source": "hackage", - "src_sha256": "dc2785d6548cec2e80700fab007d3e9467f65d3c58ab3efa21b34d9017cf0efd", - "version": "1.3.7.1" + "src_sha256": "bd8253197587d32d4553070d2de89d3817176860932b0e9ab7bb7ba3759d8e9c", + "version": "1.3.8.1" }, { "cabal_sha256": null, @@ -384,6 +396,15 @@ "src_sha256": "91ce28d8f8a6efd31788d4827ed5cdcb9a546ad4053a86c56f7947c66a30b5bf", "version": "0.1.7.3" }, + { + "cabal_sha256": "868694b09361e7c6756542bcdcfba68a9b8b93410c6798709e475abd3751afe7", + "flags": [], + "package": "semaphore-compat", + "revision": 0, + "source": "hackage", + "src_sha256": "1c6e6fab021c2ccee5d86112fb1c0bd016d15e0cf70c489dae5fb5ec156ed9e2", + "version": "1.0.0" + }, { "cabal_sha256": null, "flags": [ diff --git a/bootstrap/linux-9.4.4.json b/bootstrap/linux-9.4.4.json index 98382c20a5..bcb80b9104 100644 --- a/bootstrap/linux-9.4.4.json +++ b/bootstrap/linux-9.4.4.json @@ -49,28 +49,24 @@ "version": "0.8.9.1" }, { - "package": "filepath", - "version": "1.4.2.2" - }, - { - "package": "time", - "version": "1.12.2" + "package": "transformers", + "version": "0.5.6.2" }, { - "package": "unix", - "version": "2.7.3" + "package": "mtl", + "version": "2.2.2" }, { - "package": "directory", - "version": "1.3.7.1" + "package": "stm", + "version": "2.5.1.0" }, { - "package": "transformers", - "version": "0.5.6.2" + "package": "exceptions", + "version": "0.10.5" }, { - "package": "mtl", - "version": "2.2.2" + "package": "time", + "version": "1.12.2" }, { "package": "text", @@ -79,21 +75,38 @@ { "package": "parsec", "version": "3.1.15.0" - }, + } + ], + "dependencies": [ { - "package": "process", - "version": "1.6.16.0" + "cabal_sha256": "ab6e3330c42817b0effb14cae9c60d5cdc41d61f7354d04bc529ed031113f27c", + "flags": [ + "-cpphs" + ], + "package": "filepath", + "revision": 0, + "source": "hackage", + "src_sha256": "ed1d11173f5052461cd1df58b5ef4abbfa1803ad2b237da7ddb7c7d64e017de2", + "version": "1.4.100.3" }, { - "package": "stm", - "version": "2.5.1.0" + "cabal_sha256": "26eeda14432a2fcc6049045068ecf59525ffff9196616a642b5ab2326a003692", + "flags": [], + "package": "unix", + "revision": 0, + "source": "hackage", + "src_sha256": "5e59d9ca0f8e505d2203af7dba17c67275621fbcd4ed8fcbdd59e80df4d79a7d", + "version": "2.8.1.0" }, { - "package": "exceptions", - "version": "0.10.5" - } - ], - "dependencies": [ + "cabal_sha256": "e384a4831b0ac0f8908a1d99d14ce44bf9c5fe2092eec5b2c47ea072d477a493", + "flags": [], + "package": "directory", + "revision": 0, + "source": "hackage", + "src_sha256": "bd8253197587d32d4553070d2de89d3817176860932b0e9ab7bb7ba3759d8e9c", + "version": "1.3.8.1" + }, { "cabal_sha256": null, "flags": [], @@ -103,6 +116,15 @@ "src_sha256": null, "version": "3.11.0.0" }, + { + "cabal_sha256": "49d8a7f372d35363011591b253cae4c8db8b9ec594590448e20b7bed7acaee98", + "flags": [], + "package": "process", + "revision": 0, + "source": "hackage", + "src_sha256": "4c5c454e0f5c864c79b9fabd850307b26d8ac4037e45a6a39ab87e20b583bf06", + "version": "1.6.17.0" + }, { "cabal_sha256": null, "flags": [], @@ -365,6 +387,15 @@ "src_sha256": "91ce28d8f8a6efd31788d4827ed5cdcb9a546ad4053a86c56f7947c66a30b5bf", "version": "0.1.7.3" }, + { + "cabal_sha256": "868694b09361e7c6756542bcdcfba68a9b8b93410c6798709e475abd3751afe7", + "flags": [], + "package": "semaphore-compat", + "revision": 0, + "source": "hackage", + "src_sha256": "1c6e6fab021c2ccee5d86112fb1c0bd016d15e0cf70c489dae5fb5ec156ed9e2", + "version": "1.0.0" + }, { "cabal_sha256": null, "flags": [ diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index 1374c495e8..d20db6ced5 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -232,7 +232,8 @@ library parsec >= 3.1.13.0 && < 3.2, regex-base >= 0.94.0.0 && <0.95, regex-posix >= 0.96.0.0 && <0.97, - safe-exceptions >= 0.1.7.0 && < 0.2 + safe-exceptions >= 0.1.7.0 && < 0.2, + semaphore-compat >= 1.0.0 && < 1.1 if flag(native-dns) if os(windows) diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index 4b3853aa89..24c6c6a29f 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -434,6 +434,7 @@ instance Semigroup SavedConfig where , installSymlinkBinDir = combine installSymlinkBinDir , installPerComponent = combine installPerComponent , installNumJobs = combine installNumJobs + , installUseSemaphore = combine installUseSemaphore , installKeepGoing = combine installKeepGoing , installRunTests = combine installRunTests , installOfflineMode = combine installOfflineMode diff --git a/cabal-install/src/Distribution/Client/JobControl.hs b/cabal-install/src/Distribution/Client/JobControl.hs index d45efe3c8b..ec14847851 100644 --- a/cabal-install/src/Distribution/Client/JobControl.hs +++ b/cabal-install/src/Distribution/Client/JobControl.hs @@ -18,10 +18,13 @@ module Distribution.Client.JobControl ( JobControl , newSerialJobControl , newParallelJobControl + , newSemaphoreJobControl , spawnJob , collectJob , remainingJobs , cancelJobs + , cleanupJobControl + , jobControlSemaphore , JobLimit , newJobLimit , withJobLimit @@ -33,15 +36,17 @@ module Distribution.Client.JobControl import Distribution.Client.Compat.Prelude import Prelude () -import Control.Concurrent (forkIO) +import Control.Concurrent (forkIO, forkIOWithUnmask, threadDelay) import Control.Concurrent.MVar -import Control.Concurrent.STM (STM, atomically) +import Control.Concurrent.STM (STM, TVar, atomically, modifyTVar', newTVarIO, readTVar) import Control.Concurrent.STM.TChan -import Control.Concurrent.STM.TVar -import Control.Exception (bracket_, try) +import Control.Exception (bracket_, mask_, try) import Control.Monad (forever, replicateM_) import Distribution.Client.Compat.Semaphore import Distribution.Compat.Stack +import Distribution.Simple.Utils +import Distribution.Verbosity +import System.Semaphore -- | A simple concurrency abstraction. Jobs can be spawned and can complete -- in any order. This allows both serial and parallel implementations. @@ -57,6 +62,12 @@ data JobControl m a = JobControl -- ^ Try to cancel any outstanding but not-yet-started jobs. -- Call 'remainingJobs' after this to find out if any jobs are left -- (ie could not be cancelled). + , cleanupJobControl :: m () + -- ^ cleanup any resources created by the JobControl, intended to be used + -- as the finaliser for `bracket`. + , jobControlSemaphore :: Maybe SemaphoreName + -- ^ Name of the semaphore which can be used to control parallelism, if one + -- is available for that job control type. } -- | Make a 'JobControl' that executes all jobs serially and in order. @@ -72,6 +83,8 @@ newSerialJobControl = do , collectJob = collect qVar , remainingJobs = remaining qVar , cancelJobs = cancel qVar + , cleanupJobControl = return () + , jobControlSemaphore = Nothing } where spawn :: TChan (IO a) -> IO a -> IO () @@ -112,6 +125,8 @@ newParallelJobControl maxJobLimit = do , collectJob = collect outqVar countVar , remainingJobs = remaining countVar , cancelJobs = cancel inqVar countVar + , cleanupJobControl = return () + , jobControlSemaphore = Nothing } where worker :: TChan (IO a) -> TChan (Either SomeException a) -> IO () @@ -152,6 +167,81 @@ readAllTChan qvar = go [] Nothing -> return (reverse xs) Just x -> go (x : xs) +-- | Make a 'JobControl' where the parallism is controlled by a semaphore. +-- +-- This uses the GHC -jsem option to allow GHC to take additional semaphore slots +-- if we are not using them all. +newSemaphoreJobControl :: WithCallStack (Int -> IO (JobControl IO a)) +newSemaphoreJobControl n + | n < 1 || n > 1000 = + error $ "newParallelJobControl: not a sensible number of jobs: " ++ show n +newSemaphoreJobControl maxJobLimit = do + sem <- freshSemaphore "cabal_semaphore" maxJobLimit + notice normal $ + "Created semaphore called " + ++ getSemaphoreName (semaphoreName sem) + ++ " with " + ++ show maxJobLimit + ++ " slots." + outqVar <- newTChanIO + inqVar <- newTChanIO + countVar <- newTVarIO 0 + void (forkIO (worker sem inqVar outqVar)) + return + JobControl + { spawnJob = spawn inqVar countVar + , collectJob = collect outqVar countVar + , remainingJobs = remaining countVar + , cancelJobs = cancel inqVar countVar + , cleanupJobControl = destroySemaphore sem + , jobControlSemaphore = Just (semaphoreName sem) + } + where + worker :: Semaphore -> TChan (IO a) -> TChan (Either SomeException a) -> IO () + worker sem inqVar outqVar = + forever $ do + job <- atomically $ readTChan inqVar + -- mask here, as we need to ensure that the thread which contains the + -- release action is spawned. Otherwise, there is the chance that an + -- async exception is thrown between the semaphore being taken and the + -- thread being spawned. + mask_ $ do + waitOnSemaphore sem + void $ forkIOWithUnmask $ \unmask -> do + res <- try (unmask job) + releaseSemaphore sem 1 + atomically $ writeTChan outqVar res + -- Try to give GHC enough time to compute the module graph and then + -- request some additional capabilities if it can make use of them. The + -- ideal situation is that we have 1 GHC process running which has taken + -- all the capabilities in the semaphore, as this will reduce memory usage. + -- + -- 0.25s is chosen by discussion between MP and SD on Mar 17th 2023 as a number + -- which isn't too big and not too small but also, not scientifically. + threadDelay 250000 + + spawn :: TChan (IO a) -> TVar Int -> IO a -> IO () + spawn inqVar countVar job = + atomically $ do + modifyTVar' countVar (+ 1) + writeTChan inqVar job + + collect :: TChan (Either SomeException a) -> TVar Int -> IO a + collect outqVar countVar = do + res <- atomically $ do + modifyTVar' countVar (subtract 1) + readTChan outqVar + either throwIO return res + + remaining :: TVar Int -> IO Bool + remaining countVar = fmap (/= 0) $ atomically $ readTVar countVar + + cancel :: TChan (IO a) -> TVar Int -> IO () + cancel inqVar countVar = + atomically $ do + xs <- readAllTChan inqVar + modifyTVar' countVar (subtract (length xs)) + ------------------------- -- Job limits and locks -- diff --git a/cabal-install/src/Distribution/Client/ProjectBuilding.hs b/cabal-install/src/Distribution/Client/ProjectBuilding.hs index f915af1271..a47cad5203 100644 --- a/cabal-install/src/Distribution/Client/ProjectBuilding.hs +++ b/cabal-install/src/Distribution/Client/ProjectBuilding.hs @@ -47,6 +47,7 @@ import Prelude () import Distribution.Client.PackageHash (renderPackageHashInputs) import Distribution.Client.ProjectBuilding.Types import Distribution.Client.ProjectConfig +import Distribution.Client.ProjectConfig.Types import Distribution.Client.ProjectPlanning import Distribution.Client.ProjectPlanning.Types import Distribution.Client.RebuildMonad @@ -82,6 +83,7 @@ import Distribution.Client.Types hiding import Distribution.Client.Utils ( ProgressPhase (..) , findOpenProgramLocation + , numberOfProcessors , progressMessage , removeExistingFile ) @@ -97,6 +99,7 @@ import Distribution.Simple.Compiler ( Compiler , PackageDB (..) , compilerId + , jsemSupported ) import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.LocalBuildInfo @@ -122,10 +125,11 @@ import qualified Data.Set as Set import qualified Text.PrettyPrint as Disp -import Control.Exception (Handler (..), SomeAsyncException, assert, catches, handle) +import Control.Exception (Handler (..), SomeAsyncException, assert, bracket, catches, handle) import System.Directory (canonicalizePath, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, removeFile, renameDirectory) import System.FilePath (dropDrive, makeRelative, normalise, takeDirectory, (<.>), (</>)) import System.IO (Handle, IOMode (AppendMode), withFile) +import System.Semaphore (SemaphoreName (..)) import Distribution.Compat.Directory (listDirectory) import Distribution.Simple.Flag (fromFlagOrDefault) @@ -652,57 +656,64 @@ rebuildTargets | otherwise = do -- Concurrency control: create the job controller and concurrency limits -- for downloading, building and installing. - jobControl <- - if isParallelBuild - then newParallelJobControl buildSettingNumJobs - else newSerialJobControl + mkJobControl <- case buildSettingNumJobs of + Serial -> newSerialJobControl + NumJobs n -> newParallelJobControl (fromMaybe numberOfProcessors n) + UseSem n -> + if jsemSupported compiler + then newSemaphoreJobControl n + else do + warn verbosity "-jsem is not supported by the selected compiler, falling back to normal parallelism control." + newParallelJobControl n registerLock <- newLock -- serialise registration cacheLock <- newLock -- serialise access to setup exe cache -- TODO: [code cleanup] eliminate setup exe cache - debug verbosity $ + info verbosity $ "Executing install plan " - ++ if isParallelBuild - then " in parallel using " ++ show buildSettingNumJobs ++ " threads." - else " serially." + ++ case buildSettingNumJobs of + NumJobs n -> " in parallel using " ++ show n ++ " threads." + UseSem n -> " in parallel using a semaphore with " ++ show n ++ " slots." + Serial -> " serially." createDirectoryIfMissingVerbose verbosity True distBuildRootDirectory createDirectoryIfMissingVerbose verbosity True distTempDirectory traverse_ (createPackageDBIfMissing verbosity compiler progdb) packageDBsToUse - -- Before traversing the install plan, preemptively find all packages that - -- will need to be downloaded and start downloading them. - asyncDownloadPackages - verbosity - withRepoCtx - installPlan - pkgsBuildStatus - $ \downloadMap -> - -- For each package in the plan, in dependency order, but in parallel... - InstallPlan.execute - jobControl - keepGoing - (BuildFailure Nothing . DependentFailed . packageId) - installPlan - $ \pkg -> - -- TODO: review exception handling - handle (\(e :: BuildFailure) -> return (Left e)) $ fmap Right $ do - let uid = installedUnitId pkg - pkgBuildStatus = Map.findWithDefault (error "rebuildTargets") uid pkgsBuildStatus - - rebuildTarget - verbosity - distDirLayout - storeDirLayout - buildSettings - downloadMap - registerLock - cacheLock - sharedPackageConfig - installPlan - pkg - pkgBuildStatus + bracket (pure mkJobControl) cleanupJobControl $ \jobControl -> do + -- Before traversing the install plan, preemptively find all packages that + -- will need to be downloaded and start downloading them. + asyncDownloadPackages + verbosity + withRepoCtx + installPlan + pkgsBuildStatus + $ \downloadMap -> + -- For each package in the plan, in dependency order, but in parallel... + InstallPlan.execute + mkJobControl + keepGoing + (BuildFailure Nothing . DependentFailed . packageId) + installPlan + $ \pkg -> + -- TODO: review exception handling + handle (\(e :: BuildFailure) -> return (Left e)) $ fmap Right $ do + let uid = installedUnitId pkg + pkgBuildStatus = Map.findWithDefault (error "rebuildTargets") uid pkgsBuildStatus + + rebuildTarget + verbosity + distDirLayout + storeDirLayout + (jobControlSemaphore jobControl) + buildSettings + downloadMap + registerLock + cacheLock + sharedPackageConfig + installPlan + pkg + pkgBuildStatus where - isParallelBuild = buildSettingNumJobs >= 2 keepGoing = buildSettingKeepGoing withRepoCtx = projectConfigWithBuilderRepoContext @@ -780,6 +791,7 @@ rebuildTarget :: Verbosity -> DistDirLayout -> StoreDirLayout + -> Maybe SemaphoreName -> BuildTimeSettings -> AsyncFetchMap -> Lock @@ -793,6 +805,7 @@ rebuildTarget verbosity distDirLayout@DistDirLayout{distBuildDirectory} storeDirLayout + semaphoreName buildSettings downloadMap registerLock @@ -876,6 +889,7 @@ rebuildTarget verbosity distDirLayout storeDirLayout + semaphoreName buildSettings registerLock cacheLock @@ -894,6 +908,7 @@ rebuildTarget buildInplaceUnpackedPackage verbosity distDirLayout + semaphoreName buildSettings registerLock cacheLock @@ -1124,6 +1139,10 @@ buildAndInstallUnpackedPackage :: Verbosity -> DistDirLayout -> StoreDirLayout + -> Maybe SemaphoreName + -- ^ Whether to pass a semaphore to build process + -- this is different to BuildTimeSettings because the + -- name of the semaphore is created freshly each time. -> BuildTimeSettings -> Lock -> Lock @@ -1139,6 +1158,7 @@ buildAndInstallUnpackedPackage storeDirLayout@StoreDirLayout { storePackageDBStack } + maybe_semaphore BuildTimeSettings { buildSettingNumJobs , buildSettingLogFile @@ -1324,11 +1344,9 @@ buildAndInstallUnpackedPackage noticeProgress :: ProgressPhase -> IO () noticeProgress phase = - when isParallelBuild $ + when (isParallelBuild buildSettingNumJobs) $ progressMessage verbosity phase dispname - isParallelBuild = buildSettingNumJobs >= 2 - whenHaddock action | hasValidHaddockTargets pkg = action | otherwise = return () @@ -1344,7 +1362,10 @@ buildAndInstallUnpackedPackage configureArgs _ = setupHsConfigureArgs pkg buildCommand = Cabal.buildCommand defaultProgramDb - buildFlags _ = setupHsBuildFlags pkg pkgshared verbosity builddir + comp_par_strat = case maybe_semaphore of + Just sem_name -> Cabal.Flag (getSemaphoreName sem_name) + _ -> Cabal.NoFlag + buildFlags _ = setupHsBuildFlags comp_par_strat pkg pkgshared verbosity builddir haddockCommand = Cabal.haddockCommand haddockFlags _ = @@ -1385,7 +1406,7 @@ buildAndInstallUnpackedPackage distDirLayout srcdir builddir - isParallelBuild + (isParallelBuild buildSettingNumJobs) cacheLock setup :: CommandUI flags -> (Version -> flags) -> IO () @@ -1461,6 +1482,7 @@ hasValidHaddockTargets ElaboratedConfiguredPackage{..} buildInplaceUnpackedPackage :: Verbosity -> DistDirLayout + -> Maybe SemaphoreName -> BuildTimeSettings -> Lock -> Lock @@ -1479,6 +1501,7 @@ buildInplaceUnpackedPackage , distDirectory , distHaddockOutputDir } + maybe_semaphore BuildTimeSettings{buildSettingNumJobs, buildSettingHaddockOpen} registerLock cacheLock @@ -1641,7 +1664,9 @@ buildInplaceUnpackedPackage ipkgid = installedUnitId pkg dparams = elabDistDirParams pkgshared pkg - isParallelBuild = buildSettingNumJobs >= 2 + comp_par_strat = case maybe_semaphore of + Just sem_name -> Cabal.toFlag (getSemaphoreName sem_name) + _ -> Cabal.NoFlag packageFileMonitor = newPackageFileMonitor pkgshared distDirLayout dparams @@ -1697,6 +1722,7 @@ buildInplaceUnpackedPackage buildCommand = Cabal.buildCommand defaultProgramDb buildFlags _ = setupHsBuildFlags + comp_par_strat pkg pkgshared verbosity @@ -1751,7 +1777,7 @@ buildInplaceUnpackedPackage distDirLayout srcdir builddir - isParallelBuild + (isParallelBuild buildSettingNumJobs) cacheLock setupInteractive diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 2a0f82215c..4470d4fbfc 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -394,7 +394,12 @@ resolveBuildTimeSettings -- buildSettingLogVerbosity -- defined below, more complicated buildSettingBuildReports = fromFlag projectConfigBuildReports buildSettingSymlinkBinDir = flagToList projectConfigSymlinkBinDir - buildSettingNumJobs = determineNumJobs projectConfigNumJobs + buildSettingNumJobs = + if fromFlag projectConfigUseSemaphore + then UseSem (determineNumJobs projectConfigNumJobs) + else case (determineNumJobs projectConfigNumJobs) of + 1 -> Serial + n -> NumJobs (Just n) buildSettingKeepGoing = fromFlag projectConfigKeepGoing buildSettingOfflineMode = fromFlag projectConfigOfflineMode buildSettingKeepTempFiles = fromFlag projectConfigKeepTempFiles @@ -455,11 +460,9 @@ resolveBuildTimeSettings useDefaultTemplate | buildSettingBuildReports == DetailedReports = True | isJust givenTemplate = False - | isParallelBuild = True + | isParallelBuild buildSettingNumJobs = True | otherwise = False - isParallelBuild = buildSettingNumJobs >= 2 - substLogFileName :: PathTemplate -> Compiler @@ -489,7 +492,7 @@ resolveBuildTimeSettings overrideVerbosity | buildSettingBuildReports == DetailedReports = True | isJust givenTemplate = True - | isParallelBuild = False + | isParallelBuild buildSettingNumJobs = False | otherwise = False --------------------------------------------- diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index cf39d2940e..7814d6ef0c 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -819,6 +819,7 @@ convertLegacyBuildOnlyFlags , installReportPlanningFailure = projectConfigReportPlanningFailure , installSymlinkBinDir = projectConfigSymlinkBinDir , installNumJobs = projectConfigNumJobs + , installUseSemaphore = projectConfigUseSemaphore , installKeepGoing = projectConfigKeepGoing , installOfflineMode = projectConfigOfflineMode } = installFlags @@ -950,6 +951,7 @@ convertToLegacySharedConfig , installSymlinkBinDir = projectConfigSymlinkBinDir , installPerComponent = projectConfigPerComponent , installNumJobs = projectConfigNumJobs + , installUseSemaphore = projectConfigUseSemaphore , installKeepGoing = projectConfigKeepGoing , installRunTests = mempty , installOfflineMode = projectConfigOfflineMode @@ -1385,6 +1387,7 @@ legacySharedConfigFieldDescrs constraintSrc = , "remote-build-reporting" , "report-planning-failure" , "jobs" + , "semaphore" , "keep-going" , "offline" , "per-component" diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs index 3ae80d86d3..744a50ddc3 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Types.hs @@ -14,6 +14,9 @@ module Distribution.Client.ProjectConfig.Types -- * Resolving configuration , SolverSettings (..) , BuildTimeSettings (..) + , ParStratX (..) + , isParallelBuild + , ParStrat -- * Extra useful Monoids , MapLast (..) @@ -91,6 +94,7 @@ import Distribution.Version ) import qualified Data.Map as Map +import Distribution.Types.ParStrat ------------------------------- -- Project config types @@ -157,6 +161,7 @@ data ProjectConfigBuildOnly = ProjectConfigBuildOnly , projectConfigReportPlanningFailure :: Flag Bool , projectConfigSymlinkBinDir :: Flag FilePath , projectConfigNumJobs :: Flag (Maybe Int) + , projectConfigUseSemaphore :: Flag Bool , projectConfigKeepGoing :: Flag Bool , projectConfigOfflineMode :: Flag Bool , projectConfigKeepTempFiles :: Flag Bool @@ -454,7 +459,7 @@ data BuildTimeSettings = BuildTimeSettings , buildSettingBuildReports :: ReportLevel , buildSettingReportPlanningFailure :: Bool , buildSettingSymlinkBinDir :: [FilePath] - , buildSettingNumJobs :: Int + , buildSettingNumJobs :: ParStratInstall , buildSettingKeepGoing :: Bool , buildSettingOfflineMode :: Bool , buildSettingKeepTempFiles :: Bool diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 28066513de..10bf7c18cc 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -4280,18 +4280,23 @@ setupHsConfigureArgs elab@(ElaboratedConfiguredPackage{elabPkgOrComp = ElabCompo (compComponentName comp) setupHsBuildFlags - :: ElaboratedConfiguredPackage + :: Flag String + -> ElaboratedConfiguredPackage -> ElaboratedSharedConfig -> Verbosity -> FilePath -> Cabal.BuildFlags -setupHsBuildFlags _ _ verbosity builddir = +setupHsBuildFlags par_strat elab _ verbosity builddir = Cabal.BuildFlags { buildProgramPaths = mempty -- unused, set at configure time , buildProgramArgs = mempty -- unused, set at configure time , buildVerbosity = toFlag verbosity , buildDistPref = toFlag builddir , buildNumJobs = mempty -- TODO: [nice to have] sometimes want to use toFlag (Just numBuildJobs), + , buildUseSemaphore = + if elabSetupScriptCliVersion elab >= mkVersion [3, 9, 0, 0] + then par_strat + else mempty , buildArgs = mempty -- unused, passed via args not flags , buildCabalFilePath = mempty } diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs index 4cd7b207f7..680d4175c0 100644 --- a/cabal-install/src/Distribution/Client/Setup.hs +++ b/cabal-install/src/Distribution/Client/Setup.hs @@ -191,7 +191,6 @@ import Distribution.Simple.Setup , boolOpt , boolOpt' , falseArg - , optionNumJobs , optionVerbosity , readPackageDbList , showPackageDbList @@ -2074,6 +2073,7 @@ data InstallFlags = InstallFlags installSymlinkBinDir :: Flag FilePath , installPerComponent :: Flag Bool , installNumJobs :: Flag (Maybe Int) + , installUseSemaphore :: Flag Bool , installKeepGoing :: Flag Bool , installRunTests :: Flag Bool , installOfflineMode :: Flag Bool @@ -2116,6 +2116,7 @@ defaultInstallFlags = , installSymlinkBinDir = mempty , installPerComponent = Flag True , installNumJobs = mempty + , installUseSemaphore = Flag False , installKeepGoing = Flag False , installRunTests = mempty , installOfflineMode = Flag False @@ -2577,6 +2578,13 @@ installOptions showOrParseArgs = installRunTests (\v flags -> flags{installRunTests = v}) trueArg + , option + [] + ["semaphore"] + "Use a semaphore so GHC can compile components in parallel" + installUseSemaphore + (\v flags -> flags{installUseSemaphore = v}) + (yesNoOpt showOrParseArgs) , optionNumJobs installNumJobs (\v flags -> flags{installNumJobs = v}) @@ -2608,6 +2616,34 @@ installOptions showOrParseArgs = ] _ -> [] +optionNumJobs + :: (flags -> Flag (Maybe Int)) + -> (Flag (Maybe Int) -> flags -> flags) + -> OptionField flags +optionNumJobs get set = + option + "j" + ["jobs"] + "Run NUM jobs simultaneously (or '$ncpus' if no NUM is given)." + get + set + ( optArg + "NUM" + (fmap Flag numJobsParser) + ("", Flag Nothing) + (map (Just . maybe "$ncpus" show) . flagToList) + ) + where + numJobsParser :: ReadE (Maybe Int) + numJobsParser = ReadE $ \s -> + case s of + "$ncpus" -> Right Nothing + _ -> case reads s of + [(n, "")] + | n < 1 -> Left "The number of jobs should be 1 or more." + | otherwise -> Right (Just n) + _ -> Left "The jobs value should be a number or '$ncpus'" + instance Monoid InstallFlags where mempty = gmempty mappend = (<>) diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs index 4425a9a03a..fd9ed4ca19 100644 --- a/cabal-install/tests/IntegrationTests2.hs +++ b/cabal-install/tests/IntegrationTests2.hs @@ -85,6 +85,7 @@ import qualified Data.List as L import qualified Data.ByteString as BS import Distribution.Client.GlobalFlags (GlobalFlags, globalNix) import Distribution.Simple.Flag (Flag (Flag, NoFlag)) +import Distribution.Types.ParStrat import Data.Maybe (fromJust) #if !MIN_VERSION_directory(1,2,7) @@ -1760,7 +1761,7 @@ executePlan ((distDirLayout, cabalDirLayout, config, _, buildSettings), elaboratedShared pkgsBuildStatus -- Avoid trying to use act-as-setup mode: - buildSettings { buildSettingNumJobs = 1 } + buildSettings { buildSettingNumJobs = Serial } return (elaboratedPlan'', buildOutcomes) diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index b1108d7770..cdb34a3534 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -488,6 +488,7 @@ instance Arbitrary ProjectConfigBuildOnly where <*> arbitrary <*> arbitrary <*> arbitrary + <*> arbitrary <*> (fmap getShortToken <$> arbitrary) <*> arbitrary <*> (fmap getShortToken <$> arbitrary) @@ -508,6 +509,7 @@ instance Arbitrary ProjectConfigBuildOnly where , projectConfigReportPlanningFailure = x06 , projectConfigSymlinkBinDir = x07 , projectConfigNumJobs = x09 + , projectConfigUseSemaphore = x19 , projectConfigKeepGoing = x10 , projectConfigOfflineMode = x11 , projectConfigKeepTempFiles = x12 @@ -528,6 +530,7 @@ instance Arbitrary ProjectConfigBuildOnly where , projectConfigReportPlanningFailure = x06' , projectConfigSymlinkBinDir = x07' , projectConfigNumJobs = postShrink_NumJobs x09' + , projectConfigUseSemaphore = x19' , projectConfigKeepGoing = x10' , projectConfigOfflineMode = x11' , projectConfigKeepTempFiles = x12' @@ -540,13 +543,13 @@ instance Arbitrary ProjectConfigBuildOnly where | ( (x00', x01', x02', x03', x04') , (x05', x06', x07', x09') , (x10', x11', x12', x14') - , (x17', x18') + , (x17', x18', x19') ) <- shrink ( (x00, x01, x02, x03, x04) , (x05, x06, x07, preShrink_NumJobs x09) , (x10, x11, x12, x14) - , (x17, x18) + , (x17, x18, x19) ) ] where diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/C.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/C.hs new file mode 100644 index 0000000000..ac14f3ce5c --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/C.hs @@ -0,0 +1,3 @@ +module C where +import A +import B diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/Semaphore.cabal b/cabal-testsuite/PackageTests/NewBuild/Semaphore/Semaphore.cabal new file mode 100644 index 0000000000..4f2d0fb67a --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/Semaphore.cabal @@ -0,0 +1,22 @@ +-- Initial T3460.cabal generated by cabal init. For further documentation, +-- see http://haskell.org/cabal/users-guide/ + +name: Semaphore +version: 0.1.0.0 +-- synopsis: +-- description: +license: BSD3 +author: Edward Z. Yang +maintainer: ezyang@cs.stanford.edu +-- copyright: +-- category: +build-type: Simple +cabal-version: >=1.10 + +library + exposed-modules: C + -- other-modules: + -- other-extensions: + build-depends: base, sub-package-A, sub-package-B + -- hs-source-dirs: + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/Setup.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/Setup.hs new file mode 100644 index 0000000000..9a994af677 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.out b/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.project b/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.project new file mode 100644 index 0000000000..273ddad8cd --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.project @@ -0,0 +1,3 @@ +packages: ./Semaphore.cabal + , ./sub-package-A + , ./sub-package-B diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.test.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.test.hs new file mode 100644 index 0000000000..fe425b5c36 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/cabal.test.hs @@ -0,0 +1,6 @@ +import Test.Cabal.Prelude +main = cabalTest $ do + skipUnlessGhcVersion ">= 9.7" + -- Parallel flag means output of this test is nondeterministic + recordMode DoNotRecord $ + cabal "v2-build" ["-j", "--semaphore", "Semaphore"] diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/A.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/A.hs new file mode 100644 index 0000000000..d843c00b78 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/A.hs @@ -0,0 +1 @@ +module A where diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/Setup.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/Setup.hs new file mode 100644 index 0000000000..9a994af677 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/sub-package-A.cabal b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/sub-package-A.cabal new file mode 100644 index 0000000000..2f1f44ce52 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-A/sub-package-A.cabal @@ -0,0 +1,22 @@ +-- Initial sub-package-A.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +name: sub-package-A +version: 0.1.0.0 +-- synopsis: +-- description: +license: BSD3 +author: Edward Z. Yang +maintainer: ezyang@cs.stanford.edu +-- copyright: +-- category: +build-type: Simple +cabal-version: >=1.10 + +library + exposed-modules: A + -- other-modules: + -- other-extensions: + build-depends: base + -- hs-source-dirs: + default-language: Haskell2010 diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/B.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/B.hs new file mode 100644 index 0000000000..c759bc2d13 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/B.hs @@ -0,0 +1 @@ +module B where diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/Setup.hs b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/Setup.hs new file mode 100644 index 0000000000..9a994af677 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/sub-package-B.cabal b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/sub-package-B.cabal new file mode 100644 index 0000000000..2498184766 --- /dev/null +++ b/cabal-testsuite/PackageTests/NewBuild/Semaphore/sub-package-B/sub-package-B.cabal @@ -0,0 +1,22 @@ +-- Initial sub-package-B.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +name: sub-package-B +version: 0.1.0.0 +-- synopsis: +-- description: +license: BSD3 +author: Edward Z. Yang +maintainer: ezyang@cs.stanford.edu +-- copyright: +-- category: +build-type: Simple +cabal-version: >=1.10 + +library + exposed-modules: B + -- other-modules: + -- other-extensions: + build-depends: base + -- hs-source-dirs: + default-language: Haskell2010 diff --git a/changelog.d/pr-8557 b/changelog.d/pr-8557 new file mode 100644 index 0000000000..f2f11d2eea --- /dev/null +++ b/changelog.d/pr-8557 @@ -0,0 +1,20 @@ +synopsis: Add `--semaphore` flag to enable interaction with GHC Job Server protocol +packages: cabal-install +prs: #8557 + +description: { + +When cabal-install is passed the `--semaphore` flag it will now act as a job server +according to the GHC Jobserver Protocol. + +In particular this means that cabal-install will create a semaphore which it then +passes to `./Setup build` (and hence `ghc`) which can be used by `ghc` in order to +control how much paralellism it uses, coordinating with other simultaneously running +processes. + +This feature requires ghc-9.8 in order to use, as this is the first version of GHC +which implements the protocol. + +The protocol is specified by [GHC Proposal #540](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0540-jsem.rst). + +} diff --git a/changelog.d/pr-8557-2 b/changelog.d/pr-8557-2 new file mode 100644 index 0000000000..c472f992c3 --- /dev/null +++ b/changelog.d/pr-8557-2 @@ -0,0 +1,16 @@ +synopsis: Add --semaphore option to ./Setup build interface +packages: Cabal +prs: #8557 + +description: { + +When `./Setup build --semaphore <SEM>` is called, `ghc` will be called with the +`-jsem` option. It is the responsibility of the caller of `./Setup build` to +manage the semaphore according to the GHC Jobserver Protocol. + +This low level interface is intended to be called by a high-level tool such as +`cabal-install` which can create and manage the semaphore appropriately. + +The protocol is specified by [GHC Proposal #540](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0540-jsem.rst). + +} diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index 3c6f9f63e6..90f819a529 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -269,6 +269,22 @@ package, and thus apply globally: ``-j2`` is also supported; a bare ``--jobs`` or ``-j`` is equivalent to ``--jobs=$ncpus``. +.. cfg-field:: semaphore: boolean + --semaphore + --no-semaphore + :synopsis: Use GHC's support for semaphore based parallelism. + + :default: False + + This option instructs cabal to control parallelism by creating a new system semaphore, + whose number of tokens is specified by ``--jobs`` (or ``-j``). + This semaphore is passed to GHC, which allows it to use any leftover parallelism + that ``cabal-install`` is not using. + + Requires ``ghc >= 9.8``. + + The command line variant of this field is ``--semaphore``. + .. cfg-field:: keep-going: boolean --keep-going :synopsis: Try to continue building on failure. -- GitLab