diff --git a/src/commandline_options.cpp b/src/commandline_options.cpp index a717174777b..f0c80981640 100644 --- a/src/commandline_options.cpp +++ b/src/commandline_options.cpp @@ -20,6 +20,19 @@ namespace po = boost::program_options; +class two_strings : public boost::tuple {}; + +void validate(boost::any& v, const std::vector& values, + two_strings*, int) +{ + two_strings ret_val; + if (values.size() != 2) + throw po::validation_error(po::validation_error::invalid_option_value); + ret_val.get<0>() = values.at(0); + ret_val.get<1>() = values.at(1); + v = ret_val; +} + commandline_options::commandline_options ( int argc, char** argv ) : bpp(), campaign(), @@ -107,6 +120,9 @@ commandline_options::commandline_options ( int argc, char** argv ) : ("new-syntax", "enables the new campaign syntax parsing.") ("nocache", "disables caching of game data.") ("path", "prints the path to the data directory and exits.") + ("preprocess,p", po::value()->multitoken(), "requires two arguments: . Preprocesses a specified file/folder. The preprocessed file(s) will be written in the specified target directory: a plain cfg file and a processed cfg file.") + ("preprocess-defines", po::value(), "comma separated list of defines to be used by '--preprocess' command. If 'SKIP_CORE' is in the define list the data/core won't be preprocessed. Example: --preprocess-defines=FOO,BAR") + ("preprocess-input-macros", po::value(), "used only by the '--preprocess' command. Specifies source file that contains [preproc_define]s to be included before preprocessing.") ("preprocess-output-macros", po::value()->implicit_value(std::string()), "used only by the '--preprocess' command. Will output all preprocessed macros in the target file . If the file is not specified the output will be file '_MACROS_.cfg' in the target directory of preprocess's command.") ("rng-seed", po::value(), "seeds the random number generator with number . Example: --rng-seed 0") ("validcache", "assumes that the cache is valid. (dangerous)") @@ -135,7 +151,8 @@ commandline_options::commandline_options ( int argc, char** argv ) : all_.add(visible_).add(hidden_); po::variables_map vm; - po::store(po::parse_command_line(argc_,argv_,all_),vm); + const int parsing_style = po::command_line_style::default_style ^ po::command_line_style::allow_guessing; + po::store(po::parse_command_line(argc_,argv_,all_,parsing_style),vm); if (vm.count("ai-config")) multiplayer_ai_config = parse_to_int_string_tuples_(vm["ai-config"].as >()); @@ -175,6 +192,16 @@ commandline_options::commandline_options ( int argc, char** argv ) : nocache = true; if (vm.count("path")) path = true; + if (vm.count("preprocess")) + { + preprocess = true; + preprocess_path = vm["preprocess"].as().get<0>(); + preprocess_target = vm["preprocess"].as().get<1>(); + } + if (vm.count("preprocess-defines")) + preprocess_defines = utils::split(vm["preprocess-defines"].as(), ','); + if (vm.count("preprocess-input-macros")) + preprocess_input_macros = vm["preprocess-input-macros"].as(); if (vm.count("preprocess-output-macros")) preprocess_output_macros = vm["preprocess-output-macros"].as(); if (vm.count("rng-seed")) diff --git a/src/game.cpp b/src/game.cpp index 5d586209399..5c582ec433a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -167,6 +167,7 @@ static int process_command_args(int argc, char** argv, const commandline_options game_config::wesnoth_program_dir = directory_name(program); preprocess_options preproc; + // Options that don't change behaviour based on any others should be checked alphabetically below. if(cmdline_opts.config_dir) { set_preferences_dir(*cmdline_opts.config_dir); } @@ -224,7 +225,31 @@ static int process_command_args(int argc, char** argv, const commandline_options std::cout << game_config::path << "\n"; return 0; } - if (cmdline_opts.preprocess_output_macros) { + if(cmdline_opts.preprocess_input_macros) { + std::string file = *cmdline_opts.preprocess_input_macros; + if (file_exists(file) == false) + { + std::cerr << "please specify an existing file. File "<< file <<" doesn't exist.\n"; + return 1; + } + + std::cerr << SDL_GetTicks() << " Reading cached defines from: " << file << "\n"; + + config cfg; + std::string error_log; + scoped_istream stream = istream_file(file); + read(cfg, *stream); + + int read = 0; + // use static preproc_define::read_pair(config) to make a object + foreach (const config::any_child &value, cfg.all_children_range()) { + const preproc_map::value_type def = preproc_define::read_pair(value.cfg); + preproc.input_macros_[def.first] = def.second; + ++read; + } + std::cerr << SDL_GetTicks() << " Read " << read << " defines.\n"; + } + if(cmdline_opts.preprocess_output_macros) { if (cmdline_opts.preprocess_output_macros->empty()) preproc.output_macros_path_ = "true"; else @@ -238,6 +263,100 @@ static int process_command_args(int argc, char** argv, const commandline_options return 0; } + // Options changing their behaviour dependant on some others should be checked below. + + //TODO should be rewritten so that preprocess_input_macros and preprocess_output_macros are used inside, not before + if (cmdline_opts.preprocess) { + const std::string resourceToProcess(*cmdline_opts.preprocess_path); + const std::string targetDir(*cmdline_opts.preprocess_target); + + Uint32 startTime = SDL_GetTicks(); + // if the users add the SKIP_CORE define we won't preprocess data/core + bool skipCore = false; + bool skipTerrainGFX = false; + // the 'core_defines_map' is the one got from data/core macros + preproc_map defines_map(preproc.input_macros_); + std::string error_log; + + if (cmdline_opts.preprocess_defines) + { + // add the specified defines + foreach (const std::string &define, *cmdline_opts.preprocess_defines) + { + if (define.empty()){ + std::cerr << "empty define supplied\n"; + continue; + } + + LOG_PREPROC<<"adding define: "<< define<<'\n'; + defines_map.insert(std::make_pair(define, + preproc_define(define))); + if (define == "SKIP_CORE") + { + std::cerr << "'SKIP_CORE' defined.\n"; + skipCore = true; + } + else if (define == "NO_TERRAIN_GFX") + { + std::cerr << "'NO_TERRAIN_GFX' defined.\n"; + skipTerrainGFX = true; + } + } + } + + std::cerr << "added " << defines_map.size() << " defines.\n"; + + // preprocess core macros first if we don't skip the core + if (skipCore == false) + { + std::cerr << "preprocessing common macros from 'data/core' ...\n"; + + // process each folder explicitly to gain speed + preprocess_resource(game_config::path + "/data/core/macros",&defines_map); + if (skipTerrainGFX == false) + preprocess_resource(game_config::path + "/data/core/terrain-graphics",&defines_map); + + std::cerr << "acquired " << (defines_map.size() - preproc.input_macros_.size()) + << " 'data/core' defines.\n"; + } + else + std::cerr << "skipped 'data/core'\n"; + + // preprocess resource + std::cerr << "preprocessing specified resource: " + << resourceToProcess << " ...\n"; + preprocess_resource(resourceToProcess, &defines_map, true,true, targetDir); + std::cerr << "acquired " << (defines_map.size() - preproc.input_macros_.size()) + << " total defines.\n"; + + if (preproc.output_macros_path_ != "false") + { + std::string outputPath = targetDir + "/_MACROS_.cfg"; + if (preproc.output_macros_path_ != "true") + outputPath = preproc.output_macros_path_; + + std::cerr << "writing '" << outputPath << "' with " + << defines_map.size() << " defines.\n"; + + scoped_ostream out = ostream_file(outputPath); + if (!out->fail()) + { + config_writer writer(*out,false); + + for(preproc_map::iterator itor = defines_map.begin(); + itor != defines_map.end(); ++itor) + { + (*itor).second.write(writer, (*itor).first); + } + } + else + std::cerr << "couldn't open the file.\n"; + } + + std::cerr << "preprocessing finished. Took "<< SDL_GetTicks() - startTime << " ticks.\n"; + return 0; + } + //parse arguments that shouldn't require a display device int arg; for(arg = 1; arg != argc; ++arg) { @@ -276,146 +395,6 @@ static int process_command_args(int argc, char** argv, const commandline_options } p = q; } - } else if (val == "--preprocess-input-macros") { - if (arg + 1 < argc) - { - ++arg; - std::string file = argv[arg]; - if (file_exists(file) == false) - { - std::cerr << "please specify an existing file. File "<< file <<" doesn't exist.\n"; - return 1; - } - - std::cerr << SDL_GetTicks() << " Reading cached defines from: " << file << "\n"; - - config cfg; - std::string error_log; - scoped_istream stream = istream_file(file); - read(cfg, *stream); - - int read = 0; - // use static preproc_define::read_pair(config) to make a object - foreach (const config::any_child &value, cfg.all_children_range()) { - const preproc_map::value_type def = preproc_define::read_pair(value.cfg); - preproc.input_macros_[def.first] = def.second; - ++read; - } - std::cerr << SDL_GetTicks() << " Read " << read << " defines.\n"; - } - else { - std::cerr << "please specify input macros file.\n"; - return 2; - } - } else if (val.find("--preprocess") == 0 || val.find("-p") == 0){ - if (arg + 2 < argc){ - ++arg; - const std::string resourceToProcess(argv[arg]); - ++arg; - const std::string targetDir(argv[arg]); - - Uint32 startTime = SDL_GetTicks(); - // if the users add the SKIP_CORE define we won't preprocess data/core - bool skipCore = false; - bool skipTerrainGFX = false; - // the 'core_defines_map' is the one got from data/core macros - preproc_map defines_map(preproc.input_macros_); - std::string error_log; - - // add the specified defines - size_t pos=std::string::npos; - if (val.find("--preprocess=") == 0) - pos = val.find("="); - else if (val.find("-p=") == 0) - pos = val.find("="); - - // we have some defines specified - if (pos != std::string::npos) - { - std::string tmp_val = val.substr(pos+1); - while (pos != std::string::npos) - { - size_t tmpPos = val.find(',', pos+1); - tmp_val = val.substr(pos + 1, - tmpPos == std::string::npos ? tmpPos : tmpPos - (pos+1)); - pos = tmpPos; - - if (tmp_val.empty()){ - std::cerr << "empty define supplied\n"; - continue; - } - - LOG_PREPROC<<"adding define: "<< tmp_val<<'\n'; - defines_map.insert(std::make_pair(tmp_val, - preproc_define(tmp_val))); - if (tmp_val == "SKIP_CORE") - { - std::cerr << "'SKIP_CORE' defined.\n"; - skipCore = true; - } - else if (tmp_val == "NO_TERRAIN_GFX") - { - std::cerr << "'NO_TERRAIN_GFX' defined.\n"; - skipTerrainGFX = true; - } - } - std::cerr << "added " << defines_map.size() << " defines.\n"; - } - - // preprocess core macros first if we don't skip the core - if (skipCore == false) - { - std::cerr << "preprocessing common macros from 'data/core' ...\n"; - - // process each folder explicitly to gain speed - preprocess_resource(game_config::path + "/data/core/macros",&defines_map); - if (skipTerrainGFX == false) - preprocess_resource(game_config::path + "/data/core/terrain-graphics",&defines_map); - - std::cerr << "acquired " << (defines_map.size() - preproc.input_macros_.size()) - << " 'data/core' defines.\n"; - } - else - std::cerr << "skipped 'data/core'\n"; - - // preprocess resource - std::cerr << "preprocessing specified resource: " - << resourceToProcess << " ...\n"; - preprocess_resource(resourceToProcess, &defines_map, true,true, targetDir); - std::cerr << "acquired " << (defines_map.size() - preproc.input_macros_.size()) - << " total defines.\n"; - - if (preproc.output_macros_path_ != "false") - { - std::string outputPath = targetDir + "/_MACROS_.cfg"; - if (preproc.output_macros_path_ != "true") - outputPath = preproc.output_macros_path_; - - std::cerr << "writing '" << outputPath << "' with " - << defines_map.size() << " defines.\n"; - - scoped_ostream out = ostream_file(outputPath); - if (!out->fail()) - { - config_writer writer(*out,false); - - for(preproc_map::iterator itor = defines_map.begin(); - itor != defines_map.end(); ++itor) - { - (*itor).second.write(writer, (*itor).first); - } - } - else - std::cerr << "couldn't open the file.\n"; - } - - std::cerr << "preprocessing finished. Took "<< SDL_GetTicks() - startTime << " ticks.\n"; - return 0; - } - else{ - std::cerr << "Please specify a source file/folder and a target folder\n"; - return 2; - } } } diff --git a/src/tests/test_commandline_options.cpp b/src/tests/test_commandline_options.cpp index 6869ed9e6e6..64342cd406c 100644 --- a/src/tests/test_commandline_options.cpp +++ b/src/tests/test_commandline_options.cpp @@ -195,6 +195,9 @@ BOOST_AUTO_TEST_CASE (test_full_options) "--new-widgets", "--nocache", "--path", + "--preprocess", "preppathfoo", "preptargfoo", + "--preprocess-defines=DEFFOO,DEFBAR", + "--preprocess-input-macros=inmfoo", "--preprocess-output-macros=outmfoo", "--rng-seed=1234", "--validcache", @@ -228,9 +231,9 @@ BOOST_AUTO_TEST_CASE (test_full_options) BOOST_CHECK(co.logdomains && *co.logdomains == "filterfoo"); BOOST_CHECK(co.multiplayer); BOOST_CHECK(co.multiplayer_ai_config); - BOOST_CHECK((*co.multiplayer_ai_config).size() == 2); - BOOST_CHECK((*co.multiplayer_ai_config)[0].get<0>() == 1 && (*co.multiplayer_ai_config)[0].get<1>() == "aifoo"); - BOOST_CHECK((*co.multiplayer_ai_config)[1].get<0>() == 2 && (*co.multiplayer_ai_config)[1].get<1>() == "aibar"); + BOOST_CHECK(co.multiplayer_ai_config->size() == 2); + BOOST_CHECK(co.multiplayer_ai_config->at(0).get<0>() == 1 && co.multiplayer_ai_config->at(0).get<1>() == "aifoo"); + BOOST_CHECK(co.multiplayer_ai_config->at(1).get<0>() == 2 && co.multiplayer_ai_config->at(1).get<1>() == "aibar"); BOOST_CHECK(!co.multiplayer_algorithm); BOOST_CHECK(!co.multiplayer_controller); BOOST_CHECK(!co.multiplayer_era); @@ -248,12 +251,12 @@ BOOST_AUTO_TEST_CASE (test_full_options) BOOST_CHECK(co.new_syntax); BOOST_CHECK(co.new_widgets); BOOST_CHECK(co.path); - BOOST_CHECK(!co.preprocess); - BOOST_CHECK(!co.preprocess_defines); - BOOST_CHECK(!co.preprocess_input_macros); + BOOST_CHECK(co.preprocess && co.preprocess_path && co.preprocess_target); + BOOST_CHECK(*co.preprocess_path == "preppathfoo" && *co.preprocess_target == "preptargfoo"); + BOOST_CHECK(co.preprocess_defines && co.preprocess_defines->size() == 2); + BOOST_CHECK(co.preprocess_defines->at(0) == "DEFFOO" && co.preprocess_defines->at(1) == "DEFBAR"); + BOOST_CHECK(co.preprocess_input_macros && *co.preprocess_input_macros == "inmfoo"); BOOST_CHECK(co.preprocess_output_macros && *co.preprocess_output_macros == "outmfoo"); - BOOST_CHECK(!co.preprocess_path); - BOOST_CHECK(!co.preprocess_target); BOOST_CHECK(!co.proxy); BOOST_CHECK(!co.proxy_address); BOOST_CHECK(!co.proxy_password);