diff --git a/cabal-docspec/Changelog.md b/cabal-docspec/Changelog.md index 7101933..2b8459e 100644 --- a/cabal-docspec/Changelog.md +++ b/cabal-docspec/Changelog.md @@ -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 diff --git a/cabal-docspec/MANUAL.md b/cabal-docspec/MANUAL.md index 26d4687..f3e68dc 100644 --- a/cabal-docspec/MANUAL.md +++ b/cabal-docspec/MANUAL.md @@ -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* @@ -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 ======== diff --git a/cabal-docspec/Makefile b/cabal-docspec/Makefile index f77fce5..1b8fbf8 100644 --- a/cabal-docspec/Makefile +++ b/cabal-docspec/Makefile @@ -1,4 +1,4 @@ -VERSION=0.0.0.20230517 +VERSION=0.0.0.20240414 EXETARGET=cabal-docspec cabal-docspec.1 : MANUAL.md diff --git a/cabal-docspec/cabal-docspec.1 b/cabal-docspec/cabal-docspec.1 index d1ee21e..fa6f7bf 100644 --- a/cabal-docspec/cabal-docspec.1 +++ b/cabal-docspec/cabal-docspec.1 @@ -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 @@ -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. @@ -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 diff --git a/cabal-docspec/cabal-docspec.cabal b/cabal-docspec/cabal-docspec.cabal index 1b0a15a..9b3dba1 100644 --- a/cabal-docspec/cabal-docspec.cabal +++ b/cabal-docspec/cabal-docspec.cabal @@ -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: diff --git a/cabal-docspec/src/CabalDocspec/Main.hs b/cabal-docspec/src/CabalDocspec/Main.hs index 08e9b45..a549159 100644 --- a/cabal-docspec/src/CabalDocspec/Main.hs +++ b/cabal-docspec/src/CabalDocspec/Main.hs @@ -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