this repo has no description
at develop 27 kB view raw
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