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

Injection of source files from dependencies into executable and shared libraries #2207

Closed
wants to merge 8 commits into from
5 changes: 5 additions & 0 deletions changelog/injectfromdependency.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Injection of source files from dependencies

Each (sub)package now supports a source file that will be included in any executable or dynamic library that depends either directly or indirectly of it.

This can be used to register and unregister elements of a package within the dependant package without requiring the dependant to acknoledge that the registration mechanism needs to take place.
19 changes: 19 additions & 0 deletions examples/injected-from-dependency/dub.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"description": "Some test code for Have_druntime version",
"name": "injected-from-dependency",
"targetType": "executable",

"dependencies": {
":toload": "*"
},

"subPackages": [
{
"name": "toload",
"buildOptions": ["betterC"],
"sourcePaths": ["toload"],
"importPaths": ["toload"],
"finalBinarySourceFile": "toload/ahook.d"
}
]
}
4 changes: 4 additions & 0 deletions examples/injected-from-dependency/source/entry.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
void main() {
import something;
doSomething();
}
14 changes: 14 additions & 0 deletions examples/injected-from-dependency/toload/ahook.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module toload.ahook;

version(D_BetterC) {
pragma(crt_constructor)
extern(C) void someInitializer() {
import core.stdc.stdio;
printf("Hook ran!\n");
}
} else {
shared static this() {
import std.stdio;
writeln("We have a runtime!!!!");
}
}
12 changes: 12 additions & 0 deletions examples/injected-from-dependency/toload/something.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module something;

