Skip to content

Commit a2bd2c1

Browse files
committed
feature: batched compilation of extra-sources
All C sources for a component are now compiled with a single one-shot (the `-c` option) GHC invocation, rather than one-per-source, and ditto for each other type of extra-source. In addition, `--semaphore` now propagates `-jsem` to these GHC invocations if working with a GHC that supports this (see https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12388/ for the status of that support).
1 parent 9f004e7 commit a2bd2c1

File tree

14 files changed

+153
-87
lines changed

14 files changed

+153
-87
lines changed

Cabal-tests/tests/UnitTests/Distribution/Simple/Program/GHC.hs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Distribution.Types.ParStrat
1010
import Distribution.Simple.Flag
1111
import Distribution.Simple.Compiler (Compiler(..), CompilerId(..), CompilerFlavor(..), AbiTag(NoAbiTag))
1212
import Distribution.PackageDescription (emptyPackageDescription)
13-
import Distribution.Simple.Program.GHC (normaliseGhcArgs, renderGhcOptions, ghcOptNumJobs)
13+
import Distribution.Simple.Program.GHC (normaliseGhcArgs, renderGhcOptions, GhcMode(..), ghcOptNumJobs, ghcOptMode)
1414
import Distribution.Version (mkVersion)
1515

1616
tests :: TestTree
@@ -44,7 +44,7 @@ tests = testGroup "Distribution.Simple.Program.GHC"
4444
assertListEquals flags options_9_0_affects
4545
]
4646
, testGroup "renderGhcOptions"
47-
[ testCase "options" $ do
47+
[ testCase "parallel make" $ do
4848
let flags :: [String]
4949
flags = renderGhcOptions
5050
(Compiler
@@ -57,8 +57,27 @@ tests = testGroup "Distribution.Simple.Program.GHC"
5757
, compilerWiredInUnitIds = Nothing
5858
})
5959
(Platform X86_64 Linux)
60-
(mempty { ghcOptNumJobs = Flag (NumJobs (Just 4)) })
61-
assertListEquals flags ["-j4", "-clear-package-db"]
60+
(mempty
61+
{ ghcOptMode = Flag GhcModeMake
62+
, ghcOptNumJobs = Flag (NumJobs (Just 4)) })
63+
assertListEquals flags ["--make", "-j4", "-clear-package-db"]
64+
, testCase "parallel batch" $ do
65+
let flags :: [String]
66+
flags = renderGhcOptions
67+
(Compiler
68+
{ compilerId = CompilerId GHC (mkVersion [9,8,1])
69+
, compilerAbiTag = NoAbiTag
70+
, compilerCompat = []
71+
, compilerLanguages = []
72+
, compilerExtensions = []
73+
, compilerProperties = Map.singleton "Support parallel batch mode" "YES"
74+
, compilerWiredInUnitIds = Nothing
75+
})
76+
(Platform X86_64 Linux)
77+
(mempty
78+
{ ghcOptMode = Flag GhcModeCompile
79+
, ghcOptNumJobs = Flag (NumJobs (Just 4)) })
80+
assertListEquals flags ["-c", "-j4", "-clear-package-db"]
6281
]
6382
]
6483

Cabal/src/Distribution/Simple/Compiler.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ module Distribution.Simple.Compiler
6767
, extensionsToFlags
6868
, unsupportedExtensions
6969
, parmakeSupported
70+
, parBatchSupported
7071
, reexportedModulesSupported
7172
, renamingPackageFlagsSupported
7273
, unifiedIPIDRequired
@@ -447,6 +448,10 @@ extensionToFlag' comp ext = lookup ext (compilerExtensions comp)
447448
parmakeSupported :: Compiler -> Bool
448449
parmakeSupported = ghcSupported "Support parallel --make"
449450

451+
-- | Does this compiler support parallel batch mode?
452+
parBatchSupported :: Compiler -> Bool
453+
parBatchSupported = ghcSupported "Support parallel batch mode"
454+
450455
-- | Does this compiler support reexported-modules?
451456
reexportedModulesSupported :: Compiler -> Bool
452457
reexportedModulesSupported = ghcSupported "Support reexported-modules"

