Skip to content

Commit

Permalink
Haddock: Generate haddock for components
Browse files Browse the repository at this point in the history
Currently settings documentation: true enables documentation
generation via haddock for your whole package, including tests and benchmarks.
However, there are additional flags to control generation of
documentation for these "second class" documentation targets, which are
currently not honored at the cabal-install side of things. Namely,
tests, benchmarks, executables, etc. provided under the
haddock section in your $CABAL_HOME/config.

This patch adds a more sensible approach to documentation generation
via haddock. The new behaviour works like this:

Setting documentation: true or passing
--enable-documentation to cabal-install enables documentation
for any component in the build plan honoring the respective
flags for "second class" doc targets from configuration
or CLI.

Invoking new-haddock with a target selector will make sure
the respective flags for "second class" doc targets are set
correctly. E.g.

   $ new-haddock tests
Will generate documentation for the test suite of your package
even if you have tests: false in your haddock configuration.

Merge pull request #5226 from alexbiehl/pr/haddock-components
  • Loading branch information
ezyang authored Apr 12, 2018
2 parents 30d0c10 + d095feb commit f3cacff
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 25 deletions.
1 change: 1 addition & 0 deletions Cabal/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
into per-component condition trees anyway. Now it's finally been put
out of its misery (#4383).
* Added `Eta` to `CompilerFlavor` and to known compilers.
* `cabal haddock` now generates per-component documentation (#5226).

----

Expand Down
2 changes: 1 addition & 1 deletion Cabal/Distribution/Simple.hs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ haddockAction hooks flags args = do

hookedAction preHaddock haddockHook postHaddock
(return lbi { withPrograms = progs })
hooks flags' args
hooks flags' { haddockArgs = args } args

cleanAction :: UserHooks -> CleanFlags -> Args -> IO ()
cleanAction hooks flags args = do
Expand Down
16 changes: 15 additions & 1 deletion Cabal/Distribution/Simple/Haddock.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import Distribution.Types.ForeignLib
import Distribution.Types.UnqualComponentName
import Distribution.Types.ComponentLocalBuildInfo
import Distribution.Types.ExecutableScope
import Distribution.Types.LocalBuildInfo
import Distribution.Types.TargetInfo
import Distribution.Package
import qualified Distribution.ModuleName as ModuleName
import Distribution.PackageDescription as PD hiding (Flag)
Expand All @@ -46,6 +48,7 @@ import Distribution.Simple.Program
import Distribution.Simple.PreProcess
import Distribution.Simple.Setup
import Distribution.Simple.Build
import Distribution.Simple.BuildTarget
import Distribution.Simple.InstallDirs
import Distribution.Simple.LocalBuildInfo hiding (substPathTemplate)
import Distribution.Simple.BuildPaths
Expand Down Expand Up @@ -199,7 +202,18 @@ haddock pkg_descr lbi suffixes flags' = do
, fromFlags (haddockTemplateEnv lbi (packageId pkg_descr)) flags
, fromPackageDescription haddockTarget pkg_descr ]

withAllComponentsInBuildOrder pkg_descr lbi $ \component clbi -> do
targets <- readTargetInfos verbosity pkg_descr lbi (haddockArgs flags)

let
targets' =
case targets of
[] -> allTargetsInBuildOrder' pkg_descr lbi
_ -> targets

for_ targets' $ \target -> do
let component = targetComponent target
clbi = targetCLBI target

componentInitialBuildSteps (flag haddockDistPref) pkg_descr lbi clbi verbosity
preprocessComponent pkg_descr component lbi clbi False verbosity suffixes
let
Expand Down
12 changes: 8 additions & 4 deletions Cabal/Distribution/Simple/Setup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1470,7 +1470,8 @@ data HaddockFlags = HaddockFlags {
haddockDistPref :: Flag FilePath,
haddockKeepTempFiles:: Flag Bool,
haddockVerbosity :: Flag Verbosity,
haddockCabalFilePath :: Flag FilePath
haddockCabalFilePath :: Flag FilePath,
haddockArgs :: [String]
}
deriving (Show, Generic)

Expand All @@ -1494,7 +1495,8 @@ defaultHaddockFlags = HaddockFlags {
haddockDistPref = NoFlag,
haddockKeepTempFiles= Flag False,
haddockVerbosity = Flag normal,
haddockCabalFilePath = mempty
haddockCabalFilePath = mempty,
haddockArgs = mempty
}

haddockCommand :: CommandUI HaddockFlags
Expand All @@ -1504,8 +1506,10 @@ haddockCommand = CommandUI
, commandDescription = Just $ \_ ->
"Requires the program haddock, version 2.x.\n"
, commandNotes = Nothing
, commandUsage = \pname ->
"Usage: " ++ pname ++ " haddock [FLAGS]\n"
, commandUsage = usageAlternatives "haddock" $
[ "[FLAGS]"
, "COMPONENTS [FLAGS]"
]
, commandDefaultFlags = defaultHaddockFlags
, commandOptions = \showOrParseArgs ->
haddockOptions showOrParseArgs
Expand Down
2 changes: 1 addition & 1 deletion Cabal/Distribution/Simple/UserHooks.hs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ emptyUserHooks
preDoctest = rn,
doctestHook = ru,
postDoctest = ru,
preHaddock = rn,
preHaddock = rn',
haddockHook = ru,
postHaddock = ru,
preTest = rn',
Expand Down
14 changes: 10 additions & 4 deletions Cabal/doc/nix-local-build.rst
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,13 @@ they are up to date.
cabal new-haddock
-----------------

``cabal new-haddock [FLAGS] TARGET`` builds Haddock documentation for
``cabal new-haddock [FLAGS] [TARGET]`` builds Haddock documentation for
the specified packages within the project.

If a target is not a library :cfg-field:`haddock-benchmarks`,
:cfg-field:`haddock-executables`, :cfg-field:`haddock-internal`,
:cfg-field:`haddock-tests` will be implied as necessary.

cabal new-exec
---------------

Expand Down Expand Up @@ -1534,9 +1538,6 @@ Coverage options
Haddock options
^^^^^^^^^^^^^^^

Documentation building support is fairly sparse at the moment. Let us
know if it's a priority for you!

.. cfg-field:: documentation: boolean
--enable-documentation
--disable-documentation
Expand All @@ -1549,6 +1550,11 @@ know if it's a priority for you!
The command line variant of this flag is ``--enable-documentation``
and ``--disable-documentation``.

`documentation: true` does not imply :cfg-field:`haddock-benchmarks`,
:cfg-field:`haddock-executables`, :cfg-field:`haddock-internal` or
:cfg-field:`haddock-tests`. These need to be enabled separately if
desired.

.. cfg-field:: doc-index-file: templated path
--doc-index-file=TEMPLATE
:synopsis: Path to haddock templates.
Expand Down
3 changes: 2 additions & 1 deletion cabal-install/Distribution/Client/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ instance Semigroup SavedConfig where
haddockDistPref = combine haddockDistPref,
haddockKeepTempFiles = combine haddockKeepTempFiles,
haddockVerbosity = combine haddockVerbosity,
haddockCabalFilePath = combine haddockCabalFilePath
haddockCabalFilePath = combine haddockCabalFilePath,
haddockArgs = lastNonEmpty haddockArgs
}
where
combine = combine' savedHaddockFlags
Expand Down
12 changes: 7 additions & 5 deletions cabal-install/Distribution/Client/ProjectBuilding.hs
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,12 @@ packageFileMonitorKeyValues elab =
--
elab_config =
elab {
elabBuildTargets = [],
elabTestTargets = [],
elabBuildTargets = [],
elabTestTargets = [],
elabBenchTargets = [],
elabReplTarget = Nothing,
elabBuildHaddocks = False
elabReplTarget = Nothing,
elabHaddockTargets = [],
elabBuildHaddocks = False
}

