1
2
3
4
5
6 #include <sngcm/ast/Project.hpp>
7 #include <sngcm/ast/Solution.hpp>
8 #include <cmajor/cmtoolchain/ToolChains.hpp>
9 #include <soulng/util/Path.hpp>
10 #include <soulng/util/Unicode.hpp>
11 #include <soulng/util/Sha1.hpp>
12 #include <soulng/util/CodeFormatter.hpp>
13 #include <soulng/util/TextUtils.hpp>
14 #include <boost/filesystem.hpp>
15 #include <iostream>
16
17 namespace sngcm { namespace ast {
18
19 using namespace soulng::util;
20 using namespace soulng::unicode;
21
22 ModuleVersionTagVerifier* moduleVersionTagVerifier = nullptr;
23
24 void SetModuleVersionTagVerifier(ModuleVersionTagVerifier* verifier)
25 {
26 moduleVersionTagVerifier = verifier;
27 }
28
29 std::string TargetStr(Target target)
30 {
31 switch (target)
32 {
33 case Target::program: return "program";
34 case Target::library: return "library";
35 case Target::winapp: return "winapp";
36 case Target::winlib: return "winlib";
37 case Target::winguiapp: return "winguiapp";
38 case Target::unitTest: return "unitTest";
39 }
40 return "library";
41 }
42
43 Target ParseTarget(const std::string& targetStr)
44 {
45 if (targetStr == "program")
46 {
47 return Target::program;
48 }
49 else if (targetStr == "library")
50 {
51 return Target::library;
52 }
53 else if (targetStr == "winapp")
54 {
55 return Target::winapp;
56 }
57 else if (targetStr == "winlib")
58 {
59 return Target::winlib;
60 }
61 else if (targetStr == "winguiapp")
62 {
63 return Target::winguiapp;
64 }
65 else if (targetStr == "unitTest")
66 {
67 return Target::unitTest;
68 }
69 return Target::program;
70 }
71
72 std::string CmajorRootDir()
73 {
74 char* e = getenv("CMAJOR_ROOT");
75 if (e == nullptr || !*e)
76 {
77 throw std::runtime_error("please set 'CMAJOR_ROOT' environment variable to contain /path/to/cmajor directory.");
78 }
79 return std::string(e);
80 }
81
82 std::string CmajorSystemLibDir(const std::string& config, BackEnd backend, const std::string& toolChain, SystemDirKind systemDirKind)
83 {
84 if (backend == BackEnd::llvm)
85 {
86 boost::filesystem::path sld(CmajorRootDir());
87 if (systemDirKind == SystemDirKind::repository)
88 {
89 sld /= "repository";
90 }
91 sld /= "system";
92 sld /= "lib";
93 sld /= config;
94 return GetFullPath(sld.generic_string());
95 }
96 else if (backend == BackEnd::cmsx)
97 {
98 boost::filesystem::path sld(CmajorRootDir());
99 sld /= "projects";
100 sld /= "cmsx";
101 sld /= "system";
102 sld /= "lib";
103 sld /= config;
104 return GetFullPath(sld.generic_string());
105 }
106 else if (backend == BackEnd::cppcm)
107 {
108 boost::filesystem::path sld(CmajorRootDir());
109 if (systemDirKind == SystemDirKind::repository)
110 {
111 sld /= "repository";
112 }
113 sld /= "system";
114 sld /= "lib";
115 sld /= "cpp";
116 sld /= toolChain;
117 sld /= config;
118 return GetFullPath(sld.generic_string());
119 }
120 else
121 {
122 return std::string();
123 }
124 }
125
126 std::string CmajorResourceDir()
127 {
128 boost::filesystem::path rd(CmajorRootDir());
129 rd /= "res";
130 return GetFullPath(rd.generic_string());
131 }
132
133 std::string CmajorLogFileDir()
134 {
135 std::string cmajorRooDir = CmajorRootDir();
136 std::string logFileDir = Path::Combine(cmajorRooDir, "log");
137 boost::filesystem::create_directories(logFileDir);
138 return logFileDir;
139 }
140
141 std::string outDir;
142
143 void SetOutDir(const std::string& outDir_)
144 {
145 outDir = outDir_;
146 }
147
148 const std::string& OutDir()
149 {
150 return outDir;
151 }
152
153 std::string CmajorSystemModuleFilePath(const std::string& config, BackEnd backend, const std::string& toolChain, SystemDirKind systemDirKind)
154 {
155 boost::filesystem::path smfp(CmajorSystemLibDir(config, backend, toolChain, systemDirKind));
156 smfp /= "System.cmm";
157 return GetFullPath(smfp.generic_string());
158 }
159
160 std::string CmajorSystemWindowsModuleFilePath(const std::string& config, const std::string& toolChain, SystemDirKind systemDirKind)
161 {
162 boost::filesystem::path smfp(CmajorSystemLibDir(config, BackEnd::llvm, toolChain, systemDirKind));
163 smfp /= "System.Windows.cmm";
164 return GetFullPath(smfp.generic_string());
165 }
166
167 std::string MakeCmajorRootRelativeFilePath(const std::string& filePath)
168 {
169 std::string cmajorRootDir = GetFullPath(CmajorRootDir());
170 std::string fullPath = GetFullPath(filePath);
171 if (StartsWith(fullPath, cmajorRootDir))
172 {
173 return "$CMAJOR_ROOT$" + fullPath.substr(cmajorRootDir.length());
174 }
175 else
176 {
177 return fullPath;
178 }
179 }
180
181 std::string ExpandCmajorRootRelativeFilePath(const std::string& filePath)
182 {
183 if (StartsWith(filePath, "$CMAJOR_ROOT$"))
184 {
185 std::string cmajorRootDir = GetFullPath(CmajorRootDir());
186 return cmajorRootDir + filePath.substr(std::string("$CMAJOR_ROOT$").length());
187 }
188 else
189 {
190 return GetFullPath(filePath);
191 }
192 }
193
194 std::std::vector<Project*>GetReferencedProjects(Project*project, Solution*solution)
195 {
196 std::vector<Project*> referencedProjects;
197 for (const std::string& referencedProjectFilePath : project->ReferencedProjectFilePaths())
198 {
199 std::string rpfp = GetFullPath(referencedProjectFilePath);
200 int n = solution->Projects().size();
201 bool found = false;
202 int i = 0;
203 while (i < n && !found)
204 {
205 Project* solutionProject = solution->Projects()[i].get();
206 std::string fp = GetFullPath(solutionProject->FilePath());
207 if (fp == rpfp)
208 {
209 referencedProjects.push_back(solutionProject);
210 found = true;
211 }
212 ++i;
213 }
214 }
215 return referencedProjects;
216 }
217
218 std::std::set<Project*>GetAllReferencedProjects(Project*project, Solution*solution)
219 {
220 std::set<Project*> allReferencedProjects;
221 AddReferencedProjects(allReferencedProjects, project, solution);
222 return allReferencedProjects;
223 }
224
225 void AddReferencedProjects(std::std::set<Project*>&allReferencedProjects, Project*project, Solution*solution)
226 {
227 std::vector<Project*> referencedProjects = GetReferencedProjects(project, solution);
228 for (Project* referencedProject : referencedProjects)
229 {
230 if (allReferencedProjects.find(referencedProject) == allReferencedProjects.cend())
231 {
232 allReferencedProjects.insert(referencedProject);
233 AddReferencedProjects(allReferencedProjects, referencedProject, solution);
234 }
235 }
236 }
237
238 ProjectDeclaration::ProjectDeclaration(ProjectDeclarationType declarationType_) : declarationType(declarationType_)
239 {
240 }
241
242 ProjectDeclaration::~ProjectDeclaration()
243 {
244 }
245
246 ReferenceDeclaration::ReferenceDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::referenceDeclaration), filePath(filePath_)
247 {
248 }
249
250 void ReferenceDeclaration::Write(CodeFormatter& formatter)
251 {
252 formatter.WriteLine("reference <" + filePath + ">;");
253 }
254
255 SourceFileDeclaration::SourceFileDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::sourceFileDeclaration), filePath(filePath_)
256 {
257 }
258
259 void SourceFileDeclaration::Write(CodeFormatter& formatter)
260 {
261 formatter.WriteLine("source <" + filePath + ">;");
262 }
263
264 ResourceFileDeclaration::ResourceFileDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::resourceFileDeclaration), filePath(filePath_)
265 {
266 }
267
268 void ResourceFileDeclaration::Write(CodeFormatter& formatter)
269 {
270 formatter.WriteLine("resource <" + filePath + ">;");
271 }
272
273 TextFileDeclaration::TextFileDeclaration(const std::string& filePath_) : ProjectDeclaration(ProjectDeclarationType::textFileDeclaration), filePath(filePath_)
274 {
275 }
276
277 void TextFileDeclaration::Write(CodeFormatter& formatter)
278 {
279 formatter.WriteLine("text <" + filePath + ">;");
280 }
281
282 TargetDeclaration::TargetDeclaration(Target target_) : ProjectDeclaration(ProjectDeclarationType::targetDeclaration), target(target_)
283 {
284 }
285
286 void TargetDeclaration::Write(CodeFormatter& formatter)
287 {
288 formatter.WriteLine("target=" + TargetStr(target) + ";");
289 }
290
291 Project::Project(const std::u32string& name_, const std::string& filePath_, const std::string& config_, BackEnd backend_, const std::string& toolChain_,
292 SystemDirKind systemDirKind) :
293 backend(backend_), name(name_), filePath(filePath_), config(config_), target(Target::program), sourceBasePath(filePath), outdirBasePath(filePath),
294 isSystemProject(false), logStreamId(0), built(false), toolChain(toolChain_)
295 {
296 std::string platform = GetPlatform();
297 if (!outDir.empty())
298 {
299 sourceBasePath.remove_filename();
300 outdirBasePath = outDir;
301 outdirBasePath /= ToUtf8(name);
302 }
303 else
304 {
305 sourceBasePath.remove_filename();
306 outdirBasePath = sourceBasePath;
307 }
308 systemLibDir = CmajorSystemLibDir(config, backend, toolChain, systemDirKind);
309 boost::filesystem::path mfp(filePath);
310 boost::filesystem::path fn = mfp.filename();
311 mfp.remove_filename();
312 if (!outDir.empty())
313 {
314 mfp = outdirBasePath;
315 }
316 mfp /= "lib";
317 if (backend == BackEnd::cppcm)
318 {
319 mfp /= "cpp";
320 mfp /= toolChain;
321 }
322 mfp /= config;
323 mfp /= fn;
324 mfp.replace_extension(".cmm");
325 moduleFilePath = GetFullPath(mfp.generic_string());
326 boost::filesystem::path lfp(mfp);
327 #ifdef _WIN32
328
329
330
331
332
333
334
335
336
337
338
339
340
341 #else
342 if (backend == BackEnd::cppcm)
343 {
344 const Tool& libraryManagerTool = GetLibraryManagerTool(platform, toolChain);
345 lfp.replace_extension(libraryManagerTool.outputFileExtension);
346 }
347 else
348 {
349 lfp.replace_extension(".a");
350 }
351 #endif
352 libraryFilePath = GetFullPath(lfp.generic_string());
353 boost::filesystem::path efp(filePath);
354 efp.remove_filename();
355 if (!outDir.empty())
356 {
357 efp = outdirBasePath;
358 }
359 efp /= "bin";
360 if (backend == BackEnd::cppcm)
361 {
362 efp /= "cpp";
363 efp /= toolChain;
364 }
365 efp /= config;
366 efp /= fn;
367 #ifdef _WIN32
368
369
370
371
372
373
374
375
376
377
378
379
380
381 #else
382 if (backend == BackEnd::cppcm)
383 {
384 const Tool& linkerTool = GetLinkerTool(platform, toolChain);
385 efp.replace_extension(linkerTool.outputFileExtension);
386 }
387 else
388 {
389 efp.replace_extension();
390 }
391 #endif
392 executableFilePath = GetFullPath(efp.generic_string());
393 }
394
395 void Project::AddDeclaration(ProjectDeclaration* declaration)
396 {
397 declarations.push_back(std::unique_ptr<ProjectDeclaration>(declaration));
398 }
399
400 void Project::ResolveDeclarations()
401 {
402 for (const std::std::unique_ptr<ProjectDeclaration>&declaration : declarations)
403 {
404 switch (declaration->GetDeclarationType())
405 {
406 case ProjectDeclarationType::referenceDeclaration:
407 {
408 ReferenceDeclaration* reference = static_cast<ReferenceDeclaration*>(declaration.get());
409 boost::filesystem::path rp(reference->FilePath());
410 relativeReferencedProjectFilePaths.push_back(rp.generic_string());
411 if (rp.is_absolute())
412 {
413 referencedProjectFilePaths.push_back(GetFullPath(rp.generic_string()));
414 }
415 else
416 {
417 boost::filesystem::path ar = sourceBasePath / rp;
418 referencedProjectFilePaths.push_back(GetFullPath(ar.generic_string()));
419 }
420 boost::filesystem::path fn = rp.filename();
421 rp.remove_filename();
422 if (rp.is_relative())
423 {
424 rp = systemLibDir / rp;
425 }
426 rp /= fn;
427 if (rp.extension() == ".cmp" || rp.extension() == ".cmproj")
428 {
429 rp.replace_extension(".cmm");
430 }
431 if (rp.extension() != ".cmm")
432 {
433 throw std::runtime_error("invalid reference path extension '" + rp.generic_string() + "' (not .cmp, .cmproj or .cmm)");
434 }
435 if (!boost::filesystem::exists(rp))
436 {
437 rp = reference->FilePath();
438 rp.remove_filename();
439 if (rp.is_relative())
440 {
441 rp = outdirBasePath / rp;
442 }
443 rp /= "lib";
444 if (backend == BackEnd::cppcm)
445 {
446 rp /= "cpp";
447 rp /= toolChain;
448 }
449 rp /= config;
450 rp /= fn;
451 if (rp.extension() == ".cmp" || rp.extension() == ".cmproj")
452 {
453 rp.replace_extension(".cmm");
454 }
455 if (rp.extension() != ".cmm")
456 {
457 throw std::runtime_error("invalid reference path extension '" + rp.generic_string() + "' (not .cmp, .cmproj or .cmm)");
458 }
459 }
460 std::string referencePath = GetFullPath(rp.generic_string());
461 if (std::find(references.cbegin(), references.cend(), referencePath) == references.cend())
462 {
463 references.push_back(referencePath);
464 }
465 break;
466 }
467 case ProjectDeclarationType::sourceFileDeclaration:
468 {
469 SourceFileDeclaration* sourceFileDeclaration = static_cast<SourceFileDeclaration*>(declaration.get());
470 boost::filesystem::path sfp(sourceFileDeclaration->FilePath());
471 relativeSourceFilePaths.push_back(sfp.generic_string());
472 if (sfp.is_relative())
473 {
474 sfp = sourceBasePath / sfp;
475 }
476 if (sfp.extension() != ".cm")
477 {
478 throw std::runtime_error("invalid source file extension '" + sfp.generic_string() + "' (not .cm)");
479 }
480 if (!boost::filesystem::exists(sfp))
481 {
482 throw std::runtime_error("source file path '" + GetFullPath(sfp.generic_string()) + "' not found");
483 }
484 std::string sourceFilePath = GetFullPath(sfp.generic_string());
485 if (std::find(sourceFilePaths.cbegin(), sourceFilePaths.cend(), sourceFilePath) == sourceFilePaths.cend() && sourceFilePath != excludeSourceFilePath)
486 {
487 sourceFilePaths.push_back(sourceFilePath);
488 }
489 break;
490 }
491 case ProjectDeclarationType::resourceFileDeclaration:
492 {
493 ResourceFileDeclaration* resourceFileDeclaration = static_cast<ResourceFileDeclaration*>(declaration.get());
494 boost::filesystem::path rfp(resourceFileDeclaration->FilePath());
495 relativeResourceFilePaths.push_back(rfp.generic_string());
496 if (rfp.is_relative())
497 {
498 rfp = sourceBasePath / rfp;
499 }
500 if (rfp.extension() != ".xml")
501 {
502 throw std::runtime_error("invalid resource file extension '" + rfp.generic_string() + "' (not .xml)");
503 }
504 if (!boost::filesystem::exists(rfp))
505 {
506 throw std::runtime_error("resource file path '" + GetFullPath(rfp.generic_string()) + "' not found");
507 }
508 std::string resourceFilePath = GetFullPath(rfp.generic_string());
509 if (std::find(resourceFilePaths.cbegin(), resourceFilePaths.cend(), resourceFilePath) == resourceFilePaths.cend())
510 {
511 resourceFilePaths.push_back(resourceFilePath);
512 }
513 break;
514 }
515 case ProjectDeclarationType::targetDeclaration:
516 {
517 TargetDeclaration* targetDeclaration = static_cast<TargetDeclaration*>(declaration.get());
518 target = targetDeclaration->GetTarget();
519 break;
520 }
521 case ProjectDeclarationType::textFileDeclaration:
522 {
523 TextFileDeclaration* textFileDeclaration = static_cast<TextFileDeclaration*>(declaration.get());
524 boost::filesystem::path tfp(textFileDeclaration->FilePath());
525 relativeTextFilePaths.push_back(tfp.generic_string());
526 if (tfp.is_relative())
527 {
528 tfp = sourceBasePath / tfp;
529 }
530 std::string textFilePath = GetFullPath(tfp.generic_string());
531 textFilePaths.push_back(textFilePath);
532 break;
533 }
534 default:
535 {
536 throw std::runtime_error("unknown project declaration");
537 }
538 }
539 }
540 }
541
542 void Project::Write(const std::string& projectFilePath)
543 {
544 std::ofstream projectFile(projectFilePath);
545 CodeFormatter formatter(projectFile);
546 formatter.WriteLine("project " + ToUtf8(Name()) + ";");
547 for (const std::std::unique_ptr<ProjectDeclaration>&declaration : declarations)
548 {
549 declaration->Write(formatter);
550 }
551 }
552
553 bool Project::DependsOn(Project* that) const
554 {
555 return std::find(references.cbegin(), references.cend(), that->moduleFilePath) != references.cend();
556 }
557
558 void Project::AddDependsOnProjects(Project* dependsOnProject)
559 {
560 dependsOn.push_back(dependsOnProject);
561 }
562
563 void Project::SetModuleFilePath(const std::string& moduleFilePath_)
564 {
565 moduleFilePath = moduleFilePath_;
566 }
567
568 void Project::SetLibraryFilePath(const std::string& libraryFilePath_)
569 {
570 libraryFilePath = libraryFilePath_;
571 }
572
573 void Project::SetReferencedProjects(const std::std::vector<Project*>&referencedProjects)
574 {
575 referencedProjectFilePaths.clear();
576 relativeReferencedProjectFilePaths.clear();
577 std::string absoluteProjectDirPath = GetFullPath(sourceBasePath.generic_string());
578 for (Project* referencedProject : referencedProjects)
579 {
580 referencedProjectFilePaths.push_back(referencedProject->FilePath());
581 std::string absoluteReferenceDirPath = GetFullPath(Path::GetDirectoryName(referencedProject->FilePath()));
582 std::string referenceProjectDir = MakeRelativeDirPath(absoluteReferenceDirPath, absoluteProjectDirPath);
583 relativeReferencedProjectFilePaths.push_back(Path::Combine(referenceProjectDir, Path::GetFileName(referencedProject->FilePath())));
584 }
585 }
586
587 bool Project::IsUpToDate(const std::string& systemModuleFilePath) const
588 {
589 if (!boost::filesystem::exists(moduleFilePath))
590 {
591 return false;
592 }
593 for (const std::string& sourceFilePath : sourceFilePaths)
594 {
595 if (boost::filesystem::last_write_time(sourceFilePath) > boost::filesystem::last_write_time(moduleFilePath))
596 {
597 return false;
598 }
599 }
600 for (const std::string& resourceFilePath : resourceFilePaths)
601 {
602 if (boost::filesystem::last_write_time(resourceFilePath) > boost::filesystem::last_write_time(moduleFilePath))
603 {
604 return false;
605 }
606 }
607 for (const std::string& referenceFilePath : references)
608 {
609 if (boost::filesystem::last_write_time(referenceFilePath) > boost::filesystem::last_write_time(moduleFilePath))
610 {
611 return false;
612 }
613 }
614 if (!systemModuleFilePath.empty() && !IsSystemProject() && boost::filesystem::exists(systemModuleFilePath))
615 {
616 if (boost::filesystem::last_write_time(systemModuleFilePath) > boost::filesystem::last_write_time(moduleFilePath))
617 {
618 return false;
619 }
620 }
621 return true;
622 }
623
624 bool Project::Built()
625 {
626 return built;
627 }
628
629 void Project::SetBuilt()
630 {
631 built = true;
632 }
633
634 bool Project::Ready()
635 {
636 for (Project* dependOn : dependsOn)
637 {
638 if (!dependOn->Built())
639 {
640 return false;
641 }
642 }
643 return true;
644 }
645
646 void Project::SetExcludeSourceFilePath(const std::string& excludeSourceFilePath_)
647 {
648 excludeSourceFilePath = excludeSourceFilePath_;
649 }
650
651 std::string Project::Id() const
652 {
653 std::string id = "project_";
654 id.append(ToUtf8(name)).append(1, '_').append(GetSha1MessageDigest(filePath));
655 return id;
656 }
657
658 void Project::AddDependsOnId(const std::string& dependsOnId)
659 {
660 dependsOnIds.push_back(dependsOnId);
661 }
662
663 bool Project::HasSourceFile(const std::string& sourceFilePath) const
664 {
665 for (const auto& filePath : sourceFilePaths)
666 {
667 if (filePath == sourceFilePath)
668 {
669 return true;
670 }
671 }
672 return false;
673 }
674
675 bool Project::HasResourceFile(const std::string& resourceFilePath) const
676 {
677 for (const auto& filePath : resourceFilePaths)
678 {
679 if (filePath == resourceFilePath)
680 {
681 return true;
682 }
683 }
684 return false;
685 }
686
687 bool Project::HasTextFile(const std::string& textFilePath) const
688 {
689 for (const auto& filePath : textFilePaths)
690 {
691 if (filePath == textFilePath)
692 {
693 return true;
694 }
695 }
696 return false;
697 }
698
699 void Project::AddSourceFileName(const std::string& sourceFileName, const std::string& sourceFilePath)
700 {
701 relativeSourceFilePaths.push_back(sourceFileName);
702 std::sort(relativeSourceFilePaths.begin(), relativeSourceFilePaths.end());
703 sourceFilePaths.push_back(sourceFilePath);
704 std::sort(sourceFilePaths.begin(), sourceFilePaths.end());
705 }
706
707 void Project::AddResourceFileName(const std::string& resourceFileName, const std::string& resourceFilePath)
708 {
709 relativeResourceFilePaths.push_back(resourceFileName);
710 std::sort(relativeResourceFilePaths.begin(), relativeResourceFilePaths.end());
711 resourceFilePaths.push_back(resourceFilePath);
712 std::sort(resourceFilePaths.begin(), resourceFilePaths.end());
713 }
714
715 void Project::AddTextFileName(const std::string& textFileName, const std::string& textFilePath)
716 {
717 relativeTextFilePaths.push_back(textFileName);
718 std::sort(relativeTextFilePaths.begin(), relativeTextFilePaths.end());
719 textFilePaths.push_back(textFilePath);
720 std::sort(textFilePaths.begin(), textFilePaths.end());
721 }
722
723 void Project::RemoveFile(const std::string& filePath, const std::string& fileName)
724 {
725 relativeSourceFilePaths.erase(std::remove(relativeSourceFilePaths.begin(), relativeSourceFilePaths.end(), fileName), relativeSourceFilePaths.end());
726 sourceFilePaths.erase(std::remove(sourceFilePaths.begin(), sourceFilePaths.end(), filePath), sourceFilePaths.end());
727 relativeResourceFilePaths.erase(std::remove(relativeResourceFilePaths.begin(), relativeResourceFilePaths.end(), fileName), relativeResourceFilePaths.end());
728 resourceFilePaths.erase(std::remove(resourceFilePaths.begin(), resourceFilePaths.end(), filePath), resourceFilePaths.end());
729 relativeTextFilePaths.erase(std::remove(relativeTextFilePaths.begin(), relativeTextFilePaths.end(), fileName), relativeTextFilePaths.end());
730 textFilePaths.erase(std::remove(textFilePaths.begin(), textFilePaths.end(), filePath), textFilePaths.end());
731 }
732
733 void Project::Save()
734 {
735 std::ofstream file(filePath);
736 CodeFormatter formatter(file);
737 formatter.WriteLine("project " + ToUtf8(name) + ";");
738 formatter.WriteLine("target=" + TargetStr(target) + ";");
739 for (const std::string& relativeReferenceFilePath : relativeReferencedProjectFilePaths)
740 {
741 formatter.WriteLine("reference <" + relativeReferenceFilePath + ">;");
742 }
743 for (const std::string& relativeSourceFilePath : relativeSourceFilePaths)
744 {
745 formatter.WriteLine("source <" + relativeSourceFilePath + ">;");
746 }
747 for (const std::string& relativeResourceFilePath : relativeResourceFilePaths)
748 {
749 formatter.WriteLine("resource <" + relativeResourceFilePath + ">;");
750 }
751 for (const std::string& relativeTextFilePath : relativeTextFilePaths)
752 {
753 formatter.WriteLine("text <" + relativeTextFilePath + ">;");
754 }
755 }
756
757 } }