Newer
Older
dub_jkp / scripts / man / gen_man.d
  1. #!/usr/bin/env dub
  2. /+dub.sdl:
  3. dependency "dub" path="../.."
  4. +/
  5.  
  6. import std.algorithm, std.conv, std.format, std.path, std.range, std.stdio;
  7. import dub.commandline;
  8.  
  9. string italic(string w)
  10. {
  11. return `\fI` ~ w ~ `\fR`;
  12. }
  13.  
  14. string bold(string w)
  15. {
  16. return `\fB` ~ w ~ `\fR`;
  17. }
  18.  
  19. string header(string heading)
  20. {
  21. return ".SH " ~ heading;
  22. }
  23.  
  24. string br(string s)
  25. {
  26. return ".BR " ~ s;
  27. }
  28.  
  29. struct Config
  30. {
  31. import std.datetime;
  32. SysTime date;
  33.  
  34. static Config init(){
  35. import std.process : environment;
  36. Config config;
  37. config.date = Clock.currTime;
  38. auto diffable = environment.get("DIFFABLE", "0");
  39. if (diffable == "1")
  40. config.date = SysTime(DateTime(2018, 01, 01));
  41.  
  42. config.cwd = __FILE_FULL_PATH__.dirName;
  43. return config;
  44. }
  45. string cwd;
  46. }
  47.  
  48. void writeHeader(ref File manFile, string manName, const Config config)
  49. {
  50. static immutable manHeader =
  51. `.TH %s 1 "%s" "The D Language Foundation" "The D Language Foundation"
  52. .SH NAME`;
  53. manFile.writefln(manHeader, manName, config.date.toISOExtString.take(10));
  54. }
  55.  
  56. void writeFooter(ref File manFile, string seeAlso, const Config config)
  57. {
  58. static immutable manFooter =
  59. `.SH FILES
  60. \fIdub\&.sdl\fR, \fIdub\&.json\fR
  61. .SH AUTHOR
  62. Copyright (c) 1999-%s by The D Language Foundation
  63. .SH "ONLINE DOCUMENTATION"
  64. .UR http://code.dlang.org/docs/commandline
  65. .UE http://code.dlang.org/docs/commandline
  66. .SH "SEE ALSO"
  67. %s`;
  68. manFile.writefln(manFooter, config.date.year, seeAlso);
  69. }
  70.  
  71. void writeMainManFile(CommandArgs args, CommandGroup[] commands,
  72. string fileName, const Config config)
  73. {
  74. auto manFile = File(config.cwd.buildPath(fileName), "w");
  75. manFile.writeHeader("DUB", config);
  76. auto seeAlso = ["dmd(1)", "rdmd(1)"]
  77. .chain(commands.map!(a => a.commands).joiner
  78. .map!(cmd => format("dub-%s(1)", cmd.name)))
  79. .joiner(", ").to!string.bold;
  80. scope(exit) manFile.writeFooter(seeAlso, config);
  81.  
  82. alias writeln = (m) => manFile.writeln(m);
  83. writeln(`dub \- Package and build management system for D`);
  84. writeln("SYNOPSIS".header);
  85. writeln(`.B dub
  86. [\-\-version]
  87. [\fICOMMAND\fR]
  88. [\fIOPTIONS\&.\&.\&.\fR]
  89. [\-\- [\fIAPPLICATION ARGUMENTS\&.\&.\&.\fR]]`);
  90.  
  91. writeln("DESCRIPTION".header);
  92. writeln(`Manages the DUB project in the current directory\&. DUB can serve as a build
  93. system and a package manager, automatically keeping track of project's
  94. dependencies \- both downloading them and linking them into the application.`);
  95.  
  96. writeln(".SH COMMANDS");
  97. foreach (grp; commands) {
  98. foreach (cmd; grp.commands) {
  99. writeln(".TP");
  100. writeln(cmd.name.bold);
  101. writeln(cmd.helpText.joiner("\n"));
  102. }
  103. }
  104.  
  105. writeln("COMMON OPTIONS".header);
  106. args.writeArgs(manFile);
  107. }
  108.  
  109. string highlightArguments(string args)
  110. {
  111. import std.regex : regex, replaceAll;
  112. static auto re = regex("<([^>]*)>");
  113. static const reReplacement = "<%s>".format(`$1`.italic);
  114. return args.replaceAll(re, reReplacement);
  115. }
  116.  
  117. void writeArgs(CommandArgs args, ref File manFile)
  118. {
  119. alias write = (m) => manFile.write(m);
  120. foreach (arg; args.recognizedArgs)
  121. {
  122. auto names = arg.names.split("|");
  123. assert(names.length == 1 || names.length == 2);
  124. string sarg = names[0].length == 1 ? names[0] : null;
  125. string larg = names[0].length > 1 ? names[0] : names.length > 1 ? names[1] : null;
  126. write(".IP ");
  127. if (sarg !is null) {
  128. write("-%s".format(sarg));
  129. if (larg !is null)
  130. write(", ");
  131. }
  132. if (larg !is null) {
  133. write("--%s".format(larg));
  134. if (!arg.defaultValue.peek!bool)
  135. write("=VALUE");
  136. }
  137. manFile.writeln;
  138. manFile.writeln(arg.helpText.join("\n"));
  139. }
  140. }
  141.  
  142. void writeManFile(Command command, const Config config)
  143. {
  144. import std.uni : toUpper;
  145.  
  146. auto args = new CommandArgs(null);
  147. command.prepare(args);
  148. string fileName = format("dub-%s.1", command.name);
  149. auto manFile = File(config.cwd.buildPath(fileName), "w");
  150. auto manName = format("DUB-%s", command.name).toUpper;
  151. manFile.writeHeader(manName, config);
  152. static immutable seeAlso = ["dmd(1)", "dub(1)"].map!bold.joiner(", ").to!string;
  153. scope(exit) manFile.writeFooter(seeAlso, config);
  154.  
  155. alias writeln = (m) => manFile.writeln(m);
  156. writeln(`dub \- Package and build management system for D`);
  157.  
  158. writeln("SYNOPSIS".header);
  159. writeln("dub %s".format(command.name).bold);
  160. writeln(command.argumentsPattern.highlightArguments);
  161. writeln(`OPTIONS\&.\&.\&.`.italic);
  162. if (command.acceptsAppArgs)
  163. {
  164. writeln("[-- <%s>]".format("application arguments...".italic));
  165. }
  166.  
  167. writeln("DESCRIPTION".header);
  168. writeln(command.helpText.joiner("\n\n"));
  169. writeln("OPTIONS".header);
  170. args.writeArgs(manFile);
  171. }
  172.  
  173. void main()
  174. {
  175. Config config = Config.init;
  176. auto commands = getCommands();
  177.  
  178. // main dub.1
  179. {
  180. CommonOptions options;
  181. auto args = new CommandArgs(null);
  182. options.prepare(args);
  183. args.writeMainManFile(commands, "dub.1", config);
  184. }
  185.  
  186. // options for each specific command
  187. foreach (cmd; commands.map!(a => a.commands).joiner) {
  188. cmd.writeManFile(config);
  189. }
  190. }