Skip to content

Commit

Permalink
Move writing selections file to PackageManager
Browse files Browse the repository at this point in the history
The PackageManager is the main source of IO in Dub,
and having it take responsibility for writing the
selections file reduces the number of special casing
we need to make when doing dependency injection.
  • Loading branch information
Geod24 committed Feb 17, 2024
1 parent c0029d0 commit aa265e3
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 60 deletions.
64 changes: 64 additions & 0 deletions source/dub/packagemanager.d
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dub.internal.vibecompat.inet.path;
import dub.internal.logging;
import dub.package_;
import dub.recipe.io;
import dub.recipe.selection;
import dub.internal.configy.Exceptions;
public import dub.internal.configy.Read : StrictMode;

Expand Down Expand Up @@ -1071,6 +1072,69 @@ symlink_exit:
return digest[].dup;
}

/**
* Writes the selections file (`dub.selections.json`)
*
* The selections file is only used for the root package / project.
* However, due to it being a filesystem interaction, it is managed
* from the `PackageManager`.
*
* Params:
* project = The root package / project to read the selections file for.
* selections = The `SelectionsFile` to write.
* overwrite = Whether to overwrite an existing selections file.
* True by default.
*/
public void writeSelections(in Package project, in Selections!1 selections,
bool overwrite = true)
{
import dub.internal.vibecompat.core.file;

const path = project.path ~ "dub.selections.json";
if (!overwrite && existsFile(path))
return;
writeFile(path, selectionsToString(selections));
}

/// Package function to avoid code duplication with deprecated
/// SelectedVersions.save, merge with `writeSelections` in
/// the future.
package static string selectionsToString (in Selections!1 s)
{
Json json = selectionsToJSON(s);
assert(json.type == Json.Type.object);
assert(json.length == 2);
assert(json["versions"].type != Json.Type.undefined);

auto result = appender!string();
result.put("{\n\t\"fileVersion\": ");
result.writeJsonString(json["fileVersion"]);
result.put(",\n\t\"versions\": {");
auto vers = json["versions"].get!(Json[string]);
bool first = true;
foreach (k; vers.byKey.array.sort()) {
if (!first) result.put(",");
else first = false;
result.put("\n\t\t");
result.writeJsonString(Json(k));
result.put(": ");
result.writeJsonString(vers[k]);
}
result.put("\n\t}\n}\n");
return result.data;
}

/// Ditto
package static Json selectionsToJSON (in Selections!1 s)
{
Json serialized = Json.emptyObject;
serialized["fileVersion"] = s.fileVersion;
serialized["versions"] = Json.emptyObject;
foreach (p, dep; s.versions)
serialized["versions"][p] = dep.toJson(true);
return serialized;
}

/// Adds the package and scans for sub-packages.
protected void addPackages(ref Package[] dst_repos, Package pack)
{
Expand Down
42 changes: 8 additions & 34 deletions source/dub/project.d
Original file line number Diff line number Diff line change
Expand Up @@ -1340,10 +1340,9 @@ class Project {
const name = PackageName(m_rootPackage.basePackage.name);
if (m_selections.hasSelectedVersion(name))
m_selections.deselectVersion(name);

auto path = m_rootPackage.path ~ SelectedVersions.defaultFile;
if (m_selections.dirty || !existsFile(path))
m_selections.save(path);
this.m_packageManager.writeSelections(
this.m_rootPackage, this.m_selections.m_selections,
this.m_selections.dirty);
}

deprecated bool isUpgradeCacheUpToDate()
Expand Down Expand Up @@ -1967,30 +1966,10 @@ public class SelectedVersions {
should be used as the file name and the directory should be the root
directory of the project's root package.
*/
deprecated("Use `PackageManager.writeSelections` to write a `SelectionsFile`")
void save(NativePath path)
{
Json json = serialize();
auto result = appender!string();

assert(json.type == Json.Type.object);
assert(json.length == 2);
assert(json["versions"].type != Json.Type.undefined);

result.put("{\n\t\"fileVersion\": ");
result.writeJsonString(json["fileVersion"]);
result.put(",\n\t\"versions\": {");
auto vers = json["versions"].get!(Json[string]);
bool first = true;
foreach (k; vers.byKey.array.sort()) {
if (!first) result.put(",");
else first = false;
result.put("\n\t\t");
result.writeJsonString(Json(k));
result.put(": ");
result.writeJsonString(vers[k]);
}
result.put("\n\t}\n}\n");
path.writeFile(result.data);
path.writeFile(PackageManager.selectionsToString(this.m_selections));
m_dirty = false;
m_bare = false;
}
Expand All @@ -2014,14 +1993,9 @@ public class SelectedVersions {
else throw new Exception(format("Unexpected type for dependency: %s", j));
}

Json serialize()
const {
Json serialized = Json.emptyObject;
serialized["fileVersion"] = m_selections.fileVersion;
serialized["versions"] = Json.emptyObject;
foreach (p, dep; m_selections.versions)
serialized["versions"][p] = dep.toJson(true);
return serialized;
deprecated("JSON serialization is deprecated")
Json serialize() const {
return PackageManager.selectionsToJSON(this.m_selections);
}

deprecated("JSON deserialization is deprecated")
Expand Down
38 changes: 12 additions & 26 deletions source/dub/test/base.d
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import dub.packagemanager;
import dub.packagesuppliers.packagesupplier;
import dub.project;
import dub.recipe.io : parsePackageRecipe;
import dub.recipe.selection;

/// Example of a simple unittest for a project with a single dependency
unittest
Expand Down Expand Up @@ -219,7 +220,7 @@ public class TestDub : Dub
/// Loads a specific package as the main project package (can be a sub package)
public override void loadPackage(Package pack)
{
m_project = new Project(m_packageManager, pack, new TestSelectedVersions());
m_project = new Project(m_packageManager, pack, new SelectedVersions());
}

/// Reintroduce parent overloads
Expand All @@ -237,31 +238,6 @@ public class TestDub : Dub
}
}

/**
*
*/
public class TestSelectedVersions : SelectedVersions {
import dub.recipe.selection;

/// Forward to parent's constructor
public this(uint version_ = FileVersion) @safe pure
{
super(version_);
}

/// Ditto
public this(Selections!1 data) @safe pure nothrow @nogc
{
super(data);
}

/// Do not do IO
public override void save(NativePath path)
{
// No-op
}
}

/**
* A `PackageManager` suitable to be used in unittests
*
Expand Down Expand Up @@ -400,6 +376,16 @@ package class TestPackageManager : PackageManager
return false;
}

/// Save the `dub.selections.json` to the vfs
public override void writeSelections(in Package project,
in Selections!1 selections, bool overwrite = true)
{
const path = project.path ~ "dub.selections.json";
if (!overwrite && this.fs.existsFile(path))
return;
this.fs.writeFile(path, selectionsToString(selections));
}

/// Add a reachable SCM package to this `PackageManager`
public void addTestSCMPackage(in Repository repo, string dub_json)
{
Expand Down

0 comments on commit aa265e3

Please sign in to comment.