Skip to content

Commit

Permalink
Merge pull request #150 from phadej/subextralibs-b
Browse files Browse the repository at this point in the history
Also test sublibraries
  • Loading branch information
phadej authored Apr 14, 2024
2 parents 51acd33 + d9ce62a commit 648c338
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 63 deletions.
5 changes: 5 additions & 0 deletions cabal-docspec/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.0.0.20240414

- `--extra-package` accepts sublibraries, i.e. `mypkg:sublib` syntax.
- `cabal-docspec` tests all library components, also internal (visible and invisible) components.

# 0.0.0.20231219

- Pass `default-language` flag to GHC
Expand Down
10 changes: 9 additions & 1 deletion cabal-docspec/MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ However, in this list we mostly only list and show the --option version of them.
**\--extra-package** *pkgname*

: An extra package to make available in GHCi session. The package must
exist in the plan.
exist in the plan. Sublibrary syntax **mypkg:mysublib** is also accepted.

**\--timeout** *seconds*

Expand Down Expand Up @@ -701,6 +701,14 @@ There are a few differences.
won't cause ambiguous module problems, as long as the library being tested
itself depends only on either one.

Q: How to run tests on internal modules?
----------------------------------------

cabal-docspec can only test the exported interfaces, so it's not possible to
test **other-modules**. However, cabal-docspec does test *internal* libraries.
Therefore you can put the internal modules into internal library and then
cabal-docspec will be able to test them.

SEE ALSO
========

Expand Down
2 changes: 1 addition & 1 deletion cabal-docspec/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=0.0.0.20230517
VERSION=0.0.0.20240414
EXETARGET=cabal-docspec

cabal-docspec.1 : MANUAL.md
Expand Down
10 changes: 9 additions & 1 deletion cabal-docspec/cabal-docspec.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH CABAL-DOCSPEC 1 "May 17th, 2023" "cabal-docspec 0.0.0.20230517" "Cabal Extras"
.TH CABAL-DOCSPEC 1 "May 17th, 2023" "cabal-docspec 0.0.0.20240414" "Cabal Extras"
.SH NAME
.PP
cabal-docspec - another doctest for Haskell
Expand Down Expand Up @@ -83,6 +83,7 @@ Can be specified multiple times.
\f[B]--extra-package\f[R] \f[I]pkgname\f[R]
An extra package to make available in GHCi session.
The package must exist in the plan.
Sublibrary syntax \f[B]mypkg:mysublib\f[R] is also accepted.
.TP
\f[B]--timeout\f[R] \f[I]seconds\f[R]
Timeout for evaluation of single expression.
Expand Down Expand Up @@ -751,6 +752,13 @@ For example \f[I]Prelude.Compat\f[R] from \f[I]base-compat\f[R] and
\f[I]base-compat-batteries\f[R] won\[cq]t cause ambiguous module
problems, as long as the library being tested itself depends only on
either one.
.SS Q: How to run tests on internal modules?
.PP
cabal-docspec can only test the exported interfaces, so it\[cq]s not
possible to test \f[B]other-modules\f[R].
However, cabal-docspec does test \f[I]internal\f[R] libraries.
Therefore you can put the internal modules into internal library and
then cabal-docspec will be able to test them.
.SH SEE ALSO
.PP
doctest(1) https://hackage.haskell.org/package/doctest
Expand Down
2 changes: 1 addition & 1 deletion cabal-docspec/cabal-docspec.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 2.2
name: cabal-docspec
version: 0.0.0.20230517
version: 0.0.0.20240414
synopsis: Run examples in your docs
category: Development
description:
Expand Down
133 changes: 74 additions & 59 deletions cabal-docspec/src/CabalDocspec/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -209,69 +209,84 @@ testComponent
-> Plan.CompName
-> Plan.CompInfo
-> Peu r Summary
testComponent tracer0 tracerTop dynOptsCli ghcInfo buildDir cabalCfg plan env pkg unit cn@Plan.CompNameLib ci = do
traceApp tracerTop $ TraceComponent (C.packageId (pkgGpd pkg)) cn
testComponent tracer0 tracerTop dynOptsCli ghcInfo buildDir cabalCfg plan env pkg unit cn ci = do
case cn of
Plan.CompNameLib -> do
traceApp tracerTop $ TraceComponent (C.packageId (pkgGpd pkg)) cn

-- "configure"
lib0 <- maybe (die tracerTop "no library component in GPD") return
$ C.condLibrary $ pkgGpd pkg
let (_, lib) = simplifyCondTree ghcInfo (Map.mapKeys toCabal $ Plan.uFlags unit) lib0
let bi = C.libBuildInfo lib
let cppEnabled = Ext.EnableExtension Ext.CPP `elem` C.defaultExtensions bi

-- append options in .cabal file
dynOptsBi <- dynOptsFromBuildInfo tracerTop bi
let dynOpts = dynOptsCli (dynOptsBi defaultDynOpts)

-- Once dynOpts is read we can read adjust verbosity of our tracer
let tracer = adjustTracer (optVerbosity dynOpts) tracer0

