1 | // tRule.cpp  |
2 | //  |
3 | // The base class for a rule. Rules support functionality such as setting targets/dependencies, and checking if the  |
4 | // build rule is out of date.  |
5 | //  |
6 | // Copyright (c) 2006, 2017 Tristan Grimmer.  |
7 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
8 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
9 | //  |
10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
11 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
12 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
13 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
14 | // PERFORMANCE OF THIS SOFTWARE.  |
15 |   |
16 | #include <System/tThrow.h>  |
17 | #include <System/tPrint.h>  |
18 | #include <System/tFile.h>  |
19 | #include "Pipeline/tRule.h"  |
20 | #ifdef PLATFORM_WINDOWS  |
21 | #include "Pipeline/tSolution.h"  |
22 | #endif  |
23 | using namespace tPipeline;  |
24 |   |
25 |   |
26 | tRuleError::tRuleError(const char* format, ...) :  |
27 | tError("[tRule] " )  |
28 | {  |
29 | va_list marker;  |
30 | va_start(marker, format);  |
31 | Message += tvsPrintf(Message, format, marker);  |
32 | }  |
33 |   |
34 |   |
35 | void tRule::SetTarget(const tString& target)  |
36 | {  |
37 | while (tStringItem* s = Dependencies.Remove())  |
38 | delete s;  |
39 |   |
40 | Target = target;  |
41 | }  |
42 |   |
43 |   |
44 | bool tRule::MaybeAddToDependenciesCaseInsensitive(const tString& dep)  |
45 | {  |
46 | // This function only adds to the dependency list if the file in question hasn't already been added.  |
47 | // @todo Does not support changing between relative and absolute.  |
48 | if (dep.IsEmpty())  |
49 | return false;  |
50 |   |
51 | tString lowCase = dep;  |
52 | lowCase.Replace('\\', '/');  |
53 | lowCase.LowCase();  |
54 |   |
55 | for (tStringItem* s = Dependencies.First(); s; s = s->Next())  |
56 | {  |
57 | tString lowCasePresent = *s;  |
58 | lowCasePresent.Replace('\\', '/');  |
59 | lowCasePresent.LowCase();  |
60 |   |
61 | if (lowCase == lowCasePresent)  |
62 | return false;  |
63 | }  |
64 |   |
65 | // If we get here the file hasn't already been added... so we add it now.  |
66 | Dependencies.Append(new tStringItem(dep));  |
67 | return true;  |
68 | }  |
69 |   |
70 |   |
71 | void tRule::AddDependency(const tString& dep)  |
72 | {  |
73 | if (!tSystem::tFileExists(dep))  |
74 | throw tRuleError("Cannot add dependency [%s]" , dep.Pod());  |
75 |   |
76 | MaybeAddToDependenciesCaseInsensitive(dep);  |
77 | }  |
78 |   |
79 |   |
80 |   |
81 | void tRule::AddDependency(tStringItem* dep)  |
82 | {  |
83 | if (!tSystem::tFileExists(*dep))  |
84 | throw tRuleError("Cannot add dependency [%s]" , dep->Pod());  |
85 |   |
86 | MaybeAddToDependenciesCaseInsensitive(*dep);  |
87 | }  |
88 |   |
89 |   |
90 | void tRule::AddDependencies(tList<tStringItem>& deps)  |
91 | {  |
92 | bool success = true;  |
93 |   |
94 | tString badDependency;  |
95 | while (tStringItem* dep = deps.Remove())  |
96 | {  |
97 | if (!tSystem::tFileExists(*dep))  |
98 | {  |
99 | success = false;  |
100 | badDependency = *dep;  |
101 | delete dep;  |
102 | }  |
103 | else  |
104 | {  |
105 | MaybeAddToDependenciesCaseInsensitive(*dep);  |
106 | }  |
107 | }  |
108 |   |
109 | if (!success)  |
110 | throw tRuleError("Cannot add dependency [%s]" , badDependency.Chars());  |
111 | }  |
112 |   |
113 |   |
114 | void tRule::AddDependencyDir(const tString& dir, const tString& ext)  |
115 | {  |
116 | tList<tStringItem> deps;  |
117 | bool includeHidden = false;  |
118 | tSystem::tFindFiles(deps, dir, ext, includeHidden);  |
119 |   |
120 | AddDependencies(deps);  |
121 | tAssert(deps.IsEmpty());  |
122 | }  |
123 |   |
124 |   |
125 | void tRule::AddDependencyDirRec(const tString& dir, const tString& ext)  |
126 | {  |
127 | tList<tStringItem> deps;  |
128 | bool includeHidden = false;  |
129 | tSystem::tFindFilesRecursive(deps, dir, ext, includeHidden);  |
130 |   |
131 | AddDependencies(deps);  |
132 | tAssert(deps.IsEmpty());  |
133 | }  |
134 |   |
135 |   |
136 | bool tRule::OutOfDate(bool checkClean)  |
137 | {  |
138 | // Returns true if target has been specified and target doesn't exist or is older than any dependency.  |
139 | // Also returns false if any dep doesn't exist.  |
140 | if (Target.IsEmpty())  |
141 | return false;  |
142 |   |
143 | tStringItem* dep = Dependencies.First();  |
144 | while (dep)  |
145 | {  |
146 | if (!tSystem::tFileExists(*dep))  |
147 | throw tRuleError("Cannot find dependency [%s] while targetting [%s]." , dep->ConstText(), Target.ConstText());  |
148 |   |
149 | dep = dep->Next();  |
150 | }  |
151 |   |
152 | if (!tSystem::tFileExists(Target))  |
153 | return true;  |
154 |   |
155 | if (checkClean && Clean)  |
156 | return true;  |
157 |   |
158 | dep = Dependencies.First();  |
159 | while (dep)  |
160 | {  |
161 | if (tSystem::tIsFileNewer(*dep, Target))  |
162 | return true;  |
163 |   |
164 | dep = dep->Next();  |
165 | }  |
166 |   |
167 | return false;  |
168 | }  |
169 | |