this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
3/*
4 * Main authors:
5 * Guido Tack <guido.tack@monash.edu>
6 */
7
8/* This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11
12#include <minizinc/file_utils.hh>
13#include <minizinc/json_parser.hh>
14#include <minizinc/parser.hh>
15#include <minizinc/prettyprinter.hh>
16#include <minizinc/solver_config.hh>
17
18#include <algorithm>
19#include <cctype>
20#include <iterator>
21#include <set>
22#include <sstream>
23#include <string>
24
25using namespace std;
26
27namespace MiniZinc {
28
29namespace {
30std::string getString(AssignI* ai) {
31 if (StringLit* sl = ai->e()->dyn_cast<StringLit>()) {
32 return sl->v().str();
33 }
34 throw ConfigException("invalid configuration item (right hand side must be string)");
35}
36bool getBool(AssignI* ai) {
37 if (BoolLit* bl = ai->e()->dyn_cast<BoolLit>()) {
38 return bl->v();
39 }
40 throw ConfigException("invalid configuration item (right hand side must be bool)");
41}
42int getInt(AssignI* ai) {
43 if (IntLit* il = ai->e()->dyn_cast<IntLit>()) {
44 return static_cast<int>(il->v().toInt());
45 }
46 throw ConfigException("invalid configuration item (right hand side must be int)");
47}
48std::vector<std::string> getStringList(AssignI* ai) {
49 if (ArrayLit* al = ai->e()->dyn_cast<ArrayLit>()) {
50 std::vector<std::string> ret;
51 for (unsigned int i = 0; i < al->size(); i++) {
52 if (StringLit* sl = (*al)[i]->dyn_cast<StringLit>()) {
53 ret.push_back(sl->v().str());
54 } else {
55 throw ConfigException(
56 "invalid configuration item (right hand side must be a list of strings)");
57 }
58 }
59 return ret;
60 }
61 throw ConfigException("invalid configuration item (right hand side must be a list of strings)");
62}
63std::vector<std::pair<std::string, std::string> > getStringPairList(AssignI* ai) {
64 if (ArrayLit* al = ai->e()->dyn_cast<ArrayLit>()) {
65 std::vector<std::pair<std::string, std::string> > ret;
66 if (al->dims() != 2 || al->min(1) != 1 || al->max(1) != 2) {
67 throw ConfigException(
68 "invalid configuration item (right hand side must be a 2d array of strings)");
69 }
70 for (unsigned int i = 0; i < al->size(); i += 2) {
71 StringLit* sl1 = (*al)[i]->dyn_cast<StringLit>();
72 StringLit* sl2 = (*al)[i + 1]->dyn_cast<StringLit>();
73 if (sl1 && sl2) {
74 ret.push_back(std::make_pair(sl1->v().str(), sl2->v().str()));
75 } else {
76 throw ConfigException(
77 "invalid configuration item (right hand side must be a 2d array of strings)");
78 }
79 }
80 return ret;
81 }
82 throw ConfigException(
83 "invalid configuration item (right hand side must be a 2d array of strings)");
84}
85std::vector<std::vector<std::string> > getDefaultOptionList(AssignI* ai) {
86 if (ArrayLit* al = ai->e()->dyn_cast<ArrayLit>()) {
87 std::vector<std::vector<std::string> > ret;
88 if (al->size() == 0) return ret;
89 if (al->dims() != 2) {
90 throw ConfigException(
91 "invalid configuration item (right hand side must be a 2d array of strings)");
92 }
93 int nCols = al->max(1) - al->min(1) + 1;
94 if (nCols != 3) {
95 throw ConfigException(
96 "invalid configuration item (right hand side must be a 2d array of strings with 3 "
97 "columns)");
98 }
99 for (unsigned int i = 0; i < al->size(); i += nCols) {
100 StringLit* sl0 = (*al)[i]->dyn_cast<StringLit>();
101 StringLit* sl1 = (*al)[i + 1]->dyn_cast<StringLit>();
102 StringLit* sl2 = (*al)[i + 2]->dyn_cast<StringLit>();
103 if (sl0 && sl1 && sl2) {
104 ret.push_back(std::vector<std::string>({sl0->v().str(), sl1->v().str(), sl2->v().str()}));
105 } else {
106 throw ConfigException(
107 "invalid configuration item (right hand side must be a list of strings)");
108 }
109 }
110 return ret;
111 }
112 throw ConfigException(
113 "invalid configuration item (right hand side must be a 2d array of strings)");
114}
115std::vector<SolverConfig::ExtraFlag> getExtraFlagList(AssignI* ai) {
116 if (ArrayLit* al = ai->e()->dyn_cast<ArrayLit>()) {
117 std::vector<SolverConfig::ExtraFlag> ret;
118 if (al->size() == 0) return ret;
119 if (al->dims() != 2) {
120 throw ConfigException(
121 "invalid configuration item (right hand side must be a 2d array of strings)");
122 }
123 int nCols = al->max(1) - al->min(1) + 1;
124 if (nCols < 2 || nCols > 4) {
125 throw ConfigException(
126 "invalid configuration item (right hand side must be a 2d array of strings)");
127 }
128 bool haveType = (nCols >= 3);
129 bool haveDefault = (nCols >= 4);
130 for (unsigned int i = 0; i < al->size(); i += nCols) {
131 StringLit* sl1 = (*al)[i]->dyn_cast<StringLit>();
132 StringLit* sl2 = (*al)[i + 1]->dyn_cast<StringLit>();
133 StringLit* sl3 = haveType ? (*al)[i + 2]->dyn_cast<StringLit>() : NULL;
134 StringLit* sl4 = haveDefault ? (*al)[i + 3]->dyn_cast<StringLit>() : NULL;
135 std::string opt_type = sl3 ? sl3->v().str() : "bool";
136 std::string opt_def;
137 if (sl4) {
138 opt_def = sl4->v().str();
139 } else if (opt_type == "bool") {
140 opt_def = "false";
141 } else if (opt_type == "int") {
142 opt_def = "0";
143 } else if (opt_type == "float") {
144 opt_def = "0.0";
145 }
146 if (sl1 && sl2) {
147 ret.emplace_back(sl1->v().str(), sl2->v().str(), opt_type, opt_def);
148 } else {
149 throw ConfigException(
150 "invalid configuration item (right hand side must be a 2d array of strings)");
151 }
152 }
153 return ret;
154 }
155 throw ConfigException(
156 "invalid configuration item (right hand side must be a 2d array of strings)");
157}
158
159std::string getEnv(const char* v) {
160 std::string ret;
161#ifdef _MSC_VER
162 size_t len;
163 getenv_s(&len, NULL, 0, v);
164 if (len > 0) {
165 char* p = static_cast<char*>(malloc(len * sizeof(char)));
166 getenv_s(&len, p, len, v);
167 if (len > 0) {
168 ret = p;
169 }
170 free(p);
171 }
172#else
173 char* p = getenv(v);
174 if (p) {
175 ret = p;
176 }
177#endif
178 return ret;
179}
180
181char charToLower(char c) { return std::tolower(static_cast<unsigned char>(c)); }
182std::string stringToLower(std::string s) {
183 std::transform(s.begin(), s.end(), s.begin(), charToLower);
184 return s;
185}
186struct SortByLowercase {
187 bool operator()(const std::string& n1, const std::string& n2) {
188 for (size_t i = 0; i < n1.size() && i < n2.size(); i++) {
189 if (std::tolower(n1[i]) != std::tolower(n2[i]))
190 return std::tolower(n1[i]) < std::tolower(n2[i]);
191 }
192 return n1.size() < n2.size();
193 }
194};
195struct SortByName {
196 const std::vector<SolverConfig>& solvers;
197 SortByLowercase sortByLowercase;
198 SortByName(const std::vector<SolverConfig>& solvers0) : solvers(solvers0) {}
199 bool operator()(int idx1, int idx2) {
200 return sortByLowercase(solvers[idx1].name(), solvers[idx2].name());
201 }
202};
203
204} // namespace
205
206SolverConfig SolverConfig::load(string filename) {
207 SolverConfig sc;
208 sc._configFile = FileUtils::file_path(filename);
209 ostringstream errstream;
210 try {
211 Env confenv;
212 Model* m = NULL;
213 if (JSONParser::fileIsJSON(filename)) {
214 JSONParser jp(confenv.envi());
215 try {
216 m = new Model;
217 GCLock lock;
218 jp.parse(m, filename);
219 } catch (JSONError& e) {
220 delete m;
221 m = NULL;
222 throw ConfigException(e.msg());
223 }
224 } else {
225 vector<string> filenames;
226 filenames.push_back(filename);
227 m = parse(confenv, filenames, vector<string>(), "", "", vector<string>(), true, false, false,
228 errstream);
229 }
230 if (m) {
231 bool hadId = false;
232 bool hadVersion = false;
233 bool hadName = false;
234 string basePath = FileUtils::dir_name(sc._configFile);
235 for (unsigned int i = 0; i < m->size(); i++) {
236 if (AssignI* ai = (*m)[i]->dyn_cast<AssignI>()) {
237 if (ai->id() == "id") {
238 sc._id = getString(ai);
239 hadId = true;
240 } else if (ai->id() == "name") {
241 sc._name = getString(ai);
242 hadName = true;
243 } else if (ai->id() == "executable") {
244 std::string exePath = getString(ai);
245 sc._executable = exePath;
246 std::string exe = FileUtils::find_executable(FileUtils::file_path(exePath, basePath));
247 int nr_found = (int)(!exe.empty());
248 std::string tmp = FileUtils::file_path(FileUtils::find_executable(exePath));
249 nr_found += (int)((!tmp.empty()) && tmp != exe);
250 exe = exe.empty() ? tmp : exe;
251 if (nr_found > 0) {
252 sc._executable_resolved = exe;
253 if (nr_found > 1) {
254 std::cerr << "Warning: multiple executables '" << exePath
255 << "' found on the system, using '" << exe << "'" << std::endl;
256 }
257 }
258 } else if (ai->id() == "mznlib") {
259 std::string libPath = getString(ai);
260 sc._mznlib = libPath;
261 if (!libPath.empty()) {
262 if (libPath[0] == '-') {
263 sc._mznlib_resolved = libPath;
264 } else if (libPath.size() > 2 && libPath[0] == '.' &&
265 (libPath[1] == '/' || (libPath[1] == '.' && libPath[2] == '/'))) {
266 sc._mznlib_resolved = FileUtils::file_path(libPath, basePath);
267 } else {
268 sc._mznlib_resolved = FileUtils::file_path(libPath, basePath);
269 }
270 }
271 } else if (ai->id() == "version") {
272 sc._version = getString(ai);
273 hadVersion = true;
274 } else if (ai->id() == "mznlibVersion") {
275 sc._mznlibVersion = getInt(ai);
276 } else if (ai->id() == "description") {
277 sc._description = getString(ai);
278 } else if (ai->id() == "contact") {
279 sc._contact = getString(ai);
280 } else if (ai->id() == "website") {
281 sc._website = getString(ai);
282 } else if (ai->id() == "supportsMzn") {
283 sc._supportsMzn = getBool(ai);
284 } else if (ai->id() == "supportsFzn") {
285 sc._supportsFzn = getBool(ai);
286 } else if (ai->id() == "supportsNL") {
287 sc._supportsNL = getBool(ai);
288 } else if (ai->id() == "needsSolns2Out") {
289 sc._needsSolns2Out = getBool(ai);
290 } else if (ai->id() == "isGUIApplication") {
291 sc._isGUIApplication = getBool(ai);
292 } else if (ai->id() == "needsMznExecutable") {
293 sc._needsMznExecutable = getBool(ai);
294 } else if (ai->id() == "needsStdlibDir") {
295 sc._needsStdlibDir = getBool(ai);
296 } else if (ai->id() == "needsPathsFile") {
297 sc._needsPathsFile = getBool(ai);
298 } else if (ai->id() == "tags") {
299 sc._tags = getStringList(ai);
300 } else if (ai->id() == "stdFlags") {
301 sc._stdFlags = getStringList(ai);
302 } else if (ai->id() == "requiredFlags") {
303 sc._requiredFlags = getStringList(ai);
304 } else if (ai->id() == "extraFlags") {
305 sc._extraFlags = getExtraFlagList(ai);
306 } else {
307 throw ConfigException("invalid configuration item (" + ai->id().str() + ")");
308 }
309 } else {
310 throw ConfigException("invalid configuration item");
311 }
312 }
313 if (!hadId) {
314 throw ConfigException("invalid solver configuration (missing id)");
315 }
316 if (!hadVersion) {
317 throw ConfigException("invalid solver configuration (missing version)");
318 }
319 if (!hadName) {
320 throw ConfigException("invalid solver configuration (missing name)");
321 }
322 } else {
323 throw ConfigException(errstream.str());
324 }
325 } catch (ConfigException&) {
326 throw;
327 } catch (Exception& e) {
328 throw ConfigException(e.what());
329 }
330
331 return sc;
332}
333
334class BuiltinSolverConfigs {
335public:
336 std::unordered_map<std::string, SolverConfig> builtinSolvers;
337};
338
339BuiltinSolverConfigs& builtinSolverConfigs(void) {
340 static BuiltinSolverConfigs c;
341 return c;
342}
343
344void SolverConfigs::addConfig(const MiniZinc::SolverConfig& sc) {
345 int newIdx = static_cast<int>(_solvers.size());
346 _solvers.push_back(sc);
347 std::vector<string> sc_tags = sc.tags();
348 std::string id = sc.id();
349 id = stringToLower(id);
350 sc_tags.push_back(id);
351 std::string name = sc.name();
352 name = stringToLower(name);
353 sc_tags.push_back(name);
354 for (auto t : sc_tags) {
355 TagMap::iterator it = _tags.find(t);
356 if (it == _tags.end()) {
357 _tags.insert(std::make_pair(t, std::vector<int>({newIdx})));
358 } else {
359 it->second.push_back(newIdx);
360 }
361 }
362}
363
364std::vector<std::string> SolverConfigs::solverConfigsPath(void) const { return _solver_path; }
365
366SolverConfigs::SolverConfigs(std::ostream& log) {
367#ifdef _MSC_VER
368 const char* PATHSEP = ";";
369#else
370 const char* PATHSEP = ":";
371#endif
372 for (auto sc : builtinSolverConfigs().builtinSolvers) {
373 addConfig(sc.second);
374 }
375 std::string mzn_solver_path = getEnv("MZN_SOLVER_PATH");
376 while (!mzn_solver_path.empty()) {
377 size_t next_sep = mzn_solver_path.find(PATHSEP);
378 string cur_path = mzn_solver_path.substr(0, next_sep);
379 _solver_path.push_back(cur_path);
380 if (next_sep != string::npos)
381 mzn_solver_path = mzn_solver_path.substr(next_sep + 1, string::npos);
382 else
383 mzn_solver_path = "";
384 }
385 std::string userConfigDir = FileUtils::user_config_dir();
386 if (FileUtils::directory_exists(userConfigDir + "/solvers")) {
387 _solver_path.push_back(userConfigDir + "/solvers");
388 }
389 std::vector<std::string> configFiles(
390 {FileUtils::global_config_file(), FileUtils::user_config_file()});
391
392 for (auto& cf : configFiles) {
393 if (!cf.empty() && FileUtils::file_exists(cf)) {
394 ostringstream errstream;
395 try {
396 Env userconfenv;
397 Model* m = NULL;
398 if (JSONParser::fileIsJSON(cf)) {
399 JSONParser jp(userconfenv.envi());
400 try {
401 m = new Model;
402 GCLock lock;
403 jp.parse(m, cf);
404 } catch (JSONError&) {
405 delete m;
406 m = NULL;
407 }
408 }
409 if (m) {
410 for (unsigned int i = 0; i < m->size(); i++) {
411 if (AssignI* ai = (*m)[i]->dyn_cast<AssignI>()) {
412 if (ai->id() == "mzn_solver_path") {
413 std::vector<std::string> sp = getStringList(ai);
414 for (auto s : sp) {
415 _solver_path.push_back(s);
416 }
417 } else if (ai->id() == "mzn_lib_dir") {
418 _mznlibDir = getString(ai);
419 } else if (ai->id() == "tagDefaults") {
420 std::vector<std::pair<std::string, std::string> > tagDefs = getStringPairList(ai);
421 for (auto& td : tagDefs) {
422 std::string tag = td.first;
423 std::string solver_id = td.second;
424 _tagDefault[tag] = solver_id;
425 }
426 } else if (ai->id() == "solverDefaults") {
427 std::vector<std::vector<std::string> > solverDefs = getDefaultOptionList(ai);
428 for (auto& sd : solverDefs) {
429 assert(sd.size() == 3);
430 std::string solver = sd[0];
431 SolverDefaultMap::iterator it = _solverDefaultOptions.find(solver);
432 if (it == _solverDefaultOptions.end()) {
433 std::vector<std::string> solverOptions({sd[1], sd[2]});
434 _solverDefaultOptions.insert(std::make_pair(solver, solverOptions));
435 } else {
436 std::vector<std::string>& opts = it->second;
437 bool found = false;
438 for (unsigned int i = 0; i < opts.size(); i += 2) {
439 if (opts[i] == sd[1]) {
440 // Override existing option value
441 opts[i + 1] = sd[2];
442 found = true;
443 break;
444 }
445 }
446 if (!found) {
447 // Option didn't exist, add to end of list
448 opts.push_back(sd[1]);
449 opts.push_back(sd[2]);
450 }
451 }
452 }
453 } else {
454 throw ConfigException("invalid configuration item");
455 }
456 } else {
457 throw ConfigException("invalid configuration item");
458 }
459 }
460 } else {
461 std::cerr << errstream.str();
462 throw ConfigException("internal error");
463 }
464 } catch (ConfigException& e) {
465 log << "Warning: invalid configuration file: " << e.msg() << "\n";
466 } catch (Exception& e) {
467 log << "Warning: invalid configuration file: " << e.what() << "\n";
468 }
469 }
470 }
471
472 if (_mznlibDir.empty()) {
473 _mznlibDir = FileUtils::file_path(FileUtils::share_directory());
474 }
475 if (!_mznlibDir.empty()) {
476 _solver_path.push_back(_mznlibDir + "/solvers");
477 }
478#ifndef _MSC_VER
479 if (_mznlibDir != "/usr/local/share/minizinc" &&
480 FileUtils::directory_exists("/usr/local/share")) {
481 _solver_path.push_back("/usr/local/share/minizinc/solvers");
482 }
483 if (_mznlibDir != "/usr/share/minizinc" && FileUtils::directory_exists("/usr/share")) {
484 _solver_path.push_back("/usr/share/minizinc/solvers");
485 }
486#endif
487 for (const string& cur_path : _solver_path) {
488 std::vector<std::string> configFiles = FileUtils::directory_list(cur_path, "msc");
489 for (unsigned int i = 0; i < configFiles.size(); i++) {
490 try {
491 SolverConfig sc = SolverConfig::load(cur_path + "/" + configFiles[i]);
492 addConfig(sc);
493 } catch (ConfigException& e) {
494 log << "Warning: error loading solver configuration from file " << cur_path << "/"
495 << configFiles[i] << "\n";
496 log << "Error was:\n" << e.msg() << "\n";
497 }
498 }
499 }
500
501 // Add default options to loaded solver configurations
502 for (auto& sc : _solvers) {
503 SolverDefaultMap::const_iterator it = _solverDefaultOptions.find(sc.id());
504 if (it != _solverDefaultOptions.end()) {
505 std::vector<std::string> defaultOptions;
506 for (auto& df : it->second) {
507 if (!df.empty()) {
508 defaultOptions.push_back(df);
509 }
510 }
511 sc.defaultFlags(defaultOptions);
512 }
513 }
514}
515
516vector<string> SolverConfigs::solvers() const {
517 // Find default solver, if present
518 std::string def_id;
519 DefaultMap::const_iterator def_it = _tagDefault.find("");
520 if (def_it != _tagDefault.end()) {
521 def_id = def_it->second;
522 }
523 // Create sorted list of solvers
524 vector<string> s;
525 for (auto& sc : _solvers) {
526 if (std::find(sc.tags().begin(), sc.tags().end(), "__internal__") != sc.tags().end()) continue;
527 std::ostringstream oss;
528 oss << sc.name() << " " << sc.version() << " (" << sc.id();
529 if (!def_id.empty() && sc.id() == def_id) {
530 oss << ", default solver";
531 }
532 for (std::string t : sc.tags()) {
533 oss << ", " << t;
534 }
535 oss << ")";
536 s.push_back(oss.str());
537 }
538 SortByLowercase sortByLowercase;
539 std::sort(s.begin(), s.end(), sortByLowercase);
540 return s;
541}
542
543std::string SolverConfigs::solverConfigsJSON() const {
544 GCLock lock;
545 std::ostringstream oss;
546
547 // Find default solver, if present
548 std::string def_id;
549 DefaultMap::const_iterator def_it = _tagDefault.find("");
550 if (def_it != _tagDefault.end()) {
551 def_id = def_it->second;
552 }
553
554 SortByName sortByName(_solvers);
555 std::vector<int> solversIdx(_solvers.size());
556 for (unsigned int i = 0; i < solversIdx.size(); i++) {
557 solversIdx[i] = i;
558 }
559 std::sort(solversIdx.begin(), solversIdx.end(), sortByName);
560
561 bool hadSolver = false;
562 oss << "[\n";
563 for (unsigned int i = 0; i < _solvers.size(); i++) {
564 const SolverConfig& sc = _solvers[solversIdx[i]];
565 if (std::find(sc.tags().begin(), sc.tags().end(), "__internal__") != sc.tags().end()) continue;
566 if (hadSolver) {
567 oss << ",\n";
568 }
569 hadSolver = true;
570 oss << " {\n";
571 oss << " \"extraInfo\": {\n";
572 if (!def_id.empty() && def_id == sc.id()) {
573 oss << " \"isDefault\": true,\n";
574 }
575 if (sc.mznlib_resolved().size()) {
576 oss << " \"mznlib\": \"" << Printer::escapeStringLit(sc.mznlib_resolved()) << "\",\n";
577 }
578 if (sc.executable_resolved().size()) {
579 oss << " \"executable\": \"" << Printer::escapeStringLit(sc.executable_resolved())
580 << "\",\n";
581 }
582 oss << " \"configFile\": \"" << Printer::escapeStringLit(sc.configFile()) << "\"";
583 if (sc.defaultFlags().size()) {
584 oss << ",\n \"defaultFlags\": [";
585 for (unsigned int j = 0; j < sc.defaultFlags().size(); j++) {
586 oss << "\"" << Printer::escapeStringLit(sc.defaultFlags()[j]) << "\"";
587 if (j < sc.defaultFlags().size() - 1) oss << ",";
588 }
589 oss << "]";
590 }
591 oss << "\n";
592 oss << " },\n";
593 oss << " \"id\": \"" << Printer::escapeStringLit(sc.id()) << "\",\n";
594 oss << " \"name\": \"" << Printer::escapeStringLit(sc.name()) << "\",\n";
595 oss << " \"version\": \"" << Printer::escapeStringLit(sc.version()) << "\",\n";
596 if (sc.mznlib().size()) {
597 oss << " \"mznlib\": \"" << Printer::escapeStringLit(sc.mznlib()) << "\",\n";
598 }
599 if (sc.executable().size()) {
600 oss << " \"executable\": \"" << Printer::escapeStringLit(sc.executable()) << "\",\n";
601 }
602 oss << " \"mznlibVersion\": " << sc.mznlibVersion() << ",\n";
603 if (sc.description().size()) {
604 oss << " \"description\": \"" << Printer::escapeStringLit(sc.description()) << "\",\n";
605 }
606 if (sc.contact().size()) {
607 oss << " \"contact\": \"" << Printer::escapeStringLit(sc.contact()) << "\",\n";
608 }
609 if (sc.website().size()) {
610 oss << " \"website\": \"" << Printer::escapeStringLit(sc.website()) << "\",\n";
611 }
612 if (sc.requiredFlags().size()) {
613 oss << " \"requiredFlags\": [";
614 for (unsigned int j = 0; j < sc.requiredFlags().size(); j++) {
615 oss << "\"" << sc.requiredFlags()[j] << "\"";
616 if (j < sc.requiredFlags().size() - 1) oss << ",";
617 }
618 oss << "],\n";
619 }
620 if (sc.stdFlags().size()) {
621 oss << " \"stdFlags\": [";
622 for (unsigned int j = 0; j < sc.stdFlags().size(); j++) {
623 oss << "\"" << sc.stdFlags()[j] << "\"";
624 if (j < sc.stdFlags().size() - 1) oss << ",";
625 }
626 oss << "],\n";
627 }
628 if (sc.extraFlags().size()) {
629 oss << " \"extraFlags\": [";
630 for (unsigned int j = 0; j < sc.extraFlags().size(); j++) {
631 oss << "["
632 << "\"" << sc.extraFlags()[j].flag << "\",\"" << sc.extraFlags()[j].description
633 << "\",\"";
634 oss << sc.extraFlags()[j].flag_type << "\",\"" << sc.extraFlags()[j].default_value << "\"]";
635 if (j < sc.extraFlags().size() - 1) oss << ",";
636 }
637 oss << "],\n";
638 }
639
640 if (sc.tags().size()) {
641 oss << " \"tags\": [";
642 for (unsigned int j = 0; j < sc.tags().size(); j++) {
643 oss << "\"" << Printer::escapeStringLit(sc.tags()[j]) << "\"";
644 if (j < sc.tags().size() - 1) oss << ",";
645 }
646 oss << "],\n";
647 }
648 oss << " \"supportsMzn\": " << (sc.supportsMzn() ? "true" : "false") << ",\n";
649 oss << " \"supportsFzn\": " << (sc.supportsFzn() ? "true" : "false") << ",\n";
650 oss << " \"supportsNL\": " << (sc.supportsNL() ? "true" : "false") << ",\n";
651 oss << " \"needsSolns2Out\": " << (sc.needsSolns2Out() ? "true" : "false") << ",\n";
652 oss << " \"needsMznExecutable\": " << (sc.needsMznExecutable() ? "true" : "false") << ",\n";
653 oss << " \"needsStdlibDir\": " << (sc.needsStdlibDir() ? "true" : "false") << ",\n";
654 oss << " \"needsPathsFile\": " << (sc.needsPathsFile() ? "true" : "false") << ",\n";
655 oss << " \"isGUIApplication\": " << (sc.isGUIApplication() ? "true" : "false") << "\n";
656 oss << " }";
657 }
658 oss << "\n]\n";
659 return oss.str();
660}
661
662namespace {
663std::string getTag(const std::string& t) { return t.substr(0, t.find('@')); }
664std::string getVersion(const std::string& t) {
665 size_t sep = t.find('@');
666 return sep == string::npos ? "" : t.substr(sep + 1);
667}
668} // namespace
669
670const SolverConfig& SolverConfigs::config(const std::string& _s) {
671 std::string s;
672 if (_s.size() > 4 && _s.substr(_s.size() - 4) == ".msc") {
673 SolverConfig sc = SolverConfig::load(_s);
674 addConfig(sc);
675 s = sc.id() + "@" + sc.version();
676 } else {
677 s = _s;
678 }
679 std::remove(s.begin(), s.end(), ' ');
680 s = stringToLower(s);
681 std::vector<std::string> tags;
682 std::istringstream iss(s);
683 std::string next_s;
684 while (std::getline(iss, next_s, ',')) {
685 tags.push_back(next_s);
686 }
687 std::set<std::string> defaultSolvers;
688 std::set<int> selectedSolvers;
689
690 std::string firstTag;
691 if (tags.empty()) {
692 DefaultMap::const_iterator def_it = _tagDefault.find("");
693 if (def_it != _tagDefault.end()) {
694 firstTag = def_it->second;
695 } else {
696 throw ConfigException("no solver selected");
697 }
698 } else {
699 firstTag = tags[0];
700 }
701 TagMap::const_iterator tag_it = _tags.find(getTag(firstTag));
702
703 if (tag_it == _tags.end()) {
704 throw ConfigException("no solver with tag " + getTag(firstTag) + " found");
705 }
706 std::string tv = getVersion(firstTag);
707 for (int sidx : tag_it->second) {
708 if (tv.empty() || tv == _solvers[sidx].version()) selectedSolvers.insert(sidx);
709 }
710 DefaultMap::const_iterator def_it = _tagDefault.find(getTag(firstTag));
711 if (def_it != _tagDefault.end()) {
712 defaultSolvers.insert(def_it->second);
713 }
714 for (unsigned int i = 1; i < tags.size(); i++) {
715 tag_it = _tags.find(getTag(tags[i]));
716 if (tag_it == _tags.end()) {
717 throw ConfigException("no solver with tag " + tags[i] + " found");
718 }
719 tv = getVersion(tags[i]);
720 std::set<int> newSolvers;
721 for (int sidx : tag_it->second) {
722 if (tv.empty() || tv == _solvers[sidx].version()) newSolvers.insert(sidx);
723 }
724 std::set<int> intersection;
725 std::set_intersection(selectedSolvers.begin(), selectedSolvers.end(), newSolvers.begin(),
726 newSolvers.end(), std::inserter(intersection, intersection.begin()));
727 selectedSolvers = intersection;
728 if (selectedSolvers.empty()) {
729 throw ConfigException("no solver with tags " + s + " found");
730 }
731 def_it = _tagDefault.find(getTag(tags[i]));
732 if (def_it != _tagDefault.end()) {
733 defaultSolvers.insert(def_it->second);
734 }
735 }
736 int selectedSolver = -1;
737 if (selectedSolvers.size() > 1) {
738 // use default information for the tags to select a solver
739 for (int sc_idx : selectedSolvers) {
740 if (defaultSolvers.find(_solvers[sc_idx].id()) != defaultSolvers.end()) {
741 selectedSolver = sc_idx;
742 break;
743 }
744 }
745 if (selectedSolver == -1) {
746 selectedSolver = *selectedSolvers.begin();
747 }
748 } else {
749 selectedSolver = *selectedSolvers.begin();
750 }
751 return _solvers[selectedSolver];
752}
753
754void SolverConfigs::registerBuiltinSolver(const SolverConfig& sc) {
755 builtinSolverConfigs().builtinSolvers.insert(make_pair(sc.id(), sc));
756}
757
758} // namespace MiniZinc