1 /**
2 High-level rules for compiling D files
3  */
4 
5 module reggae.rules.d;
6 
7 import reggae.types;
8 import reggae.build;
9 import reggae.sorting;
10 import reggae.dependencies: dMainDepSrcs;
11 import reggae.rules.common;
12 import std.algorithm;
13 
14 private string dCompileCommand(in string flags = "",
15                                in string[] importPaths = [], in string[] stringImportPaths = [],
16                                in string projDir = "$project") @safe pure {
17     immutable importParams = importPaths.map!(a => "-I" ~ buildPath(projDir, a)).join(",");
18     immutable stringParams = stringImportPaths.map!(a => "-J" ~ buildPath(projDir, a)).join(",");
19     immutable flagParams = flags.splitter.join(",");
20     return ["_dcompile ", "includes=" ~ importParams, "flags=" ~ flagParams,
21             "stringImports=" ~ stringParams].join(" ");
22 }
23 
24 Target[] dCompileGrouped(in string[] srcFiles, in string flags = "",
25                          in string[] importPaths = [], in string[] stringImportPaths = [],
26                          in string projDir = "$project") @safe {
27     import reggae.config;
28     auto func = perModule ? &dCompilePerModule : &dCompilePerPackage;
29     return func(srcFiles, flags, importPaths, stringImportPaths, projDir);
30 }
31 
32 Target[] dCompilePerPackage(in string[] srcFiles, in string flags = "",
33                             in string[] importPaths = [], in string[] stringImportPaths = [],
34                             in string projDir = "$project") @safe {
35 
36     immutable command = dCompileCommand(flags, importPaths, stringImportPaths, projDir);
37     return srcFiles.byPackage.map!(a => Target(a[0].packagePath.objFileName,
38                                                command,
39                                                a.map!(a => Target(a)).array)).array;
40 }
41 
42 Target[] dCompilePerModule(in string[] srcFiles, in string flags = "",
43                            in string[] importPaths = [], in string[] stringImportPaths = [],
44                            in string projDir = "$project") @safe {
45 
46     immutable command = dCompileCommand(flags, importPaths, stringImportPaths, projDir);
47     return srcFiles.map!(a => dCompile(a, flags, importPaths, stringImportPaths, projDir)).array;
48 }
49 
50 
51 //@trusted because of join
52 Target dCompile(in string srcFileName, in string flags = "",
53                 in string[] importPaths = [], in string[] stringImportPaths = [],
54                 in string projDir = "$project") @trusted pure {
55 
56     immutable command = dCompileCommand(flags, importPaths, stringImportPaths, projDir);
57     return Target(srcFileName.objFileName, command, [Target(srcFileName)]);
58 }
59 
60 /**
61  * Compile-time function to that returns a list of Target objects
62  * corresponding to D source files from a particular directory
63  */
64 Target[] dObjects(SrcDirs dirs = SrcDirs(),
65                   Flags flags = Flags(),
66                   ImportPaths includes = ImportPaths(),
67                   StringImportPaths stringImports = StringImportPaths(),
68                   SrcFiles srcFiles = SrcFiles(),
69                   ExcludeFiles excludeFiles = ExcludeFiles())
70     () {
71 
72     Target[] dCompileInner(in string[] files) {
73         return dCompileGrouped(files, flags.value, ["."] ~ includes.value, stringImports.value);
74     }
75 
76     return srcObjects!dCompileInner("d", dirs.value, srcFiles.value, excludeFiles.value);
77 }
78 
79 //compile-time verson of dExe, to be used with alias
80 //all paths relative to projectPath
81 Target dExe(App app,
82             Flags flags = Flags(),
83             ImportPaths importPaths = ImportPaths(),
84             StringImportPaths stringImportPaths = StringImportPaths(),
85             alias linkWithFunction = () { return cast(Target[])[];})
86     () {
87     auto linkWith = linkWithFunction();
88     return dExe(app, flags, importPaths, stringImportPaths, linkWith);
89 }
90 
91 
92 //regular runtime version of dExe
93 //all paths relative to projectPath
94 //@trusted because of .array
95 Target dExe(in App app, in Flags flags,
96             in ImportPaths importPaths,
97             in StringImportPaths stringImportPaths,
98             in Target[] linkWith) @trusted {
99 
100     auto mainObj = dCompile(app.srcFileName, flags.value, importPaths.value, stringImportPaths.value);
101     const output = runDCompiler(buildPath(projectPath, app.srcFileName), flags.value,
102                                 importPaths.value, stringImportPaths.value);
103 
104     const files = dMainDepSrcs(output).map!(a => a.removeProjectPath).array;
105     const dependencies = [mainObj] ~ dCompileGrouped(files, flags.value,
106                                                      importPaths.value, stringImportPaths.value);
107 
108     return dLink(app.exeFileName, dependencies ~ linkWith);
109 }
110 
111 
112 Target dLink(in string exeName, in Target[] dependencies, in string flags = "") @safe pure nothrow {
113     auto cmd = "_dlink";
114     if(flags != "") cmd ~= " flags=" ~ flags;
115     return Target(exeName, cmd, dependencies);
116 }
117 
118 
119 //@trusted because of splitter
120 private auto runDCompiler(in string srcFileName, in string flags,
121                           in string[] importPaths, in string[] stringImportPaths) @trusted {
122 
123     import std.process: execute;
124     import std.exception: enforce;
125     import std.conv:text;
126 
127     immutable compiler = "dmd";
128     const compArgs = [compiler] ~ flags.splitter.array ~
129         importPaths.map!(a => "-I" ~ buildPath(projectPath, a)).array ~
130         stringImportPaths.map!(a => "-J" ~ buildPath(projectPath, a)).array ~
131         ["-o-", "-v", "-c", srcFileName];
132     const compRes = execute(compArgs);
133     enforce(compRes.status == 0, text("dExe could not run ", compArgs.join(" "), ":\n", compRes.output));
134     return compRes.output;
135 }