From 34908e1e8e1aef10605c11a7644f3e2983042df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20G=C3=BCndling?= Date: Wed, 11 Dec 2024 20:46:35 +0100 Subject: [PATCH] cli changes (#685) * cli changes * wip * update nigiri --- .pkg | 2 +- .pkg.lock | 4 +- README.md | 12 +++- exe/flags.h | 38 ++++++++++++ exe/main.cc | 162 +++++++++++++++++++++++----------------------------- 5 files changed, 122 insertions(+), 96 deletions(-) create mode 100644 exe/flags.h diff --git a/.pkg b/.pkg index 28cb2557b..182c8b65c 100644 --- a/.pkg +++ b/.pkg @@ -1,7 +1,7 @@ [nigiri] url=git@github.com:motis-project/nigiri.git branch=master - commit=2cf648ac22315fb6578be3f5d689f4d94e81b457 + commit=f647fb695d730f78f925eaed312ca4d849cb1da9 [cista] url=git@github.com:felixguendling/cista.git branch=master diff --git a/.pkg.lock b/.pkg.lock index b0ba6172d..c6c514f33 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,4 +1,4 @@ -11919117500806763401 +12451556664381891228 cista 6362f3ad8c3133a0abf64e5d8c9ea3e21f531ee8 zlib-ng 68ab3e2d80253ec5dc3c83691d9ff70477b32cd3 boost 930f38eb0365ceb7853273e03da4d9e7787abfb9 @@ -27,7 +27,7 @@ opentelemetry-cpp 60770dc9dc63e3543fc87d605b2e88fd53d7a414 pugixml 60175e80e2f5e97e027ac78f7e14c5acc009ce50 unordered_dense b33b037377ca966bbdd9cccc3417e46e88f83bfb wyhash 1e012b57fc2227a9e583a57e2eacb3da99816d99 -nigiri 2cf648ac22315fb6578be3f5d689f4d94e81b457 +nigiri f647fb695d730f78f925eaed312ca4d849cb1da9 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 expat 636c9861e8e7c119f3626d1e6c260603ab624516 libosmium 6e6d6b3081cc8bdf25dda89730e25c36eb995516 diff --git a/README.md b/README.md index 3adfc35b9..217ff156e 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,9 @@ Features can be turned on and off as needed. - Download one or more GTFS datasets and place them in the folder ```bash -./motis my.osm.pbf my.gtfs.zip +./motis config my.osm.pbf gtfs.zip # generates a minimal config.yml +./motis import # preprocesses data +./motis server # starts a HTTP server on port 8080 ``` This will preprocess the input files and create a `data` folder. @@ -81,7 +83,9 @@ wget https://github.com/motis-project/motis/releases/latest/download/motis-${TAR tar xf motis-${TARGET}.tar.bz2 wget https://github.com/motis-project/test-data/raw/aachen/aachen.osm.pbf wget https://opendata.avv.de/current_GTFS/AVV_GTFS_Masten_mit_SPNV.zip -./motis aachen.osm.pbf AVV_GTFS_Masten_mit_SPNV.zip +./motis config aachen.osm.pbf AVV_GTFS_Masten_mit_SPNV.zip +./motis import +./motis server ``` **Windows** @@ -90,7 +94,9 @@ wget https://opendata.avv.de/current_GTFS/AVV_GTFS_Masten_mit_SPNV.zip Invoke-WebRequest https://github.com/motis-project/motis/releases/latest/download/motis-windows.zip -OutFile motis-windows.zip Expand-Archive motis-windows.zip Invoke-WebRequest https://github.com/motis-project/test-data/archive/refs/heads/aachen.zip -OutFile aachen.zip -./motis aachen.osm.pbf AVV_GTFS_Masten_mit_SPNV.zip +./motis config aachen.osm.pbf AVV_GTFS_Masten_mit_SPNV.zip +./motis import +./motis server ``` # Documentation diff --git a/exe/flags.h b/exe/flags.h new file mode 100644 index 000000000..890ecd4d9 --- /dev/null +++ b/exe/flags.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "boost/program_options.hpp" + +namespace motis { + +inline void add_data_path_opt(boost::program_options::options_description& desc, + std::filesystem::path& p) { + desc.add_options()( + "data,d", boost::program_options::value(&p)->default_value(p), + "The data path contains all preprocessed data as well as a `config.yml`. " + "It will be created by the `motis import` command. After the import has " + "finished, `motis server` only needs the `data` folder and can run " + "without the input files (such as OpenStreetMap file, GTFS datasets, " + "tiles-profiles, etc.)"); +} + +inline void add_config_path_opt( + boost::program_options::options_description& desc, + std::filesystem::path& p) { + desc.add_options()( + "config,c", boost::program_options::value(&p)->default_value(p), + "Configuration YAML file. Legacy INI files are still supported but this " + "support will be dropped in the future."); +} + +inline boost::program_options::variables_map parse_opt( + int ac, char** av, boost::program_options::options_description& desc) { + namespace po = boost::program_options; + auto vm = po::variables_map{}; + po::store(po::command_line_parser(ac, av).options(desc).run(), vm); + po::notify(vm); + return vm; +} + +} // namespace motis \ No newline at end of file diff --git a/exe/main.cc b/exe/main.cc index c3683a66d..60dcfe393 100644 --- a/exe/main.cc +++ b/exe/main.cc @@ -12,6 +12,8 @@ #include "motis/import.h" #include "motis/server.h" +#include "./flags.h" + #if !defined(MOTIS_VERSION) #define MOTIS_VERSION "unknown" #endif @@ -23,86 +25,68 @@ namespace fs = std::filesystem; using namespace motis; int main(int ac, char** av) { - auto data_path = fs::path{"data"}; - auto config_path = fs::path{"config.yml"}; - - auto desc = po::options_description{"Global options"}; - desc.add_options() // - ("version", "Prints the MOTIS version") // - ("help", "Prints this help message") // - ("data,d", po::value(&data_path)->default_value(data_path), - "The data path contains all preprocessed data as well as a `config.yml` " - "and is required by `motis server`. It will be created by the `motis " - "import` command. After the import has finished, `motis server` only " - "needs the `data` folder and can run without the input files (such as " - "OpenStreetMap file, GTFS datasets, tiles-profiles, etc.)") // - ("config,c", po::value(&config_path)->default_value(config_path), - "Configuration YAML file. Legacy INI files are still supported but this " - "support will be dropped in the future.") // - ("command", po::value(), - "Command to execute:\n" - " - \"import\": preprocesses the input data\n" - " and creates the `data` folder.\n" - " - \"server\": serves static files\n" - " and all API endpoints such as\n" - " routing, geocoding, tiles, etc.") // - ("paths", po::value>(), - "List of paths to import for the simple mode. File type will be " - "determined based on extension:\n" - " - \".osm.pbf\" will be used as\n" - " OpenStreetMap file.\n" - " This enables street routing,\n" - " geocoding and map tiles\n" - " - the rest will be interpreted as\n" - " static timetables.\n" - " This enables transit routing"); - - auto const help = [&]() { - std::cout << "MOTIS " << MOTIS_VERSION << "\n\n" - << "Usage:\n" - " - simple: motis [PATHS...]\n" - " - import: motis import [-c config.yml] [-d data_dir]\n" - " - server: motis server [-d data_dir]\n\n" - << desc << "\n"; - }; - - enum mode { kImport, kServer, kSimple } mode = kSimple; - if (ac > 1) { - auto const cmd = std::string_view{av[1]}; - switch (cista::hash(cmd)) { - case cista::hash("import"): - mode = kImport; - --ac; - ++av; - break; - case cista::hash("server"): - mode = kServer; - --ac; - ++av; - break; - } - } else { - help(); - return 1; - } - - auto pos = po::positional_options_description{}.add("paths", -1); - auto vm = po::variables_map{}; - po::store(po::command_line_parser(ac, av).options(desc).positional(pos).run(), - vm); - po::notify(vm); - - if (vm.count("version")) { - std::cout << MOTIS_VERSION << "\n"; + if (ac > 1 && av[1] == "--help"sv) { + fmt::println( + "MOTIS {}\n\n" + "Usage:\n" + " --help print this help message\n" + " --version print program version\n\n" + "Commands:\n" + " config generate a config file from a list of input files\n" + " import prepare input data, creates the data directory\n" + " server starts a web server serving the API\n", + MOTIS_VERSION); return 0; - } else if (vm.count("help")) { - help(); + } else if (ac <= 1 || (ac >= 2 && av[1] == "--version"sv)) { + fmt::println("{}", MOTIS_VERSION); return 0; } - switch (mode) { - case kServer: + // Skip program argument, quit if no command. + --ac; + ++av; + + // Execute command. + auto const cmd = std::string_view{av[0]}; + --ac; + ++av; + switch (cista::hash(cmd)) { + case cista::hash("config"): { + auto paths = std::vector{}; + for (auto i = 0; i != ac; ++i) { + paths.push_back(std::string{av[i]}); + } + if (paths.empty() || paths.front() == "--help") { + fmt::println( + "usage: motis config [PATHS...]\n\n" + "Generates a config.yml file in the current working " + "directory.\n\n" + "File type will be determined based on extension:\n" + " - \".osm.pbf\" will be used as OpenStreetMap file.\n" + " This enables street routing, geocoding and map tiles\n" + " - the rest will be interpreted as static timetables.\n" + " This enables transit routing." + "\n\n" + "Example: motis config germany-latest.osm.pbf " + "germany.gtfs.zip\n"); + return paths.front() == "--help" ? 0 : 1; + } + std::ofstream{"config.yml"} << config::read_simple(paths) << "\n"; + return 0; + } + + case cista::hash("server"): try { + auto data_path = fs::path{"data"}; + + auto desc = po::options_description{"Server Options"}; + add_data_path_opt(desc, data_path); + auto vm = parse_opt(ac, av, desc); + if (vm.count("help")) { + std::cout << desc << "\n"; + return 0; + } + auto const c = config::read(data_path / "config.yml"); return server(data{data_path, c}, c); } catch (std::exception const& e) { @@ -110,9 +94,21 @@ int main(int ac, char** av) { return 1; } - case kImport: { + case cista::hash("import"): { auto c = config{}; try { + auto data_path = fs::path{"data"}; + auto config_path = fs::path{"config.yml"}; + + auto desc = po::options_description{"Import Options"}; + add_data_path_opt(desc, data_path); + add_config_path_opt(desc, config_path); + auto vm = parse_opt(ac, av, desc); + if (vm.count("help")) { + std::cout << desc << "\n"; + return 0; + } + c = config_path.extension() == ".ini" ? config::read_legacy(config_path) : config::read(config_path); auto const bars = utl::global_progress_bars{false}; @@ -124,20 +120,6 @@ int main(int ac, char** av) { return 1; } } - - case kSimple: - try { - auto const bars = utl::global_progress_bars{false}; - auto args = vm.count("paths") - ? vm.at("paths").as>() - : std::vector{}; - - auto const c = config::read_simple(args); - server(import(c, data_path), c); - } catch (std::exception const& e) { - std::cerr << "error: " << e.what() << "\n"; - } - return 0; } google::protobuf::ShutdownProtobufLibrary();