diff --git a/source/app.d b/source/app.d index 0a7b291..573dbf9 100644 --- a/source/app.d +++ b/source/app.d @@ -133,18 +133,19 @@ return 0; case "install": enforce(args.length >= 2, "Missing package name."); + dub.loadPackageFromCwd(); 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, new Dependency(install_version), location); + if( install_version.length ) dub.install(name, new Dependency(install_version), location, true); else { - try dub.install(name, new Dependency(">=0.0.0"), location); + try dub.install(name, new 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, new Dependency("~master"), location); + dub.install(name, new Dependency("~master"), location, true); } } break; diff --git a/source/dub/dub.d b/source/dub/dub.d index 277a78a..d7768c3 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -55,7 +55,7 @@ Path m_userDubPath, m_systemDubPath; Json m_systemConfig, m_userConfig; PackageManager m_packageManager; - Project m_app; + Project m_project; } /// Initiales the package manager for the vibe application @@ -83,35 +83,35 @@ /// Returns the name listed in the package.json of the current /// application. - @property string projectName() const { return m_app.name; } + @property string projectName() const { return m_project.name; } @property Path projectPath() const { return m_root; } - @property string[] configurations() const { return m_app.configurations; } + @property string[] configurations() const { return m_project.configurations; } @property inout(PackageManager) packageManager() inout { return m_packageManager; } - @property Path binaryPath() const { return m_app.binaryPath; } + @property Path binaryPath() const { return m_project.binaryPath; } void loadPackageFromCwd() { m_root = m_cwd; m_packageManager.projectPackagePath = m_root ~ ".dub/packages/"; - m_app = new Project(m_packageManager, m_root); + m_project = new Project(m_packageManager, m_root); } - string getDefaultConfiguration(BuildPlatform platform) const { return m_app.getDefaultConfiguration(platform); } + string getDefaultConfiguration(BuildPlatform platform) const { return m_project.getDefaultConfiguration(platform); } /// Lists all installed modules void list() { - logInfo(m_app.info()); + logInfo(m_project.info()); } /// Performs installation and uninstallation as necessary for /// the application. /// @param options bit combination of UpdateOptions bool update(UpdateOptions options) { - Action[] actions = m_app.determineActions(m_packageSupplier, options); + Action[] actions = m_project.determineActions(m_packageSupplier, options); if( actions.length == 0 ) return true; logInfo("The following changes could be performed:"); @@ -145,8 +145,8 @@ if(a.type == Action.Type.install) install(a.packageId, a.vers, a.location); - m_app.reinit(); - Action[] newActions = m_app.determineActions(m_packageSupplier, 0); + m_project.reinit(); + Action[] newActions = m_project.determineActions(m_packageSupplier, 0); if(newActions.length > 0) { logInfo("There are still some actions to perform:"); foreach(Action a; newActions) @@ -161,32 +161,38 @@ /// Generate project files for a specified IDE. /// Any existing project files will be overridden. void generateProject(string ide, GeneratorSettings settings) { - auto generator = createProjectGenerator(ide, m_app, m_packageManager); + auto generator = createProjectGenerator(ide, m_project, m_packageManager); generator.generateProject(settings); } /// Creates a zip from the application. void createZip(string zipFile) { - m_app.createZip(zipFile); + m_project.createZip(zipFile); } /// Prints some information to the log. void info() { logInfo("Status for %s", m_root); - logInfo("\n" ~ m_app.info()); + logInfo("\n" ~ m_project.info()); } /// Gets all installed packages as a "packageId" = "version" associative array - string[string] installedPackages() const { return m_app.installedPackagesIDs(); } + string[string] installedPackages() const { return m_project.installedPackagesIDs(); } /// Installs the package matching the dependency into the application. /// @param addToApplication if true, this will also add an entry in the /// list of dependencies in the application's package.json - void install(string packageId, const Dependency dep, InstallLocation location = InstallLocation.projectLocal) + void install(string packageId, const Dependency dep, InstallLocation location = InstallLocation.projectLocal, bool addToApplication = false) { auto pinfo = m_packageSupplier.packageJson(packageId, dep); string ver = pinfo["version"].get!string; + // Perform addToApplication + if(addToApplication && !m_project.tryAddDependency(packageId, dep)) { + logError("Installation of '%s' failed.", packageId); + return; + } + if( m_packageManager.hasPackage(packageId, ver, location) ){ logInfo("Package %s %s (%s) is already installed with the latest version, skipping upgrade.", packageId, ver, location); diff --git a/source/dub/package_.d b/source/dub/package_.d index 92e359c..8375529 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -62,7 +62,7 @@ /// "versions-X" /// "importPaths-X" /// "stringImportPaths-X" -/// +/// "sourcePath" /// } /// } /// @@ -189,4 +189,26 @@ scope(exit) dstFile.close(); dstFile.writePrettyJsonString(m_meta); } -} \ No newline at end of file + + /// Adds an dependency, if the package is already a dependency and it cannot be + /// merged with the supplied dependency, an exception will be generated. + void addDependency(string packageId, const Dependency dependency) { + Dependency dep = new Dependency(dependency); + if(packageId in m_dependencies) { + dep = dependency.merge(m_dependencies[packageId]); + if(!dep.valid()) throw new Exception("Cannot merge with existing dependency."); + } + m_dependencies[packageId] = dep; + Json[string] empty; + if("dependencies" !in m_meta) m_meta["dependencies"] = empty; + m_meta["dependencies"][packageId] = Json(to!string(dep)); + } + + /// Removes a dependecy. + void removeDependency(string packageId) { + if(packageId !in m_dependencies) + return; + m_dependencies.remove(packageId); + m_meta.remove(packageId); + } +} \ No newline at end of file diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 535c519..6d20401 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -169,6 +169,9 @@ auto package_name = package_info.name.get!string(); auto package_version = package_info["version"].get!string(); + logDebug("Installing package '%s' version '%s' to location '%s' from file '%s'", + package_name, package_version, to!string(location), zip_file_path.toNativeString()); + Path destination; final switch( location ){ case InstallLocation.local: destination = Path(package_name); break; @@ -184,12 +187,13 @@ // open zip file ZipArchive archive; { + logTrace("Opening file %s", zip_file_path); auto f = openFile(zip_file_path, FileMode.Read); scope(exit) f.close(); archive = new ZipArchive(f.readAll()); } - logDebug("Installing from zip."); + logTrace("Installing from zip."); // In a github zip, the actual contents are in a subfolder Path zip_prefix; @@ -207,7 +211,7 @@ zip_prefix = Path(am.name); } - logDebug("zip root folder: %s", zip_prefix); + logTrace("zip root folder: %s", zip_prefix); Path getCleanedPath(string fileName) { auto path = Path(fileName); @@ -218,12 +222,14 @@ // install mkdirRecurse(destination.toNativeString()); auto journal = new Journal; + logDebug("Copying all files..."); + int countFiles = 0; foreach(ArchiveMember a; archive.directory) { auto cleanedPath = getCleanedPath(a.name); if(cleanedPath.empty) continue; auto dst_path = destination~cleanedPath; - logDebug("Creating %s", cleanedPath); + logTrace("Creating %s", cleanedPath); if( dst_path.endsWithSlash ){ if( !existsDirectory(dst_path) ) mkdirRecurse(dst_path.toNativeString()); @@ -235,8 +241,10 @@ scope(exit) dstFile.close(); dstFile.put(archive.expand(a)); journal.add(Journal.Entry(Journal.Type.RegularFile, cleanedPath)); + ++countFiles; } } + logDebug("%s file(s) copied.", to!string(countFiles)); // overwrite package.json (this one includes a version field) Json pi = jsonFromFile(destination~PackageJsonFilename); @@ -252,12 +260,14 @@ logInfo("%s has been installed with version %s", package_name, package_version); auto pack = new Package(location, destination); + final switch( location ){ case InstallLocation.local: break; case InstallLocation.projectLocal: m_projectPackages[package_name] = pack; break; case InstallLocation.userWide: m_userPackages[package_name] ~= pack; break; case InstallLocation.systemWide: m_systemPackages[package_name] ~= pack; break; } + return pack; } diff --git a/source/dub/project.d b/source/dub/project.d index 7152cd0..2726f6c 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -1,5 +1,5 @@ /** - A package manager. + Representing a full project, with a root Package and several dependencies. Copyright: © 2012 Matthias Dondorff License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. @@ -35,7 +35,7 @@ import stdx.process; -/// During check to build task list, which can then be executed. +/// Representing a full project, with a root Package and several dependencies. class Project { private { Path m_root; @@ -54,7 +54,10 @@ reinit(); } - @property Path binaryPath() const { auto p = m_main.binaryPath; return p.length ? Path(p) : Path("./"); } + @property Path binaryPath() const + { + return m_main.binaryPath.length ? Path(m_main.binaryPath) : Path("./"); + } /// Gathers information @property string info() @@ -77,8 +80,10 @@ return pkgs; } + /// List of installed Packages @property const(Package[]) installedPackages() const { return m_dependencies; } + /// Main package. @property const (Package) mainPackage() const { return m_main; } string getDefaultConfiguration(BuildPlatform platform) @@ -325,6 +330,23 @@ */ } + /// Tries to add the specified dependency. + /// If an existing dependencies is already current, this one is compared to + /// the supplied one, in order to check if these are compatible. + /// @return true, if the dependency was succesfully added, false if the + /// new dependency cannot be added because of current settings. + bool tryAddDependency(string packageId, const Dependency dependency) { + try { + m_main.addDependency(packageId, dependency); + m_main.writeJson(m_root); + return true; + } + catch(Exception e) { + logError("The dependency '%s' '%s' could not be added. Try to check the package.json of your project if this is a conflict with an existing dependency.", packageId, dependency); + return false; + } + } + private bool gatherMissingDependencies(PackageSupplier packageSupplier, DependencyGraph graph) { RequestedDependency[string] missing = graph.missing(); RequestedDependency[string] oldMissing; @@ -392,7 +414,7 @@ } catch(Exception t) return true; } - void markUpToDate(string packageId) { + private void markUpToDate(string packageId) { logTrace("markUpToDate(%s)", packageId); Json create(ref Json json, string object) { if( object !in json ) json[object] = Json.EmptyObject; diff --git a/source/vibecompat/data/json.d b/source/vibecompat/data/json.d index 3fb6e9d..1711253 100644 --- a/source/vibecompat/data/json.d +++ b/source/vibecompat/data/json.d @@ -204,6 +204,11 @@ inout(Json[]) opSlice(size_t from, size_t to) inout { checkType!(Json[])(); return m_array[from .. to]; } /** + Removes an entry from an object. + */ + void remove(string item) { checkType!(Json[string])(); m_object.remove(item); } + + /** Returns the number of entries of string, array or object typed JSON values. */ @property size_t length()