1 // Written in the D programming language 2 3 /++ 4 Ddoc generator 5 6 This is a program for simplifying ddoc generation. 7 8 It ensures that the names of the generated .html files include the full 9 module path (with underscores instead of dots) rather than simply being 10 named after the modules (since just using the module names results in 11 connflicts if any packages have modules with the same name). 12 13 It also provides an easy way to exclude files from ddoc generation. Any 14 modules or packages with the name internal are excluded as well as any 15 files that are passed on the command line. And package.d files have their 16 corresponding .html files renamed to match the package name. 17 18 Also, the program generates a .ddoc file intended for use in a navigation 19 bar on the side of the documentation (similar to what dlang.org has) uses 20 it in the ddoc generation (it's deleted afterwards). The navigation bar 21 contains the full module hierarchy to allow for easy navigation among the 22 modules in the project. Of course, the other .ddoc files have to actually 23 use the MODULE_MENU macro in the generated .ddoc file, or the documentation 24 won't end up with a navigation bar. 25 26 The program assumes a layout similar to dub in that the source files are 27 expected to be in a directory called "source", and the generated 28 documentation goes in the "docs" directory (which is deleted before 29 documentation generation to ensure a clean build). 30 31 It's expected that any .ddoc files being used will be in the "ddoc" 32 directory, which isn't a dub thing, but they have to go somewhere. 33 34 In addition, the program expects there to be a "source_docs" directory. Any 35 .dd files that are there will have corresponding .html files generated for 36 them (e.g. for generating index.html), and any other files or directories 37 (e.g. a "css" or "js" folder) will be copied over to the "docs" folder. 38 39 Note that this program does assume that all module names match their file 40 names and that all package names match their folder names. 41 42 Copyright: Copyright 2017 - 2018 43 License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 44 Author: Jonathan M Davis, SHOO 45 +/ 46 module gendoc.main; 47 48 49 import gendoc.config; 50 import gendoc.generator; 51 import gendoc.modmgr; 52 import gendoc.cmdpipe; 53 alias Config = gendoc.config.Config; 54 55 /******************************************************************************* 56 * Main routine of gendoc 57 */ 58 int gendocMain(string[] args) 59 { 60 import std.path, std.file; 61 gendoc.config.Config cfg; 62 DocumentGenerator generator; 63 ModuleManager modmgr; 64 try 65 { 66 auto getoptInfo = cfg.setup(args); 67 if (getoptInfo.helpWanted) 68 { 69 import std.getopt; 70 defaultGetoptPrinter("Usage: gendoc [options]\nOptions:", getoptInfo.options); 71 return 0; 72 } 73 74 initializeDocsDirecotry(cfg.gendocData.target); 75 modmgr.setup(cfg); 76 77 generator.setup(cfg, modmgr); 78 79 generator.createTemporaryDir(); 80 scope(exit) 81 generator.removeTemporaryDir(); 82 83 // generate JSON 84 if (cfg.gendocData.enableGenerateJSON) 85 { 86 foreach (pkg; modmgr.dubPackages) 87 generator.generateJson(pkg); 88 } 89 90 // set *.ddoc 91 foreach (dir; cfg.gendocData.ddocs) 92 { 93 string[] ddocFiles; 94 foreach (de; dir.dirEntries("*.ddoc", SpanMode.shallow)) 95 ddocFiles ~= de.name; 96 foreach (de; dir.dirEntries("*.ddoc.mustache", SpanMode.shallow)) 97 { 98 auto absDir = dir.absolutePath.buildNormalizedPath(); 99 auto absFile = de.name.absolutePath.buildNormalizedPath(); 100 ddocFiles ~= generator.generateFromMustache( 101 modmgr.dubPackages, absDir, absFile.relativePath(absDir).stripExtension); 102 } 103 import std.algorithm: sort; 104 ddocFiles.sort!((a, b) => filenameCmp(a, b) < 0); 105 generator.ddocFiles ~= ddocFiles; 106 } 107 108 foreach (pkg; modmgr.dubPackages) 109 generator.generate(pkg, cfg.singleFile); 110 111 // set source_doc (*.dd|*.js|*.css|*.*) 112 foreach (sdocs; cfg.gendocData.sourceDocs) 113 { 114 auto absSrcDir = sdocs.absolutePath.buildNormalizedPath; 115 generator.rootDir = absSrcDir; 116 foreach (de; absSrcDir.dirEntries(SpanMode.depth)) 117 { 118 import std.algorithm: endsWith; 119 if (de.isDir) 120 continue; 121 auto targetFile = relativePath(de.name, absSrcDir); 122 switch (de.name.endsWith(".mustache")) 123 { 124 case 1: 125 auto absFile = de.name.absolutePath.buildNormalizedPath(); 126 auto mustachName = absFile.relativePath(absSrcDir).stripExtension; 127 auto srcFile = generator.generateFromMustache(modmgr.dubPackages, absSrcDir, mustachName); 128 generator.generate(targetFile, srcFile, cfg.packageData.options); 129 break; 130 default: 131 generator.generate(targetFile, de.name, cfg.packageData.options); 132 break; 133 } 134 } 135 } 136 } 137 catch(Exception e) 138 { 139 import std.stdio : stderr, writeln; 140 stderr.writeln(e.msg); 141 return -1; 142 } 143 144 return 0; 145 } 146 147 /******************************************************************************* 148 * setup the module manager 149 */ 150 void setup(ref ModuleManager modmgr, const ref Config cfg) 151 { 152 setupModuleManagerImpl(modmgr, cfg); 153 } 154 /// ditto 155 void setup(ref ModuleManager modmgr, Config cfg) 156 { 157 setupModuleManagerImpl(modmgr, cfg); 158 } 159 160 private void setupModuleManagerImpl(ref ModuleManager modmgr, const ref Config cfg) 161 { 162 modmgr.exclude( 163 cfg.gendocData.excludePackages, 164 cfg.gendocData.excludePackagePatterns, 165 cfg.gendocData.excludePaths, 166 cfg.gendocData.excludePatterns); 167 168 modmgr.target = cfg.gendocData.target; 169 170 void addPkg(in ref PackageConfig pkgcfg) 171 { 172 modmgr.addSources( 173 pkgcfg.name, 174 pkgcfg.packageVersion, 175 pkgcfg.path, 176 pkgcfg.files, 177 pkgcfg.options); 178 foreach (subpkg; pkgcfg.subPackages) 179 addPkg(subpkg); 180 } 181 addPkg(cfg.packageData); 182 } 183 184 /******************************************************************************* 185 * setup the module manager 186 */ 187 void setup(ref DocumentGenerator generator, const ref Config cfg, const ref ModuleManager modmgr) 188 { 189 setupDocumentGenerator(generator, cfg, modmgr); 190 } 191 /// ditto 192 void setup(ref DocumentGenerator generator, Config cfg, ModuleManager modmgr) 193 { 194 setupDocumentGenerator(generator, cfg, modmgr); 195 } 196 197 198 private void setupDocumentGenerator( 199 ref DocumentGenerator generator, const ref Config cfg, const ref ModuleManager modmgr) 200 { 201 import std.stdio, std.path, std..string; 202 203 generator.combinedDubPackagePatterns = cfg.gendocData.combinedDubPackagePatterns; 204 205 if (!cfg.quiet) 206 { 207 generator.preGenerateCallback = (string pkgName, in ModInfo[] modInfo, in string[] args) 208 { 209 if (cfg.varbose) 210 { 211 writef("Generate %s ------------------\n", pkgName); 212 foreach (m; modInfo) 213 { 214 writef(" |from: %s\n", generator.rootDir.buildPath(m.src)); 215 writef(" | to: %s\n", generator.targetDir.buildPath(m.dst)); 216 } 217 writef(" with args:\n%-( %-s\n%)\n ...", args); 218 } 219 else 220 { 221 writef("Generate %s ...", pkgName.length > 0 222 ? pkgName 223 : modInfo.length > 0 ? modInfo[0].dst : "html"); 224 } 225 }; 226 227 generator.postGenerateCallback = (string pkgName, in ModInfo[] modInfo, int status, string resMsg) 228 { 229 if (status == 0) 230 { 231 writeln(" done."); 232 } 233 else 234 { 235 if (cfg.varbose) 236 { 237 writefln(" Failed\n with code = 0x%08x msg: %s", status, resMsg); 238 } 239 else 240 { 241 writeln(" Failed."); 242 } 243 throw new Exception(resMsg); 244 } 245 }; 246 247 generator.postCopyCallback = (string src, string dst) 248 { 249 if (cfg.varbose) 250 { 251 writefln("Copyfrom %s\n to %s\n ... done.", 252 generator.targetDir.buildPath(src), 253 generator.rootDir.buildPath(dst)); 254 } 255 else 256 { 257 writefln("Copy %s ... done.", dst); 258 } 259 }; 260 } 261 262 generator.commandProcessor = (string args, string workDir, string[string] env) 263 { 264 auto pipe = CommandPipe(cfg, modmgr.dubPackages); 265 pipe.run(args, workDir, env); 266 return pipe.result.join("\n"); 267 }; 268 269 generator.executeProcessor = (string[] args, string workDir, string[string] env) 270 { 271 auto pipe = CommandPipe(cfg, modmgr.dubPackages); 272 pipe.run(args, workDir, env); 273 return pipe.result.join("\n"); 274 }; 275 generator.compiler = cfg.compiler; 276 generator.targetDir = cfg.gendocData.target.absolutePath.buildNormalizedPath; 277 278 } 279 280 281 /******************************************************************************* 282 * Setup docs directory 283 */ 284 void initializeDocsDirecotry(string docsDir) 285 { 286 import std.file, std.path, std.algorithm; 287 if(!docsDir.exists) 288 mkdir(docsDir); 289 foreach (de; docsDir.dirEntries(SpanMode.shallow)) 290 { 291 if (de.name.startsWith(docsDir.buildPath("."))) 292 continue; 293 if (de.isDir) 294 { 295 rmdirRecurse(de); 296 } 297 else 298 { 299 remove(de.name); 300 } 301 } 302 }