- #!/usr/bin/env dub
- /+dub.sdl:
- dependency "dub" path="../.."
- +/
-
- import std.algorithm, std.conv, std.format, std.path, std.range, std.stdio;
- import dub.commandline;
-
- string italic(string w)
- {
- return `\fI` ~ w ~ `\fR`;
- }
-
- string bold(string w)
- {
- return `\fB` ~ w ~ `\fR`;
- }
-
- string header(string heading)
- {
- return ".SH " ~ heading;
- }
-
- string br(string s)
- {
- return ".BR " ~ s;
- }
-
- struct Config
- {
- import std.datetime;
- SysTime date;
- string[] relatedSubCommands;
-
- static Config init(){
- import std.process : environment;
- Config config;
- config.date = Clock.currTime;
- auto diffable = environment.get("DIFFABLE", "0");
- if (diffable == "1")
- config.date = SysTime(DateTime(2018, 01, 01));
-
- config.cwd = __FILE_FULL_PATH__.dirName;
- return config;
- }
- string cwd;
- }
-
- void writeHeader(ref File manFile, string manName, const Config config)
- {
- static immutable manHeader =
- `.TH %s 1 "%s" "The D Language Foundation" "The D Language Foundation"
- .SH NAME`;
- manFile.writefln(manHeader, manName, config.date.toISOExtString.take(10));
- }
-
- void writeFooter(ref File manFile, string seeAlso, const Config config)
- {
- static immutable manFooter =
- `.SH FILES
- \fIdub\&.sdl\fR, \fIdub\&.json\fR
- .SH AUTHOR
- Copyright (c) 1999-%s by The D Language Foundation
- .SH "ONLINE DOCUMENTATION"
- .UR http://code.dlang.org/docs/commandline
- http://code.dlang.org/docs/commandline
- .UE
- .SH "SEE ALSO"
- %s`;
- manFile.writefln(manFooter, config.date.year, seeAlso);
- }
-
- void writeMainManFile(CommandArgs args, CommandGroup[] commands,
- string fileName, const Config config)
- {
- auto manFile = File(config.cwd.buildPath(fileName), "w");
- manFile.writeHeader("DUB", config);
- auto seeAlso = ["dmd(1)", "rdmd(1)"]
- .chain(commands.map!(a => a.commands).joiner
- .map!(cmd => format("dub-%s(1)", cmd.name)))
- .joiner(", ").to!string.bold;
- scope(exit) manFile.writeFooter(seeAlso, config);
-
- alias writeln = (m) => manFile.writeln(m);
- writeln(`dub \- Package and build management system for D`);
- writeln("SYNOPSIS".header);
- writeln(`.B dub
- [\-\-version]
- [\fICOMMAND\fR]
- [\fIOPTIONS\&.\&.\&.\fR]
- [\-\- [\fIAPPLICATION ARGUMENTS\&.\&.\&.\fR]]`);
-
- writeln("DESCRIPTION".header);
- writeln(`Manages the DUB project in the current directory\&. DUB can serve as a build
- system and a package manager, automatically keeping track of project's
- dependencies \- both downloading them and linking them into the application.`);
-
- writeln(".SH COMMANDS");
- foreach (grp; commands) {
- foreach (cmd; grp.commands) {
- writeln(".TP");
- writeln(cmd.name.bold);
- writeln(cmd.helpText.joiner("\n"));
- }
- }
-
- writeln("COMMON OPTIONS".header);
- args.writeArgs(manFile);
- }
-
- string highlightArguments(string args)
- {
- import std.regex : regex, replaceAll;
- static auto re = regex("<([^>]*)>");
- static const reReplacement = "<%s>".format(`$1`.italic);
- return args.replaceAll(re, reReplacement);
- }
-
- void writeArgs(CommandArgs args, ref File manFile)
- {
- alias write = (m) => manFile.write(m.replace(`-`, `\-`));
- foreach (arg; args.recognizedArgs)
- {
- auto names = arg.names.split("|");
- assert(names.length == 1 || names.length == 2);
- string sarg = names[0].length == 1 ? names[0] : null;
- string larg = names[0].length > 1 ? names[0] : names.length > 1 ? names[1] : null;
- manFile.writeln(".PP");
- if (sarg !is null) {
- write("-%s".format(sarg).bold);
- if (larg !is null)
- write(", ");
- }
- if (larg !is null) {
- write("--%s".format(larg).bold);
- if (!arg.defaultValue.peek!bool)
- write("=VALUE");
- }
- manFile.writeln;
- manFile.writeln(".RS 4");
- manFile.writeln(arg.helpText.join("\n"));
- manFile.writeln(".RE");
- }
- }
-
- void writeManFile(Command command, const Config config)
- {
- import std.uni : toUpper;
-
- auto args = new CommandArgs(null);
- command.prepare(args);
- string fileName = format("dub-%s.1", command.name);
- auto manFile = File(config.cwd.buildPath(fileName), "w");
- auto manName = format("DUB-%s", command.name).toUpper;
- manFile.writeHeader(manName, config);
-
- string[] extraRelated;
- foreach (arg; args.recognizedArgs) {
- if (arg.names.canFind("rdmd"))
- extraRelated ~= "rdmd(1)";
- }
- if (command.name == "dustmite")
- extraRelated ~= "dustmite(1)";
-
- const seeAlso = ["dub(1)"]
- .chain(config.relatedSubCommands.map!(s => s.format!"dub-%s(1)"))
- .chain(extraRelated)
- .map!bold
- .joiner(", ")
- .to!string;
- scope(exit) manFile.writeFooter(seeAlso, config);
-
- alias writeln = (m) => manFile.writeln(m);
- manFile.writefln(`dub-%s \- %s`, command.name, command.description);
-
- writeln("SYNOPSIS".header);
- writeln("dub %s".format(command.name).bold);
- writeln(command.argumentsPattern.highlightArguments);
- writeln(`OPTIONS\&.\&.\&.`.italic);
- if (command.acceptsAppArgs)
- {
- writeln("[-- <%s>]".format("application arguments...".italic));
- }
-
- writeln("DESCRIPTION".header);
- writeln(command.helpText.joiner("\n\n"));
- writeln("OPTIONS".header);
- args.writeArgs(manFile);
-
- static immutable exitStatus =
- `.SH EXIT STATUS
- .TP
- .BR 0
- DUB succeeded
- .TP
- .BR 1
- usage errors, unknown command line flags
- .TP
- .BR 2
- package not found, package failed to load, miscellaneous error`;
- static immutable exitStatusDustmite =
- `.SH EXIT STATUS
- Forwards the exit code from ` ~ `dustmite(1)`.bold;
- if (command.name == "dustmite")
- manFile.writeln(exitStatusDustmite);
- else
- manFile.writeln(exitStatus);
- }
-
- void main()
- {
- Config config = Config.init;
- auto commands = getCommands();
-
- // main dub.1
- {
- CommonOptions options;
- auto args = new CommandArgs(null);
- options.prepare(args);
- args.writeMainManFile(commands, "dub.1", config);
- }
-
- string[][] relatedSubCommands = [
- ["run", "build", "test"],
- ["test", "dustmite", "lint"],
- ["describe", "gemerate"],
- ["add", "fetch"],
- ["init", "add", "convert"],
- ["add-path", "remove-path"],
- ["add-local", "remove-local"],
- ["list", "search"],
- ["add-override", "remove-override", "list-overrides"],
- ["clean-caches", "clean", "remove"],
- ];
-
- // options for each specific command
- foreach (cmd; commands.map!(a => a.commands).joiner) {
- string[] related;
- foreach (relatedList; relatedSubCommands) {
- if (relatedList.canFind(cmd.name))
- related ~= relatedList;
- }
- related = related.sort!"a<b".uniq.array;
- related = related.remove!(c => c == cmd.name);
- config.relatedSubCommands = related;
-
- cmd.writeManFile(config);
- }
- }