diff --git a/source/dub/dub.d b/source/dub/dub.d index 75f268b6c..eaba44de0 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -1933,6 +1933,7 @@ private class DependencyVersionResolver : DependencyResolver!(Dependency, Depend import dub.recipe.json; // for sub packages, first try to get them from the base package + // FIXME: avoid this, PackageManager.getSubPackage() is costly if (name.main != name) { auto subname = name.sub; auto basepack = getPackage(name.main, dep); @@ -1948,12 +1949,15 @@ private class DependencyVersionResolver : DependencyResolver!(Dependency, Depend return m_rootPackage.basePackage; if (!dep.repository.empty) { + // note: would handle sub-packages directly auto ret = m_dub.packageManager.loadSCMPackage(name, dep.repository); return ret !is null && dep.matches(ret.version_) ? ret : null; } if (!dep.path.empty) { try { - return m_dub.packageManager.getOrLoadPackage(dep.path); + // note: would handle sub-packages directly + return m_dub.packageManager.getOrLoadPackage(dep.path, NativePath.init, false, + StrictMode.Ignore, name); } catch (Exception e) { logDiagnostic("Failed to load path based dependency %s: %s", name, e.msg); logDebug("Full error: %s", e.toString().sanitize); diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index e1546cbbc..6a63cc998 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -271,7 +271,8 @@ class PackageManager { foreach (ovr; repo.overrides) if (ovr.package_ == name.toString() && ovr.source.matches(ver)) { Package pack = ovr.target.match!( - (NativePath path) => getOrLoadPackage(path), + (NativePath path) => getOrLoadPackage(path, NativePath.init, false, + StrictMode.Ignore, name), (Version vers) => getPackage(name, vers, false), ); if (pack) return pack; @@ -377,21 +378,41 @@ class PackageManager { Params: path = NativePath to the root directory of the package recipe_path = Optional path to the recipe file of the package - allow_sub_packages = Also return a sub package if it resides in the given folder + allow_sub_packages = Also return an already-loaded sub package if it resides in the given folder mode = Whether to issue errors, warning, or ignore unknown keys in dub.json + name = Optional (sub-)package name if known in advance Returns: The packages loaded from the given path Throws: Throws an exception if no package can be loaded */ Package getOrLoadPackage(NativePath path, NativePath recipe_path = NativePath.init, - bool allow_sub_packages = false, StrictMode mode = StrictMode.Ignore) + bool allow_sub_packages = false, StrictMode mode = StrictMode.Ignore, + PackageName name = PackageName.init) { path.endsWithSlash = true; - foreach (p; this.m_internal.fromPath) - if (p.path == path && (!p.parentPackage || (allow_sub_packages && p.parentPackage.path != p.path))) + const nameString = name.toString(); + + foreach (p; this.m_internal.fromPath) { + if (p.path != path) continue; + if (!nameString.empty && p.name != nameString) continue; + if (!p.parentPackage || (allow_sub_packages && p.parentPackage.path != p.path)) return p; + } + auto pack = this.load(path, recipe_path, null, null, mode); - addPackages(this.m_internal.fromPath, pack); + auto nameToResolve = PackageName(pack.name); + + if (!nameString.empty) { + nameToResolve = PackageName(nameString); + const loadedName = PackageName(pack.name); + enforce(loadedName == nameToResolve || loadedName == nameToResolve.main, + "Package %s loaded from '%s' does not match expected name %s".format( + loadedName, path.toNativeString(), nameToResolve)); + } + + pack = addPackagesAndResolveSubPackage(this.m_internal.fromPath, pack, nameToResolve); + enforce(pack !is null, "No sub-package %s in parent package loaded from '%s'".format( + nameToResolve, path.toNativeString())); return pack; } diff --git a/source/dub/project.d b/source/dub/project.d index f2496378a..13964594c 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -553,8 +553,8 @@ class Project { p = vspec.visit!( (NativePath path_) { auto path = path_.absolute ? path_ : m_rootPackage.path ~ path_; - auto tmp = m_packageManager.getOrLoadPackage(path, NativePath.init, true); - return resolveSubPackage(tmp, subname, true); + return m_packageManager.getOrLoadPackage(path, NativePath.init, true, + StrictMode.Ignore, dep.name); }, (Repository repo) { return m_packageManager.loadSCMPackage(dep.name, repo); @@ -587,12 +587,12 @@ class Project { NativePath path = vspec.path; if (!path.absolute) path = pack.path ~ path; logDiagnostic("%sAdding local %s in %s", indent, dep.name, path); - p = m_packageManager.getOrLoadPackage(path, NativePath.init, true); + p = m_packageManager.getOrLoadPackage(path, NativePath.init, true, + StrictMode.Ignore, dep.name); if (p.parentPackage !is null) { logWarn("%sSub package %s must be referenced using the path to it's parent package.", indent, dep.name); p = p.parentPackage; } - p = resolveSubPackage(p, subname, false); enforce(p.name == dep.name.toString(), format("Path based dependency %s is referenced with a wrong name: %s vs. %s", path.toNativeString(), dep.name, p.name)); diff --git a/source/dub/test/subpackages.d b/source/dub/test/subpackages.d index f1e1104a2..512e72b46 100644 --- a/source/dub/test/subpackages.d +++ b/source/dub/test/subpackages.d @@ -44,14 +44,19 @@ unittest { scope dub = new TestDub((scope Filesystem root) { root.writeFile(TestDub.ProjectPath ~ "dub.json", - `{ "name": "a", "dependencies": { "b:a": "~>1.0" } }`); + `{ "name": "a", "dependencies": { "b:a": "~>1.0", "c:a": "~>1.0" } }`); root.writeFile(TestDub.ProjectPath ~ "dub.selections.json", - `{ "fileVersion": 1, "versions": { "b": "1.0.0" } }`); + `{ "fileVersion": 1, "versions": { "b": "1.0.0", "c": { "path": "c" } } }`); root.writePackageFile("b", "1.0.0", `{ "name": "b", "version": "1.0.0", "subPackages": [ { "name": "a" } ] }`); + const cDir = TestDub.ProjectPath ~ "c"; + root.mkdir(cDir); + root.writeFile(cDir ~ "dub.json", + `{ "name": "c", "version": "1.0.0", "subPackages": [ { "name": "a" } ] }`); }); dub.loadPackage(); assert(dub.project.hasAllDependencies(), "project has missing dependencies"); assert(dub.project.getDependency("b:a", true), "Missing 'b:a' dependency"); + assert(dub.project.getDependency("c:a", true), "Missing 'c:a' dependency"); }