Skip to content

Commit 6fc4f05

Browse files
committed
Add --cabal-path option
Allows usage with custom cabal path. Additionally, adds general --cabal-options argument, for passing through arbitrary cabal options. The latter subsumes the --jobs and --cabal-verbosity args, hence they are removed. The previous behavior can be achieved with e.g. clc-stackage --cabal-options='--jobs=semaphore --verbose=1'
1 parent d8099b8 commit 6fc4f05

File tree

6 files changed

+83
-130
lines changed

6 files changed

+83
-130
lines changed

clc-stackage.cabal

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ library utils
4343
, bytestring >=0.10.12.0 && <0.13
4444
, directory ^>=1.3.5.0
4545
, file-io ^>=0.1.0.0
46-
, filepath >=1.4.2.1 && <1.6
46+
, filepath >=1.4.100.0 && <1.6
4747
, pretty-terminal ^>=0.1.0.0
4848
, text >=1.2.3.2 && <2.2
4949
, time >=1.9.3 && <1.15
@@ -130,9 +130,7 @@ executable clc-stackage
130130

131131
library test-utils
132132
import: common-lang
133-
exposed-modules:
134-
Test.Utils
135-
133+
exposed-modules: Test.Utils
136134
build-depends:
137135
, base
138136
, tasty >=1.1.0.3 && <1.6
@@ -157,7 +155,7 @@ test-suite unit
157155
, runner
158156
, tasty
159157
, tasty-golden
160-
, tasty-hunit >=0.9 && <0.11
158+
, tasty-hunit >=0.9 && <0.11
161159
, test-utils
162160
, time
163161
, utils
@@ -177,9 +175,9 @@ test-suite functional
177175
, env-guard ^>=0.2
178176
, filepath
179177
, runner
180-
, test-utils
181178
, tasty
182179
, tasty-golden
180+
, test-utils
183181
, text
184182
, time
185183
, utils

src/builder/CLC/Stackage/Builder/Env.hs

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
-- | Provides the environment for building.
22
module CLC.Stackage.Builder.Env
33
( BuildEnv (..),
4-
CabalVerbosity (..),
5-
cabalVerbosityToArg,
6-
Jobs (..),
7-
jobsToArg,
84
Progress (..),
95
WriteLogs (..),
106
)
@@ -15,40 +11,6 @@ import CLC.Stackage.Utils.Logging qualified as Logging
1511
import Data.IORef (IORef)
1612
import Data.List.NonEmpty (NonEmpty)
1713
import Data.Set (Set)
18-
import Data.Word (Word8)
19-
20-
-- | Cabal's --verbose flag
21-
data CabalVerbosity
22-
= -- | V0
23-
CabalVerbosity0
24-
| -- | V1
25-
CabalVerbosity1
26-
| -- | V2
27-
CabalVerbosity2
28-
| -- | V3
29-
CabalVerbosity3
30-
deriving stock (Eq, Show)
31-
32-
cabalVerbosityToArg :: CabalVerbosity -> String
33-
cabalVerbosityToArg CabalVerbosity0 = "--verbose=0"
34-
cabalVerbosityToArg CabalVerbosity1 = "--verbose=1"
35-
cabalVerbosityToArg CabalVerbosity2 = "--verbose=2"
36-
cabalVerbosityToArg CabalVerbosity3 = "--verbose=3"
37-
38-
-- | Number of build jobs.
39-
data Jobs
40-
= -- | Literal number of jobs.
41-
JobsN Word8
42-
| -- | String "$ncpus"
43-
JobsNCpus
44-
| -- | Job semaphore. Requires GHC 9.8 and Cabal 3.12
45-
JobsSemaphore
46-
deriving stock (Eq, Show)
47-
48-
jobsToArg :: Jobs -> String
49-
jobsToArg (JobsN n) = "--jobs=" ++ show n
50-
jobsToArg JobsNCpus = "--jobs=$ncpus"
51-
jobsToArg JobsSemaphore = "--semaphore"
5214

