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