-- find extra units
extraUnitIds <- findExtraPackages tracer plan $ Set.toList $ propPkgs dynOpts <> optExtraPkgs dynOpts

-- find library module paths
modulePaths <- findModules
tracer
(pkgDir pkg)
(C.hsSourceDirs bi)
(C.exposedModules lib)

let pkgIds :: [PackageIdentifier]
pkgIds =
[ toCabal $ Plan.uPId unit'
| unitId <- toList (Plan.ciLibDeps ci)
, Just unit' <- return $ Plan.pjUnits plan ^? ix unitId
]
lib0 <- maybe (die tracerTop "no library component in GPD") return
$ C.condLibrary $ pkgGpd pkg
aux lib0

let unitIds :: [UnitId]
unitIds = ordNub $
toCabal (Plan.uId unit) :
map toCabal (toList (Plan.ciLibDeps ci)) ++
extraUnitIds
Plan.CompNameSubLib ln -> do
traceApp tracerTop $ TraceComponent (C.packageId (pkgGpd pkg)) cn
let qn = C.mkUnqualComponentName (T.unpack ln)

-- cpp include dirs
cppDirs <- traverse makeAbsolute (optCppIncludeDirs dynOpts)
lib0 <- maybe (die tracerTop $ "no sublibrary component in GPD" <> prettyShow qn) return
$ Map.lookup qn sublibs
aux lib0

-- first phase: read modules and extract the comments
let pkgVer = C.packageVersion (pkgGpd pkg)
modules <- for modulePaths $ \(modname, modpath) ->
phase1 tracer ghcInfo (Just buildDir) (C.packageName (pkgGpd pkg)) pkgVer (pkgDir pkg) cppEnabled cppDirs pkgIds bi modname modpath

-- extract doctests from the modules.
let parsed :: [Module [Located DocTest]]
parsed = fmap4 (doctestStripComments (optStripComs dynOpts))
$ parseModules modules where

validated <- validate tracer parsed
if optPhase dynOpts > Phase1
then do
phase2 tracer dynOpts unitIds ghcInfo (Just buildDir) cabalCfg (pkgDir pkg) env validated
else
return $ foldMap skipModule parsed

-- Skip other components
testComponent _tracer0 _tracerTop _dynOpts _ghcInfo _buildDir _cabalCfg _plan _env _pkg _unit _cn _ci =
return mempty
-- Skip other components
_ -> return mempty
where
sublibs :: Map C.UnqualComponentName (C.CondTree C.ConfVar [C.Dependency] C.Library)
sublibs = Map.fromList (C.condSubLibraries $ pkgGpd pkg)

aux lib0 = do
-- "configure"
let (_, lib) = simplifyCondTree ghcInfo (Map.mapKeys toCabal $ Plan.uFlags unit) lib0
let bi = C.libBuildInfo lib
let cppEnabled = Ext.EnableExtension Ext.CPP `elem` C.defaultExtensions bi

-- append options in .cabal file
dynOptsBi <- dynOptsFromBuildInfo tracerTop bi
let dynOpts = dynOptsCli (dynOptsBi defaultDynOpts)

-- Once dynOpts is read we can read adjust verbosity of our tracer
let tracer = adjustTracer (optVerbosity dynOpts) tracer0

-- find extra units
extraUnitIds <- findExtraPackages tracer plan $ Set.toList $ propPkgs dynOpts <> optExtraPkgs dynOpts

-- find library module paths
modulePaths <- findModules
tracer
(pkgDir pkg)
(C.hsSourceDirs bi)
(C.exposedModules lib)

let pkgIds :: [PackageIdentifier]
pkgIds =
[ toCabal $ Plan.uPId unit'
| unitId <- toList (Plan.ciLibDeps ci)
, Just unit' <- return $ Plan.pjUnits plan ^? ix unitId
]

let unitIds :: [UnitId]
unitIds = ordNub $
toCabal (Plan.uId unit) :
map toCabal (toList (Plan.ciLibDeps ci)) ++
extraUnitIds

-- cpp include dirs
cppDirs <- traverse makeAbsolute (optCppIncludeDirs dynOpts)

-- first phase: read modules and extract the comments
let pkgVer = C.packageVersion (pkgGpd pkg)
modules <- for modulePaths $ \(modname, modpath) ->
phase1 tracer ghcInfo (Just buildDir) (C.packageName (pkgGpd pkg)) pkgVer (pkgDir pkg) cppEnabled cppDirs pkgIds bi modname modpath

-- extract doctests from the modules.
let parsed :: [Module [Located DocTest]]
parsed = fmap4 (doctestStripComments (optStripComs dynOpts))
$ parseModules modules where

validated <- validate tracer parsed
if optPhase dynOpts > Phase1
then do
phase2 tracer dynOpts unitIds ghcInfo (Just buildDir) cabalCfg (pkgDir pkg) env validated
else
return $ foldMap skipModule parsed

-------------------------------------------------------------------------------
-- Test component without
Expand Down

0 comments on commit 648c338

Please sign in to comment.