Newer
Older
dub_jkp / source / app.d
@Sönke Ludwig Sönke Ludwig on 1 Nov 2013 16 KB Rename Dub.get() to Dub.fetch(). See #150.
  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. // split application arguments from DUB arguments
  47. string[] app_args;
  48. auto app_args_idx = args.countUntil("--");
  49. if (app_args_idx >= 0) {
  50. app_args = args[app_args_idx+1 .. $];
  51. args = args[0 .. app_args_idx];
  52. }
  53.  
  54. // parse general options
  55. bool verbose, vverbose, quiet, vquiet;
  56. bool help, nodeps, annotate;
  57. LogLevel loglevel = LogLevel.info;
  58. string build_type, build_config;
  59. string compiler_name = "dmd";
  60. string arch;
  61. bool rdmd = false;
  62. bool print_platform, print_builds, print_configs;
  63. bool place_system_wide = false, place_locally = false;
  64. string retrieved_version;
  65. string[] registry_urls;
  66. string[] debug_versions;
  67. string root_path = getcwd();
  68. getopt(args,
  69. "v|verbose", &verbose,
  70. "vverbose", &vverbose,
  71. "q|quiet", &quiet,
  72. "vquiet", &vquiet,
  73. "h|help", &help, // obsolete
  74. "nodeps", &nodeps,
  75. "annotate", &annotate,
  76. "build", &build_type,
  77. "compiler", &compiler_name,
  78. "arch", &arch,
  79. "rdmd", &rdmd,
  80. "config", &build_config,
  81. "debug", &debug_versions,
  82. "print-builds", &print_builds,
  83. "print-configs", &print_configs,
  84. "print-platform", &print_platform,
  85. "system", &place_system_wide,
  86. "local", &place_locally,
  87. "version", &retrieved_version,
  88. "registry", &registry_urls,
  89. "root", &root_path
  90. );
  91.  
  92. if( vverbose ) loglevel = LogLevel.debug_;
  93. else if( verbose ) loglevel = LogLevel.diagnostic;
  94. else if( vquiet ) loglevel = LogLevel.none;
  95. else if( quiet ) loglevel = LogLevel.warn;
  96. setLogLevel(loglevel);
  97.  
  98. // extract the command
  99. if( args.length > 1 && !args[1].startsWith("-") ){
  100. cmd = args[1];
  101. args = args[0] ~ args[2 .. $];
  102. } else cmd = "run";
  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. static void warnRenamed(string prev, string curr)
  170. {
  171. logWarn("Command '%s' was renamed to '%s'. Old name is deprecated, please update your scripts", prev, curr);
  172. }
  173.  
  174. // handle the command
  175. switch( cmd ){
  176. default:
  177. enforce(false, "Command is unknown: " ~ cmd);
  178. assert(false);
  179. case "help":
  180. if(args.length >= 2) cmd = args[1];
  181. showHelp(cmd);
  182. return 0;
  183. case "init":
  184. string dir;
  185. string type = "minimal";
  186. if (args.length >= 2) dir = args[1];
  187. if (args.length >= 3) type = args[2];
  188. dub.createEmptyPackage(Path(dir), type);
  189. return 0;
  190. case "upgrade":
  191. dub.loadPackageFromCwd();
  192. logInfo("Upgrading project in %s", dub.projectPath.toNativeString());
  193. dub.update(UpdateOptions.Upgrade | (annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None));
  194. return 0;
  195. case "install":
  196. warnRenamed(cmd, "fetch");
  197. goto case "fetch";
  198. case "fetch":
  199. enforce(args.length >= 2, "Missing package name.");
  200. auto location = PlacementLocation.userWide;
  201. auto name = args[1];
  202. enforce(!place_locally || !place_system_wide, "Cannot place package locally and system wide at the same time.");
  203. if (place_locally) location = PlacementLocation.local;
  204. else if (place_system_wide) location = PlacementLocation.systemWide;
  205. if (retrieved_version.length) dub.fetch(name, Dependency(retrieved_version), location, true);
  206. else {
  207. try {
  208. dub.fetch(name, Dependency(">=0.0.0"), location, true);
  209. logInfo(
  210. "Please note that you need to use `dub run <pkgname>` " ~
  211. "or add it to dependencies of your package to actually use/run it. " ~
  212. "dub does not do actual installation of packages outside of its own ecosystem.");
  213. }
  214. catch(Exception e){
  215. logInfo("Getting a release version failed: %s", e.msg);
  216. logInfo("Retry with ~master...");
  217. dub.fetch(name, Dependency("~master"), location, true);
  218. }
  219. }
  220. break;
  221. case "uninstall":
  222. warnRenamed(cmd, "remove");
  223. goto case "remove";
  224. case "remove":
  225. enforce(args.length >= 2, "Missing package name.");
  226. auto location = PlacementLocation.userWide;
  227. auto package_id = args[1];
  228. enforce(!place_locally || !place_system_wide, "Cannot place package locally and system wide at the same time.");
  229. if ( place_locally ) location = PlacementLocation.local;
  230. else if( place_system_wide ) location = PlacementLocation.systemWide;
  231. try dub.remove(package_id, retrieved_version, location);
  232. catch logError("Please specify a individual version or use the wildcard identifier '%s' (without quotes).", Dub.RemoveVersionWildcard);
  233. break;
  234. case "add-local":
  235. enforce(args.length >= 3, "Missing arguments.");
  236. dub.addLocalPackage(args[1], args[2], place_system_wide);
  237. break;
  238. case "remove-local":
  239. enforce(args.length >= 2, "Missing path to package.");
  240. dub.removeLocalPackage(args[1], place_system_wide);
  241. break;
  242. case "add-path":
  243. enforce(args.length >= 2, "Missing search path.");
  244. dub.addSearchPath(args[1], place_system_wide);
  245. break;
  246. case "remove-path":
  247. enforce(args.length >= 2, "Missing search path.");
  248. dub.removeSearchPath(args[1], place_system_wide);
  249. break;
  250. case "list-installed":
  251. warnRenamed(cmd, "list");
  252. goto case "list";
  253. case "list":
  254. logInfo("Packages present in the system and known to dub:");
  255. foreach (p; dub.packageManager.getPackageIterator())
  256. logInfo(" %s %s: %s", p.name, p.ver, p.path.toNativeString());
  257. logInfo("");
  258. break;
  259. case "run":
  260. case "build":
  261. case "generate":
  262. string generator;
  263. if( cmd == "run" || cmd == "build" ) {
  264. generator = rdmd ? "rdmd" : "build";
  265. if (args.length >= 2) package_name = args[1];
  266. } else {
  267. if (args.length >= 2) generator = args[1];
  268. if (args.length >= 3) package_name = args[2];
  269. if(generator.empty) {
  270. logInfo("Usage: dub generate <generator_name> [<package name>]");
  271. return 1;
  272. }
  273. }
  274.  
  275. if (!loadSelectedPackage()) return 1;
  276.  
  277. if( print_builds ){
  278. logInfo("Available build types:");
  279. foreach( tp; ["debug", "release", "unittest", "profile"] )
  280. logInfo(" %s", tp);
  281. logInfo("");
  282. }
  283.  
  284. if( print_configs ){
  285. logInfo("Available configurations:");
  286. foreach( tp; dub.configurations )
  287. logInfo(" %s%s", tp, tp == def_config ? " [default]" : null);
  288. logInfo("");
  289. }
  290.  
  291. if( !nodeps ){
  292. logInfo("Checking dependencies in '%s'", dub.projectPath.toNativeString());
  293. dub.update(annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None);
  294. }
  295.  
  296. enforce(build_config.length == 0 || dub.configurations.canFind(build_config), "Unknown build configuration: "~build_config);
  297.  
  298. if (build_type.length == 0) {
  299. if (environment.get("DFLAGS")) build_type = "$DFLAGS";
  300. else build_type = "debug";
  301. }
  302.  
  303. GeneratorSettings gensettings;
  304. gensettings.platform = build_platform;
  305. gensettings.config = build_config;
  306. gensettings.buildType = build_type;
  307. gensettings.compiler = compiler;
  308. gensettings.buildSettings = build_settings;
  309. gensettings.run = cmd == "run";
  310. gensettings.runArgs = app_args;
  311.  
  312. logDiagnostic("Generating using %s", generator);
  313. dub.generateProject(generator, gensettings);
  314. if( build_type == "ddox" ) dub.runDdox();
  315. break;
  316. case "describe":
  317. if (args.length >= 2) package_name = args[1];
  318. if (!loadSelectedPackage()) return 1;
  319. dub.describeProject(build_platform, build_config);
  320. break;
  321. }
  322.  
  323. return 0;
  324. }
  325. catch(Throwable e)
  326. {
  327. logError("Error: %s\n", e.msg);
  328. logDiagnostic("Full exception: %s", sanitize(e.toString()));
  329. logInfo("Run 'dub help' for usage information.");
  330. return 1;
  331. }
  332. }
  333.  
  334. private void showHelp(string command)
  335. {
  336. if(command == "remove" || command == "fetch") {
  337. logInfo(
  338. `Usage: dub <fetch|remove> <package> [<options>]
  339.  
  340. Note: use dependencies (package.json) if you want to add a dependency, you
  341. don't have to fiddle with caching stuff.
  342.  
  343. Explicit retrieval/removal of packages is only needed when you want to put packages to a
  344. place where several applications can share these. If you just have an
  345. dependency to a package, just add it to your package.json, dub will do the rest
  346. for you.
  347.  
  348. Without specified options, placement/removal will default to a user wide shared
  349. location.
  350.  
  351. Complete applications can be retrieved and run easily by e.g.
  352. dub fetch vibelog --local
  353. cd vibelog
  354. dub
  355. This will grab all needed dependencies and compile and run the application.
  356.  
  357. Note: dub does not do any real "installation" of packages, those are registered
  358. only within dub internal ecosystem. Generation of native system packages / installer
  359. may be added later.
  360.  
  361. Options:
  362. --version Use the specified version/branch instead of the latest
  363. For the remove command, this may be a wildcard
  364. string: "*", which will remove all packages from the
  365. specified location.
  366. --system Put package into system wide dub cache instead of user local one
  367. --local Put package to a sub folder of the current directory
  368. Note that system and local cannot be mixed.
  369. `);
  370. return;
  371. }
  372.  
  373. // No specific help, show general help.
  374. logInfo(
  375. `Usage: dub [<command>] [<options...>] [-- <application arguments...>]
  376.  
  377. Manages the DUB project in the current directory. "--" can be used to separate
  378. DUB options from options passed to the application. If the command is omitted,
  379. dub will default to "run".
  380.  
  381. Available commands:
  382. help Prints this help screen
  383. init [<directory> [<type>]]
  384. Initializes an empty project of the specified type in
  385. the given directory. By default, the current working
  386. dirctory is used. Available types:
  387. minimal (default), vibe.d
  388. run [<package>] Builds and runs a package (default command)
  389. build [<package>] Builds a package (uses the main package in the current
  390. working directory by default)
  391. upgrade Forces an upgrade of all dependencies
  392. fetch <name> Manually retrieves a package. See 'dub help fetch'.
  393. remove <name> Removes present package. See 'dub help remove'.
  394. add-local <dir> <version>
  395. Adds a local package directory (e.g. a git repository)
  396. remove-local <dir> Removes a local package directory
  397. add-path <dir> Adds a default package search path
  398. remove-path <dir> Removes a package search path
  399. list Prints a list of all present packages dub is aware of
  400. generate <name> [<package>]
  401. Generates project files using the specified generator:
  402. visuald, visuald-combined, mono-d, build, rdmd
  403. describe [<package>] Prints a JSON description of the project and its
  404. dependencies
  405.  
  406. General options:
  407. --annotate Do not execute dependency retrieval, just print
  408. -v --verbose Also output debug messages
  409. --vverbose Also output trace messages (produces a lot of output)
  410. -q --quiet Only output warnings and errors
  411. --vquiet No output
  412. --registry=URL Search the given DUB registry URL first when resolving
  413. dependencies. Can be specified multiple times.
  414. --root=PATH Path to operate in instead of the current working dir
  415.  
  416. Build/run options:
  417. --build=NAME Specifies the type of build to perform. Note that
  418. setting the DFLAGS environment variable will override
  419. the build type with custom flags.
  420. Possible names:
  421. debug (default), plain, release, unittest, profile,
  422. docs, ddox, cov, unittest-cov and custom types
  423. --config=NAME Builds the specified configuration. Configurations can
  424. be defined in package.json
  425. --compiler=NAME Specifies the compiler binary to use. Arbitrary pre-
  426. and suffixes to the identifiers below are recognized
  427. (e.g. ldc2 or dmd-2.063) and matched to the proper
  428. compiler type:
  429. dmd (default), gdc, ldc, gdmd, ldmd
  430. --arch=NAME Force a different architecture (e.g. x86 or x86_64)
  431. --nodeps Do not check dependencies for 'run' or 'build'
  432. --print-builds Prints the list of available build types
  433. --print-configs Prints the list of available configurations
  434. --print-platform Prints the identifiers for the current build platform
  435. as used for the build fields in package.json
  436. --rdmd Use rdmd instead of directly invoking the compiler
  437. --debug=NAME Define the specified debug version identifier when
  438. building - can be used multiple times
  439.  
  440. Fetch/remove options:
  441. --version Use the specified version/branch instead of the latest
  442. --system Put package into system wide dub cache instead of user local one
  443. --local Put packahe to a sub folder of the current directory
  444.  
  445. `);
  446. logInfo("DUB version %s", dubVersion);
  447. }