1 /**
2  High-level rules for building dub projects. The rules in this module
3  only replicate what dub does itself. This allows a reggaefile.d to
4  reuse the information that dub already knows about.
5  */
6 
7 module reggae.rules.dub;
8 
9 import reggae.config; // isDubProject
10 
11 static if(isDubProject) {
12 
13     import reggae.dub.info;
14     import reggae.types;
15     import reggae.build;
16     import reggae.rules.common;
17     import std.typecons;
18     import std.traits;
19 
20     /**
21      Builds the main dub target (equivalent of "dub build")
22     */
23     Target dubDefaultTarget(CompilerFlags compilerFlags = CompilerFlags(),
24                             LinkerFlags linkerFlags = LinkerFlags(),
25                             Flag!"allTogether" allTogether = No.allTogether)
26         ()
27     {
28         import std.string: split;
29 
30         enum config = "default";
31         const dubInfo = configToDubInfo[config];
32         enum targetName = dubInfo.targetName;
33         enum linkerFlags = dubInfo.mainLinkerFlags ~ linkerFlags.value.split(" ");
34         return dubTarget!(() { Target[] t; return t;})
35             (
36                 targetName,
37                 dubInfo,
38                 compilerFlags.value,
39                 linkerFlags,
40                 Yes.main,
41                 allTogether,
42             );
43     }
44 
45 
46     /**
47        A target corresponding to `dub test`
48      */
49     Target dubTestTarget(CompilerFlags compilerFlags = CompilerFlags(), LinkerFlags linkerFlags = LinkerFlags())() {
50         import std.string: split;
51 
52         const config = "unittest" in configToDubInfo ? "unittest" : "default";
53 
54         auto actualCompilerFlags = compilerFlags.value;
55         if("unittest" !in configToDubInfo) actualCompilerFlags ~= " -unittest";
56 
57         const hasMain = configToDubInfo[config].packages[0].mainSourceFile != "";
58         const extraLinkerFlags = hasMain ? [] : ["-main"];
59         const actualLinkerFlags = extraLinkerFlags ~ linkerFlags.value.split(" ");
60 
61         // since dmd has a bug pertaining to separate compilation and __traits(getUnitTests),
62         // we default here to compiling all-at-once for the unittest build
63         return dubTarget!()(TargetName("ut"),
64                             configToDubInfo[config],
65                             actualCompilerFlags,
66                             actualLinkerFlags,
67                             Yes.main,
68                             Yes.allTogether);
69     }
70 
71     /**
72      Builds a particular dub configuration (executable, unittest, etc.)
73      */
74     Target dubConfigurationTarget(Configuration config = Configuration("default"),
75                                   CompilerFlags compilerFlags = CompilerFlags(),
76                                   LinkerFlags linkerFlags = LinkerFlags(),
77                                   Flag!"main" includeMain = Yes.main,
78                                   Flag!"allTogether" allTogether = No.allTogether,
79                                   alias objsFunction = () { Target[] t; return t; },
80                                   )
81         () if(isCallable!objsFunction)
82     {
83         import std.string: split;
84 
85         const dubInfo = configToDubInfo[config.value];
86         return dubTarget!objsFunction(dubInfo.targetName,
87                                       dubInfo,
88                                       compilerFlags.value,
89                                       linkerFlags.value.split(" "),
90                                       includeMain,
91                                       allTogether);
92     }
93 
94 
95     Target dubTarget(alias objsFunction = () { Target[] t; return t;})
96                     (in TargetName targetName,
97                      in DubInfo dubInfo,
98                      in string compilerFlags,
99                      in string[] linkerFlags = [],
100                      in Flag!"main" includeMain = Yes.main,
101                      in Flag!"allTogether" allTogether = No.allTogether)
102     {
103 
104         import reggae.rules.common: staticLibraryTarget;
105         import std.array: join;
106         import std.path: buildPath;
107 
108         const isStaticLibrary =
109             dubInfo.targetType == TargetType.library ||
110             dubInfo.targetType == TargetType.staticLibrary;
111         const sharedFlags = dubInfo.targetType == TargetType.dynamicLibrary
112             ? "-shared"
113             : "";
114         const allLinkerFlags = (linkerFlags ~ dubInfo.linkerFlags ~ sharedFlags).join(" ");
115         auto dubObjs = dubInfo.toTargets(includeMain, compilerFlags, allTogether);
116         auto allObjs = objsFunction() ~ dubObjs;
117 
118         const postBuildCommands = dubInfo.postBuildCommands;
119 
120         string realName() {
121             // otherwise the target wouldn't be top-level in the presence of
122             // postBuildCommands
123             return postBuildCommands == ""
124                 ? targetName.value
125                 : buildPath("$project", targetName.value);
126         }
127 
128         auto target = isStaticLibrary
129             ? staticLibraryTarget(realName, allObjs)[0]
130             : link(ExeName(realName),
131                    allObjs,
132                    Flags(allLinkerFlags));
133 
134         return postBuildCommands == ""
135             ? target
136             : Target.phony("postBuild", postBuildCommands, target);
137     }
138 }