1 module reggae.dub;
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;
12 import std.array: array, join;
13 import std.path: buildPath;
14 import std.traits: isCallable;
15 
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) @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             const versions = dubPackage.allOf!(a => a.versions)(packages);
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 = dubPackage.flags.join(" ") ~ dflags ~ " " ~
48                 versions.map!(a => "-version=" ~ a).join(" ");
49 
50             auto files = dubPackage.
51                 files.
52                 filter!(a => includeMain || a != dubPackage.mainSourceFile).
53                 map!(a => buildPath(dubPackage.path, a));
54 
55             targets ~= dCompileGrouped(files.array, 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         return dLink(packages[0].targetFileName, toTargets(), flags.join(","));
74     }
75 
76     string[] targetImportPaths() @trusted nothrow const {
77         return packages[0].allOf!(a => a.packagePaths(a.importPaths))(packages);
78     }
79 
80     string[][] fetchCommands() @safe pure nothrow const {
81         return packages[0].dependencies.map!(a => ["dub", "fetch", a]).array;
82     }
83 }
84 
85 
86 private auto packagePaths(in DubPackage pack, in string[] paths) @trusted nothrow {
87     return paths.map!(a => buildPath(pack.path, a)).array;
88 }
89 
90 //@trusted because of map.array
91 private string[] allOf(alias F)(in DubPackage pack, in DubPackage[] packages) @trusted nothrow {
92     string[] paths;
93     //foreach(d; [pack.name] ~ pack.dependencies) doesn't compile with CTFE
94     //it seems to have to do with constness, replace string[] with const(string)[]
95     //and it won't compile
96     string[] dependencies = [pack.name];
97     dependencies ~= pack.dependencies;
98     foreach(dependency; dependencies) {
99         import std.range;
100         const depPack = packages.find!(a => a.name == dependency).front;
101         paths ~= F(depPack).array;
102     }
103     return paths;
104 }
105 
106 
107 Target dubMainTarget(string flags)() {
108     import reggae.config;
109     return dubInfo.mainTarget(flags);
110 }
111 
112 
113 Target dExeWithDubObjs(ExeName exeName,
114                        Configuration config = Configuration("default"),
115                        alias objsFunction = () { Target[] t; return t; },
116                        Flag!"main" includeMain = Yes.main,
117                        Flags flags = Flags())()
118     if(isCallable!objsFunction) {
119 
120     import reggae.config;
121     return dLink(exeName.name,
122                  objsFunction() ~ configToDubInfo[config.config].toTargets(includeMain),
123                  flags.flags);
124 }