Skip to content

Commit c6c8a6b

Browse files
committed
PackageManager: Support directly loading a sub-package via path-based getOrLoadPackage()
To avoid a costly full packages scan to resolve the sub-package later.
1 parent b3f8473 commit c6c8a6b

File tree

4 files changed

+49
-19
lines changed

4 files changed

+49
-19
lines changed

source/dub/dub.d

+5-1
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,7 @@ private class DependencyVersionResolver : DependencyResolver!(Dependency, Depend
19331933
import dub.recipe.json;
19341934

19351935
// for sub packages, first try to get them from the base package
1936+
// FIXME: avoid this, PackageManager.getSubPackage() is costly
19361937
if (name.main != name) {
19371938
auto subname = name.sub;
19381939
auto basepack = getPackage(name.main, dep);
@@ -1948,12 +1949,15 @@ private class DependencyVersionResolver : DependencyResolver!(Dependency, Depend
19481949
return m_rootPackage.basePackage;
19491950

19501951
if (!dep.repository.empty) {
1952+
// note: would handle sub-packages directly
19511953
auto ret = m_dub.packageManager.loadSCMPackage(name, dep.repository);
19521954
return ret !is null && dep.matches(ret.version_) ? ret : null;
19531955
}
19541956
if (!dep.path.empty) {
19551957
try {
1956-
return m_dub.packageManager.getOrLoadPackage(dep.path);
1958+
// note: would handle sub-packages directly
1959+
return m_dub.packageManager.getOrLoadPackage(dep.path, NativePath.init, false,
1960+
StrictMode.Ignore, name);
19571961
} catch (Exception e) {
19581962
logDiagnostic("Failed to load path based dependency %s: %s", name, e.msg);
19591963
logDebug("Full error: %s", e.toString().sanitize);

source/dub/packagemanager.d

+32-7
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ class PackageManager {
271271
foreach (ovr; repo.overrides)
272272
if (ovr.package_ == name.toString() && ovr.source.matches(ver)) {
273273
Package pack = ovr.target.match!(
274-
(NativePath path) => getOrLoadPackage(path),
274+
(NativePath path) => getOrLoadPackage(path, NativePath.init, false,
275+
StrictMode.Ignore, name),
275276
(Version vers) => getPackage(name, vers, false),
276277
);
277278
if (pack) return pack;
@@ -377,21 +378,45 @@ class PackageManager {
377378
Params:
378379
path = NativePath to the root directory of the package
379380
recipe_path = Optional path to the recipe file of the package
380-
allow_sub_packages = Also return a sub package if it resides in the given folder
381+
allow_sub_packages = Also return an already-loaded sub package if it resides in the given folder.
382+
Ignored when specifying `name`, as a matching already-loaded sub package is always returned in that case.
381383
mode = Whether to issue errors, warning, or ignore unknown keys in dub.json
384+
name = Optional (sub-)package name if known in advance
382385
383386
Returns: The packages loaded from the given path
384387
Throws: Throws an exception if no package can be loaded
385388
*/
386389
Package getOrLoadPackage(NativePath path, NativePath recipe_path = NativePath.init,
387-
bool allow_sub_packages = false, StrictMode mode = StrictMode.Ignore)
390+
bool allow_sub_packages = false, StrictMode mode = StrictMode.Ignore,
391+
PackageName name = PackageName.init)
388392
{
389393
path.endsWithSlash = true;
390-
foreach (p; this.m_internal.fromPath)
391-
if (p.path == path && (!p.parentPackage || (allow_sub_packages && p.parentPackage.path != p.path)))
392-
return p;
394+
const nameString = name.toString();
395+
396+
foreach (p; this.m_internal.fromPath) {
397+
if (!nameString.empty) {
398+
if (p.name == nameString && (p.path == path || p.basePackage.path == path))
399+
return p;
400+
} else {
401+
if (p.path == path && (!p.parentPackage || (allow_sub_packages && p.parentPackage.path != p.path)))
402+
return p;
403+
}
404+
}
405+
393406
auto pack = this.load(path, recipe_path, null, null, mode);
394-
addPackages(this.m_internal.fromPath, pack);
407+
auto nameToResolve = PackageName(pack.name);
408+
409+
if (!nameString.empty) {
410+
nameToResolve = PackageName(nameString);
411+
const loadedName = PackageName(pack.name);
412+
enforce(loadedName == nameToResolve || loadedName == nameToResolve.main,
413+
"Package %s loaded from '%s' does not match expected name %s".format(
414+
loadedName, path.toNativeString(), nameToResolve));
415+
}
416+
417+
pack = addPackagesAndResolveSubPackage(this.m_internal.fromPath, pack, nameToResolve);
418+
enforce(pack !is null, "No sub-package %s in parent package loaded from '%s'".format(
419+
nameToResolve, path.toNativeString()));
395420
return pack;
396421
}
397422

source/dub/project.d

+5-9
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,8 @@ class Project {
553553
p = vspec.visit!(
554554
(NativePath path_) {
555555
auto path = path_.absolute ? path_ : m_rootPackage.path ~ path_;
556-
auto tmp = m_packageManager.getOrLoadPackage(path, NativePath.init, true);
557-
return resolveSubPackage(tmp, subname, true);
556+
return m_packageManager.getOrLoadPackage(path, NativePath.init, false,
557+
StrictMode.Ignore, dep.name);
558558
},
559559
(Repository repo) {
560560
return m_packageManager.loadSCMPackage(dep.name, repo);
@@ -587,15 +587,11 @@ class Project {
587587
NativePath path = vspec.path;
588588
if (!path.absolute) path = pack.path ~ path;
589589
logDiagnostic("%sAdding local %s in %s", indent, dep.name, path);
590-
p = m_packageManager.getOrLoadPackage(path, NativePath.init, true);
591-
if (p.parentPackage !is null) {
590+
p = m_packageManager.getOrLoadPackage(path, NativePath.init, false,
591+
StrictMode.Ignore, dep.name);
592+
if (path != p.basePackage.path) {
592593
logWarn("%sSub package %s must be referenced using the path to it's parent package.", indent, dep.name);
593-
p = p.parentPackage;
594594
}
595-
p = resolveSubPackage(p, subname, false);
596-
enforce(p.name == dep.name.toString(),
597-
format("Path based dependency %s is referenced with a wrong name: %s vs. %s",
598-
path.toNativeString(), dep.name, p.name));
599595
} else {
600596
logDiagnostic("%sMissing dependency %s %s of %s", indent, dep.name, vspec, pack.name);
601597
if (is_desired) m_missingDependencies ~= dep.name.toString();

source/dub/test/subpackages.d

+7-2
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,19 @@ unittest
4444
{
4545
scope dub = new TestDub((scope Filesystem root) {
4646
root.writeFile(TestDub.ProjectPath ~ "dub.json",
47-
`{ "name": "a", "dependencies": { "b:a": "~>1.0" } }`);
47+
`{ "name": "a", "dependencies": { "b:a": "~>1.0", "c:a": "~>1.0" } }`);
4848
root.writeFile(TestDub.ProjectPath ~ "dub.selections.json",
49-
`{ "fileVersion": 1, "versions": { "b": "1.0.0" } }`);
49+
`{ "fileVersion": 1, "versions": { "b": "1.0.0", "c": { "path": "c" } } }`);
5050
root.writePackageFile("b", "1.0.0",
5151
`{ "name": "b", "version": "1.0.0", "subPackages": [ { "name": "a" } ] }`);
52+
const cDir = TestDub.ProjectPath ~ "c";
53+
root.mkdir(cDir);
54+
root.writeFile(cDir ~ "dub.json",
55+
`{ "name": "c", "version": "1.0.0", "subPackages": [ { "name": "a" } ] }`);
5256
});
5357
dub.loadPackage();
5458

5559
assert(dub.project.hasAllDependencies(), "project has missing dependencies");
5660
assert(dub.project.getDependency("b:a", true), "Missing 'b:a' dependency");
61+
assert(dub.project.getDependency("c:a", true), "Missing 'c:a' dependency");
5762
}

0 commit comments

Comments
 (0)