1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  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& configBackEnd 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& configBackEnd backend)
 80 {
 81     boost::filesystem::path smfp(CmajorSystemLibDir(configbackend));
 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(configBackEnd::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(configbackend);
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 } } // namespace sngcm::ast