-- The second part is the value used to guard the build step. So this is
Expand Down Expand Up @@ -1220,7 +1221,7 @@ buildInplaceUnpackedPackage verbosity
-- Haddock phase
whenHaddock $
annotateFailureNoLog HaddocksFailed $ do
setup haddockCommand haddockFlags []
setup haddockCommand haddockFlags haddockArgs
let haddockTarget = elabHaddockForHackage pkg
when (haddockTarget == Cabal.ForHackage) $ do
let dest = distDirectory </> name <.> "tar.gz"
Expand Down Expand Up @@ -1307,6 +1308,7 @@ buildInplaceUnpackedPackage verbosity
haddockCommand = Cabal.haddockCommand
haddockFlags _ = setupHsHaddockFlags pkg pkgshared
verbosity builddir
haddockArgs = setupHsHaddockArgs pkg

scriptOptions = setupHsScriptOptions rpkg plan pkgshared
srcdir builddir
Expand Down
6 changes: 4 additions & 2 deletions cabal-install/Distribution/Client/ProjectConfig/Legacy.hs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ commandLineFlagsToProjectConfig globalFlags configFlags configExFlags
splitConfig pc = (pc
, mempty { packageConfigProgramPaths = packageConfigProgramPaths pc
, packageConfigProgramArgs = packageConfigProgramArgs pc
, packageConfigProgramPathExtra = packageConfigProgramPathExtra pc })
, packageConfigProgramPathExtra = packageConfigProgramPathExtra pc
, packageConfigDocumentation = packageConfigDocumentation pc })