Cabal/src/Distribution/Simple/GHC/Build.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ build numJobs pkg_descr pbci = do
145145
(Nothing, Just mainFile)
146146
Nothing -> (Nothing, Nothing)
147147
buildOpts <- buildHaskellModules numJobs ghcProg hsMainFile inputModules buildTargetDir finalModBuildWays pbci
148-
extraSources <- buildAllExtraSources nonHsMainFile ghcProg buildTargetDir wantedWays pbci
148+
extraSources <- buildAllExtraSources numJobs nonHsMainFile ghcProg buildTargetDir wantedWays pbci
149149
linkOrLoadComponent
150150
ghcProg
151151
pkg_descr

Cabal/src/Distribution/Simple/GHC/Build/ExtraSources.hs

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
module Distribution.Simple.GHC.Build.ExtraSources where
88

9-
import Control.Monad
9+
import Control.Monad (forM, when)
1010
import Data.Foldable
1111
import Distribution.Simple.Flag
1212
import qualified Distribution.Simple.GHC.Internal as Internal
@@ -16,6 +16,7 @@ import Distribution.Utils.NubList
1616

1717
import Distribution.Types.BuildInfo
1818
import Distribution.Types.Component
19+
import Distribution.Types.ParStrat
1920
import Distribution.Types.TargetInfo
2021

2122
import Distribution.Simple.Build.Inputs
@@ -32,7 +33,8 @@ import Distribution.Verbosity (Verbosity)
3233
-- | An action that builds all the extra build sources of a component, i.e. C,
3334
-- C++, Js, Asm, C-- sources.
3435
buildAllExtraSources
35-
:: Maybe (SymbolicPath Pkg File)
36+
:: Flag ParStrat
37+
-> Maybe (SymbolicPath Pkg File)
3638
-- ^ An optional non-Haskell Main file
3739
-> ConfiguredProgram
3840
-- ^ The GHC configured program
@@ -58,7 +60,8 @@ buildCSources
5860
, buildJsSources
5961
, buildAsmSources
6062
, buildCmmSources
61-
:: Maybe (SymbolicPath Pkg File)
63+
:: Flag ParStrat
64+
-> Maybe (SymbolicPath Pkg File)
6265
-- ^ An optional non-Haskell Main file
6366
-> ConfiguredProgram
6467
-- ^ The GHC configured program
@@ -70,7 +73,7 @@ buildCSources
7073
-- ^ The context and component being built in it.
7174
-> IO (NubListR (SymbolicPath Pkg File))
7275
-- ^ Returns the list of extra sources that were built
73-
buildCSources mbMainFile =
76+
buildCSources parStrat mbMainFile =
7477
buildExtraSources
7578
"C Sources"
7679
Internal.componentCcGhcOptions
@@ -83,7 +86,8 @@ buildCSources mbMainFile =
8386
cFiles ++ [main]
8487
_otherwise -> cFiles
8588
)
86-
buildCxxSources mbMainFile =
89+
parStrat
90+
buildCxxSources parStrat mbMainFile =
8791
buildExtraSources
8892
"C++ Sources"
8993
Internal.componentCxxGhcOptions
@@ -96,7 +100,8 @@ buildCxxSources mbMainFile =
96100
cxxFiles ++ [main]
97101
_otherwise -> cxxFiles
98102
)
99-
buildJsSources _mbMainFile ghcProg buildTargetDir neededWays = do
103+
parStrat
104+
buildJsSources parStrat _mbMainFile ghcProg buildTargetDir neededWays = do
100105
Platform hostArch _ <- hostPlatform <$> localBuildInfo
101106
let hasJsSupport = hostArch == JavaScript
102107
buildExtraSources
@@ -111,19 +116,22 @@ buildJsSources _mbMainFile ghcProg buildTargetDir neededWays = do
111116
jsSources (componentBuildInfo c)
112117
else mempty
113118
)
119+
parStrat
114120
ghcProg
115121
buildTargetDir
116122
neededWays
117-
buildAsmSources _mbMainFile =
123+
buildAsmSources parStrat _mbMainFile =
118124
buildExtraSources
119125
"Assembler Sources"
120126
Internal.componentAsmGhcOptions
121127
(asmSources . componentBuildInfo)
122-
buildCmmSources _mbMainFile =
128+
parStrat
129+
buildCmmSources parStrat _mbMainFile =
123130
buildExtraSources
124131
"C-- Sources"
125132
Internal.componentCmmGhcOptions
126133
(cmmSources . componentBuildInfo)
134+
parStrat
127135

