Skip to content

Commit b6ed312

Browse files
committed
Merge pull request #753 from commercialhaskell/651-target-parse-overhaul
651 target parse overhaul
2 parents 6e19976 + eb01438 commit b6ed312

File tree

19 files changed

+856
-431
lines changed

19 files changed

+856
-431
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
## Unreleased changes
22

33
* Detect unlisted modules and TemplateHaskell dependent files (#32, #105)
4+
* Overhauled target parsing, added `--test` and `--bench` options [#651](https://github.com/commercialhaskell/stack/issues/651)
5+
* For details, see [Build commands Wiki page](https://github.com/commercialhaskell/stack/wiki/Build-command)
6+
* `--exec` option [#651](https://github.com/commercialhaskell/stack/issues/651)
7+
* `--only-dependencies` implemented correctly [#387](https://github.com/commercialhaskell/stack/issues/387)
48

59
## 0.1.2.2
610

src/Options/Applicative/Args.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
module Options.Applicative.Args
66
(argsArgument
77
,argsOption
8+
,cmdOption
89
,parseArgsFromString)
910
where
1011

@@ -27,6 +28,16 @@ argsOption =
2728
(do string <- O.str
2829
either O.readerError return (parseArgsFromString string))
2930

31+
-- | An option which accepts a command and a list of arguments e.g. @--exec "echo hello world"@
32+
cmdOption :: O.Mod O.OptionFields (String, [String]) -> O.Parser (String, [String])
33+
cmdOption =
34+
O.option
35+
(do string <- O.str
36+
xs <- either O.readerError return (parseArgsFromString string)
37+
case xs of
38+
[] -> O.readerError "Must provide a command"
39+
x:xs' -> return (x, xs'))
40+
3041
-- | Parse from a string.
3142
parseArgsFromString :: String -> Either String [String]
3243
parseArgsFromString = P.parseOnly (argsParser Escaping) . T.pack

src/Stack/Build.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ build setLocalFiles mbuildLk bopts = do
9797
preFetch plan
9898

9999
if boptsDryrun bopts
100-
then printPlan (boptsFinalAction bopts) plan
100+
then printPlan plan
101101
else executePlan menv bopts baseConfigOpts locals sourceMap plan
102102
where
103103
profiling = boptsLibProfile bopts || boptsExeProfile bopts

src/Stack/Build/ConstructPlan.hs

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,22 @@ data AddDepRes
7474
| ADRFound InstallLocation Version Installed
7575
deriving Show
7676

77+
data W = W
78+
{ wFinals :: !(Map PackageName (Either ConstructPlanException (Task, LocalPackageTB)))
79+
, wInstall :: !(Map Text InstallLocation)
80+
-- ^ executable to be installed, and location where the binary is placed
81+
, wDirty :: !(Map PackageName Text)
82+
-- ^ why a local package is considered dirty
83+
, wDeps :: !(Set PackageName)
84+
-- ^ Packages which count as dependencies
85+
}
86+
instance Monoid W where
87+
mempty = W mempty mempty mempty mempty
88+
mappend (W a b c d) (W w x y z) = W (mappend a w) (mappend b x) (mappend c y) (mappend d z)
89+
7790
type M = RWST
7891
Ctx
79-
( Map PackageName (Either ConstructPlanException Task) -- finals
80-
, Map Text InstallLocation -- executable to be installed, and location where the binary is placed
81-
, Map PackageName Text -- why a local package is considered dirty
82-
)
92+
W
8393
(Map PackageName (Either ConstructPlanException AddDepRes))
8494
IO
8595

@@ -121,14 +131,25 @@ constructPlan mbp0 baseConfigOpts0 locals extraToBuild0 locallyRegistered loadPa
121131
let latest = Map.fromListWith max $ map toTuple $ Map.keys caches
122132

123133
econfig <- asks getEnvConfig
124-
let onWanted =
125-
case boptsFinalAction $ bcoBuildOpts baseConfigOpts0 of
126-
DoNothing -> void . addDep . packageName . lpPackage
127-
_ -> addFinal
134+
let onWanted lp = do
135+
{-
136+
- Arguably this is the right thing to do. However, forcing the
137+
- library to rebuild causes the cabal_macros.h file to change,
138+
- which makes GHC rebuild everything...
139+
140+
case lpExeComponents lp of
141+
Nothing -> return ()
142+
Just _ -> void $ addDep $ packageName $ lpPackage lp
143+
-}
144+
145+
case lpTestBench lp of
146+
Just tb -> addFinal lp tb
147+
-- See comment above
148+
Nothing -> void $ addDep False $ packageName $ lpPackage lp
128149
let inner = do
129150
mapM_ onWanted $ filter lpWanted locals
130-
mapM_ addDep $ Set.toList extraToBuild0
131-
((), m, (efinals, installExes, dirtyReason)) <- liftIO $ runRWST inner (ctx econfig latest) M.empty
151+
mapM_ (addDep False) $ Set.toList extraToBuild0
152+
((), m, W efinals installExes dirtyReason deps) <- liftIO $ runRWST inner (ctx econfig latest) M.empty
132153
let toEither (_, Left e) = Left e
133154
toEither (k, Right v) = Right (k, v)
134155
(errlibs, adrs) = partitionEithers $ map toEither $ M.toList m
@@ -139,11 +160,12 @@ constructPlan mbp0 baseConfigOpts0 locals extraToBuild0 locallyRegistered loadPa
139160
let toTask (_, ADRFound _ _ _) = Nothing
140161
toTask (name, ADRToInstall task) = Just (name, task)
141162
tasks = M.fromList $ mapMaybe toTask adrs
142-
maybeStripLocals
143-
| boptsOnlySnapshot $ bcoBuildOpts baseConfigOpts0 =
144-
stripLocals
145-
| otherwise = id
146-
return $ maybeStripLocals Plan
163+
takeSubset =
164+
case boptsBuildSubset $ bcoBuildOpts baseConfigOpts0 of
165+
BSAll -> id
166+
BSOnlySnapshot -> stripLocals
167+
BSOnlyDependencies -> stripNonDeps deps
168+
return $ takeSubset Plan
147169
{ planTasks = tasks
148170
, planFinals = M.fromList finals
149171
, planUnregisterLocal = mkUnregisterLocal tasks dirtyReason locallyRegistered
@@ -192,14 +214,14 @@ mkUnregisterLocal tasks dirtyReason locallyRegistered =
192214
ident = ghcPkgIdPackageIdentifier gid
193215
name = packageIdentifierName ident
194216

195-
addFinal :: LocalPackage -> M ()
196-
addFinal lp = do
197-
depsRes <- addPackageDeps package
217+
addFinal :: LocalPackage -> LocalPackageTB -> M ()
218+
addFinal lp lptb = do
219+
depsRes <- addPackageDeps False package
198220
res <- case depsRes of
199221
Left e -> return $ Left e
200222
Right (missing, present, _minLoc) -> do
201223
ctx <- ask
202-
return $ Right Task
224+
return $ Right (Task
203225
{ taskProvides = PackageIdentifier
204226
(packageName package)
205227
(packageVersion package)
@@ -214,32 +236,38 @@ addFinal lp = do
214236
package
215237
, taskPresent = present
216238
, taskType = TTLocal lp
217-
}
218-
tell (Map.singleton (packageName package) res, mempty, mempty)
239+
}, lptb)
240+
tell mempty { wFinals = Map.singleton (packageName package) res }
219241
where
220-
package = lpPackageFinal lp
242+
package = lptbPackage lptb
221243

222-
addDep :: PackageName -> M (Either ConstructPlanException AddDepRes)
223-
addDep name = do
244+
addDep :: Bool -- ^ is this being used by a dependency?
245+
-> PackageName -> M (Either ConstructPlanException AddDepRes)
246+
addDep treatAsDep' name = do
247+
ctx <- ask
248+
let treatAsDep = treatAsDep' || name `Set.notMember` wanted ctx
249+
when treatAsDep $ markAsDep name
224250
m <- get
225251
case Map.lookup name m of
226252
Just res -> return res
227253
Nothing -> do
228-
res <- addDep' name
254+
res <- addDep' treatAsDep name
229255
modify $ Map.insert name res
230256
return res
231257

232-
addDep' :: PackageName -> M (Either ConstructPlanException AddDepRes)
233-
addDep' name = do
258+
addDep' :: Bool -- ^ is this being used by a dependency?
259+
-> PackageName -> M (Either ConstructPlanException AddDepRes)
260+
addDep' treatAsDep name = do
234261
ctx <- ask
235262
if name `elem` callStack ctx
236263
then return $ Left $ DependencyCycleDetected $ name : callStack ctx
237264
else local
238265
(\ctx' -> ctx' { callStack = name : callStack ctx' }) $ do
239-
(addDep'' name)
266+
(addDep'' treatAsDep name)
240267

241-
addDep'' :: PackageName -> M (Either ConstructPlanException AddDepRes)
242-
addDep'' name = do
268+
addDep'' :: Bool -- ^ is this being used by a dependency?
269+
-> PackageName -> M (Either ConstructPlanException AddDepRes)
270+
addDep'' treatAsDep name = do
243271
ctx <- ask
244272
case Map.lookup name $ combinedMap ctx of
245273
-- TODO look up in the package index and see if there's a
@@ -250,12 +278,12 @@ addDep'' name = do
250278
return $ Right $ ADRFound loc version installed
251279
Just (PIOnlySource ps) -> do
252280
tellExecutables name ps
253-
installPackage name ps
281+
installPackage treatAsDep name ps
254282
Just (PIBoth ps installed) -> do
255283
tellExecutables name ps
256-
needInstall <- checkNeedInstall name ps installed (wanted ctx)
284+
needInstall <- checkNeedInstall treatAsDep name ps installed (wanted ctx)
257285
if needInstall
258-
then installPackage name ps
286+
then installPackage treatAsDep name ps
259287
else return $ Right $ ADRFound (piiLocation ps) (piiVersion ps) installed
260288

261289
tellExecutables :: PackageName -> PackageSource -> M () -- TODO merge this with addFinal above?
@@ -283,10 +311,10 @@ tellExecutablesPackage loc p = do
283311
Just (PIOnlySource ps) -> goSource ps
284312
Just (PIBoth ps _) -> goSource ps
285313

286-
goSource (PSLocal lp) = lpComponents lp
314+
goSource (PSLocal lp) = fromMaybe Set.empty $ lpExeComponents lp
287315
goSource (PSUpstream _ _ _) = Set.empty
288316

289-
tell (Map.empty, m myComps, Map.empty)
317+
tell mempty { wInstall = m myComps }
290318
where
291319
m myComps = Map.fromList $ map (, loc) $ Set.toList
292320
$ filterComps myComps $ packageExes p
@@ -300,11 +328,12 @@ tellExecutablesPackage loc p = do
300328
-- TODO There are a lot of duplicated computations below. I've kept that for
301329
-- simplicity right now
302330

303-
installPackage :: PackageName -> PackageSource -> M (Either ConstructPlanException AddDepRes)
304-
installPackage name ps = do
331+
installPackage :: Bool -- ^ is this being used by a dependency?
332+
-> PackageName -> PackageSource -> M (Either ConstructPlanException AddDepRes)
333+
installPackage treatAsDep name ps = do
305334
ctx <- ask
306335
package <- psPackage name ps
307-
depsRes <- addPackageDeps package
336+
depsRes <- addPackageDeps treatAsDep package
308337
case depsRes of
309338
Left e -> return $ Left e
310339
Right (missing, present, minLoc) -> do
@@ -331,29 +360,31 @@ installPackage name ps = do
331360
PSUpstream _ loc _ -> TTUpstream package $ loc <> minLoc
332361
}
333362

334-
checkNeedInstall :: PackageName -> PackageSource -> Installed -> Set PackageName -> M Bool
335-
checkNeedInstall name ps installed wanted = assert (piiLocation ps == Local) $ do
363+
checkNeedInstall :: Bool
364+
-> PackageName -> PackageSource -> Installed -> Set PackageName -> M Bool
365+
checkNeedInstall treatAsDep name ps installed wanted = assert (piiLocation ps == Local) $ do
336366
package <- psPackage name ps
337-
depsRes <- addPackageDeps package
367+
depsRes <- addPackageDeps treatAsDep package
338368
case depsRes of
339369
Left _e -> return True -- installPackage will find the error again
340370
Right (missing, present, _loc)
341371
| Set.null missing -> checkDirtiness ps installed package present wanted
342372
| otherwise -> do
343-
tell (Map.empty, Map.empty, Map.singleton name $
373+
tell mempty { wDirty = Map.singleton name $
344374
let t = T.intercalate ", " $ map (T.pack . packageNameString . packageIdentifierName) (Set.toList missing)
345375
in T.append "missing dependencies: " $
346376
if T.length t < 100
347377
then t
348-
else T.take 97 t <> "...")
378+
else T.take 97 t <> "..." }
349379
return True
350380

351-
addPackageDeps :: Package -> M (Either ConstructPlanException (Set PackageIdentifier, Set GhcPkgId, InstallLocation))
352-
addPackageDeps package = do
381+
addPackageDeps :: Bool -- ^ is this being used by a dependency?
382+
-> Package -> M (Either ConstructPlanException (Set PackageIdentifier, Set GhcPkgId, InstallLocation))
383+
addPackageDeps treatAsDep package = do
353384
ctx <- ask
354385
deps' <- packageDepsWithTools package
355386
deps <- forM (Map.toList deps') $ \(depname, range) -> do
356-
eres <- addDep depname
387+
eres <- addDep treatAsDep depname
357388
let mlatest = Map.lookup depname $ latestVersions ctx
358389
case eres of
359390
Left e ->
@@ -403,7 +434,7 @@ checkDirtiness ps installed package present wanted = do
403434
, configCacheDeps = present
404435
, configCacheComponents =
405436
case ps of
406-
PSLocal lp -> Set.map encodeUtf8 $ lpComponents lp
437+
PSLocal lp -> Set.map renderComponent $ lpComponents lp
407438
PSUpstream _ _ _ -> Set.empty
408439
, configCacheHaddock =
409440
shouldHaddockPackage buildOpts wanted (packageName package) ||
@@ -420,7 +451,7 @@ checkDirtiness ps installed package present wanted = do
420451
case mreason of
421452
Nothing -> return False
422453
Just reason -> do
423-
tell (Map.empty, Map.empty, Map.singleton (packageName package) reason)
454+
tell mempty { wDirty = Map.singleton (packageName package) reason }
424455
return True
425456

426457
describeConfigDiff :: ConfigCache -> ConfigCache -> Text
@@ -493,3 +524,15 @@ stripLocals plan = plan
493524
TTLocal _ -> False
494525
TTUpstream _ Local -> False
495526
TTUpstream _ Snap -> True
527+
528+
stripNonDeps :: Set PackageName -> Plan -> Plan
529+
stripNonDeps deps plan = plan
530+
{ planTasks = Map.filter checkTask $ planTasks plan
531+
, planFinals = Map.empty
532+
, planInstallExes = Map.empty -- TODO maybe don't disable this?
533+
}
534+
where
535+
checkTask task = packageIdentifierName (taskProvides task) `Set.member` deps
536+
537+
markAsDep :: PackageName -> M ()
538+
markAsDep name = tell mempty { wDeps = Set.singleton name }

0 commit comments

Comments
 (0)