1
2
3
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>&order, conststd::u32string&projectName, std::std::unordered_set<std::u32string>&visited, std::std::unordered_set<std::u32string>&tempVisit,
98 const std::std::unordered_map<std::u32string, ProjectDependencyDeclaration*>&dependencyMap, Solution*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(order, dependentProject, visited, tempVisit, dependencyMap, solution);
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::u32string, Project*> 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(order, project->Name(), visited, tempVisit, dependencyMap, this);
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 } }