-- | Convert from the types currently used for the user-wide @~/.cabal/config@
-- file into the 'ProjectConfig' type.
Expand Down Expand Up @@ -733,7 +734,8 @@ convertToLegacyPerPackageConfig PackageConfig {..} =
haddockDistPref = mempty,
haddockKeepTempFiles = mempty,
haddockVerbosity = mempty,
haddockCabalFilePath = mempty
haddockCabalFilePath = mempty,
haddockArgs = mempty
}


Expand Down
55 changes: 49 additions & 6 deletions cabal-install/Distribution/Client/ProjectPlanning.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module Distribution.Client.ProjectPlanning (
setupHsCopyFlags,
setupHsRegisterFlags,
setupHsHaddockFlags,
setupHsHaddockArgs,

packageHashInputs,

Expand Down Expand Up @@ -1727,6 +1728,8 @@ elaborateInstallPlan verbosity platform compiler compilerprogdb pkgConfigDB
elabTestTargets = []
elabBenchTargets = []
elabReplTarget = Nothing
elabHaddockTargets = []

elabBuildHaddocks =
perPkgOptionFlag pkgid False packageConfigDocumentation

Expand Down Expand Up @@ -2437,6 +2440,7 @@ pkgHasEphemeralBuildTargets elab =
isJust (elabReplTarget elab)
|| (not . null) (elabTestTargets elab)
|| (not . null) (elabBenchTargets elab)
|| (not . null) (elabHaddockTargets elab)
|| (not . null) [ () | ComponentTarget _ subtarget <- elabBuildTargets elab
, subtarget /= WholeComponent ]

Expand Down Expand Up @@ -2535,11 +2539,22 @@ setRootTargets targetAction perPkgTargetsMap =
(Just tgts, TargetActionBuild) -> elab { elabBuildTargets = tgts }
(Just tgts, TargetActionTest) -> elab { elabTestTargets = tgts }
(Just tgts, TargetActionBench) -> elab { elabBenchTargets = tgts }
(Just [tgt], TargetActionRepl) -> elab { elabReplTarget = Just tgt }
(Just _, TargetActionHaddock) -> elab { elabBuildHaddocks = True }
(Just [tgt], TargetActionRepl) -> elab { elabReplTarget = Just tgt
, elabBuildHaddocks = False }
(Just tgts, TargetActionHaddock) ->
foldr setElabHaddockTargets (elab { elabHaddockTargets = tgts
, elabBuildHaddocks = True }) tgts
(Just _, TargetActionRepl) ->
error "pruneInstallPlanToTargets: multiple repl targets"

setElabHaddockTargets tgt elab
| isTestComponentTarget tgt = elab { elabHaddockTestSuites = True }
| isBenchComponentTarget tgt = elab { elabHaddockBenchmarks = True }
| isForeignLibComponentTarget tgt = elab { elabHaddockForeignLibs = True }
| isExeComponentTarget tgt = elab { elabHaddockExecutables = True }
| isSubLibComponentTarget tgt = elab { elabHaddockInternal = True }
| otherwise = elab

-- | Assuming we have previously set the root build targets (i.e. the user
-- targets but not rev deps yet), the first pruning pass does two things:
--
Expand All @@ -2560,14 +2575,16 @@ pruneInstallPlanPass1 pkgs =
roots = mapMaybe find_root pkgs'

prune elab = PrunedPackage elab' (pruneOptionalDependencies elab')
where elab' = addOptionalStanzas elab
where elab' =
setDocumentation
$ addOptionalStanzas elab

find_root (InstallPlan.Configured (PrunedPackage elab _)) =
if not (null (elabBuildTargets elab)
&& null (elabTestTargets elab)
&& null (elabBenchTargets elab)
&& isNothing (elabReplTarget elab)
&& not (elabBuildHaddocks elab))
&& null (elabHaddockTargets elab))
then Just (installedUnitId elab)
else Nothing
find_root _ = Nothing
Expand Down Expand Up @@ -2613,6 +2630,26 @@ pruneInstallPlanPass1 pkgs =
<> optionalStanzasWithDepsAvailable availablePkgs elab pkg
addOptionalStanzas elab = elab

