Newer
Older
dub_jkp / source / app.d
  1. /**
  2. The entry point to dub
  3.  
  4. Copyright: © 2012-2013 Matthias Dondorff
  5. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  6. Authors: Matthias Dondorff, Sönke Ludwig
  7. */
  8. module app;
  9.  
  10. import dub.compilers.compiler;
  11. import dub.dependency;
  12. import dub.dub;
  13. import dub.generators.generator;
  14. import dub.internal.std.process;
  15. import dub.internal.vibecompat.core.file;
  16. import dub.internal.vibecompat.core.log;
  17. import dub.internal.vibecompat.inet.url;
  18. import dub.package_;
  19. import dub.packagesupplier;
  20. import dub.project;
  21. import dub.version_;
  22.  
  23. import std.algorithm;
  24. import std.array;
  25. import std.conv;
  26. import std.encoding;
  27. import std.exception;
  28. import std.file;
  29. import std.getopt;
  30.  
  31.  
  32. int main(string[] args)
  33. {
  34. logDiagnostic("DUB version %s", dubVersion);
  35.  
  36. string cmd;
  37.  
  38. version(Windows){
  39. // rdmd uses $TEMP to compute a temporary path. since cygwin substitutes backslashes
  40. // with slashes, this causes OPTLINK to fail (it thinks path segments are options)
  41. // we substitute the other way around here to fix this.
  42. environment["TEMP"] = environment["TEMP"].replace("/", "\\");
  43. }
  44.  
  45. try {
  46. // parse general options
  47. bool verbose, vverbose, quiet, vquiet;
  48. bool help, nodeps, annotate;
  49. LogLevel loglevel = LogLevel.info;
  50. string build_type, build_config;
  51. string compiler_name = "dmd";
  52. string arch;
  53. bool rdmd = false;
  54. bool print_platform, print_builds, print_configs;
  55. bool install_system = false, install_local = false;
  56. string install_version;
  57. string[] registry_urls;
  58. string[] debug_versions;
  59. string[] app_args;
  60. string root_path = getcwd();
  61. getopt(args,
  62. "v|verbose", &verbose,
  63. "vverbose", &vverbose,
  64. "q|quiet", &quiet,
  65. "vquiet", &vquiet,
  66. "h|help", &help, // obsolete
  67. "nodeps", &nodeps,
  68. "annotate", &annotate,
  69. "build", &build_type,
  70. "compiler", &compiler_name,
  71. "arch", &arch,
  72. "rdmd", &rdmd,
  73. "config", &build_config,
  74. "debug", &debug_versions,
  75. "print-builds", &print_builds,
  76. "print-configs", &print_configs,
  77. "print-platform", &print_platform,
  78. "system", &install_system,
  79. "local", &install_local,
  80. "version", &install_version,
  81. "registry", &registry_urls,
  82. "root", &root_path
  83. );
  84.  
  85. if( vverbose ) loglevel = LogLevel.debug_;
  86. else if( verbose ) loglevel = LogLevel.diagnostic;
  87. else if( vquiet ) loglevel = LogLevel.none;
  88. else if( quiet ) loglevel = LogLevel.warn;
  89. setLogLevel(loglevel);
  90.  
  91. // extract the command
  92. if( args.length > 1 && !args[1].startsWith("-") ){
  93. cmd = args[1];
  94. args = args[0] ~ args[2 .. $];
  95. } else cmd = "run";
  96.  
  97. // contrary to the documentation, getopt does not remove --
  98. auto app_args_idx = args.countUntil("--");
  99. if (app_args_idx >= 0) {
  100. app_args = args[app_args_idx+1 .. $];
  101. args = args[0 .. app_args_idx];
  102. }
  103.  
  104. // display help if requested (obsolete)
  105. if( help ){
  106. showHelp(cmd);
  107. return 0;
  108. }
  109.  
  110. BuildSettings build_settings;
  111. auto compiler = getCompiler(compiler_name);
  112. auto build_platform = compiler.determinePlatform(build_settings, compiler_name, arch);
  113. build_settings.addDebugVersions(debug_versions);
  114.  
  115. if( print_platform ){
  116. logInfo("Build platform:");
  117. logInfo(" Compiler: %s", build_platform.compiler);
  118. logInfo(" System: %s", build_platform.platform.join(" "));
  119. logInfo(" Architecture: %s", build_platform.architecture.join(" "));
  120. logInfo("");
  121. }
  122.  
  123. auto package_suppliers = registry_urls.map!(url => cast(PackageSupplier)new RegistryPackageSupplier(Url(url))).array;
  124. Dub dub = new Dub(package_suppliers, root_path);
  125. string def_config;
  126.  
  127. // make the CWD package available so that for example sub packages can reference their
  128. // parent package.
  129. try dub.packageManager.getTemporaryPackage(Path(root_path), Version("~master"));
  130. catch (Exception e) { logDiagnostic("No package found in current working directory."); }
  131.  
  132. bool loadCwdPackage(Package pack)
  133. {
  134. if (!existsFile(dub.rootPath~"package.json") && !existsFile(dub.rootPath~"source/app.d")) {
  135. logInfo("");
  136. logInfo("Neither package.json, nor source/app.d was found in the current directory.");
  137. logInfo("Please run dub from the root directory of an existing package, or create a new");
  138. logInfo("package using \"dub init <name>\".");
  139. logInfo("");
  140. showHelp(null);
  141. return false;
  142. }
  143.  
  144. if (pack) dub.loadPackage(pack);
  145. else dub.loadPackageFromCwd();
  146.  
  147. def_config = dub.getDefaultConfiguration(build_platform);
  148.  
  149. return true;
  150. }
  151.  
  152. string package_name;
  153. bool loadSelectedPackage()
  154. {
  155. Package pack;
  156. if (!package_name.empty) {
  157. // load package in root_path to enable searching for sub packages
  158. loadCwdPackage(null);
  159. pack = dub.packageManager.getFirstPackage(package_name);
  160. enforce(pack, "Failed to find a package named '"~package_name~"'.");
  161. logInfo("Building package %s in %s", pack.name, pack.path.toNativeString());
  162. dub.rootPath = pack.path;
  163. }
  164. if (!loadCwdPackage(pack)) return false;
  165. if (!build_config.length) build_config = def_config;
  166. return true;
  167. }
  168.  
  169. // handle the command
  170. switch( cmd ){
  171. default:
  172. enforce(false, "Command is unknown: " ~ cmd);
  173. assert(false);
  174. case "help":
  175. if(args.length >= 2) cmd = args[1];
  176. showHelp(cmd);
  177. return 0;
  178. case "init":
  179. string dir;
  180. if( args.length >= 2 ) dir = args[1];
  181. dub.createEmptyPackage(Path(dir));
  182. return 0;
  183. case "upgrade":
  184. dub.loadPackageFromCwd();
  185. logInfo("Upgrading project in %s", dub.projectPath.toNativeString());
  186. dub.update(UpdateOptions.Upgrade | (annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None));
  187. return 0;
  188. case "install":
  189. enforce(args.length >= 2, "Missing package name.");
  190. auto location = InstallLocation.userWide;
  191. auto name = args[1];
  192. enforce(!install_local || !install_system, "Cannot install locally and system wide at the same time.");
  193. if (install_local) location = InstallLocation.local;
  194. else if (install_system) location = InstallLocation.systemWide;
  195. if (install_version.length) dub.install(name, Dependency(install_version), location, true);
  196. else {
  197. try dub.install(name, Dependency(">=0.0.0"), location, true);
  198. catch(Exception e){
  199. logInfo("Installing a release version failed: %s", e.msg);
  200. logInfo("Retry with ~master...");
  201. dub.install(name, Dependency("~master"), location, true);
  202. }
  203. }
  204. break;
  205. case "uninstall":
  206. enforce(args.length >= 2, "Missing package name.");
  207. auto location = InstallLocation.userWide;
  208. auto package_id = args[1];
  209. enforce(!install_local || !install_system, "Cannot install locally and system wide at the same time.");
  210. if( install_local ) location = InstallLocation.local;
  211. else if( install_system ) location = InstallLocation.systemWide;
  212. try dub.uninstall(package_id, install_version, location);
  213. catch logError("Please specify a individual version or use the wildcard identifier '%s' (without quotes).", Dub.UninstallVersionWildcard);
  214. break;
  215. case "add-local":
  216. enforce(args.length >= 3, "Missing arguments.");
  217. dub.addLocalPackage(args[1], args[2], install_system);
  218. break;
  219. case "remove-local":
  220. enforce(args.length >= 2, "Missing path to package.");
  221. dub.removeLocalPackage(args[1], install_system);
  222. break;
  223. case "add-path":
  224. enforce(args.length >= 2, "Missing search path.");
  225. dub.addSearchPath(args[1], install_system);
  226. break;
  227. case "remove-path":
  228. enforce(args.length >= 2, "Missing search path.");
  229. dub.removeSearchPath(args[1], install_system);
  230. break;
  231. case "list-installed":
  232. logInfo("Installed packages:");
  233. foreach (p; dub.packageManager.getPackageIterator())
  234. logInfo(" %s %s: %s", p.name, p.ver, p.path.toNativeString());
  235. logInfo("");
  236. break;
  237. case "run":
  238. case "build":
  239. case "generate":
  240. string generator;
  241. if( cmd == "run" || cmd == "build" ) {
  242. generator = rdmd ? "rdmd" : "build";
  243. if (args.length >= 2) package_name = args[1];
  244. } else {
  245. if (args.length >= 2) generator = args[1];
  246. if (args.length >= 3) package_name = args[2];
  247. if(generator.empty) {
  248. logInfo("Usage: dub generate <generator_name> [<package name>]");
  249. return 1;
  250. }
  251. }
  252.  
  253. loadSelectedPackage();
  254.  
  255. if( print_builds ){
  256. logInfo("Available build types:");
  257. foreach( tp; ["debug", "release", "unittest", "profile"] )
  258. logInfo(" %s", tp);
  259. logInfo("");
  260. }
  261.  
  262. if( print_configs ){
  263. logInfo("Available configurations:");
  264. foreach( tp; dub.configurations )
  265. logInfo(" %s%s", tp, tp == def_config ? " [default]" : null);
  266. logInfo("");
  267. }
  268.  
  269. if( !nodeps ){
  270. logInfo("Checking dependencies in '%s'", dub.projectPath.toNativeString());
  271. dub.update(annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None);
  272. }
  273.  
  274. enforce(build_config.length == 0 || dub.configurations.canFind(build_config), "Unknown build configuration: "~build_config);
  275.  
  276. if (build_type.length == 0) {
  277. if (environment.get("DFLAGS")) build_type = "$DFLAGS";
  278. else build_type = "debug";
  279. }
  280.  
  281. GeneratorSettings gensettings;
  282. gensettings.platform = build_platform;
  283. gensettings.config = build_config;
  284. gensettings.buildType = build_type;
  285. gensettings.compiler = compiler;
  286. gensettings.buildSettings = build_settings;
  287. gensettings.run = cmd == "run";
  288. gensettings.runArgs = app_args;
  289.  
  290. logDiagnostic("Generating using %s", generator);
  291. dub.generateProject(generator, gensettings);
  292. if( build_type == "ddox" ) dub.runDdox();
  293. break;
  294. case "describe":
  295. if (args.length >= 2) package_name = args[1];
  296. if (!loadSelectedPackage()) return 1;
  297. dub.describeProject(build_platform, build_config);
  298. break;
  299. }
  300.  
  301. return 0;
  302. }
  303. catch(Throwable e)
  304. {
  305. logError("Error: %s\n", e.msg);
  306. logDiagnostic("Full exception: %s", sanitize(e.toString()));
  307. logInfo("Run 'dub help' for usage information.");
  308. return 1;
  309. }
  310. }
  311.  
  312. private void showHelp(string command)
  313. {
  314. if(command == "uninstall" || command == "install") {
  315. logInfo(
  316. `Usage: dub <install|uninstall> <package> [<options>]
  317.  
  318. Note: use dependencies (package.json) if you want to add a dependency, you
  319. don't have to fiddle with installation stuff.
  320.  
  321. (Un)Installation of packages is only needed when you want to put packages to a
  322. place where several applications can share these. If you just have an
  323. dependency to a package, just add it to your package.json, dub will do the rest
  324. for you.
  325.  
  326. Without specified options, (un)installation will default to a user wide shared
  327. location.
  328.  
  329. Complete applications can be installed and run easily by e.g.
  330. dub install vibelog --local
  331. cd vibelog
  332. dub
  333. This will grab all needed dependencies and compile and run the application.
  334.  
  335. Install options:
  336. --version Use the specified version/branch instead of the latest
  337. For the uninstall command, this may be a wildcard
  338. string: "*", which will remove all packages from the
  339. specified location.
  340. --system Install system wide instead of user local
  341. --local Install as in a sub folder of the current directory
  342. Note that system and local cannot be mixed.
  343. `);
  344. return;
  345. }
  346.  
  347. // No specific help, show general help.
  348. logInfo(
  349. `Usage: dub [<command>] [<options...>] [-- <application arguments...>]
  350.  
  351. Manages the DUB project in the current directory. "--" can be used to separate
  352. DUB options from options passed to the application. If the command is omitted,
  353. dub will default to "run".
  354.  
  355. Available commands:
  356. help Prints this help screen
  357. init [<directory>] Initializes an empty project in the specified directory
  358. run [<package>] Builds and runs a package (default command)
  359. build [<package>] Builds a package (uses the main package in the current
  360. working directory by default)
  361. upgrade Forces an upgrade of all dependencies
  362. install <name> Manually installs a package. See 'dub help install'.
  363. uninstall <name> Uninstalls a package. See 'dub help uninstall'.
  364. add-local <dir> <version>
  365. Adds a local package directory (e.g. a git repository)
  366. remove-local <dir> Removes a local package directory
  367. add-path <dir> Adds a default package search path
  368. remove-path <dir> Removes a package search path
  369. list-installed Prints a list of all installed packages
  370. generate <name> [<package>]
  371. Generates project files using the specified generator:
  372. visuald, visuald-combined, mono-d, build, rdmd
  373. describe [<package>] Prints a JSON description of the project and its
  374. dependencies
  375.  
  376. General options:
  377. --annotate Do not execute dependency installations, just print
  378. -v --verbose Also output debug messages
  379. --vverbose Also output trace messages (produces a lot of output)
  380. -q --quiet Only output warnings and errors
  381. --vquiet No output
  382. --registry=URL Search the given DUB registry URL first when resolving
  383. dependencies. Can be specified multiple times.
  384. --root=PATH Path to operate in instead of the current working dir
  385.  
  386. Build/run options:
  387. --build=NAME Specifies the type of build to perform. Note that
  388. setting the DFLAGS environment variable will override
  389. the build type with custom flags.
  390. Possible names:
  391. debug (default), plain, release, unittest, profile,
  392. docs, ddox, cov, unittest-cov and custom types
  393. --config=NAME Builds the specified configuration. Configurations can
  394. be defined in package.json
  395. --compiler=NAME Specifies the compiler binary to use. Arbitrary pre-
  396. and suffixes to the identifiers below are recognized
  397. (e.g. ldc2 or dmd-2.063) and matched to the proper
  398. compiler type:
  399. dmd (default), gdc, ldc, gdmd, ldmd
  400. --arch=NAME Force a different architecture (e.g. x86 or x86_64)
  401. --nodeps Do not check dependencies for 'run' or 'build'
  402. --print-builds Prints the list of available build types
  403. --print-configs Prints the list of available configurations
  404. --print-platform Prints the identifiers for the current build platform
  405. as used for the build fields in package.json
  406. --rdmd Use rdmd instead of directly invoking the compiler
  407. --debug=NAME Define the specified debug version identifier when
  408. building - can be used multiple times
  409.  
  410. Install options:
  411. --version Use the specified version/branch instead of the latest
  412. --system Install system wide instead of user local
  413. --local Install as in a sub folder of the current directory
  414.  
  415. `);
  416. logInfo("DUB version %s", dubVersion);
  417. }