1 module reggae.dub.info;
2 
3 import reggae.build;
4 import reggae.rules;
5 import reggae.types;
6 import reggae.config: options;
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     bool active;
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 = allImportPaths();
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                 " " ~ options.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 ~= dlangPackageObjectFiles(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(ExeName(packages[0].targetFileName),
75                     toTargets(),
76                     Flags(flags.join(" ")));
77     }
78 
79     string[] mainTargetImportPaths() @trusted nothrow const {
80         return packages[0].allOf!(a => a.packagePaths(a.importPaths))(packages);
81     }
82 
83     string[][] fetchCommands() @safe pure nothrow const {
84         return packages[0].dependencies.map!(a => ["dub", "fetch", a]).array;
85     }
86 
87     string[] allImportPaths() @safe nothrow const {
88         string[] paths;
89         auto rng = packages.map!(a => a.packagePaths(a.importPaths));
90         foreach(p; rng) paths ~= p;
91         return paths ~ options.projectPath;
92     }
93 }
94 
95 
96 private auto packagePaths(in DubPackage pack, in string[] paths) @trusted nothrow {
97     return paths.map!(a => buildPath(pack.path, a)).array;
98 }
99 
100 //@trusted because of map.array
101 private string[] allOf(alias F)(in DubPackage pack, in DubPackage[] packages) @trusted nothrow {
102     string[] paths;
103     //foreach(d; [pack.name] ~ pack.dependencies) doesn't compile with CTFE
104     //it seems to have to do with constness, replace string[] with const(string)[]
105     //and it won't compile
106     string[] dependencies = [pack.name];
107     dependencies ~= pack.dependencies;
108     foreach(dependency; dependencies) {
109         import std.range;
110         const depPack = packages.find!(a => a.name == dependency).front;
111         paths ~= F(depPack).array;
112     }
113     return paths;
114 }