setDocumentation :: ElaboratedConfiguredPackage -> ElaboratedConfiguredPackage
setDocumentation elab@ElaboratedConfiguredPackage { elabPkgOrComp = ElabComponent comp } =
elab {
elabBuildHaddocks =
elabBuildHaddocks elab && documentationEnabled (compSolverName comp) elab
}

where
documentationEnabled c =
case c of
CD.ComponentLib -> const True
CD.ComponentSubLib _ -> elabHaddockInternal
CD.ComponentFLib _ -> elabHaddockForeignLibs
CD.ComponentExe _ -> elabHaddockExecutables
CD.ComponentTest _ -> elabHaddockTestSuites
CD.ComponentBench _ -> elabHaddockBenchmarks
CD.ComponentSetup -> const False

setDocumentation elab = elab

-- Calculate package dependencies but cut out those needed only by
-- optional stanzas that we've determined we will not enable.
-- These pruned deps are not persisted in this pass since they're based on
Expand All @@ -2639,6 +2676,7 @@ pruneInstallPlanPass1 pkgs =
++ elabTestTargets pkg
++ elabBenchTargets pkg
++ maybeToList (elabReplTarget pkg)
++ elabHaddockTargets pkg
, stanza <- maybeToList (componentOptionalStanza cname)
]

Expand Down Expand Up @@ -3393,9 +3431,15 @@ setupHsHaddockFlags (ElaboratedConfiguredPackage{..}) _ verbosity builddir =
haddockDistPref = toFlag builddir,
haddockKeepTempFiles = mempty, --TODO: from build settings
haddockVerbosity = toFlag verbosity,
haddockCabalFilePath = mempty
haddockCabalFilePath = mempty,
haddockArgs = mempty
}

setupHsHaddockArgs :: ElaboratedConfiguredPackage -> [String]
-- TODO: Does the issue #3335 affects test as well
setupHsHaddockArgs elab =
map (showComponentTarget (packageId elab)) (elabHaddockTargets elab)

{-
setupHsTestFlags :: ElaboratedConfiguredPackage
-> ElaboratedSharedConfig
Expand Down Expand Up @@ -3584,4 +3628,3 @@ inplaceBinRoot
inplaceBinRoot layout config package
= distBuildDirectory layout (elabDistDirParams config package)
</> "build"

22 changes: 22 additions & 0 deletions cabal-install/Distribution/Client/ProjectPlanning/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ module Distribution.Client.ProjectPlanning.Types (
showBenchComponentTarget,
SubComponentTarget(..),

isSubLibComponentTarget,
isForeignLibComponentTarget,
isExeComponentTarget,
isTestComponentTarget,
isBenchComponentTarget,

-- * Setup script
SetupScriptStyle(..),
Expand Down Expand Up @@ -293,6 +297,8 @@ data ElaboratedConfiguredPackage
elabTestTargets :: [ComponentTarget],
elabBenchTargets :: [ComponentTarget],
elabReplTarget :: Maybe ComponentTarget,
elabHaddockTargets :: [ComponentTarget],

elabBuildHaddocks :: Bool,

--pkgSourceDir ? -- currently passed in later because they can use temp locations
Expand Down Expand Up @@ -703,6 +709,22 @@ showBenchComponentTarget :: PackageId -> ComponentTarget -> Maybe String
showBenchComponentTarget _ (ComponentTarget (CBenchName n) _) = Just $ display n
showBenchComponentTarget _ _ = Nothing

isBenchComponentTarget :: ComponentTarget -> Bool
isBenchComponentTarget (ComponentTarget (CBenchName _) _) = True
isBenchComponentTarget _ = False

isForeignLibComponentTarget :: ComponentTarget -> Bool
isForeignLibComponentTarget (ComponentTarget (CFLibName _) _) = True
isForeignLibComponentTarget _ = False

isExeComponentTarget :: ComponentTarget -> Bool
isExeComponentTarget (ComponentTarget (CExeName _) _ ) = True
isExeComponentTarget _ = False

isSubLibComponentTarget :: ComponentTarget -> Bool
isSubLibComponentTarget (ComponentTarget (CSubLibName _) _) = True
isSubLibComponentTarget _ = False

---------------------------
-- Setup.hs script policy
--
Expand Down

0 comments on commit f3cacff

Please sign in to comment.