5315
data Progress = MkProgress
5416
{ -- | Dependencies that built successfully.
@@ -74,6 +36,8 @@ data BuildEnv = MkBuildEnv
7436
batch :: Maybe Int,
7537
-- | Build arguments for cabal.
7638
buildArgs :: [String],
39+
-- | Optional path to cabal executable.
40+
cabalPath :: FilePath,
7741
-- | If true, colors logs.
7842
colorLogs :: Bool,
7943
-- | If true, the first group that fails to completely build stops

src/builder/CLC/Stackage/Builder/Process.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ where
88
import CLC.Stackage.Builder.Batch (PackageGroup (unPackageGroup))
99
import CLC.Stackage.Builder.Env
1010
( BuildEnv
11-
( colorLogs,
11+
( cabalPath,
12+
colorLogs,
1213
groupFailFast,
1314
hLogger,
1415
progress,
@@ -45,15 +46,15 @@ buildProject env idx pkgs = do
4546
let buildNoLogs :: IO ExitCode
4647
buildNoLogs =
4748
withGeneratedDir $
48-
(\(ec, _, _) -> ec) <$> P.readProcessWithExitCode "cabal" env.buildArgs ""
49+
(\(ec, _, _) -> ec) <$> P.readProcessWithExitCode env.cabalPath env.buildArgs ""
4950

5051
buildLogs :: Bool -> IO ExitCode
5152
buildLogs saveFailures = do
5253
(dirPath, stdoutPath, stderrPath) <- createCurrentLogsDir
5354

5455
IO.withBinaryFileWriteMode stdoutPath $ \stdoutHandle ->
5556
IO.withBinaryFileWriteMode stderrPath $ \stderrHandle -> do
56-
let createProc = P.proc "cabal" env.buildArgs
57+
let createProc = P.proc env.cabalPath env.buildArgs
5758
createProc' =
5859
createProc
5960
{ P.std_out = P.UseHandle stdoutHandle,

src/runner/CLC/Stackage/Runner/Args.hs

Lines changed: 37 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,9 @@ module CLC.Stackage.Runner.Args
66
where
77

88
import CLC.Stackage.Builder.Env
9-
( CabalVerbosity
10-
( CabalVerbosity0,
11-
CabalVerbosity1,
12-
CabalVerbosity2,
13-
CabalVerbosity3
14-
),
15-
Jobs (JobsN, JobsNCpus, JobsSemaphore),
16-
WriteLogs (WriteLogsCurrent, WriteLogsNone, WriteLogsSaveFailures),
9+
( WriteLogs (WriteLogsCurrent, WriteLogsNone, WriteLogsSaveFailures),
1710
)
11+
import Data.String qualified as Str
1812
import Options.Applicative
1913
( Mod,
2014
Parser,
@@ -35,7 +29,8 @@ import Options.Applicative.Help.Chunk (Chunk (Chunk))
3529
import Options.Applicative.Help.Chunk qualified as Chunk
3630
import Options.Applicative.Help.Pretty qualified as Pretty
3731
import Options.Applicative.Types (ArgPolicy (Intersperse))
38-
import Text.Read qualified as TR
32+
import System.OsPath (OsPath)
33+
import System.OsPath qualified as OsP
3934

4035
-- | Log coloring option.
4136
data ColorLogs
@@ -49,16 +44,16 @@ data Args = MkArgs
4944
{ -- | If given, batches packages together so we build more than one.
5045
-- Defaults to batching everything together in the same group.
5146
batch :: Maybe Int,
52-
-- | Cabal's --verbosity flag.
53-
cabalVerbosity :: Maybe CabalVerbosity,
47+
-- | Options to pass to cabal e.g. --semaphore.
48+
cabalOpts :: [String],
49+
-- | Optional path to cabal executable.
50+
cabalPath :: Maybe OsPath,
5451
-- | Determines if we color the logs. If 'Nothing', attempts to detect
5552
-- if colors are supported.
5653
colorLogs :: ColorLogs,
5754
-- | If true, the first group that fails to completely build stops
5855
-- clc-stackage.
5956
groupFailFast :: Bool,
60-
-- | Number of build jobs.
61-
jobs :: Maybe Jobs,
6257
-- | Disables the cache, which otherwise saves the outcome of a run in a
6358
-- json file. The cache is used for resuming a run that was interrupted.
6459
noCache :: Bool,
@@ -119,10 +114,10 @@ parseCliArgs :: Parser Args
119114
parseCliArgs =
120115
( do
121116
batch <- parseBatch
122-
cabalVerbosity <- parseCabalVerbosity
117+
cabalOpts <- parseCabalOpts
118+
cabalPath <- parseCabalPath
123119
colorLogs <- parseColorLogs
124120
groupFailFast <- parseGroupFailFast
125-
jobs <- parseJobs
126121
noCache <- parseNoCache
127122
noCleanup <- parseNoCleanup
128123
packageFailFast <- parsePackageFailFast
@@ -132,10 +127,10 @@ parseCliArgs =
132127
pure $
133128
MkArgs
134129
{ batch,
135-
cabalVerbosity,
130+
cabalOpts,
131+
cabalPath,
136132
colorLogs,
137133
groupFailFast,
138-
jobs,
139134
noCache,
140135
noCleanup,
141136
packageFailFast,
@@ -164,27 +159,37 @@ parseBatch =
164159
]
165160
)
166161

167-
parseCabalVerbosity :: Parser (Maybe CabalVerbosity)
168-
parseCabalVerbosity =
162+
parseCabalOpts :: Parser [String]
163+
parseCabalOpts =
164+
OA.option
165+
readOpts
166+
( mconcat
167+
[ OA.long "cabal-options",
168+
OA.metavar "ARGS...",
169+
OA.value [],
170+
mkHelp "Quoted arguments to pass to cabal e.g. '--semaphore --verbose=1'"
171+
]
172+
)
173+
where
174+
readOpts = Str.words <$> OA.str
175+
176+
parseCabalPath :: Parser (Maybe OsPath)
177+
parseCabalPath =
169178
OA.optional $
170179
OA.option
171-
readCabalVerbosity
180+
readOsPath
172181
( mconcat
173-
[ OA.long "cabal-verbosity",
174-
OA.metavar "(0 | 1 | 2 | 3)",
175-
mkHelp
176-
"Cabal's --verbose flag. Uses cabal's default if not given (1)."
182+
[ OA.long "cabal-path",
183+
OA.metavar "PATH",
184+
mkHelp "Optional path to cabal executable."
177185
]
178186
)
179187
where
180-
readCabalVerbosity =
181-
OA.str >>= \case
182-
"0" -> pure CabalVerbosity0
183-
"1" -> pure CabalVerbosity1
184-
"2" -> pure CabalVerbosity2
185-
"3" -> pure CabalVerbosity3
186-
other ->
187-
fail $ "Expected one of (0 | 1 | 2 | 3), received: " ++ other
188+
readOsPath = do
189+
fp <- OA.str
190+
case OsP.encodeUtf fp of
191+
Just osp -> pure osp
192+
Nothing -> fail $ "Failed encoding to ospath: " ++ fp
188193

189194
parseColorLogs :: Parser ColorLogs
190195
parseColorLogs =
@@ -220,44 +225,6 @@ parseGroupFailFast =
220225
"clc-stackage."
221226
]
222227

223-
parseJobs :: Parser (Maybe Jobs)
224-
parseJobs =
225-
OA.optional $
226-
OA.option
227-
readJobs
228-
( mconcat
229-
[ OA.short 'j',
230-
OA.long "jobs",
231-
OA.metavar "(NAT | $ncpus | semaphore)",
232-
mkHelp $
233-
mconcat
234-
[ "Controls the number of build jobs i.e. the flag passed to ",
235-
"cabal's --jobs option. Can be a natural number in [1, 255] ",
236-
"or the literal string '$ncpus', meaning all cpus. The ",
237-
"literal 'semaphore' will instead use cabal's --semaphore ",
238-
"option. This requires GHC 9.8+ and Cabal 3.12+. No option ",
239-
"uses cabal's default i.e. $ncpus."
240-
]
241-
]
242-
)
243-
where
244-
readJobs =
245-
OA.str >>= \case
246-
"$ncpus" -> pure JobsNCpus
247-
"semaphore" -> pure JobsSemaphore
248-
other -> case TR.readMaybe @Int other of
249-
Just n ->
250-
if n > 0 && n < 256
251-
then pure $ JobsN $ fromIntegral n
252-
else fail $ "Expected NAT in [1, 255], received: " ++ other
253-
Nothing ->
254-
fail $
255-
mconcat
256-
[ "Expected one of (NAT in [1, 255] | $ncpus | semaphore), ",
257-
"received: ",
258-
other
259-
]
260-
261228
parseNoCache :: Parser Bool
262229
parseNoCache =
263230
OA.switch

src/runner/CLC/Stackage/Runner/Env.hs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{-# LANGUAGE QuasiQuotes #-}
2+
13
module CLC.Stackage.Runner.Env
24
( RunnerEnv (..),
35
setup,
@@ -40,6 +42,7 @@ import CLC.Stackage.Runner.Args
4042
import CLC.Stackage.Runner.Args qualified as Args
4143
import CLC.Stackage.Runner.Report (Results (MkResults))
4244
import CLC.Stackage.Runner.Report qualified as Report
45+
import CLC.Stackage.Utils.Exception qualified as Ex
4346
import CLC.Stackage.Utils.IO qualified as IO
4447
import CLC.Stackage.Utils.Logging qualified as Logging
4548
import CLC.Stackage.Utils.Paths qualified as Paths
@@ -49,15 +52,17 @@ import Data.Bool (Bool (False, True), not)
4952
import Data.Foldable (Foldable (foldl'))
5053
import Data.IORef (newIORef, readIORef)
5154
import Data.List.NonEmpty (NonEmpty ((:|)))
52-
import Data.Maybe (Maybe (Just, Nothing), fromMaybe, maybe)
55+
import Data.Maybe (Maybe (Just, Nothing), fromMaybe)
5356
import Data.Set (Set)
5457
import Data.Set qualified as Set
5558
import Data.Text qualified as T
5659
import Data.Time (LocalTime)
5760
import System.Console.Pretty (supportsPretty)
5861
import System.Directory.OsPath qualified as Dir
5962
import System.Exit (ExitCode (ExitSuccess))
60-
import Prelude (IO, mconcat, pure, show, ($), (++), (.), (<$>), (<>))
63+
import System.OsPath (osp)
64+
import System.OsPath qualified as OsP
65+
import Prelude (IO, Monad ((>>=)), mconcat, pure, show, ($), (++), (.), (<$>), (<>))
6166

6267
-- | Args used for building all packages.
6368
data RunnerEnv = MkRunnerEnv
@@ -90,18 +95,22 @@ setup hLogger modifyPackages = do
9095

9196
-- Set up build args for cabal, filling in missing defaults
9297
let buildArgs =
93-
["build"]
94-
++ cabalVerboseArg
95-
++ jobsArg
96-
++ keepGoingArg
97-
98-
cabalVerboseArg = toArgs Builder.Env.cabalVerbosityToArg cliArgs.cabalVerbosity
99-
jobsArg = toArgs Builder.Env.jobsToArg cliArgs.jobs
98+
"build"
99+
: keepGoingArg
100+
++ cliArgs.cabalOpts
100101

101102
-- when packageFailFast is false, add keep-going so that we build as many
102103
-- packages in the group.
103104
keepGoingArg = ["--keep-going" | not cliArgs.packageFailFast]
104105

106+
let cabalPathRaw = fromMaybe [osp|cabal|] cliArgs.cabalPath
107+
cabalPath <-
108+
Dir.findExecutable cabalPathRaw >>= \case
109+
-- TODO: It would be nice to avoid the decode here and keep everything
110+
-- in OsPath, though that is blocked until process support OsPath.
111+
Just p -> OsP.decodeUtf p
112+
Nothing -> Ex.throwText "Cabal not found"
113+
105114
successesRef <- newIORef Set.empty
106115
failuresRef <- newIORef Set.empty
107116

@@ -158,6 +167,7 @@ setup hLogger modifyPackages = do
158167
MkBuildEnv
159168
{ batch = cliArgs.batch,
160169
buildArgs,
170+
cabalPath,
161171
colorLogs,
162172
groupFailFast = cliArgs.groupFailFast,
163173
hLogger,
@@ -187,9 +197,6 @@ setup hLogger modifyPackages = do
187197
version = p.version
188198
}
189199

190-
toArgs :: (a -> b) -> Maybe a -> [b]
191-
toArgs f = maybe [] (\x -> [f x])
192-
193200
-- | Prints summary and writes results to disk.
194201
teardown :: RunnerEnv -> IO ()
195202
teardown env = do

0 commit comments

Comments
 (0)