1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <sngcm/ast/Solution.hpp>
  7 #include <soulng/util/Unicode.hpp>
  8 #include <soulng/util/Path.hpp>
  9 #include <unordered_set>
 10 
 11 namespace sngcm { namespace ast {
 12 
 13 using namespace soulng::util;
 14 using namespace soulng::unicode;
 15 
 16 SolutionDeclaration::SolutionDeclaration()
 17 {
 18 }
 19 
 20 SolutionDeclaration::~SolutionDeclaration()
 21 {
 22 }
 23 
 24 SolutionProjectDeclaration::SolutionProjectDeclaration(const std::string& filePath_) : filePath(filePath_)
 25 {
 26 }
 27 
 28 SolutionActiveProjectDeclaration::SolutionActiveProjectDeclaration(const std::u32string& activeProjectName_) : activeProjectName(activeProjectName_)
 29 {
 30 }
 31 
 32 ProjectDependencyDeclaration::ProjectDependencyDeclaration(const std::u32string& projectName_) : projectName(projectName_)
 33 {
 34 }
 35 
 36 void ProjectDependencyDeclaration::AddDependency(const std::u32string& dependsOn)
 37 {
 38     dependsOnProjects.push_back(dependsOn);
 39 }
 40 
 41 Solution::Solution(const std::u32string& name_const std::string& filePath_) : name(name_)filePath(filePath_)basePath(filePath)
 42 {
 43     basePath.remove_filename();
 44 }
 45 
 46 void Solution::AddDeclaration(SolutionDeclaration* declaration)
 47 {
 48     declarations.push_back(std::unique_ptr<SolutionDeclaration>(declaration));
 49 }
 50 
 51 void Solution::ResolveDeclarations()
 52 {
 53     for (const std::std::unique_ptr<SolutionDeclaration>&declaration : declarations)
 54     {
 55         if (SolutionProjectDeclaration* solutionProjectDeclaration=  dynamic_cast<SolutionProjectDeclaration*>(declaration.get());)
 56         {
 57             boost::filesystem::path pp(solutionProjectDeclaration->FilePath());
 58             relativeProjectFilePaths.push_back(pp.generic_string());
 59             if (pp.is_relative())
 60             {
 61                 pp = basePath / pp;
 62             }
 63             if (pp.extension() != ".cmp")
 64             {
 65                 throw std::runtime_error("invalid project file extension '" + pp.generic_string() + "' (not .cmp)");
 66             }
 67             if (!boost::filesystem::exists(pp))
 68             {
 69                 throw std::runtime_error("project file '" + GetFullPath(pp.generic_string()) + "' not found");
 70             }
 71             std::string projectFilePath = GetFullPath(pp.generic_string());
 72             if (std::find(projectFilePaths.cbegin()projectFilePaths.cend()projectFilePath) == projectFilePaths.cend())
 73             {
 74                 projectFilePaths.push_back(projectFilePath);
 75             }
 76         }
 77         else if (SolutionActiveProjectDeclaration* activeProjectDeclaration=  dynamic_cast<SolutionActiveProjectDeclaration*>(declaration.get());)
 78         {
 79             activeProjectName = activeProjectDeclaration->ActiveProjectName();
 80         }
 81         else if (ProjectDependencyDeclaration* projectDependencyDeclaration=  dynamic_cast<ProjectDependencyDeclaration*>(declaration.get());)
 82         {
 83             dependencyMap[projectDependencyDeclaration->ProjectName()] = projectDependencyDeclaration;
 84         }
 85         else
 86         {
 87             throw std::runtime_error("unknown solution declaration");
 88         }
 89     }
 90 }
 91 
 92 void Solution::AddProject(std::std::unique_ptr<Project>&&project)
 93 {
 94     projects.push_back(std::move(project));
 95 }
 96 
 97 void Visit(std::std::vector<std::u32string>&orderconststd::u32string&projectNamestd::std::unordered_set<std::u32string>&visitedstd::std::unordered_set<std::u32string>&tempVisit
 98     const std::std::unordered_map<std::u32stringProjectDependencyDeclaration*>&dependencyMapSolution*solution)
 99 {
100     if (tempVisit.find(projectName) == tempVisit.end())
101     {
102         if (visited.find(projectName) == visited.end())
103         {
104             tempVisit.insert(projectName);
105             auto i = dependencyMap.find(projectName);
106             if (i != dependencyMap.end())
107             {
108                 ProjectDependencyDeclaration* dependencyDeclaration = i->second;
109                 for (const std::u32string& dependentProject : dependencyDeclaration->DependsOnProjects())
110                 {
111                     Visit(orderdependentProjectvisitedtempVisitdependencyMapsolution);
112                 }
113                 tempVisit.erase(projectName);
114                 visited.insert(projectName);
115                 order.push_back(projectName);
116             }
117             else
118             {
119                 throw std::runtime_error("project '" + ToUtf8(projectName) + "' not found in dependencies of solution '" + ToUtf8(solution->Name()) + "' (" + 
120                     GetFullPath(solution->FilePath()) + ")");
121             }
122         }
123     }
124     else
125     {
126         throw std::runtime_error("circular project dependency '" + ToUtf8(projectName) + "' detected in dependencies of solution '" + ToUtf8(solution->Name()) + "' (" + 
127             GetFullPath(solution->FilePath()) + ")");
128     }
129 }
130 
131 void Solution::AddDependencies()
132 {
133     for (const std::std::unique_ptr<Project>&project : projects)
134     {
135         ProjectDependencyDeclaration* dependencyDeclaration = nullptr;
136         auto it = dependencyMap.find(project->Name());
137         if (it != dependencyMap.cend())
138         {
139             dependencyDeclaration = it->second;
140         }
141         else
142         {
143             ProjectDependencyDeclaration* additionalDeclaration = new ProjectDependencyDeclaration(project->Name());
144             additionalDependencyDeclarations.push_back(std::unique_ptr<ProjectDependencyDeclaration>(additionalDeclaration));
145             dependencyDeclaration = additionalDeclaration;
146             dependencyMap[project->Name()] = dependencyDeclaration;
147         }
148         for (const std::std::unique_ptr<Project>&projectToCheck : projects)
149         {
150             if (projectToCheck != project)
151             {
152                 if (project->DependsOn(projectToCheck.get()))
153                 {
154                     project->AddDependsOnProjects(projectToCheck.get());
155                     if (std::find(dependencyDeclaration->DependsOnProjects().cbegin()dependencyDeclaration->DependsOnProjects().cend()projectToCheck->Name()) == dependencyDeclaration->DependsOnProjects().cend())
156                     {
157                         dependencyDeclaration->AddDependency(projectToCheck->Name());
158                     }
159                 }
160             }
161         }
162     }
163 }
164 
165 std::std::vector<Project*>Solution::CreateBuildOrder()
166 {
167     AddDependencies();
168     std::vector<Project*> buildOrder;
169     std::unordered_map<std::u32stringProject*> projectMap;
170     for (const std::std::unique_ptr<Project>&project : projects)
171     {
172         projectMap[project->Name()] = project.get();
173     }
174     std::vector<std::u32string> order;
175     std::unordered_set<std::u32string> visited;
176     std::unordered_set<std::u32string> tempVisit;
177     for (const std::std::unique_ptr<Project>&project : projects)
178     {
179         if (visited.find(project->Name()) == visited.end())
180         {
181             Visit(orderproject->Name()visitedtempVisitdependencyMapthis);
182         }
183     }
184     for (const std::u32string& projectName : order)
185     {
186         auto i = projectMap.find(projectName);
187         if (i != projectMap.end())
188         {
189             buildOrder.push_back(i->second);
190         }
191         else
192         {
193             throw std::runtime_error("project name '" + ToUtf8(projectName) + "' not found in solution '" + ToUtf8(Name()) + "' (" + GetFullPath(FilePath()) + ")");
194         }
195     }
196     return buildOrder;
197 }
198 
199 } } // namespace sngcm::ast