128136
-- | Create 'PreBuildComponentRules' for a given type of extra build sources
129137
-- which are compiled via a GHC invocation with the given options. Used to
@@ -136,7 +144,6 @@ buildExtraSources
136144
-> BuildInfo
137145
-> ComponentLocalBuildInfo
138146
-> SymbolicPath Pkg (Dir Artifacts)
139-
-> SymbolicPath Pkg File
140147
-> GhcOptions
141148
)
142149
-- ^ Function to determine the @'GhcOptions'@ for the
@@ -149,6 +156,7 @@ buildExtraSources
149156
-- @'Executable'@ components might additionally add the
150157
-- program entry point (@main-is@ file) to the extra sources,
151158
-- if it should be compiled as the rest of them.
159+
-> Flag ParStrat
152160
-> ConfiguredProgram
153161
-- ^ The GHC configured program
154162
-> SymbolicPath Pkg (Dir Artifacts)
@@ -163,6 +171,7 @@ buildExtraSources
163171
description
164172
componentSourceGhcOptions
165173
viewSources
174+
parStrat
166175
ghcProg
167176
buildTargetDir
168177
(neededLibWays, neededFLibWay, neededExeWay) =
@@ -189,84 +198,82 @@ buildExtraSources
189198
platform
190199
mbWorkDir
191200

192-
buildAction :: SymbolicPath Pkg File -> IO ()
193-
buildAction sourceFile = do
201+
buildAction :: [SymbolicPath Pkg File] -> IO ()
202+
buildAction sourceFiles = do
194203
let baseSrcOpts =
195204
componentSourceGhcOptions
196205
verbosity
197206
lbi
198207
bi
199208
clbi
200209
buildTargetDir
201-
sourceFile
202210
vanillaSrcOpts =
203-
-- -fPIC is used in case you are using the repl
204-
-- of a dynamically linked GHC
205-
baseSrcOpts{ghcOptFPic = toFlag True}
211+
baseSrcOpts
212+
{ ghcOptFPic = toFlag True
213+
, -- -fPIC is always used in case you are using the repl of a
214+
-- dynamically linked GHC
215+
ghcOptNumJobs = parStrat
216+
}
206217
profSrcOpts =
207218
vanillaSrcOpts
208-
`mappend` mempty
209-
{ ghcOptProfilingMode = toFlag True
210-
}
219+
{ ghcOptProfilingMode = toFlag True
220+
, ghcOptObjSuffix = toFlag "p_o"
221+
}
211222
sharedSrcOpts =
212223
vanillaSrcOpts
213-
`mappend` mempty
214-
{ ghcOptFPic = toFlag True
215-
, ghcOptDynLinkMode = toFlag GhcDynamicOnly
216-
}
224+
{ ghcOptDynLinkMode = toFlag GhcDynamicOnly
225+
, ghcOptObjSuffix = toFlag "dyn_o"
226+
}
217227
profSharedSrcOpts =
218228
vanillaSrcOpts
219-
`mappend` mempty
220-
{ ghcOptProfilingMode = toFlag True
221-
, ghcOptFPic = toFlag True
222-
, ghcOptDynLinkMode = toFlag GhcDynamicOnly
223-
}
229+
{ ghcOptProfilingMode = toFlag True
230+
, ghcOptDynLinkMode = toFlag GhcDynamicOnly
231+
, ghcOptObjSuffix = toFlag "p_dyn_o"
232+
}
224233
-- TODO: Placing all Haskell, C, & C++ objects in a single directory
225234
-- Has the potential for file collisions. In general we would
226235
-- consider this a user error. However, we should strive to
227236
-- add a warning if this occurs.
228237
odir = fromFlag (ghcOptObjDir vanillaSrcOpts)
229238

