1
2
3
4
5
6 #include <cmajor/build/ServerConfig.hpp>
7 #include <cmajor/symbols/GlobalFlags.hpp>
8 #include <sngcm/ast/Project.hpp>
9 #include <sngxml/dom/Parser.hpp>
10 #include <sngxml/dom/Document.hpp>
11 #include <sngxml/dom/Element.hpp>
12 #include <sngxml/xpath/XPathEvaluate.hpp>
13 #include <soulng/util/Path.hpp>
14 #include <soulng/util/Log.hpp>
15 #include <soulng/util/Unicode.hpp>
16 #include <boost/filesystem.hpp>
17 #include <boost/lexical_cast.hpp>
18 #include <iostream>
19
20 namespace cmajor { namespace build {
21
22 using namespace sngcm::ast;
23 using namespace sngxml::dom;
24 using namespace sngxml::xpath;
25 using namespace soulng::unicode;
26 using namespace cmajor::symbols;
27
28 std::string GetDefaultToolChainForCurrentPlatform()
29 {
30 #ifdef _WIN32
31
32 #else
33 return "gcc";
34 #endif
35 }
36
37 int GetDefaultLocalPort()
38 {
39 return 52360;
40 }
41
42 ServerInfo::ServerInfo(const std::string& serverName_, const std::string& host_, int port_, const std::string& defaultToolChain_) :
43 serverName(serverName_), host(host_), port(port_), defaultToolChain(defaultToolChain_)
44 {
45 }
46
47 void ServerInfo::SetDefaultToolChain(const std::string& defaultToolChain_)
48 {
49 defaultToolChain = defaultToolChain_;
50 }
51
52 void ServerInfo::Print(CodeFormatter& formatter)
53 {
54 std::string toolChainStr;
55 std::string hostStr;
56 if (!defaultToolChain.empty())
57 {
58 toolChainStr = ", default tool chain '" + defaultToolChain + "'";
59 }
60 if (!host.empty())
61 {
62 hostStr = ", host " + host;
63 }
64 formatter.WriteLine("server '" + serverName + "'" + hostStr + ", port " + std::to_string(port) + toolChainStr);
65 }
66
67 void ServerConfig::Init()
68 {
69 instance.reset(new ServerConfig());
70 }
71
72 void ServerConfig::Done()
73 {
74 instance.reset();
75 }
76
77 std::string CmajorConfigDir()
78 {
79 return Path::Combine(CmajorRootDir(), "config");
80 }
81
82 std::string CmajorServerConfigFilePath()
83 {
84 return Path::Combine(CmajorConfigDir(), "server-config.xml");
85 }
86
87 std::unique_ptr<ServerConfig> ServerConfig::instance;
88
89 void ServerConfig::Read()
90 {
91 std::string serverConfigFilePath = CmajorServerConfigFilePath();
92 try
93 {
94 if (boost::filesystem::exists(serverConfigFilePath))
95 {
96 serverInfos.clear();
97 serverMap.clear();
98 hostPortMap.clear();
99 std::unique_ptr<sngxml::dom::Document> configDoc = ReadDocument(serverConfigFilePath);
100 std::unique_ptr<sngxml::xpath::XPathObject> servers = Evaluate(U"/servers/server", configDoc.get());
101 if (servers)
102 {
103 if (servers->Type() == sngxml::xpath::XPathObjectType::nodeSet)
104 {
105 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(servers.get());
106 int n = nodeSet->Length();
107 for (int i = 0; i < n; ++i)
108 {
109 sngxml::dom::Node* node = (*nodeSet)[i];
110 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
111 {
112 sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
113 std::string serverName = ToUtf8(element->GetAttribute(U"name"));
114 std::string serverHost = ToUtf8(element->GetAttribute(U"host"));
115 std::string serverPort = ToUtf8(element->GetAttribute(U"port"));
116 int port = boost::lexical_cast<int>(serverPort);
117 std::string defaultToolChain = ToUtf8(element->GetAttribute(U"defaultToolChain"));
118 Add(serverName, serverHost, port, defaultToolChain, true, false, false);
119 }
120 }
121 }
122 }
123 }
124 }
125 catch (const std::exception& ex;)
126 {
127 LogMessage(-1, "server config: reading server configuration from '" + serverConfigFilePath + "' failed: " + ex.what());
128 }
129 }
130
131 void ServerConfig::Write()
132 {
133 std::string serverConfigFilePath = CmajorServerConfigFilePath();
134 try
135 {
136 sngxml::dom::Document serverConfigDoc;
137 sngxml::dom::Element* serversElement = new sngxml::dom::Element(U"servers");
138 serverConfigDoc.AppendChild(std::unique_ptr<sngxml::dom::Node>(serversElement));
139 for (const std::std::unique_ptr<ServerInfo>&serverInfo : serverInfos)
140 {
141 sngxml::dom::Element* serverElement = new sngxml::dom::Element(U"server");
142 serverElement->SetAttribute(U"name", ToUtf32(serverInfo->ServerName()));
143 if (!serverInfo->Host().empty())
144 {
145 serverElement->SetAttribute(U"host", ToUtf32(serverInfo->Host()));
146 }
147 serverElement->SetAttribute(U"port", ToUtf32(std::to_string(serverInfo->Port())));
148 serverElement->SetAttribute(U"defaultToolChain", ToUtf32(serverInfo->DefaultToolChain()));
149 serversElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(serverElement));
150 }
151 std::ofstream serverConfigFile(serverConfigFilePath);
152 CodeFormatter formatter(serverConfigFile);
153 serverConfigDoc.Write(formatter);
154 }
155 catch (const std::exception& ex;)
156 {
157 LogMessage(-1, "server config: writing server configuration to '" + serverConfigFilePath + "' failed: " + ex.what());
158 }
159 }
160
161 void ServerConfig::Add(const std::string& serverName, const std::string& hostName, int port, const std::string& defaultToolChain, bool force, bool read, bool write)
162 {
163 if (read)
164 {
165 Read();
166 }
167 std::string host = hostName;
168 if (host == "localhost" || host == "127.0.0.1")
169 {
170 host.clear();
171 }
172 if (GetGlobalFlag(GlobalFlags::verbose))
173 {
174 std::string toolChainStr;
175 if (!defaultToolChain.empty())
176 {
177 toolChainStr = ", default tool chain '" + defaultToolChain + "'";
178 }
179 std::string hostStr;
180 if (!host.empty())
181 {
182 hostStr = ", host " + host;
183 }
184 }
185 ServerInfo* serverInfo = GetServerInfo(serverName, false, false);
186 if (serverInfo != nullptr)
187 {
188 if (!force)
189 {
190 throw std::runtime_error("server config: server '" + serverName + "' already exists, use --force to add anyway");
191 }
192 auto it = hostPortMap.find(std::make_pair(host, port));
193 if (it != hostPortMap.cend())
194 {
195 std::string prevServerName = it->second;
196 if (prevServerName != serverName)
197 {
198 std::string hostPortStr;
199 if (!host.empty())
200 {
201 hostPortStr = "host " + host + ", port " + std::to_string(port);
202 }
203 else
204 {
205 hostPortStr = "port " + std::to_string(port);
206 }
207 LogMessage(-1, "server config: warning: " + hostPortStr + " already in use for server '" + prevServerName + "'");
208 }
209 }
210 serverInfo->SetHost(host);
211 serverInfo->SetPort(port);
212 serverInfo->SetDefaultToolChain(defaultToolChain);
213 hostPortMap[std::make_pair(host, port)] = serverName;
214 }
215 else
216 {
217 auto it = hostPortMap.find(std::make_pair(host, port));
218 if (it != hostPortMap.cend())
219 {
220 std::string prevServerName = it->second;
221 if (prevServerName != serverName)
222 {
223 std::string hostPortStr;
224 if (!host.empty())
225 {
226 hostPortStr.append("host ").append(host).append(", port ").append(std::to_string(port));
227 }
228 else
229 {
230 hostPortStr.append("port ").append(std::to_string(port));
231 }
232 throw std::runtime_error("server config: error: " + hostPortStr + " already in use for server '" + prevServerName + "'");
233 }
234 }
235 std::unique_ptr<ServerInfo> newServerInfo(new ServerInfo(serverName, host, port, defaultToolChain));
236 serverMap[serverName] = newServerInfo.get();
237 serverInfos.push_back(std::move(newServerInfo));
238 hostPortMap[std::make_pair(host, port)] = serverName;
239 }
240 if (write)
241 {
242 Write();
243 }
244 }
245
246 void ServerConfig::Remove(const std::string& serverName)
247 {
248 bool found = false;
249 int n = serverInfos.size();
250 for (int i = 0; i < n; ++i)
251 {
252 if (serverInfos[i]->ServerName() == serverName)
253 {
254 serverInfos.erase(serverInfos.begin() + i);
255 found = true;
256 break;
257 }
258 }
259 if (found)
260 {
261 Write();
262 }
263 else
264 {
265 throw std::runtime_error("server '" + serverName + "' not found");
266 }
267 }
268
269 void ServerConfig::Show()
270 {
271 Read();
272 CodeFormatter formatter(std::cout);
273 for (const auto& p : serverMap)
274 {
275 ServerInfo* serverInfo = p.second;
276 serverInfo->Print(formatter);
277 }
278 }
279
280 ServerConfig::ServerConfig()
281 {
282 Read();
283 ServerInfo* local = GetServerInfo("local", false, false);
284 if (!local)
285 {
286 Add("local", std::string(), GetDefaultLocalPort(), GetDefaultToolChainForCurrentPlatform(), true, false, true);
287 }
288 }
289
290 ServerInfo* ServerConfig::GetServerInfo(const std::string& serverName, bool failIfNotExist, bool read)
291 {
292 if (read)
293 {
294 Read();
295 }
296 auto it = serverMap.find(serverName);
297 if (it != serverMap.cend())
298 {
299 return it->second;
300 }
301 else
302 {
303 if (failIfNotExist)
304 {
305 throw std::runtime_error("server config: error: server name '" + serverName + "' not found from configuration file '" + CmajorServerConfigFilePath() + "'");
306 }
307 return nullptr;
308 }
309 }
310
311 void ServerInit()
312 {
313 ServerConfig::Init();
314 }
315
316 void ServerDone()
317 {
318 ServerConfig::Done();
319 }
320
321 } }