1
2
3
4
5
6 #include <sngcm/ast/Project.hpp>
7 #include <soulng/util/Path.hpp>
8 #include <soulng/util/Unicode.hpp>
9 #include <boost/filesystem.hpp>
10 #include <iostream>
11
12 namespace sngcm { namespace ast {
13
14 using namespace soulng::util;
15 using namespace soulng::unicode;
16
17 ModuleVersionTagVerifier* moduleVersionTagVerifier = nullptr;
18
19 void SetModuleVersionTagVerifier(ModuleVersionTagVerifier* verifier)
20 {
21 moduleVersionTagVerifier = verifier;
22 }
23
24 std::string CmajorRootDir()
25 {
26 char* e = getenv("CMAJOR_ROOT");
27 if (e == nullptr || !*e)
28 {
29 throw std::runtime_error("please set 'CMAJOR_ROOT' environment variable to contain /path/to/cmajor directory.");
30 }
31 return std::string(e);
32 }
33
34 std::string CmajorSystemLibDir(const std::string& config, BackEnd backend)
35 {
36 if (backend == BackEnd::llvm)
37 {
38 boost::filesystem::path sld(CmajorRootDir());
39 sld /= "system";
40 sld /= "lib";
41 sld /= config;
42 return GetFullPath(sld.generic_string());
43 }
44 else if (backend == BackEnd::cmsx)
45 {
46 boost::filesystem::path sld(CmajorRootDir());
47 sld /= "projects";
48 sld /= "cmsx";
49 sld /= "system";
50 sld /= "lib";
51 sld /= config;
52 return GetFullPath(sld.generic_string());
53 }
54 else
55 {
56 return std::string();
57 }
58 }
59
60 std::string CmajorResourceDir()
61 {
62 boost::filesystem::path rd(CmajorRootDir());
63 rd /= "res";
64 return GetFullPath(rd.generic_string());
65 }
66
67 std::string outDir;
68
69 void SetOutDir(const std::string& outDir_)
70 {
71 outDir = outDir_;
72 }
73
74 const std::string& OutDir()
75 {
76 return outDir;
77 }
78
79 std::string CmajorSystemModuleFilePath(const std::string& config, BackEnd backend)
80 {
81 boost::filesystem::path smfp(CmajorSystemLibDir(config, backend));
82 smfp /= "System.cmm";
83 return GetFullPath(smfp.generic_string());
84 }
85
86 std::string CmajorSystemWindowsModuleFilePath(const std::string& config)
87 {
88 boost::filesystem::path smfp(CmajorSystemLibDir(config, BackEnd::llvm));
89 smfp /= "System.Windows.cmm";
90 return GetFullPath(smfp.generic_string());
91 }
92
93 ProjectDeclaration::ProjectDeclaration(ProjectDeclarationType declarationType_) : declarationType(declarationType_)
94 {
95 }
96
97 ProjectDeclaration::~ProjectDeclaration()
98 {
99 }
100
101 ReferenceDeclaration::ReferenceDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::referenceDeclaration), filePath(filePath_)
102 {
103 }
104
105 SourceFileDeclaration::SourceFileDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::sourceFileDeclaration), filePath(filePath_)
106 {
107 }
108
109 ResourceFileDeclaration::ResourceFileDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::resourceFileDeclaration), filePath(filePath_)
110 {
111 }
112
113 TextFileDeclaration::TextFileDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::textFileDeclaration), filePath(filePath_)
114 {
115 }
116
117 TargetDeclaration::TargetDeclaration(Target target_) : ProjectDeclaration(ProjectDeclarationType::targetDeclaration), target(target_)
118 {
119 }
120
121 Project::Project(const std::u32string& name_, const std::string& filePath_, const std::string& config_, BackEnd backend) :
122 name(name_), filePath(filePath_), config(config_), target(Target::program), sourceBasePath(filePath), outdirBasePath(filePath), isSystemProject(false), logStreamId(0), built(false)
123 {
124 if (!outDir.empty())
125 {
126 sourceBasePath.remove_filename();
127 outdirBasePath = outDir;
128 outdirBasePath /= ToUtf8(name);
129 }
130 else
131 {
132 sourceBasePath.remove_filename();
133 outdirBasePath = sourceBasePath;
134 }
135 systemLibDir = CmajorSystemLibDir(config, backend);
136 boost::filesystem::path mfp(filePath);
137 boost::filesystem::path fn = mfp.filename();
138 mfp.remove_filename();
139 if (!outDir.empty())
140 {
141 mfp = outdirBasePath;
142 }
143 mfp /= "lib";
144 mfp /= config;
145 mfp /= fn;
146 mfp.replace_extension(".cmm");
147 moduleFilePath = GetFullPath(mfp.generic_string());
148 boost::filesystem::path lfp(mfp);
149 #ifdef _WIN32
150
151
152
153
154
155
156
157
158 #else
159 lfp.replace_extension(".a");
160 #endif
161 libraryFilePath = GetFullPath(lfp.generic_string());
162 boost::filesystem::path efp(filePath);
163 efp.remove_filename();
164 if (!outDir.empty())
165 {
166 efp = outdirBasePath;
167 }
168 efp /= "bin";
169 efp /= config;
170 efp /= fn;
171 #ifdef _WIN32
172
173
174
175
176
177
178
179
180 #else
181 efp.replace_extension();
182 #endif
183 executableFilePath = GetFullPath(efp.generic_string());
184 }
185
186 void Project::AddDeclaration(ProjectDeclaration* declaration)
187 {
188 declarations.push_back(std::unique_ptr<ProjectDeclaration>(declaration));
189 }
190
191 void Project::ResolveDeclarations()
192 {
193 for (const std::std::unique_ptr<ProjectDeclaration>&declaration : declarations)
194 {
195 switch (declaration->GetDeclarationType())
196 {
197 case ProjectDeclarationType::referenceDeclaration:
198 {
199 ReferenceDeclaration* reference = static_cast<ReferenceDeclaration*>(declaration.get());
200 boost::filesystem::path rp(reference->FilePath());
201 relativeReferencedProjectFilePaths.push_back(rp.generic_string());
202 if (rp.is_absolute())
203 {
204 referencedProjectFilePaths.push_back(GetFullPath(rp.generic_string()));
205 }
206 else
207 {
208 boost::filesystem::path ar = sourceBasePath / rp;
209 referencedProjectFilePaths.push_back(GetFullPath(ar.generic_string()));
210 }
211 boost::filesystem::path fn = rp.filename();
212 rp.remove_filename();
213 if (rp.is_relative())
214 {
215 rp = systemLibDir / rp;
216 }
217 rp /= fn;
218 if (rp.extension() == ".cmp" || rp.extension() == ".cmproj")
219 {
220 rp.replace_extension(".cmm");
221 }
222 if (rp.extension() != ".cmm")
223 {
224 throw std::runtime_error("invalid reference path extension '" + rp.generic_string() + "' (not .cmp, .cmproj or .cmm)");
225 }
226 if (!boost::filesystem::exists(rp))
227 {
228 rp = reference->FilePath();
229 rp.remove_filename();
230 if (rp.is_relative())
231 {
232 rp = outdirBasePath / rp;
233 }
234 rp /= "lib";
235 rp /= config;
236 rp /= fn;
237 if (rp.extension() == ".cmp" || rp.extension() == ".cmproj")
238 {
239 rp.replace_extension(".cmm");
240 }
241 if (rp.extension() != ".cmm")
242 {
243 throw std::runtime_error("invalid reference path extension '" + rp.generic_string() + "' (not .cmp, .cmproj or .cmm)");
244 }
245 }
246 std::string referencePath = GetFullPath(rp.generic_string());
247 if (std::find(references.cbegin(), references.cend(), referencePath) == references.cend())
248 {
249 references.push_back(referencePath);
250 }
251 break;
252 }
253 case ProjectDeclarationType::sourceFileDeclaration:
254 {
255 SourceFileDeclaration* sourceFileDeclaration = static_cast<SourceFileDeclaration*>(declaration.get());
256 boost::filesystem::path sfp(sourceFileDeclaration->FilePath());
257 relativeSourceFilePaths.push_back(sfp.generic_string());
258 if (sfp.is_relative())
259 {
260 sfp = sourceBasePath / sfp;
261 }
262 if (sfp.extension() != ".cm")
263 {
264 throw std::runtime_error("invalid source file extension '" + sfp.generic_string() + "' (not .cm)");
265 }
266 if (!boost::filesystem::exists(sfp))
267 {
268 throw std::runtime_error("source file path '" + GetFullPath(sfp.generic_string()) + "' not found");
269 }
270 std::string sourceFilePath = GetFullPath(sfp.generic_string());
271 if (std::find(sourceFilePaths.cbegin(), sourceFilePaths.cend(), sourceFilePath) == sourceFilePaths.cend() && sourceFilePath != excludeSourceFilePath)
272 {
273 sourceFilePaths.push_back(sourceFilePath);
274 }
275 break;
276 }
277 case ProjectDeclarationType::resourceFileDeclaration:
278 {
279 ResourceFileDeclaration* resourceFileDeclaration = static_cast<ResourceFileDeclaration*>(declaration.get());
280 boost::filesystem::path rfp(resourceFileDeclaration->FilePath());
281 relativeResourceFilePaths.push_back(rfp.generic_string());
282 if (rfp.is_relative())
283 {
284 rfp = sourceBasePath / rfp;
285 }
286 if (rfp.extension() != ".xml")
287 {
288 throw std::runtime_error("invalid resource file extension '" + rfp.generic_string() + "' (not .xml)");
289 }
290 if (!boost::filesystem::exists(rfp))
291 {
292 throw std::runtime_error("resource file path '" + GetFullPath(rfp.generic_string()) + "' not found");
293 }
294 std::string resourceFilePath = GetFullPath(rfp.generic_string());
295 if (std::find(resourceFilePaths.cbegin(), resourceFilePaths.cend(), resourceFilePath) == resourceFilePaths.cend())
296 {
297 resourceFilePaths.push_back(resourceFilePath);
298 }
299 break;
300 }
301 case ProjectDeclarationType::targetDeclaration:
302 {
303 TargetDeclaration* targetDeclaration = static_cast<TargetDeclaration*>(declaration.get());
304 target = targetDeclaration->GetTarget();
305 break;
306 }
307 case ProjectDeclarationType::textFileDeclaration:
308 {
309 TextFileDeclaration* textFileDeclaration = static_cast<TextFileDeclaration*>(declaration.get());
310 boost::filesystem::path tfp(textFileDeclaration->FilePath());
311 relativeTextFilePaths.push_back(tfp.generic_string());
312 if (tfp.is_relative())
313 {
314 tfp = sourceBasePath / tfp;
315 }
316 std::string textFilePath = GetFullPath(tfp.generic_string());
317 textFilePaths.push_back(textFilePath);
318 break;
319 }
320 default:
321 {
322 throw std::runtime_error("unknown project declaration");
323 }
324 }
325 }
326 }
327
328 bool Project::DependsOn(Project* that) const
329 {
330 return std::find(references.cbegin(), references.cend(), that->moduleFilePath) != references.cend();
331 }
332
333 void Project::AddDependsOnProjects(Project* dependsOnProject)
334 {
335 dependsOn.push_back(dependsOnProject);
336 }
337
338 void Project::SetModuleFilePath(const std::string& moduleFilePath_)
339 {
340 moduleFilePath = moduleFilePath_;
341 }
342
343 void Project::SetLibraryFilePath(const std::string& libraryFilePath_)
344 {
345 libraryFilePath = libraryFilePath_;
346 }
347
348 bool Project::IsUpToDate(const std::string& systemModuleFilePath) const
349 {
350 if (!boost::filesystem::exists(moduleFilePath))
351 {
352 return false;
353 }
354 for (const std::string& sourceFilePath : sourceFilePaths)
355 {
356 if (boost::filesystem::last_write_time(sourceFilePath) > boost::filesystem::last_write_time(moduleFilePath))
357 {
358 return false;
359 }
360 }
361 for (const std::string& referenceFilePath : references)
362 {
363 if (boost::filesystem::last_write_time(referenceFilePath) > boost::filesystem::last_write_time(moduleFilePath))
364 {
365 return false;
366 }
367 }
368 if (!systemModuleFilePath.empty() && !IsSystemProject() && boost::filesystem::exists(systemModuleFilePath))
369 {
370 if (boost::filesystem::last_write_time(systemModuleFilePath) > boost::filesystem::last_write_time(moduleFilePath))
371 {
372 return false;
373 }
374 }
375 return true;
376 }
377
378 bool Project::Ready() const
379 {
380 for (Project* dependOn : dependsOn)
381 {
382 if (!dependOn->Built())
383 {
384 return false;
385 }
386 }
387 return true;
388 }
389
390 void Project::SetExcludeSourceFilePath(const std::string& excludeSourceFilePath_)
391 {
392 excludeSourceFilePath = excludeSourceFilePath_;
393 }
394
395 } }