230-
compileIfNeeded :: GhcOptions -> IO ()
231-
compileIfNeeded opts = do
232-
needsRecomp <- checkNeedsRecompilation mbWorkDir sourceFile opts
233-
when needsRecomp $ runGhcProg opts
234-
235239
createDirectoryIfMissingVerbose verbosity True (i odir)
236-
case targetComponent targetInfo of
237-
-- For libraries, we compile extra objects in the four ways: vanilla, shared, profiled and profiled shared.
238-
-- We suffix shared objects with `.dyn_o`, profiled ones with `.p_o` and profiled shared ones with `.p_dyn_o`.
239-
CLib _lib
240-
-- Unless for repl, in which case we only need the vanilla way
241-
| BuildRepl _ <- buildingWhat ->
242-
compileIfNeeded vanillaSrcOpts
243-
| otherwise ->
244-
do
245-
forM_ (neededLibWays isIndef) $ \case
246-
StaticWay -> compileIfNeeded vanillaSrcOpts
247-
DynWay -> compileIfNeeded sharedSrcOpts{ghcOptObjSuffix = toFlag "dyn_o"}
248-
ProfWay -> compileIfNeeded profSrcOpts{ghcOptObjSuffix = toFlag "p_o"}
249-
ProfDynWay -> compileIfNeeded profSharedSrcOpts{ghcOptObjSuffix = toFlag "p_dyn_o"}
250-
CFLib flib ->
251-
case neededFLibWay (withDynFLib flib) of
252-
StaticWay -> compileIfNeeded vanillaSrcOpts
253-
DynWay -> compileIfNeeded sharedSrcOpts
254-
ProfWay -> compileIfNeeded profSrcOpts
255-
ProfDynWay -> compileIfNeeded profSharedSrcOpts
256-
-- For the remaining component types (Exec, Test, Bench), we also
257-
-- determine with which options to build the objects (vanilla vs shared vs
258-
-- profiled), but predicate is the same for the three kinds.
259-
_exeLike ->
260-
case neededExeWay of
261-
StaticWay -> compileIfNeeded vanillaSrcOpts
262-
DynWay -> compileIfNeeded sharedSrcOpts
263-
ProfWay -> compileIfNeeded profSrcOpts
264-
ProfDynWay -> compileIfNeeded profSharedSrcOpts
240+
let optsForWay = \case
241+
StaticWay -> vanillaSrcOpts
242+
DynWay -> sharedSrcOpts
243+
ProfWay -> profSrcOpts
244+
ProfDynWay -> profSharedSrcOpts
245+
-- we don't tell GHC to suffix the filenames of objects produced from
246+
-- extra-sources for executables or foreign libraries
247+
let stripObjectSuffix opts =
248+
opts{ghcOptObjSuffix = ghcOptObjSuffix baseSrcOpts}
265249

250+
let optsForNeededWays =
251+
case targetComponent targetInfo of
252+
-- For libraries (foreign or not), we compile extra objects in the four ways: vanilla, shared,
253+
-- profiled and profiled shared. We suffix shared objects with `.dyn_o`, profiled ones with `.p_o`
254+
-- and profiled shared ones with `.p_dyn_o`.
255+
CLib _lib
256+
-- Unless for repl, in which case we only need the vanilla way
257+
| BuildRepl _ <- buildingWhat ->
258+
[vanillaSrcOpts]
259+
| otherwise ->
260+
optsForWay <$> neededLibWays isIndef
261+
CFLib flib ->
262+
[stripObjectSuffix (optsForWay (neededFLibWay (withDynFLib flib)))]
263+
-- For the remaining component types (Exec, Test, Bench) we
264+
-- only need to build them with one set of options.
265+
_exeLike ->
266+
[stripObjectSuffix (optsForWay neededExeWay)]
267+
forM_ optsForNeededWays $ \opts -> do
268+
files <- fmap concat $ forM sourceFiles $ \sourceFile -> do
269+
needsRecomp <- checkNeedsRecompilation mbWorkDir sourceFile opts
270+
return [sourceFile | needsRecomp]
271+
when (not (null files)) $
272+
runGhcProg opts{ghcOptInputFiles = toNubListR files}
266273
-- build any sources
267274
if (null sources || componentIsIndefinite clbi)
268275
then return mempty
269276
else do
270277
info verbosity ("Building " ++ description ++ "...")
271-
traverse_ buildAction sources
278+
buildAction sources
272279
return (toNubListR sources)

Cabal/src/Distribution/Simple/GHC/Build/Link.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ linkOrLoadComponent
112112
let
113113
extraSourcesObjs :: [RelativePath Artifacts File]
114114
extraSourcesObjs =
115+
-- note that extra-sources objects are not suffixed with the way suffix
116+
-- right now
115117
[ makeRelativePathEx $ getSymbolicPath src `replaceExtension` objExtension
116118
| src <- extraSources
117119
]

0 commit comments

Comments
 (0)