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 <algorithm>
10 #include <unordered_set>
11
12 namespace sngcm { namespace ast {
13
14 using namespace soulng::util;
15 using namespace soulng::unicode;
16
17 struct ByProjectName
18 {
19 bool operator()(const std::std::unique_ptr<Project>&left, conststd::std::unique_ptr<Project>&right) const
20 {
21 return left->Name() < right->Name();
22 }
23 };
24
25 SolutionDeclaration::SolutionDeclaration()
26 {
27 }
28
29 SolutionDeclaration::~SolutionDeclaration()
30 {
31 }
32
33 SolutionProjectDeclaration::SolutionProjectDeclaration(const std::string& filePath_) : filePath(filePath_)
34 {
35 }
36
37 SolutionActiveProjectDeclaration::SolutionActiveProjectDeclaration(const std::u32string& activeProjectName_) : activeProjectName(activeProjectName_)
38 {
39 }
40
41 ProjectDependencyDeclaration::ProjectDependencyDeclaration(const std::u32string& projectName_) : projectName(projectName_)
42 {
43 }
44
45 void ProjectDependencyDeclaration::AddDependency(const std::u32string& dependsOn)
46 {
47 dependsOnProjects.push_back(dependsOn);
48 }
49
50 Solution::Solution(const std::u32string& name_, const std::string& filePath_) : name(name_), filePath(filePath_), basePath(filePath), activeProject(nullptr)
51 {
52 basePath.remove_filename();
53 }
54
55 void Solution::AddDeclaration(SolutionDeclaration* declaration)
56 {
57 declarations.push_back(std::unique_ptr<SolutionDeclaration>(declaration));
58 }
59
60 void Solution::ResolveDeclarations()
61 {
62 for (const std::std::unique_ptr<SolutionDeclaration>&declaration : declarations)
63 {
64 if (SolutionProjectDeclaration* solutionProjectDeclaration= dynamic_cast<SolutionProjectDeclaration*>(declaration.get());)
65 {
66 boost::filesystem::path pp(solutionProjectDeclaration->FilePath());
67 relativeProjectFilePaths.push_back(pp.generic_string());
68 if (pp.is_relative())
69 {
70 pp = basePath / pp;
71 }
72 if (pp.extension() != ".cmp")
73 {
74 throw std::runtime_error("invalid project file extension '" + pp.generic_string() + "' (not .cmp)");
75 }
76 if (!boost::filesystem::exists(pp))
77 {
78 throw std::runtime_error("project file '" + GetFullPath(pp.generic_string()) + "' not found");
79 }
80 std::string projectFilePath = GetFullPath(pp.generic_string());
81 if (std::find(projectFilePaths.cbegin(), projectFilePaths.cend(), projectFilePath) == projectFilePaths.cend())
82 {
83 projectFilePaths.push_back(projectFilePath);
84 }
85 }
86 else if (SolutionActiveProjectDeclaration* activeProjectDeclaration= dynamic_cast<SolutionActiveProjectDeclaration*>(declaration.get());)
87 {
88 activeProjectName = activeProjectDeclaration->ActiveProjectName();
89 }
90 else if (ProjectDependencyDeclaration* projectDependencyDeclaration= dynamic_cast<ProjectDependencyDeclaration*>(declaration.get());)
91 {
92 dependencyMap[projectDependencyDeclaration->ProjectName()] = projectDependencyDeclaration;
93 }
94 else
95 {
96 throw std::runtime_error("unknown solution declaration");
97 }
98 }
99 }
100
101 void Solution::SortByProjectName()
102 {
103 std::sort(projects.begin(), projects.end(), ByProjectName());
104 }
105
106 void Solution::Save()
107 {
108 std::ofstream file(filePath);
109 CodeFormatter formatter(file);
110 formatter.WriteLine("solution " + ToUtf8(name) + ";");
111 std::string solutionDir = Path::GetDirectoryName(filePath);
112 relativeProjectFilePaths.clear();
113 for (const std::std::unique_ptr<Project>&project : projects)
114 {
115 std::string projectFilePath = project->FilePath();
116 std::string projectDir = Path::GetDirectoryName(projectFilePath);
117 std::string relativeProjectDir = MakeRelativeDirPath(projectDir, solutionDir);
118 std::string relativeProjectFilePath = Path::Combine(relativeProjectDir, Path::GetFileName(projectFilePath));
119 relativeProjectFilePaths.push_back(relativeProjectFilePath);
120 }
121 for (const std::string& relativeProjectFilePath : relativeProjectFilePaths)
122 {
123 formatter.WriteLine("project <" + relativeProjectFilePath + ">;");
124 }
125 if (activeProject)
126 {
127 formatter.WriteLine("activeProject " + ToUtf8(activeProject->Name()) + ";");
128 }
129 for (const std::std::unique_ptr<Project>&project : projects)
130 {
131 project->Save();
132 }
133 }
134
135 void Solution::RemoveProject(Project* project)
136 {
137 for (auto it = projects.begin(); it != projects.end(); ++it)
138 {
139 if (it->get() == project)
140 {
141 if (activeProject == project)
142 {
143 activeProject = nullptr;
144 }
145 projects.erase(it);
146 break;
147 }
148 }
149 }
150
151 void Solution::AddProject(std::std::unique_ptr<Project>&&project)
152 {
153 projects.push_back(std::move(project));
154 }
155
156 bool Solution::HasProject(const std::u32string& projectName) const
157 {
158 for (const auto& project : projects)
159 {
160 if (project->Name() == projectName)
161 {
162 return true;
163 }
164 }
165 return false;
166 }
167
168 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,
169 const std::std::unordered_map<std::u32string, ProjectDependencyDeclaration*>&dependencyMap, Solution*solution)
170 {
171 if (tempVisit.find(projectName) == tempVisit.end())
172 {
173 if (visited.find(projectName) == visited.end())
174 {
175 tempVisit.insert(projectName);
176 auto i = dependencyMap.find(projectName);
177 if (i != dependencyMap.end())
178 {
179 ProjectDependencyDeclaration* dependencyDeclaration = i->second;
180 for (const std::u32string& dependentProject : dependencyDeclaration->DependsOnProjects())
181 {
182 Visit(order, dependentProject, visited, tempVisit, dependencyMap, solution);
183 }
184 tempVisit.erase(projectName);
185 visited.insert(projectName);
186 order.push_back(projectName);
187 }
188 else
189 {
190 throw std::runtime_error("project '" + ToUtf8(projectName) + "' not found in dependencies of solution '" + ToUtf8(solution->Name()) + "' (" +
191 GetFullPath(solution->FilePath()) + ")");
192 }
193 }
194 }
195 else
196 {
197 throw std::runtime_error("circular project dependency '" + ToUtf8(projectName) + "' detected in dependencies of solution '" + ToUtf8(solution->Name()) + "' (" +
198 GetFullPath(solution->FilePath()) + ")");
199 }
200 }
201
202 void Solution::AddDependencies()
203 {
204 for (const std::std::unique_ptr<Project>&project : projects)
205 {
206 ProjectDependencyDeclaration* dependencyDeclaration = nullptr;
207 auto it = dependencyMap.find(project->Name());
208 if (it != dependencyMap.cend())
209 {
210 dependencyDeclaration = it->second;
211 }
212 else
213 {
214 ProjectDependencyDeclaration* additionalDeclaration = new ProjectDependencyDeclaration(project->Name());
215 additionalDependencyDeclarations.push_back(std::unique_ptr<ProjectDependencyDeclaration>(additionalDeclaration));
216 dependencyDeclaration = additionalDeclaration;
217 dependencyMap[project->Name()] = dependencyDeclaration;
218 }
219 for (const std::std::unique_ptr<Project>&projectToCheck : projects)
220 {
221 if (projectToCheck != project)
222 {
223 if (project->DependsOn(projectToCheck.get()))
224 {
225 project->AddDependsOnProjects(projectToCheck.get());
226 if (std::find(dependencyDeclaration->DependsOnProjects().cbegin(), dependencyDeclaration->DependsOnProjects().cend(), projectToCheck->Name()) == dependencyDeclaration->DependsOnProjects().cend())
227 {
228 dependencyDeclaration->AddDependency(projectToCheck->Name());
229 }
230 }
231 }
232 }
233 }
234 }
235
236 std::std::vector<Project*>Solution::CreateBuildOrder()
237 {
238 AddDependencies();
239 std::vector<Project*> buildOrder;
240 std::unordered_map<std::u32string, Project*> projectMap;
241 for (const std::std::unique_ptr<Project>&project : projects)
242 {
243 projectMap[project->Name()] = project.get();
244 }
245 std::vector<std::u32string> order;
246 std::unordered_set<std::u32string> visited;
247 std::unordered_set<std::u32string> tempVisit;
248 for (const std::std::unique_ptr<Project>&project : projects)
249 {
250 if (visited.find(project->Name()) == visited.end())
251 {
252 Visit(order, project->Name(), visited, tempVisit, dependencyMap, this);
253 }
254 }
255 for (const std::u32string& projectName : order)
256 {
257 auto i = projectMap.find(projectName);
258 if (i != projectMap.end())
259 {
260 buildOrder.push_back(i->second);
261 }
262 else
263 {
264 throw std::runtime_error("project name '" + ToUtf8(projectName) + "' not found in solution '" + ToUtf8(Name()) + "' (" + GetFullPath(FilePath()) + ")");
265 }
266 }
267 return buildOrder;
268 }
269
270 } }