diff --git a/.gitignore b/.gitignore index 75e23c8..d80aba9 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /bin/__test__library-nonet__ /bin/__test__library__ /bin/dub-test-library +/bin/libdub.a # Ignore files or directories created by the test suite. /test/custom-source-main-bug487/custom-source-main-bug487 diff --git a/scripts/man/.gitignore b/scripts/man/.gitignore new file mode 100644 index 0000000..2c2a370 --- /dev/null +++ b/scripts/man/.gitignore @@ -0,0 +1,2 @@ +*.1 +/gen_man diff --git a/scripts/man/README.md b/scripts/man/README.md new file mode 100644 index 0000000..1b7484a --- /dev/null +++ b/scripts/man/README.md @@ -0,0 +1,13 @@ +1) Build +-------- + +```shell +./gen_man.d +``` + +2) Preview +---------- + +```shell +man -l dub.1 +``` diff --git a/scripts/man/gen_man.d b/scripts/man/gen_man.d new file mode 100755 index 0000000..1524ea1 --- /dev/null +++ b/scripts/man/gen_man.d @@ -0,0 +1,187 @@ +#!/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; + + 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 +.UE http://code.dlang.org/docs/commandline +.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)".br, "rdmd(1)"].joiner("\n").to!string; + 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 immutable re = regex("<([^>]*)>"); + static immutable reReplacement = "<%s>".format(`$1`.italic); + return args.replaceAll(re, reReplacement); +} + +void writeArgs(CommandArgs args, ref File manFile) +{ + alias write = (m) => manFile.write(m); + 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; + write(".IP "); + if (sarg !is null) { + write("-%s".format(sarg)); + if (larg !is null) + write(", "); + } + if (larg !is null) { + write("--%s".format(larg)); + if (!arg.defaultValue.peek!bool) + write("=VALUE"); + } + manFile.writeln; + manFile.writeln(arg.helpText.join("\n")); + } +} + +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); + static immutable seeAlso = ["dmd(1)".br, "dub(1)"].joiner("\n").to!string; + 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("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); +} + +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); + } + + // options for each specific command + foreach (cmd; commands.map!(a => a.commands).joiner) { + cmd.writeManFile(config); + } +} diff --git a/travis-ci.sh b/travis-ci.sh index c483599..7b71df3 100755 --- a/travis-ci.sh +++ b/travis-ci.sh @@ -25,6 +25,7 @@ git clean -dxf -- test source $(~/dlang/install.sh gdc --activate) DUB=`pwd`/bin/dub DC=${DC} test/run-unittest.sh + deactivate else ./build.sh DUB=`pwd`/bin/dub DC=${DC} test/run-unittest.sh @@ -39,3 +40,9 @@ if [ "$COVERAGE" = true ]; then find . -type f -name '*.d' -exec grep -Hn "[[:blank:]]$" {} \; fi + +# check that the man page generation still works (only once) +if [ "$COVERAGE" = true ]; then + source $(~/dlang/install.sh dmd --activate) + dub --single -v scripts/man/gen_man.d +fi