1 module reggae.rules.c_and_cpp;
2 
3 import reggae.build;
4 import reggae.rules.common;
5 import reggae.types;
6 import std.range;
7 import std.traits;
8 import std.stdio;
9 import std.file;
10 
11 @safe:
12 
13 Target unityBuild(ExeName exeName,
14                   alias sourcesFunc,
15                   Flags flags = Flags(),
16                   IncludePaths includes = IncludePaths(),
17                   alias dependenciesFunc = emptyTargets,
18                   alias implicitsFunc = emptyTargets)() @trusted {
19 
20     const srcFiles = sourcesToFileNames!(sourcesFunc);
21 
22     immutable dirName = topLevelDirName(Target(exeName.value));
23     dirName.exists || mkdirRecurse(dirName);
24 
25     immutable fileName = buildPath(dirName, "unity.cpp");
26     auto unityFile = File(fileName, "w");
27 
28     import reggae.config: options;
29     unityFile.write(unityFileContents(options.projectPath, srcFiles));
30 
31     return unityTarget(exeName, options.projectPath, srcFiles, flags, includes,
32                        dependenciesFunc(), implicitsFunc());
33 }
34 
35 
36 
37 /**
38  Returns the contents of the unity build file for these source files.
39  The source files have to all be in the same language and the only
40  supported languages are C and C++
41  */
42 string unityFileContents(in string projectPath, in string[] files) pure {
43     import std.array;
44     import std.algorithm;
45     import std.path;
46 
47     if(files.empty)
48         throw new Exception("Cannot perform a unity build with no files");
49 
50     immutable languages = files.map!getLanguage.array;
51 
52     if(!languages.all!(a => a == Language.C) && !languages.all!(a => a == Language.Cplusplus))
53         throw new Exception("Unity build can only be done if all files are C or C++");
54 
55 
56     return files.map!(a => `#include "` ~ buildPath(projectPath, a) ~ `"`).join("\n");
57 }
58 
59 
60 /**
61  Returns the unity build target for these parameters.
62  */
63 Target unityTarget(ExeName exeName,
64                    string projectPath,
65                    string[] srcFiles,
66                    Flags flags = Flags(),
67                    IncludePaths includes = IncludePaths(),
68                    alias dependenciesFunc = emptyTargets,
69                    alias implicitsFunc = emptyTargets,
70     )() {
71     return unityTarget(exeName, projectPath, srcFiles, flags, includes, dependenciesFunc());
72 }
73 
74 Target unityTarget(R1, R2)(in ExeName exeName,
75                            in string projectPath,
76                            in string[] srcFiles,
77                            in Flags flags = Flags(),
78                            in IncludePaths includes = IncludePaths(),
79                            R1 dependencies = emptyTargets(),
80                            R2 implicits = emptyTargets(),
81 
82     )
83     pure if(isInputRange!R1 && is(ElementType!R1 == Target) && isInputRange!R2 && is(ElementType!R2 == Target)) {
84 
85     import std.algorithm;
86 
87     const justFileName = srcFiles.map!getLanguage.front == Language.C ? "unity.c" : "unity.cpp";
88     const unityFileName = buildPath(gBuilddir, topLevelDirName(Target(exeName.value)), justFileName);
89     const command = compileCommand(unityFileName,
90                                    flags.value,
91                                    includes.value,
92                                    [],
93                                    projectPath,
94                                    No.justCompile);
95     const unityFileTarget = Target.phony(unityFileName, "", [], srcFiles.map!(a => Target(a)).array);
96     return Target(exeName.value, command, unityFileTarget ~ dependencies.array);
97 }
98 
99 
100 private Target[] emptyTargets() {
101     return [];
102 }