1 module reggae.dub.info;
2 
3 import reggae.build;
4 import reggae.rules;
5 import reggae.types;
6 import reggae.config: dflags, perModule;
7 import reggae.sorting;
8 
9 public import std.typecons: Yes, No;
10 import std.typecons: Flag;
11 import std.algorithm: map, filter, find, splitter;
12 import std.array: array, join;
13 import std.path: buildPath;
14 import std.traits: isCallable;
15 import std.range: chain;
16 
17 struct DubPackage {
18     string name;
19     string path;
20     string mainSourceFile;
21     string targetFileName;
22     string[] flags;
23     string[] importPaths;
24     string[] stringImportPaths;
25     string[] files;
26     string targetType;
27     string[] versions;
28     string[] dependencies;
29     string[] libs;
30 }
31 
32 
33 struct DubInfo {
34     DubPackage[] packages;
35 
36     Target[] toTargets(Flag!"main" includeMain = Yes.main, in string compilerFlags = "") @safe const {
37         Target[] targets;
38 
39         foreach(const i, const dubPackage; packages) {
40             const importPaths = dubPackage.allOf!(a => a.packagePaths(a.importPaths))(packages);
41             const stringImportPaths = dubPackage.allOf!(a => a.packagePaths(a.stringImportPaths))(packages);
42             auto versions = dubPackage.allOf!(a => a.versions)(packages).map!(a => "-version=" ~ a);
43             //the path must be explicit for the other packages, implicit for the "main"
44             //package
45             const projDir = i == 0 ? "" : dubPackage.path;
46 
47             immutable flags = chain(dubPackage.flags, versions).join(" ") ~
48                 " " ~ dflags ~ " " ~ compilerFlags;
49 
50             const files = dubPackage.
51                 files.
52                 filter!(a => includeMain || a != dubPackage.mainSourceFile).
53                 map!(a => buildPath(dubPackage.path, a)).array;
54 
55             targets ~= objectFiles(files, flags, importPaths, stringImportPaths, projDir);
56         }
57 
58         return targets;
59     }
60 
61     //@trusted: array
62     Target mainTarget(string flagsStr = "") @trusted const {
63         string[] libs;
64         foreach(p; packages) {
65             libs ~= p.libs;
66         }
67 
68         const pack = packages[0];
69         auto flags = flagsStr.splitter(" ").array;
70         flags ~= pack.targetType == "library" ? ["-lib"] : [];
71         //hacky hack for dub describe on vibe.d projects
72         flags ~= libs.filter!(a => a != "ev").map!(a => "-L-l" ~ a).array;
73         if(packages[0].targetType == "staticLibrary") flags ~= "-lib";
74         return link(packages[0].targetFileName, toTargets(), flags.join(","));
75     }
76 
77     string[] mainTargetImportPaths() @trusted nothrow const {
78         return packages[0].allOf!(a => a.packagePaths(a.importPaths))(packages);
79     }
80 
81     string[][] fetchCommands() @safe pure nothrow const {
82         return packages[0].dependencies.map!(a => ["dub", "fetch", a]).array;
83     }
84 }
85 
86 
87 private auto packagePaths(in DubPackage pack, in string[] paths) @trusted nothrow {
88     return paths.map!(a => buildPath(pack.path, a)).array;
89 }
90 
91 //@trusted because of map.array
92 private string[] allOf(alias F)(in DubPackage pack, in DubPackage[] packages) @trusted nothrow {
93     string[] paths;
94     //foreach(d; [pack.name] ~ pack.dependencies) doesn't compile with CTFE
95     //it seems to have to do with constness, replace string[] with const(string)[]
96     //and it won't compile
97     string[] dependencies = [pack.name];
98     dependencies ~= pack.dependencies;
99     foreach(dependency; dependencies) {
100         import std.range;
101         const depPack = packages.find!(a => a.name == dependency).front;
102         paths ~= F(depPack).array;
103     }
104     return paths;
105 }