void doSomething() {
import core.stdc.stdio;

version(D_BetterC) {
printf("druntime is not in the executable :(\n");

} else {
printf("druntime is in executable!\n");
}
}
3 changes: 3 additions & 0 deletions source/dub/compilers/buildsettings.d
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct BuildSettings {
string targetName;
string workingDirectory;
string mainSourceFile;
string finalBinarySourceFile;
string[] dflags;
string[] lflags;
string[] libs;
Expand Down Expand Up @@ -88,6 +89,7 @@ struct BuildSettings {
addLibs(bs.libs);
addLinkerFiles(bs.linkerFiles);
addSourceFiles(bs.sourceFiles);
addFinalBinarySourceFile(bs.finalBinarySourceFile);
addCopyFiles(bs.copyFiles);
addExtraDependencyFiles(bs.extraDependencyFiles);
addVersions(bs.versions);
Expand All @@ -113,6 +115,7 @@ struct BuildSettings {
void addLibs(in string[] value...) { add(libs, value); }
void addLinkerFiles(in string[] value...) { add(linkerFiles, value); }
void addSourceFiles(in string[] value...) { add(sourceFiles, value); }
void addFinalBinarySourceFile(in string value) { this.finalBinarySourceFile = value; }
void prependSourceFiles(in string[] value...) { prepend(sourceFiles, value); }
void removeSourceFiles(in string[] value...) { removePaths(sourceFiles, value); }
void addCopyFiles(in string[] value...) { add(copyFiles, value); }
Expand Down
1 change: 1 addition & 0 deletions source/dub/description.d
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ struct PackageDescription {
string targetFileName;
string workingDirectory;
string mainSourceFile;
string finalBinarySourceFile;
string[] dflags; /// Flags passed to the D compiler
string[] lflags; /// Flags passed to the linker
string[] libs; /// Librariy names to link against (typically using "-l<name>")
Expand Down
73 changes: 67 additions & 6 deletions source/dub/generators/generator.d
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,12 @@ class ProjectGenerator
compatible. This also transports all Have_dependency_xyz version
identifiers to `rootPackage`.

4. Filter unused versions and debugVersions from all targets. The
filters have previously been upwards inherited (3.) so that versions
4. Merge finalBinarySourceFile from dependencies into their dependents.
This is based upon binary images and will transcend direct relationships
including shared libraries.

5. Filter unused versions and debugVersions from all targets. The
filters have previously been upwards inherited (3. and 4.) so that versions
used in a dependency are also applied to all dependents.

Note: The upwards inheritance is done at last so that siblings do not
Expand Down Expand Up @@ -423,10 +427,12 @@ class ProjectGenerator
// embedded non-binary dependencies
foreach (deppack; ti.packages[1 .. $])
ti.buildSettings.add(targets[deppack.name].buildSettings);

// binary dependencies
foreach (depname; ti.dependencies)
{
auto pdepti = &targets[depname];

configureDependents(*pdepti, targets, level + 1);
mergeFromDependency(pdepti.buildSettings, ti.buildSettings, genSettings.platform);
}
Expand All @@ -435,7 +441,61 @@ class ProjectGenerator
configureDependents(*roottarget, targets);
visited.clear();

// 4. Filter applicable version and debug version identifiers
// 4. As an extension to configureDependents we need to copy any finalBinarySourceFile
// in our dependencies (ignoring targetType)
void configureDependentsFinalImages(TargetInfo* ti, TargetInfo[string] targets, TargetInfo* ifRootIsFinalImage = null, size_t level = 0)
rikkimax marked this conversation as resolved.
Show resolved Hide resolved
{
// use `visited` here as pkgs cannot depend on themselves
if (ti.pack in visited)
return;
visited[ti.pack] = typeof(visited[ti.pack]).init;

logDiagnostic("%sConfiguring dependent %s, deps:%(%s, %) for finalBinarySourceFile", ' '.repeat(2 * level), ti.pack.name, ti.dependencies);

if (ifRootIsFinalImage !is null)
rikkimax marked this conversation as resolved.
Show resolved Hide resolved
{
foreach (depname; ti.dependencies)
{
auto pdepti = &targets[depname];

if (ifRootIsFinalImage !is null && !pdepti.buildSettings.finalBinarySourceFile.empty)
ifRootIsFinalImage.buildSettings.addSourceFiles(pdepti.buildSettings.finalBinarySourceFile);
}
}


switch (ti.buildSettings.targetType)
{
case TargetType.executable:
case TargetType.dynamicLibrary:
ifRootIsFinalImage = ti;
break;

default:
break;
}

foreach (depname; ti.dependencies)
{
auto pdepti = &targets[depname];
configureDependentsFinalImages(pdepti, targets, ifRootIsFinalImage, level + 1);
}
}

switch (roottarget.buildSettings.targetType)
{
case TargetType.executable:
case TargetType.dynamicLibrary:
configureDependentsFinalImages(roottarget, targets, roottarget);
break;

default:
configureDependentsFinalImages(roottarget, targets);
break;
}
visited.clear();

// 5. Filter applicable version and debug version identifiers
if (genSettings.filterVersions)
{
foreach (name, ref ti; targets)
Expand All @@ -454,7 +514,7 @@ class ProjectGenerator
}
}

// 5. override string import files in dependencies
// 6. override string import files in dependencies
static void overrideStringImports(ref TargetInfo target,
ref TargetInfo parent, TargetInfo[string] targets, string[] overrides)
{
Expand Down Expand Up @@ -505,7 +565,7 @@ class ProjectGenerator
overrideStringImports(targets[depname], *roottarget, targets,
roottarget.buildSettings.stringImportFiles);

// 6. downwards inherits dependency build settings
// 7. downwards inherits dependency build settings
static void applyForcedSettings(const scope ref TargetInfo ti, TargetInfo[string] targets,
BuildSettings[string] dependBS, size_t level = 0)
{
Expand Down Expand Up @@ -669,6 +729,7 @@ class ProjectGenerator
parent.addDebugVersionFilters(child.debugVersionFilters);
parent.addImportPaths(child.importPaths);
parent.addStringImportPaths(child.stringImportPaths);
parent.addFinalBinarySourceFile(child.finalBinarySourceFile);
// linking of static libraries is done by parent
if (child.targetType == TargetType.staticLibrary) {
parent.addSourceFiles(child.sourceFiles.filter!(f => isLinkerFile(platform, f)).array);
Expand Down Expand Up @@ -962,7 +1023,7 @@ void runBuildCommands(in string[] commands, in Package pack, in Project proj,
env["DUB_ROOT_PACKAGE_TARGET_TYPE"] = to!string(rootPackageBuildSettings.targetType);
env["DUB_ROOT_PACKAGE_TARGET_PATH"] = rootPackageBuildSettings.targetPath;
env["DUB_ROOT_PACKAGE_TARGET_NAME"] = rootPackageBuildSettings.targetName;

foreach (aa; extraVars) {
foreach (k, v; aa)
env[k] = v;
Expand Down
1 change: 1 addition & 0 deletions source/dub/package_.d
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ class Package {
ret.targetFileName = compiler.getTargetFileName(bs, platform);
ret.workingDirectory = bs.workingDirectory;
ret.mainSourceFile = bs.mainSourceFile;
ret.finalBinarySourceFile = bs.finalBinarySourceFile;
ret.dflags = bs.dflags;
ret.lflags = bs.lflags;
ret.libs = bs.libs;
Expand Down
9 changes: 6 additions & 3 deletions source/dub/project.d
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,7 @@ void processVars(ref BuildSettings dst, in Project project, in Package pack,
dst.addPostBuildEnvironments(processVerEnvs(settings.postBuildEnvironments, gsettings.buildSettings.postBuildEnvironments));
dst.addPreRunEnvironments(processVerEnvs(settings.preRunEnvironments, gsettings.buildSettings.preRunEnvironments));
dst.addPostRunEnvironments(processVerEnvs(settings.postRunEnvironments, gsettings.buildSettings.postRunEnvironments));

auto buildEnvs = [dst.environments, dst.buildEnvironments];
auto runEnvs = [dst.environments, dst.runEnvironments];
auto preGenEnvs = [dst.environments, dst.preGenerateEnvironments];
Expand All @@ -1213,7 +1213,7 @@ void processVars(ref BuildSettings dst, in Project project, in Package pack,
auto postBuildEnvs = buildEnvs ~ [dst.postBuildEnvironments];
auto preRunEnvs = runEnvs ~ [dst.preRunEnvironments];
auto postRunEnvs = runEnvs ~ [dst.postRunEnvironments];

dst.addDFlags(processVars(project, pack, gsettings, settings.dflags, false, buildEnvs));
dst.addLFlags(processVars(project, pack, gsettings, settings.lflags, false, buildEnvs));
dst.addLibs(processVars(project, pack, gsettings, settings.libs, false, buildEnvs));
Expand All @@ -1237,6 +1237,9 @@ void processVars(ref BuildSettings dst, in Project project, in Package pack,
dst.addRequirements(settings.requirements);
dst.addOptions(settings.options);

if (!settings.finalBinarySourceFile.empty)
dst.addFinalBinarySourceFile(processVars(settings.finalBinarySourceFile, project, pack, gsettings, true, buildEnvs));

if (include_target_settings) {
dst.targetType = settings.targetType;
dst.targetPath = processVars(settings.targetPath, project, pack, gsettings, true, buildEnvs);
Expand Down Expand Up @@ -1473,7 +1476,7 @@ private string getVariable(Project, Package)(string name, in Project project, in
else if (name == "LFLAGS")
return join(buildSettings.lflags," ");
}

import std.range;
foreach (aa; retro(extraVars))
if (auto exvar = name in aa)
Expand Down
7 changes: 6 additions & 1 deletion source/dub/recipe/json.d
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ private void parseJson(ref BuildSettingsTemplate bs, Json json, string package_n
enforce(suffix.empty, "mainSourceFile does not support platform customization.");
bs.mainSourceFile = value.get!string;
break;
case "finalBinarySourceFile":
enforce(suffix.empty, "finalBinarySourceFile does not support platform customization.");
bs.finalBinarySourceFile = value.get!string;
break;
case "subConfigurations":
enforce(suffix.empty, "subConfigurations does not support platform customization.");
bs.subConfigurations = deserializeJson!(string[string])(value);
Expand Down Expand Up @@ -275,6 +279,7 @@ private Json toJson(const scope ref BuildSettingsTemplate bs)
if (!bs.targetName.empty) ret["targetName"] = bs.targetName;
if (!bs.workingDirectory.empty) ret["workingDirectory"] = bs.workingDirectory;
if (!bs.mainSourceFile.empty) ret["mainSourceFile"] = bs.mainSourceFile;
if (!bs.finalBinarySourceFile.empty) ret["finalBinarySourceFile"] = bs.finalBinarySourceFile;
if (bs.subConfigurations.length > 0) ret["subConfigurations"] = serializeToJson(bs.subConfigurations);
foreach (suffix, arr; bs.dflags) ret["dflags"~suffix] = serializeToJson(arr);
foreach (suffix, arr; bs.lflags) ret["lflags"~suffix] = serializeToJson(arr);
Expand Down Expand Up @@ -376,7 +381,7 @@ unittest {
parseJson(rec1, jsonValue, null);
PackageRecipe rec;
parseJson(rec, rec1.toJson(), null); // verify that all fields are serialized properly

assert(rec.name == "projectname");
assert(rec.buildSettings.environments == ["": ["Var1": "env"]]);
assert(rec.buildSettings.buildEnvironments == ["": ["Var2": "buildEnv"]]);
Expand Down
6 changes: 6 additions & 0 deletions source/dub/recipe/packagerecipe.d
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ struct BuildSettingsTemplate {
string targetName;
string workingDirectory;
string mainSourceFile;
string finalBinarySourceFile;
string[string] subConfigurations;
string[][string] dflags;
string[][string] lflags;
Expand Down Expand Up @@ -230,6 +231,11 @@ struct BuildSettingsTemplate {
dst.mainSourceFile = p.toNativeString();
dst.addSourceFiles(dst.mainSourceFile);
}
if (!this.finalBinarySourceFile.empty) {
auto p = NativePath(this.finalBinarySourceFile);
p.normalize();
dst.finalBinarySourceFile = p.toNativeString();
}

string[] collectFiles(in string[][string] paths_map, string pattern)
{
Expand Down
4 changes: 4 additions & 0 deletions source/dub/recipe/sdl.d
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ private void parseBuildSetting(Tag setting, ref BuildSettingsTemplate bs, string
case "sourcePaths": setting.parsePlatformStringArray(bs.sourcePaths); break;
case "excludedSourceFiles": setting.parsePlatformStringArray(bs.excludedSourceFiles); break;
case "mainSourceFile": bs.mainSourceFile = setting.stringTagValue; break;
case "finalBinarySourceFile": bs.finalBinarySourceFile = setting.stringTagValue; break;
case "copyFiles": setting.parsePlatformStringArray(bs.copyFiles); break;
case "extraDependencyFiles": setting.parsePlatformStringArray(bs.extraDependencyFiles); break;
case "versions": setting.parsePlatformStringArray(bs.versions); break;
Expand Down Expand Up @@ -278,6 +279,7 @@ private Tag[] toSDL(const scope ref BuildSettingsTemplate bs)
if (bs.targetName.length) add("targetName", bs.targetName);
if (bs.workingDirectory.length) add("workingDirectory", bs.workingDirectory);
if (bs.mainSourceFile.length) add("mainSourceFile", bs.mainSourceFile);
if (bs.finalBinarySourceFile.length) add("finalBinarySourceFile", bs.finalBinarySourceFile);
foreach (pack, conf; bs.subConfigurations) ret ~= new Tag(null, "subConfiguration", [Value(pack), Value(conf)]);
foreach (suffix, arr; bs.dflags) adda("dflags", suffix, arr);
foreach (suffix, arr; bs.lflags) adda("lflags", suffix, arr);
Expand Down Expand Up @@ -464,6 +466,7 @@ sourcePaths "sourcepath3"
excludedSourceFiles "excluded1" "excluded2"
excludedSourceFiles "excluded3"
mainSourceFile "main source"
finalBinarySourceFile "final binary source"
copyFiles "copy1" "copy2"
copyFiles "copy3"
extraDependencyFiles "extradepfile1" "extradepfile2"
Expand Down Expand Up @@ -566,6 +569,7 @@ lflags "lf3"
assert(rec.buildSettings.sourcePaths == ["": ["sourcepath1", "sourcepath2", "sourcepath3"]]);
assert(rec.buildSettings.excludedSourceFiles == ["": ["excluded1", "excluded2", "excluded3"]]);
assert(rec.buildSettings.mainSourceFile == "main source");
assert(rec.buildSettings.finalBinarySourceFile == "final binary source");
assert(rec.buildSettings.copyFiles == ["": ["copy1", "copy2", "copy3"]]);
assert(rec.buildSettings.extraDependencyFiles == ["": ["extradepfile1", "extradepfile2", "extradepfile3"]]);
assert(rec.buildSettings.versions == ["": ["version1", "version2", "version3"]]);
Expand Down