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 //objectFile, objectFiles and link are the only default rules
15 //They work by serialising the rule to use piggy-backing on Target's string
16 //command attribute. It's horrible, but it works with the original decision
17 //of using strings as commands. Should be changed to be a sum type where
18 //a string represents a shell command and other variants call D code.
19 
20 //generate object file(s) for a D package. By default generates one per package,
21 //if reggae.config.perModule is true, generates one per module
22 Target[] objectFiles(in string[] srcFiles, in string flags = "",
23                      in string[] importPaths = [], in string[] stringImportPaths = [],
24                      in string projDir = "$project") @safe pure {
25     import reggae.config;
26     auto func = perModule ? &objectFilesPerModule : &objectFilesPerPackage;
27     return func(srcFiles, flags, importPaths, stringImportPaths, projDir);
28 }
29 
30 Target[] objectFilesPerPackage(in string[] srcFiles, in string flags = "",
31                                in string[] importPaths = [], in string[] stringImportPaths = [],
32                                in string projDir = "$project") @safe pure {
33 
34     immutable command = compileCommand(srcFiles[0], flags, importPaths, stringImportPaths, projDir);
35     return srcFiles.byPackage.map!(a => Target(a[0].packagePath.objFileName,
36                                                command,
37                                                a.map!(a => Target(a)).array)).array;
38 }
39 
40 Target[] objectFilesPerModule(in string[] srcFiles, in string flags = "",
41                               in string[] importPaths = [], in string[] stringImportPaths = [],
42                               in string projDir = "$project") @safe pure {
43 
44     immutable command = compileCommand(srcFiles[0], flags, importPaths, stringImportPaths, projDir);
45     return srcFiles.map!(a => objectFile(a, flags, importPaths, stringImportPaths, projDir)).array;
46 }
47 
48 
49 /**
50  * Compile-time function to that returns a list of Target objects
51  * corresponding to D source files from a particular directory
52  */
53 Target[] dObjects(SrcDirs dirs = SrcDirs(),
54                   Flags flags = Flags(),
55                   ImportPaths includes = ImportPaths(),
56                   StringImportPaths stringImports = StringImportPaths(),
57                   SrcFiles srcFiles = SrcFiles(),
58                   ExcludeFiles excludeFiles = ExcludeFiles())
59     () {
60 
61     Target[] dCompileInner(in string[] files) {
62         return objectFiles(files, flags.value, ["."] ~ includes.value, stringImports.value);
63     }
64 
65     return srcObjects!dCompileInner("d", dirs.value, srcFiles.value, excludeFiles.value);
66 }
67 
68 //compile-time verson of dExe, to be used with alias
69 //all paths relative to projectPath
70 Target dExe(App app,
71             Flags flags = Flags(),
72             ImportPaths importPaths = ImportPaths(),
73             StringImportPaths stringImportPaths = StringImportPaths(),
74             alias linkWithFunction = () { return cast(Target[])[];})
75     () {
76     auto linkWith = linkWithFunction();
77     return dExe(app, flags, importPaths, stringImportPaths, linkWith);
78 }
79 
80 
81 //regular runtime version of dExe
82 //all paths relative to projectPath
83 //@trusted because of .array
84 Target dExe(in App app, in Flags flags,
85             in ImportPaths importPaths,
86             in StringImportPaths stringImportPaths,
87             in Target[] linkWith) @trusted {
88 
89     auto mainObj = objectFile(app.srcFileName, flags.value, importPaths.value, stringImportPaths.value);
90     const output = runDCompiler(buildPath(projectPath, app.srcFileName), flags.value,
91                                 importPaths.value, stringImportPaths.value);
92 
93     const files = dMainDepSrcs(output).map!(a => a.removeProjectPath).array;
94     const dependencies = [mainObj] ~ objectFiles(files, flags.value,
95                                                  importPaths.value, stringImportPaths.value);
96 
97     return link(app.exeFileName, dependencies ~ linkWith);
98 }
99 
100 
101 //@trusted because of splitter
102 private auto runDCompiler(in string srcFileName, in string flags,
103                           in string[] importPaths, in string[] stringImportPaths) @trusted {
104 
105     import std.process: execute;
106     import std.exception: enforce;
107     import std.conv:text;
108 
109     immutable compiler = "dmd";
110     const compArgs = [compiler] ~ flags.splitter.array ~
111         importPaths.map!(a => "-I" ~ buildPath(projectPath, a)).array ~
112         stringImportPaths.map!(a => "-J" ~ buildPath(projectPath, a)).array ~
113         ["-o-", "-v", "-c", srcFileName];
114     const compRes = execute(compArgs);
115     enforce(compRes.status == 0, text("dExe could not run ", compArgs.join(" "), ":\n", compRes.output));
116     return compRes.output;
117 }