/** The entry point to dub Copyright: © 2012-2013 Matthias Dondorff License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Matthias Dondorff, Sönke Ludwig */ module app; import dub.compilers.compiler; import dub.dependency; import dub.dub; import dub.generators.generator; import dub.internal.std.process; import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.url; import dub.package_; import dub.packagesupplier; import dub.project; import dub.version_; import std.algorithm; import std.array; import std.conv; import std.encoding; import std.exception; import std.file; import std.getopt; int main(string[] args) { logDiagnostic("DUB version %s", dubVersion); string cmd; version(Windows){ // rdmd uses $TEMP to compute a temporary path. since cygwin substitutes backslashes // with slashes, this causes OPTLINK to fail (it thinks path segments are options) // we substitute the other way around here to fix this. environment["TEMP"] = environment["TEMP"].replace("/", "\\"); } try { // split application arguments from DUB arguments string[] app_args; auto app_args_idx = args.countUntil("--"); if (app_args_idx >= 0) { app_args = args[app_args_idx+1 .. $]; args = args[0 .. app_args_idx]; } // parse general options bool verbose, vverbose, quiet, vquiet; bool help, nodeps, annotate; LogLevel loglevel = LogLevel.info; string build_type, build_config; string compiler_name = "dmd"; string arch; bool rdmd = false; bool print_platform, print_builds, print_configs; bool install_system = false, install_local = false; string install_version; string[] registry_urls; string[] debug_versions; string root_path = getcwd(); getopt(args, "v|verbose", &verbose, "vverbose", &vverbose, "q|quiet", &quiet, "vquiet", &vquiet, "h|help", &help, // obsolete "nodeps", &nodeps, "annotate", &annotate, "build", &build_type, "compiler", &compiler_name, "arch", &arch, "rdmd", &rdmd, "config", &build_config, "debug", &debug_versions, "print-builds", &print_builds, "print-configs", &print_configs, "print-platform", &print_platform, "system", &install_system, "local", &install_local, "version", &install_version, "registry", ®istry_urls, "root", &root_path ); if( vverbose ) loglevel = LogLevel.debug_; else if( verbose ) loglevel = LogLevel.diagnostic; else if( vquiet ) loglevel = LogLevel.none; else if( quiet ) loglevel = LogLevel.warn; setLogLevel(loglevel); // extract the command if( args.length > 1 && !args[1].startsWith("-") ){ cmd = args[1]; args = args[0] ~ args[2 .. $]; } else cmd = "run"; // display help if requested (obsolete) if( help ){ showHelp(cmd); return 0; } BuildSettings build_settings; auto compiler = getCompiler(compiler_name); auto build_platform = compiler.determinePlatform(build_settings, compiler_name, arch); build_settings.addDebugVersions(debug_versions); if( print_platform ){ logInfo("Build platform:"); logInfo(" Compiler: %s", build_platform.compiler); logInfo(" System: %s", build_platform.platform.join(" ")); logInfo(" Architecture: %s", build_platform.architecture.join(" ")); logInfo(""); } auto package_suppliers = registry_urls.map!(url => cast(PackageSupplier)new RegistryPackageSupplier(Url(url))).array; Dub dub = new Dub(package_suppliers, root_path); string def_config; // make the CWD package available so that for example sub packages can reference their // parent package. try dub.packageManager.getTemporaryPackage(Path(root_path), Version("~master")); catch (Exception e) { logDiagnostic("No package found in current working directory."); } bool loadCwdPackage(Package pack) { if (!existsFile(dub.rootPath~"package.json") && !existsFile(dub.rootPath~"source/app.d")) { logInfo(""); logInfo("Neither package.json, nor source/app.d was found in the current directory."); logInfo("Please run dub from the root directory of an existing package, or create a new"); logInfo("package using \"dub init <name>\"."); logInfo(""); showHelp(null); return false; } if (pack) dub.loadPackage(pack); else dub.loadPackageFromCwd(); def_config = dub.getDefaultConfiguration(build_platform); return true; } string package_name; bool loadSelectedPackage() { Package pack; if (!package_name.empty) { // load package in root_path to enable searching for sub packages loadCwdPackage(null); pack = dub.packageManager.getFirstPackage(package_name); enforce(pack, "Failed to find a package named '"~package_name~"'."); logInfo("Building package %s in %s", pack.name, pack.path.toNativeString()); dub.rootPath = pack.path; } if (!loadCwdPackage(pack)) return false; if (!build_config.length) build_config = def_config; return true; } // handle the command switch( cmd ){ default: enforce(false, "Command is unknown: " ~ cmd); assert(false); case "help": if(args.length >= 2) cmd = args[1]; showHelp(cmd); return 0; case "init": string dir; if( args.length >= 2 ) dir = args[1]; dub.createEmptyPackage(Path(dir)); return 0; case "upgrade": dub.loadPackageFromCwd(); logInfo("Upgrading project in %s", dub.projectPath.toNativeString()); dub.update(UpdateOptions.Upgrade | (annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None)); return 0; case "install": enforce(args.length >= 2, "Missing package name."); auto location = InstallLocation.userWide; auto name = args[1]; enforce(!install_local || !install_system, "Cannot install locally and system wide at the same time."); if (install_local) location = InstallLocation.local; else if (install_system) location = InstallLocation.systemWide; if (install_version.length) dub.install(name, Dependency(install_version), location, true); else { try dub.install(name, Dependency(">=0.0.0"), location, true); catch(Exception e){ logInfo("Installing a release version failed: %s", e.msg); logInfo("Retry with ~master..."); dub.install(name, Dependency("~master"), location, true); } } break; case "uninstall": enforce(args.length >= 2, "Missing package name."); auto location = InstallLocation.userWide; auto package_id = args[1]; enforce(!install_local || !install_system, "Cannot install locally and system wide at the same time."); if( install_local ) location = InstallLocation.local; else if( install_system ) location = InstallLocation.systemWide; try dub.uninstall(package_id, install_version, location); catch logError("Please specify a individual version or use the wildcard identifier '%s' (without quotes).", Dub.UninstallVersionWildcard); break; case "add-local": enforce(args.length >= 3, "Missing arguments."); dub.addLocalPackage(args[1], args[2], install_system); break; case "remove-local": enforce(args.length >= 2, "Missing path to package."); dub.removeLocalPackage(args[1], install_system); break; case "add-path": enforce(args.length >= 2, "Missing search path."); dub.addSearchPath(args[1], install_system); break; case "remove-path": enforce(args.length >= 2, "Missing search path."); dub.removeSearchPath(args[1], install_system); break; case "list-installed": logInfo("Installed packages:"); foreach (p; dub.packageManager.getPackageIterator()) logInfo(" %s %s: %s", p.name, p.ver, p.path.toNativeString()); logInfo(""); break; case "run": case "build": case "generate": string generator; if( cmd == "run" || cmd == "build" ) { generator = rdmd ? "rdmd" : "build"; if (args.length >= 2) package_name = args[1]; } else { if (args.length >= 2) generator = args[1]; if (args.length >= 3) package_name = args[2]; if(generator.empty) { logInfo("Usage: dub generate <generator_name> [<package name>]"); return 1; } } loadSelectedPackage(); if( print_builds ){ logInfo("Available build types:"); foreach( tp; ["debug", "release", "unittest", "profile"] ) logInfo(" %s", tp); logInfo(""); } if( print_configs ){ logInfo("Available configurations:"); foreach( tp; dub.configurations ) logInfo(" %s%s", tp, tp == def_config ? " [default]" : null); logInfo(""); } if( !nodeps ){ logInfo("Checking dependencies in '%s'", dub.projectPath.toNativeString()); dub.update(annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None); } enforce(build_config.length == 0 || dub.configurations.canFind(build_config), "Unknown build configuration: "~build_config); if (build_type.length == 0) { if (environment.get("DFLAGS")) build_type = "$DFLAGS"; else build_type = "debug"; } GeneratorSettings gensettings; gensettings.platform = build_platform; gensettings.config = build_config; gensettings.buildType = build_type; gensettings.compiler = compiler; gensettings.buildSettings = build_settings; gensettings.run = cmd == "run"; gensettings.runArgs = app_args; logDiagnostic("Generating using %s", generator); dub.generateProject(generator, gensettings); if( build_type == "ddox" ) dub.runDdox(); break; case "describe": if (args.length >= 2) package_name = args[1]; if (!loadSelectedPackage()) return 1; dub.describeProject(build_platform, build_config); break; } return 0; } catch(Throwable e) { logError("Error: %s\n", e.msg); logDiagnostic("Full exception: %s", sanitize(e.toString())); logInfo("Run 'dub help' for usage information."); return 1; } } private void showHelp(string command) { if(command == "uninstall" || command == "install") { logInfo( `Usage: dub <install|uninstall> <package> [<options>] Note: use dependencies (package.json) if you want to add a dependency, you don't have to fiddle with installation stuff. (Un)Installation of packages is only needed when you want to put packages to a place where several applications can share these. If you just have an dependency to a package, just add it to your package.json, dub will do the rest for you. Without specified options, (un)installation will default to a user wide shared location. Complete applications can be installed and run easily by e.g. dub install vibelog --local cd vibelog dub This will grab all needed dependencies and compile and run the application. Install options: --version Use the specified version/branch instead of the latest For the uninstall command, this may be a wildcard string: "*", which will remove all packages from the specified location. --system Install system wide instead of user local --local Install as in a sub folder of the current directory Note that system and local cannot be mixed. `); return; } // No specific help, show general help. logInfo( `Usage: dub [<command>] [<options...>] [-- <application arguments...>] Manages the DUB project in the current directory. "--" can be used to separate DUB options from options passed to the application. If the command is omitted, dub will default to "run". Available commands: help Prints this help screen init [<directory>] Initializes an empty project in the specified directory run [<package>] Builds and runs a package (default command) build [<package>] Builds a package (uses the main package in the current working directory by default) upgrade Forces an upgrade of all dependencies install <name> Manually installs a package. See 'dub help install'. uninstall <name> Uninstalls a package. See 'dub help uninstall'. add-local <dir> <version> Adds a local package directory (e.g. a git repository) remove-local <dir> Removes a local package directory add-path <dir> Adds a default package search path remove-path <dir> Removes a package search path list-installed Prints a list of all installed packages generate <name> [<package>] Generates project files using the specified generator: visuald, visuald-combined, mono-d, build, rdmd describe [<package>] Prints a JSON description of the project and its dependencies General options: --annotate Do not execute dependency installations, just print -v --verbose Also output debug messages --vverbose Also output trace messages (produces a lot of output) -q --quiet Only output warnings and errors --vquiet No output --registry=URL Search the given DUB registry URL first when resolving dependencies. Can be specified multiple times. --root=PATH Path to operate in instead of the current working dir Build/run options: --build=NAME Specifies the type of build to perform. Note that setting the DFLAGS environment variable will override the build type with custom flags. Possible names: debug (default), plain, release, unittest, profile, docs, ddox, cov, unittest-cov and custom types --config=NAME Builds the specified configuration. Configurations can be defined in package.json --compiler=NAME Specifies the compiler binary to use. Arbitrary pre- and suffixes to the identifiers below are recognized (e.g. ldc2 or dmd-2.063) and matched to the proper compiler type: dmd (default), gdc, ldc, gdmd, ldmd --arch=NAME Force a different architecture (e.g. x86 or x86_64) --nodeps Do not check dependencies for 'run' or 'build' --print-builds Prints the list of available build types --print-configs Prints the list of available configurations --print-platform Prints the identifiers for the current build platform as used for the build fields in package.json --rdmd Use rdmd instead of directly invoking the compiler --debug=NAME Define the specified debug version identifier when building - can be used multiple times Install options: --version Use the specified version/branch instead of the latest --system Install system wide instead of user local --local Install as in a sub folder of the current directory `); logInfo("DUB version %s", dubVersion); }