Advertisement
Guest User

OptionsParser.cpp

a guest
May 29th, 2019
121
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.77 KB | None | 0 0
  1. #include "OptionsParser.hpp"
  2. #include "Main.hpp"
  3.  
  4. #include "eyaml/eyaml.h"
  5. #include "OS_Switchboard.h"
  6.  
  7. #include <boost/filesystem.hpp>
  8. #include <boost/foreach.hpp>
  9. #include <boost/iostreams/device/mapped_file.hpp>
  10. #include <boost/algorithm/string.hpp>
  11. #include <boost/exception/diagnostic_information.hpp>
  12.  
  13. #include <iostream>
  14.  
  15. namespace fs = boost::filesystem;
  16.  
  17. inline std::string word_wrap(std::string text, unsigned per_line)
  18. {
  19. unsigned line_begin = 0;
  20.  
  21. while (line_begin < text.size())
  22. {
  23. const unsigned ideal_end = line_begin + per_line ;
  24. unsigned line_end = ideal_end <= text.size() ? ideal_end : text.size()-1;
  25.  
  26. if (line_end == text.size() - 1)
  27. ++line_end;
  28. else if (std::isspace(text[line_end]))
  29. {
  30. text[line_end] = '\n';
  31. ++line_end;
  32. }
  33. else
  34. {
  35. unsigned end = line_end;
  36. while ( end > line_begin && !std::isspace(text[end]))
  37. --end;
  38.  
  39. if (end != line_begin)
  40. {
  41. line_end = end;
  42. text[line_end++] = '\n';
  43. }
  44. else
  45. text.insert(line_end++, 1, '\n');
  46. }
  47.  
  48. line_begin = line_end;
  49. }
  50.  
  51. size_t pos = text.find("\n");
  52. while (pos != std::string::npos)
  53. {
  54. text.insert(pos + 1, 2, '\t');
  55. pos = text.find("\n", pos + 1);
  56. }
  57.  
  58. return text;
  59. }
  60.  
  61. inline list_t splitString(const std::string &str)
  62. {
  63. list_t list;
  64.  
  65. std::stringstream ss(str);
  66. while( ss.good() )
  67. {
  68. std::string substr;
  69. getline( ss, substr, ',' );
  70. list.push_back( substr );
  71. }
  72.  
  73. return list;
  74. }
  75.  
  76. OptionsParser::OptionsParser() : _desc("Options")
  77. {
  78. // Platform Defaults
  79. std::string def_platform, def_workdir, def_compiler;
  80. #if CURRENT_PLATFORM_ID == OS_WINDOWS
  81. def_platform = "Win32";
  82. def_workdir = "%LOCALAPPDATA%/ENIGMA/";
  83. def_compiler = "gcc";
  84. #elif CURRENT_PLATFORM_ID == OS_MACOSX
  85. def_platform = "SDL";
  86. def_workdir = "/tmp/ENIGMA/";
  87. def_compiler = "clang";
  88. #else
  89. def_platform = "xlib";
  90. def_workdir = "/tmp/ENIGMA/";
  91. def_compiler = "gcc";
  92. #endif
  93.  
  94. _desc.add_options()
  95. ("help,h", "Print help messages")
  96. ("list,l", "List available types, globals & functions")
  97. ("info,i", opt::value<std::string>(), "Provides a listing of Platforms, APIs and Extensions")
  98. ("input", opt::value<std::string>()->default_value(""), "Input game file; currently, only test harness single-object games (*.sog) are supported. The --input string is optional.")
  99. ("quiet,q", opt::bool_switch()->default_value(false), "Suppresses output to std::out and std::err streams.")
  100. #ifdef CLI_ENABLE_SERVER
  101. ("server,s", opt::bool_switch()->default_value(false), "Starts the CLI in server mode (ignores input file).")
  102. ("ip", opt::value<std::string>()->default_value("localhost"), "The ip address of the server when running in server mode.")
  103. ("port", opt::value<int>()->default_value(37818), "The port number to bind when in server mode.")
  104. #endif
  105. ("output,o", opt::value<std::string>(), "Output executable file")
  106. ("platform,p", opt::value<std::string>()->default_value(def_platform), "Target Platform (XLib, Win32, SDL)")
  107. ("workdir,d", opt::value<std::string>()->default_value(def_workdir), "Working Directory")
  108. ("codegen,k", opt::value<std::string>()->default_value(def_workdir), "Codegen Directory")
  109. ("mode,m", opt::value<std::string>()->default_value("Debug"), "Game Mode (Run, Release, Debug, Design)")
  110. ("graphics,g", opt::value<std::string>()->default_value("OpenGL1"), "Graphics System (OpenGL1, OpenGL3, DirectX)")
  111. ("audio,a", opt::value<std::string>()->default_value("None"), "Audio System (OpenAL, DirectSound, SFML, None)")
  112. ("widgets,w", opt::value<std::string>()->default_value("None"), "Widget System (Win32, GTK, None)")
  113. ("network,n", opt::value<std::string>()->default_value("None"), "Networking System (Async, Berkeley, DirectPlay)")
  114. ("collision,c", opt::value<std::string>()->default_value("None"), "Collision System")
  115. ("extensions,e", opt::value<std::string>()->default_value("None"), "Extensions (Paths, Timelines, Particles)")
  116. ("compiler,x", opt::value<std::string>()->default_value(def_compiler), "Compiler.ey Descriptor")
  117. ("run,r", opt::bool_switch()->default_value(false), "Automatically run the game after it is built")
  118. ;
  119.  
  120. _positional.add("input", 1);
  121.  
  122. _handler["info"] = std::bind(&OptionsParser::printInfo, this, std::placeholders::_1);
  123. _handler["mode"] = std::bind(&OptionsParser::mode, this, std::placeholders::_1);
  124. _handler["graphics"] = std::bind(&OptionsParser::graphics, this, std::placeholders::_1);
  125. _handler["audio"] = std::bind(&OptionsParser::audio, this, std::placeholders::_1);
  126. _handler["collision"] = std::bind(&OptionsParser::collision, this, std::placeholders::_1);
  127. _handler["widgets"] = std::bind(&OptionsParser::widgets, this, std::placeholders::_1);
  128. _handler["network"] = std::bind(&OptionsParser::network, this, std::placeholders::_1);
  129. _handler["platform"] = std::bind(&OptionsParser::platform, this, std::placeholders::_1);
  130. _handler["extensions"] = std::bind(&OptionsParser::extensions, this, std::placeholders::_1);
  131. _handler["compiler"] = std::bind(&OptionsParser::compiler, this, std::placeholders::_1);
  132. }
  133.  
  134. opt::variable_value OptionsParser::GetOption(std::string option)
  135. {
  136. return _rawArgs[option];
  137. }
  138.  
  139. bool OptionsParser::HasOption(std::string option)
  140. {
  141. return _rawArgs.count(option) > 0;
  142. }
  143.  
  144. int OptionsParser::ReadArgs(int argc, char* argv[])
  145. {
  146. _readArgsFail = false;
  147.  
  148. try
  149. {
  150. opt::store(opt::command_line_parser(argc, argv)
  151. .options(_desc).positional(_positional).run(),
  152. _rawArgs);
  153.  
  154. if (!_rawArgs.count("info"))
  155. opt::notify(_rawArgs);
  156. #ifndef CLI_DISABLE_SERVER
  157. if (!_rawArgs.count("help") && !_rawArgs.count("list") && !_rawArgs.count("info") && !_rawArgs.count("server") && !_rawArgs.count("output")) {
  158. throw std::logic_error("Option 'help', 'list', 'info', 'server', or option 'output' is required.");
  159. }
  160. #else
  161. if (!_rawArgs.count("help") && !_rawArgs.count("list") && !_rawArgs.count("info") && !_rawArgs.count("output")) {
  162. throw std::logic_error("Option 'help', 'list', 'info', or option 'output' is required.");
  163. }
  164. #endif
  165. }
  166. catch(std::exception& e)
  167. {
  168. if (!_rawArgs.count("help"))
  169. errorStream << "OPTIONS_ERROR: " << e.what() << std::endl << std::endl;
  170.  
  171. _readArgsFail = true;
  172.  
  173. return OPTIONS_ERROR;
  174. }
  175.  
  176. find_ey("ENIGMAsystem/SHELL/");
  177.  
  178. // Platform Compilers
  179. #if CURRENT_PLATFORM_ID == OS_WINDOWS
  180. find_ey("Compilers/Windows");
  181. #elif CURRENT_PLATFORM_ID == OS_MACOSX
  182. find_ey("Compilers/MacOSX");
  183. #else
  184. find_ey("Compilers/Linux");
  185. #endif
  186.  
  187. return OPTIONS_SUCCESS;
  188. }
  189.  
  190. int OptionsParser::HandleArgs()
  191. {
  192. // Exit early on list
  193. if (_rawArgs.count("list"))
  194. return OPTIONS_SUCCESS;
  195.  
  196. // Exit early on help
  197. if (_readArgsFail || _rawArgs.count("help"))
  198. {
  199. printHelp();
  200. return OPTIONS_HELP;
  201. }
  202.  
  203. for (auto &&handle : _handler)
  204. {
  205. if (_rawArgs.count(handle.first)) {
  206. int result = handle.second(_rawArgs[handle.first].as<std::string>());
  207. if (result == OPTIONS_ERROR || result == OPTIONS_HELP)
  208. return result;
  209. }
  210. }
  211.  
  212. return OPTIONS_SUCCESS;
  213. }
  214.  
  215. std::string OptionsParser::APIyaml()
  216. {
  217. std::string yaml;
  218. yaml += "%e-yaml\n";
  219. yaml += "---\n";
  220. yaml += "treat-literals-as: 0\n";
  221. yaml += "sample-lots-of-radios: 0\n";
  222. yaml += "inherit-equivalence-from: 0\n";
  223. yaml += "eobjs-directory: " + boost::filesystem::absolute(_rawArgs["workdir"].as<std::string>()).string() + "\n";
  224. yaml += "codegen-directory: " + boost::filesystem::absolute(_rawArgs["codegen"].as<std::string>()).string() + "\n";
  225. yaml += "sample-checkbox: on\n";
  226. yaml += "sample-edit: DEADBEEF\n";
  227. yaml += "sample-combobox: 0\n";
  228. yaml += "inherit-strings-from: 0\n";
  229. yaml += "inherit-negatives-as: 0\n";
  230. yaml += "inherit-escapes-from: 0\n";
  231. yaml += "inherit-objects: true \n";
  232. yaml += "inherit-increment-from: 0\n";
  233. yaml += " \n";
  234. yaml += "target-audio: " + _rawArgs["audio"].as<std::string>() + "\n";
  235. yaml += "target-windowing: " + _rawArgs["platform"].as<std::string>() + "\n";
  236. yaml += "target-compiler: " + _rawArgs["compiler"].as<std::string>() + "\n";
  237. yaml += "target-graphics: " + _rawArgs["graphics"].as<std::string>() + "\n";
  238. yaml += "target-widget: " + _rawArgs["widgets"].as<std::string>() + "\n";
  239. yaml += "target-collision: " + _rawArgs["collision"].as<std::string>() + "\n";
  240. yaml += "target-networking: " + _rawArgs["network"].as<std::string>() + "\n";
  241. yaml += "extensions: " + _extensions + "\n";
  242.  
  243. return yaml;
  244. }
  245.  
  246. int OptionsParser::find_ey(const char* dir)
  247. {
  248. boost::filesystem::path targetDir(dir);
  249. boost::filesystem::recursive_directory_iterator iter(targetDir), eod;
  250.  
  251. BOOST_FOREACH(boost::filesystem::path const& i, std::make_pair(iter, eod))
  252. {
  253. if (is_regular_file(i))
  254. {
  255. auto ey = i.string().find(".ey");
  256.  
  257. list_t apiType(8);
  258. apiType[0] = "Platform";
  259. apiType[1] = "Graphics";
  260. apiType[2] = "Audio";
  261. apiType[3] = "Collision";
  262. apiType[4] = "Network";
  263. apiType[5] = "Widget";
  264. apiType[6] = "Extensions";
  265. apiType[7] = "Compilers";
  266.  
  267. if (ey != std::string::npos)
  268. {
  269. for (auto &&t : apiType)
  270. {
  271. auto ey = i.string().find(t);
  272. if (ey != std::string::npos)
  273. _api[t].push_back(i.string());
  274. }
  275. }
  276. }
  277. }
  278.  
  279. return OPTIONS_SUCCESS;
  280. }
  281.  
  282. int OptionsParser::printInfo(const std::string &api)
  283. {
  284. auto it = _api.find(api);
  285. if (it != std::end(_api))
  286. {
  287. outputStream << std::endl << "Target " << api << ":" << std::endl;
  288.  
  289. for (auto&& p : _api[api])
  290. {
  291. std::ifstream ifabout(p, std::ios_base::in);
  292.  
  293. if (ifabout.is_open())
  294. {
  295. ey_data about = parse_eyaml(ifabout, p);
  296.  
  297. std::string name = about.get("name");
  298. std::string desc = about.get("description");
  299. std::string id = about.get("identifier");
  300. std::string target = about.get("target-platform");
  301.  
  302. // Why is this different in extensions?
  303. if (id.empty())
  304. id = about.get("id");
  305.  
  306. if (!target.empty() && !name.empty())
  307. {
  308. boost::filesystem::path ey(p);
  309. outputStream << '\t' << name << " (" << ey.stem().string() << "):" << std::endl;
  310. outputStream << "\t\t Target: " << target << std::endl << std::endl;
  311. }
  312. else if (!name.empty() && !desc.empty() && !id.empty())
  313. {
  314. outputStream << '\t' << name << " (" << id << "):" << std::endl;
  315. outputStream << "\t\t" << word_wrap(desc, 80) << std::endl << std::endl;
  316. }
  317. }
  318. }
  319. }
  320. else
  321. {
  322. errorStream << "OPTIONS_ERROR: Unknown System: \"" << api << '"'
  323. << std::endl << std::endl << "Avaliable Systems: " << std::endl;
  324.  
  325. for (auto &&a : _api)
  326. errorStream << a.first << std::endl;
  327.  
  328. return OPTIONS_ERROR;
  329. }
  330.  
  331. return OPTIONS_HELP;
  332. }
  333.  
  334. void OptionsParser::printHelp()
  335. {
  336. outputStream << "Enigma Command Line Compiler" << std::endl
  337. << _desc << std::endl;
  338. }
  339.  
  340. int OptionsParser::help(const std::string &str)
  341. {
  342. // More Info on Systems (Graphics Audio Etc)
  343. auto it = _api.find(str);
  344. if (it != std::end(_api))
  345. {
  346. printInfo(str);
  347. }
  348. else
  349. printHelp();
  350.  
  351. return OPTIONS_HELP;
  352. }
  353.  
  354. int OptionsParser::parse(const std::string &str)
  355. {
  356. try
  357. {
  358. fs::path file(str);
  359. if (fs::is_directory(file))
  360. {
  361. errorStream << "OPTIONS_ERROR: " << str << " Is a Directory!" << std::endl;
  362. return OPTIONS_ERROR;
  363. }
  364.  
  365. bool exists = fs::exists(file);
  366. if (exists)
  367. {
  368. outputStream << "Parsing: " << str <<std::endl;
  369. // call parser
  370. return OPTIONS_SUCCESS;
  371. }
  372. else
  373. {
  374. errorStream << "OPTIONS_ERROR: No Such File " << str << std::endl;
  375. }
  376.  
  377. return OPTIONS_ERROR;
  378. }
  379. catch (const fs::filesystem_error& ex)
  380. {
  381. errorStream << "OPTIONS_ERROR: " << ex.what() << std::endl;
  382. return OPTIONS_ERROR;
  383. }
  384. }
  385.  
  386. int OptionsParser::mode(const std::string &str)
  387. {
  388. if (str == "Run" || str == "Debug" || str == "Compile" || str == "Design" || str == "Rebuild")
  389. {
  390. return OPTIONS_SUCCESS;
  391. }
  392. else
  393. errorStream << "OPTIONS_ERROR: invalid mode: " << str << std::endl
  394. << "Available Modes: " << std::endl
  395. << "Run" << std::endl
  396. << "Debug" << std::endl
  397. << "Compile" << std::endl
  398. << "Design" << std::endl
  399. << "Rebuild" << std::endl;
  400.  
  401. return OPTIONS_ERROR;
  402. }
  403.  
  404. int OptionsParser::searchCompilers(const std::string &target)
  405. {
  406. auto it = std::find_if(std::begin(_api["Compilers"]), std::end(_api["Compilers"]), [target](std::string &a)
  407. {
  408. boost::filesystem::path ey(a);
  409. return ey.stem().string() == target;
  410. });
  411.  
  412. if (it != std::end(_api["Compilers"]))
  413. {
  414. return OPTIONS_SUCCESS;
  415. }
  416. else
  417. errorStream << "OPTIONS_ERROR: Unknown Compiler Target: " << target << std::endl
  418. << "Run \"emake --info Compiler\" For a List of Available Targets" << std::endl;
  419.  
  420. return OPTIONS_ERROR;
  421.  
  422. }
  423.  
  424. int OptionsParser::searchAPI(const std::string &api, const std::string &target)
  425. {
  426. auto it = std::find_if(std::begin(_api[api]), std::end(_api[api]), [target](std::string &a)
  427. {
  428. std::ifstream ifabout(a, std::ios_base::in);
  429. ey_data about = parse_eyaml(ifabout, a);
  430. std::string id = about.get("identifier");
  431. // Why is this different in extensions?
  432. if (id.empty())
  433. id = about.get("id");
  434. return (id == target);
  435. });
  436.  
  437. if (it != std::end(_api[api]))
  438. {
  439. //set api
  440. std::string lower = api;
  441. boost::algorithm::to_lower(lower);
  442.  
  443. if (lower == "extensions")
  444. {
  445. _extensions += "Universal_System/Extensions/" + target + ",";
  446. }
  447.  
  448. return OPTIONS_SUCCESS;
  449. }
  450. else
  451. errorStream << "OPTIONS_ERROR: Unknown " << api << " Target: " << target << std::endl
  452. << "Run \"emake --info " << api << "\" For a List of Available Targets" << std::endl;
  453.  
  454. return OPTIONS_ERROR;
  455. }
  456.  
  457. int OptionsParser::graphics(const std::string &str)
  458. {
  459. return searchAPI("Graphics", str);
  460. }
  461.  
  462. int OptionsParser::audio(const std::string &str)
  463. {
  464. return searchAPI("Audio", str);
  465. }
  466.  
  467. int OptionsParser::collision(const std::string &str)
  468. {
  469. return searchAPI("Collision", str);
  470. }
  471.  
  472. int OptionsParser::widgets(const std::string &str)
  473. {
  474. return searchAPI("Widget", str);
  475. }
  476.  
  477. int OptionsParser::network(const std::string &str)
  478. {
  479. return searchAPI("Network", str);
  480. }
  481.  
  482. int OptionsParser::platform(const std::string &str)
  483. {
  484. return searchAPI("Platform", str);
  485. }
  486.  
  487. int OptionsParser::extensions(const std::string &str)
  488. {
  489. if (str == "None")
  490. {
  491. _extensions = "";
  492. }
  493. else
  494. {
  495. list_t ext = splitString(str);
  496.  
  497. _extensions = "";
  498. for (auto &&e : ext)
  499. {
  500. int valid = searchAPI("Extensions", e);
  501. if (valid == OPTIONS_ERROR)
  502. return OPTIONS_ERROR;
  503. }
  504. }
  505.  
  506. return OPTIONS_SUCCESS;
  507. }
  508.  
  509. int OptionsParser::compiler(const std::string &str)
  510. {
  511. return searchCompilers(str);
  512. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement