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 
23using namespace tPipeline; 
24 
25 
26tRuleError::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 
35void tRule::SetTarget(const tString& target) 
36{ 
37 while (tStringItem* s = Dependencies.Remove()) 
38 delete s; 
39 
40 Target = target; 
41} 
42 
43 
44bool 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 
71void 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 
81void 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 
90void 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 
114void 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 
125void 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 
136bool 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