Skip to content

Commit 3923101

Browse files
committed
Merge pull request #2877 from mmakowski/848-manpage-consolidated
Manual page generation
2 parents a555616 + c142d83 commit 3923101

File tree

8 files changed

+364
-81
lines changed

8 files changed

+364
-81
lines changed

Cabal/Distribution/Simple/Command.hs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
{-# LANGUAGE CPP #-}
1+
{-# LANGUAGE CPP, ExistentialQuantification #-}
22
-----------------------------------------------------------------------------
33
-- |
44
-- Module : Distribution.Simple.Command
55
-- Copyright : Duncan Coutts 2007
66
-- License : BSD3
77
--
88
-- Maintainer : [email protected]
9-
-- Portability : portable
9+
-- Portability : non-portable (ExistentialQuantification)
1010
--
1111
-- This is to do with command line handling. The Cabal command line is
1212
-- organised into a number of named sub-commands (much like darcs). The
@@ -38,6 +38,11 @@ module Distribution.Simple.Command (
3838
commandAddAction,
3939
noExtraFlags,
4040

41+
-- ** Building lists of commands
42+
CommandType(..),
43+
CommandSpec(..),
44+
commandFromSpec,
45+
4146
-- ** Running commands
4247
commandsRun,
4348

@@ -605,3 +610,13 @@ helpCommandUI =
605610
++ " " ++ pname ++ " help help\n"
606611
++ " Oh, appararently you already know this.\n"
607612
}
613+
614+
-- | wraps a @CommandUI@ together with a function that turns it into a @Command@.
615+
-- By hiding the type of flags for the UI allows construction of a list of all UIs at the
616+
-- top level of the program. That list can then be used for generation of manual page
617+
-- as well as for executing the selected command.
618+
data CommandSpec action
619+
= forall flags. CommandSpec (CommandUI flags) (CommandUI flags -> Command action) CommandType
620+
621+
commandFromSpec :: CommandSpec a -> Command a
622+
commandFromSpec (CommandSpec ui action _) = action ui
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
{-# LANGUAGE CPP #-}
2+
-----------------------------------------------------------------------------
3+
-- |
4+
-- Module : Distribution.Client.Manpage
5+
-- Copyright : (c) Maciek Makowski 2015
6+
-- License : BSD-like
7+
--
8+
-- Maintainer : [email protected]
9+
-- Stability : provisional
10+
-- Portability : portable
11+
--
12+
-- Functions for building the manual page.
13+
14+
module Distribution.Client.Manpage
15+
( -- * Manual page generation
16+
manpage
17+
) where
18+
19+
import Distribution.Simple.Command
20+
import Distribution.Client.Setup (globalCommand)
21+
22+
import Data.Char (toUpper)
23+
import Data.List (intercalate)
24+
25+
data FileInfo = FileInfo String String -- ^ path, description
26+
27+
-- | A list of files that should be documented in the manual page.
28+
files :: [FileInfo]
29+
files =
30+
[ (FileInfo "~/.cabal/config" "The defaults that can be overridden with command-line options.")
31+
, (FileInfo "~/.cabal/world" "A list of all packages whose installation has been explicitly requested.")
32+
]
33+
34+
-- | Produces a manual page with @troff@ markup.
35+
manpage :: String -> [CommandSpec a] -> String
36+
manpage pname commands = unlines $
37+
[ ".TH " ++ map toUpper pname ++ " 1"
38+
, ".SH NAME"
39+
, pname ++ " \\- a system for building and packaging Haskell libraries and programs"
40+
, ".SH SYNOPSIS"
41+
, ".B " ++ pname
42+
, ".I command"
43+
, ".RI < arguments |[ options ]>..."
44+
, ""
45+
, "Where the"
46+
, ".I commands"
47+
, "are"
48+
, ""
49+
] ++
50+
concatMap (commandSynopsisLines pname) commands ++
51+
[ ".SH DESCRIPTION"
52+
, "Cabal is the standard package system for Haskell software. It helps people to configure, "
53+
, "build and install Haskell software and to distribute it easily to other users and developers."
54+
, ""
55+
, "The command line " ++ pname ++ " tool (also referred to as cabal-install) helps with "
56+
, "installing existing packages and developing new packages. "
57+
, "It can be used to work with local packages or to install packages from online package archives, "
58+
, "including automatically installing dependencies. By default it is configured to use Hackage, "
59+
, "which is Haskell’s central package archive that contains thousands of libraries and applications "
60+
, "in the Cabal package format."
61+
, ".SH OPTIONS"
62+
, "Global options:"
63+
, ""
64+
] ++
65+
optionsLines (globalCommand []) ++
66+
[ ".SH COMMANDS"
67+
] ++
68+
concatMap (commandDetailsLines pname) commands ++
69+
[ ".SH FILES"
70+
] ++
71+
concatMap fileLines files ++
72+
[ ".SH BUGS"
73+
, "To browse the list of known issues or report a new one please see "
74+
, "https://github.com/haskell/cabal/labels/cabal-install."
75+
]
76+
77+
commandSynopsisLines :: String -> CommandSpec action -> [String]
78+
commandSynopsisLines pname (CommandSpec ui _ NormalCommand) =
79+
[ ".B " ++ pname ++ " " ++ (commandName ui)
80+
, ".R - " ++ commandSynopsis ui
81+
, ".br"
82+
]
83+
commandSynopsisLines _ (CommandSpec _ _ HiddenCommand) = []
84+
85+
commandDetailsLines :: String -> CommandSpec action -> [String]
86+
commandDetailsLines pname (CommandSpec ui _ NormalCommand) =
87+
[ ".B " ++ pname ++ " " ++ (commandName ui)
88+
, ""
89+
, commandUsage ui pname
90+
, ""
91+
] ++
92+
optional commandDescription ++
93+
optional commandNotes ++
94+
[ "Flags:"
95+
, ".RS"
96+
] ++
97+
optionsLines ui ++
98+
[ ".RE"
99+
, ""
100+
]
101+
where
102+
optional field =
103+
case field ui of
104+
Just text -> [text pname, ""]
105+
Nothing -> []
106+
commandDetailsLines _ (CommandSpec _ _ HiddenCommand) = []
107+
108+
optionsLines :: CommandUI flags -> [String]
109+
optionsLines command = concatMap optionLines (concatMap optionDescr (commandOptions command ParseArgs))
110+
111+
data ArgumentRequired = Optional | Required
112+
type OptionArg = (ArgumentRequired, ArgPlaceHolder)
113+
114+
optionLines :: OptDescr flags -> [String]
115+
optionLines (ReqArg description (optionChars, optionStrings) placeHolder _ _) =
116+
argOptionLines description optionChars optionStrings (Required, placeHolder)
117+
optionLines (OptArg description (optionChars, optionStrings) placeHolder _ _ _) =
118+
argOptionLines description optionChars optionStrings (Optional, placeHolder)
119+
optionLines (BoolOpt description (trueChars, trueStrings) (falseChars, falseStrings) _ _) =
120+
optionLinesIfPresent trueChars trueStrings ++
121+
optionLinesIfPresent falseChars falseStrings ++
122+
optionDescriptionLines description
123+
optionLines (ChoiceOpt options) =
124+
concatMap choiceLines options
125+
where
126+
choiceLines (description, (optionChars, optionStrings), _, _) =
127+
[ optionsLine optionChars optionStrings ] ++
128+
optionDescriptionLines description
129+
130+
argOptionLines :: String -> [Char] -> [String] -> OptionArg -> [String]
131+
argOptionLines description optionChars optionStrings arg =
132+
[ optionsLine optionChars optionStrings
133+
, optionArgLine arg
134+
] ++
135+
optionDescriptionLines description
136+
137+
optionLinesIfPresent :: [Char] -> [String] -> [String]
138+
optionLinesIfPresent optionChars optionStrings =
139+
if null optionChars && null optionStrings then []
140+
else [ optionsLine optionChars optionStrings, ".br" ]
141+
142+
optionDescriptionLines :: String -> [String]
143+
optionDescriptionLines description =
144+
[ ".RS"
145+
, description
146+
, ".RE"
147+
, ""
148+
]
149+
150+
optionsLine :: [Char] -> [String] -> String
151+
optionsLine optionChars optionStrings =
152+
intercalate ", " (shortOptions optionChars ++ longOptions optionStrings)
153+
154+
shortOptions :: [Char] -> [String]
155+
shortOptions = map (\c -> "\\-" ++ [c])
156+
157+
longOptions :: [String] -> [String]
158+
longOptions = map (\s -> "\\-\\-" ++ s)
159+
160+
optionArgLine :: OptionArg -> String
161+
optionArgLine (Required, placeHolder) = ".I " ++ placeHolder
162+
optionArgLine (Optional, placeHolder) = ".RI [ " ++ placeHolder ++ " ]"
163+
164+
fileLines :: FileInfo -> [String]
165+
fileLines (FileInfo path description) =
166+
[ path
167+
, ".RS"
168+
, description
169+
, ".RE"
170+
, ""
171+
]

cabal-install/Distribution/Client/Setup.hs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module Distribution.Client.Setup
3939
, sandboxCommand, defaultSandboxLocation, SandboxFlags(..)
4040
, execCommand, ExecFlags(..)
4141
, userConfigCommand, UserConfigFlags(..)
42+
, manpageCommand
4243

4344
, parsePackageArgs
4445
--TODO: stop exporting these:
@@ -878,6 +879,18 @@ uninstallCommand = CommandUI {
878879
commandOptions = \_ -> []
879880
}
880881

882+
manpageCommand :: CommandUI (Flag Verbosity)
883+
manpageCommand = CommandUI {
884+
commandName = "manpage",
885+
commandSynopsis = "Outputs manpage source.",
886+
commandDescription = Just $ \_ ->
887+
"Output manpage source to STDOUT.\n",
888+
commandNotes = Nothing,
889+
commandUsage = usageFlags "manpage",
890+
commandDefaultFlags = toFlag normal,
891+
commandOptions = \_ -> [optionVerbosity id const]
892+
}
893+
881894
runCommand :: CommandUI (BuildFlags, BuildExFlags)
882895
runCommand = CommandUI {
883896
commandName = "run",
@@ -2302,6 +2315,7 @@ paragraph = (++"\n")
23022315

23032316
indentParagraph :: String -> String
23042317
indentParagraph = unlines
2318+
. (flip (++)) [""]
23052319
. map ((" "++).unwords)
23062320
. wrapLine 77
23072321
. words

0 commit comments

Comments
 (0)