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(),
50                          LinkerFlags linkerFlags = LinkerFlags())
51                          ()
52     {
53         import std.typecons: No, Yes;
54         static if (__VERSION__ >= 2077)
55             enum allTogether = Yes.allTogether;
56         else
57             enum allTogether = Yes.allTogether;
58 
59         return dubTestTarget!(compilerFlags, linkerFlags, allTogether)();
60     }
61 
62     /**
63        A target corresponding to `dub test`
64      */
65     Target dubTestTarget(CompilerFlags compilerFlags = CompilerFlags(),
66                          LinkerFlags linkerFlags = LinkerFlags(),
67                          Flag!"allTogether" allTogether)
68                          ()
69     {
70         import std.string: split;
71 
72         const config = "unittest" in configToDubInfo ? "unittest" : "default";
73 
74         auto actualCompilerFlags = compilerFlags.value;
75         if("unittest" !in configToDubInfo) actualCompilerFlags ~= " -unittest";
76 
77         const hasMain = configToDubInfo[config].packages[0].mainSourceFile != "";
78         const extraLinkerFlags = hasMain ? [] : ["-main"];
79         const actualLinkerFlags = extraLinkerFlags ~ linkerFlags.value.split(" ");
80 
81         // since dmd has a bug pertaining to separate compilation and __traits(getUnitTests),
82         // we default here to compiling all-at-once for the unittest build
83         return dubTarget!()(TargetName("ut"),
84                             configToDubInfo[config],
85                             actualCompilerFlags,
86                             actualLinkerFlags,
87                             Yes.main,
88                             allTogether);
89     }
90 
91     /**
92      Builds a particular dub configuration (executable, unittest, etc.)
93      */
94     Target dubConfigurationTarget(Configuration config = Configuration("default"),
95                                   CompilerFlags compilerFlags = CompilerFlags(),
96                                   LinkerFlags linkerFlags = LinkerFlags(),
97                                   Flag!"main" includeMain = Yes.main,
98                                   Flag!"allTogether" allTogether = No.allTogether,
99                                   alias objsFunction = () { Target[] t; return t; },
100                                   )
101         () if(isCallable!objsFunction)
102     {
103         import std.string: split;
104 
105         const dubInfo = configToDubInfo[config.value];
106         return dubTarget!objsFunction(dubInfo.targetName,
107                                       dubInfo,
108                                       compilerFlags.value,
109                                       linkerFlags.value.split(" "),
110                                       includeMain,
111                                       allTogether);
112     }
113 
114 
115     Target dubTarget(alias objsFunction = () { Target[] t; return t;})
116                     (in TargetName targetName,
117                      in DubInfo dubInfo,
118                      in string compilerFlags,
119                      in string[] linkerFlags = [],
120                      in Flag!"main" includeMain = Yes.main,
121                      in Flag!"allTogether" allTogether = No.allTogether)
122     {
123 
124         import reggae.rules.common: staticLibraryTarget;
125         import reggae.config: options;
126         import reggae.dub.info: DubObjsDir;
127         import std.array: join;
128         import std.path: buildPath;
129         import std.file: getcwd;
130 
131         const isStaticLibrary =
132             dubInfo.targetType == TargetType.library ||
133             dubInfo.targetType == TargetType.staticLibrary;
134         const sharedFlags = dubInfo.targetType == TargetType.dynamicLibrary
135             ? "-shared"
136             : "";
137         const allLinkerFlags = (linkerFlags ~ dubInfo.linkerFlags ~ sharedFlags).join(" ");
138         const postBuildCommands = dubInfo.postBuildCommands;
139 
140         // otherwise the target wouldn't be top-level in the presence of
141         // postBuildCommands
142         const realName = postBuildCommands == ""
143             ? targetName.value
144             : buildPath("$project", targetName.value);
145 
146         auto dubObjs = dubInfo.toTargets(includeMain,
147                                          compilerFlags,
148                                          allTogether,
149                                          DubObjsDir(options.dubObjsDir, realName ~ ".objs"));
150         auto allObjs = objsFunction() ~ dubObjs;
151 
152         auto target = isStaticLibrary
153             ? staticLibraryTarget(realName, allObjs)[0]
154             : link(ExeName(realName),
155                    allObjs,
156                    Flags(allLinkerFlags));
157 
158         return postBuildCommands == ""
159             ? target
160             : Target.phony("postBuild", postBuildCommands, target);
161     }
162 
163     private string deabsolutePath(in string path) {
164         version(Windows) throw new Exception("not implemented yet");
165         return path[1..$];
166     }
167 
168 }