Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache json db #2642

Merged
merged 8 commits into from May 18, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions source/dub/dub.d
Expand Up @@ -1883,6 +1883,11 @@ private struct SpecialDirs {
* project directory, but this led to issues with packages stored on
* read-only file system / location, and lingering artifacts scattered
* through the file system.
*
* Dub writes in the cache directory some Json description files
* of the available artifacts. These files are intended to be read by
* 3rd party software (e.g. Meson). The default cache location specified
* in this function should therefore not change across future Dub versions.
*/
NativePath cache;

Expand Down
58 changes: 57 additions & 1 deletion source/dub/generators/build.d
Expand Up @@ -285,12 +285,68 @@ class BuildGenerator : ProjectGenerator {
buildWithCompiler(settings, cbuildsettings);
target_binary_path = getTargetPath(cbuildsettings, settings);

if (!settings.tempBuild)
if (!settings.tempBuild) {
copyTargetFile(target_path, buildsettings, settings);
updateCacheDatabase(settings, cbuildsettings, pack, config, build_id, target_binary_path.toNativeString());
}

return false;
}

private void updateCacheDatabase(GeneratorSettings settings, BuildSettings buildsettings, in Package pack, string config,
string build_id, string target_binary_path)
{
import dub.internal.vibecompat.data.json;
import core.time : seconds;

// Generate a `db.json` in the package version cache directory.
// This is read by 3rd party software (e.g. Meson) in order to find
// relevant build artifacts in Dub's cache.

enum jsonFileName = "db.json";
enum lockFileName = "db.lock";

const pkgCacheDir = packageCache(settings.cache, pack);
auto lock = lockFile((pkgCacheDir ~ lockFileName).toNativeString(), 3.seconds);

const dbPath = pkgCacheDir ~ jsonFileName;
const dbPathStr = dbPath.toNativeString();
Json db;
if (exists(dbPathStr)) {
const text = stripUTF8Bom(cast(string)readFile(dbPath));
db = parseJsonString(text, dbPathStr);
enforce(db.type == Json.Type.array, "Expected a JSON array in " ~ dbPathStr);
}
else {
db = Json.emptyArray;
}

foreach (entry; db) {
rtbo marked this conversation as resolved.
Show resolved Hide resolved
if (entry["buildId"].get!string == build_id) {
// duplicate
return;
}
}

Json entry = Json.emptyObject;

entry["architecture"] = serializeToJson(settings.platform.architecture);
entry["buildId"] = build_id;
entry["buildType"] = settings.buildType;
entry["compiler"] = settings.platform.compiler;
entry["compilerBinary"] = settings.platform.compilerBinary;
entry["compilerVersion"] = settings.platform.compilerVersion;
entry["configuration"] = config;
entry["package"] = pack.name;
entry["platform"] = serializeToJson(settings.platform.platform);
entry["targetBinaryPath"] = target_binary_path;
entry["version"] = pack.version_.toString();

db ~= entry;
rtbo marked this conversation as resolved.
Show resolved Hide resolved

writeFile(dbPath, representation(db.toPrettyString()));
}

private void performRDMDBuild(GeneratorSettings settings, ref BuildSettings buildsettings, in Package pack, string config, out NativePath target_path)
{
auto cwd = settings.toolWorkingDirectory;
Expand Down
8 changes: 7 additions & 1 deletion source/dub/generators/generator.d
Expand Up @@ -771,9 +771,15 @@ class ProjectGenerator
/**
* Compute and returns the path were artifacts are stored for a given package
*
* Artifacts are usually stored in:
* Artifacts are stored in:
* `$DUB_HOME/cache/$PKG_NAME/$PKG_VERSION[/+$SUB_PKG_NAME]/`
* Note that the leading `+` in the sub-package name is to avoid any ambiguity.
*
* Dub writes in the returned path a Json description file of the available
* artifacts in this cache location. This Json file is read by 3rd party
* software (e.g. Meson). Returned path should therefore not change across
* future Dub versions.
*
* Build artifacts are usually stored in a sub-folder named "build",
* as their names are based on user-supplied values.
*
Expand Down
2 changes: 2 additions & 0 deletions test/pr2642-cache-db/.gitignore
@@ -0,0 +1,2 @@
dubhome/
pr2642-cache-db
Empty file added test/pr2642-cache-db/.no_test
Empty file.
2 changes: 2 additions & 0 deletions test/pr2642-cache-db/dub.sdl
@@ -0,0 +1,2 @@
name "pr2642-cache-db";
targetType "executable";
78 changes: 78 additions & 0 deletions test/pr2642-cache-db/source/test_cache_db.d
@@ -0,0 +1,78 @@
module test_cache_db;

import std.path;
import std.file;
import std.process;
import std.stdio;
import std.json;

void main()
{
const dubhome = __FILE_FULL_PATH__.dirName().dirName().buildNormalizedPath("dubhome");
if (exists(dubhome))
{
rmdirRecurse(dubhome);
}

const string[string] env = [
"DUB_HOME": dubhome,
];
const fetchProgram = [
environment["DUB"],
"fetch",
"vibe-d@0.9.6",
rtbo marked this conversation as resolved.
Show resolved Hide resolved
];
auto dubFetch = spawnProcess(fetchProgram, stdin, stdout, stderr, env);
wait(dubFetch);
const buildProgram = [
environment["DUB"],
"build",
"--build=debug",
"vibe-d:http@0.9.6",
];
auto dubBuild = spawnProcess(buildProgram, stdin, stdout, stderr, env);
wait(dubBuild);

scope (success)
{
// leave dubhome in the tree for analysis in case of failure
rmdirRecurse(dubhome);
}

const buildDbPath = buildNormalizedPath(dubhome, "cache", "vibe-d", "0.9.6", "+http", "db.json");
assert(exists(buildDbPath), buildDbPath ~ " should exist");
const buildDbStr = readText(buildDbPath);
auto json = parseJSON(buildDbStr);
assert(json.type == JSONType.array, "build db should be an array");
assert(json.array.length == 1);
auto db = json.array[0].object;

void assertArray(string field)
{
assert(field in db, "db.json should have an array field " ~ field);
assert(db[field].type == JSONType.array, "expected field " ~ field ~ " to be an array");
}

void assertString(string field, string value = null)
{
assert(field in db, "db.json should have an string field " ~ field);
assert(db[field].type == JSONType.string, "expected field " ~ field ~ " to be a string");
if (value)
assert(db[field].str == value, "expected field " ~ field ~ " to equal " ~ value);
}

assertArray("architecture");
assertString("buildId");
assertString("buildType", "debug");
assertString("compiler");
assertString("compilerBinary");
assertString("compilerVersion");
assertString("configuration", "library");
assertString("package", "vibe-d:http");
assertArray("platform");
assertString("targetBinaryPath");
assertString("version", "0.9.6");

const binName = db["targetBinaryPath"].str;
assert(isFile(binName), "expected " ~ binName ~ " to be a file.");
}