10#include <boost/program_options.hpp>
11#include <fmt/format.h>
12#include <log4cplus/configurator.h>
13#include <log4cplus/fileappender.h>
14#include <log4cplus/logger.h>
15#include <log4cplus/loggingmacros.h>
16#include <nlohmann/json.hpp>
62static std::pair<std::string, std::string> CustomParser(std::string
const& element) {
64 return std::make_pair(
"specification-file",
"-");
69static auto Resolve(std::filesystem::path
const& path, std::filesystem::path
const& root)
70 -> std::filesystem::path {
71 if (path.is_relative()) {
78static void LogArgs(
int argc,
char** argv) {
80 std::for_each(argv, argv + argc, [&](
char* arg) {
88 LOG4CPLUS_INFO(
"daq.dpmmerge",
89 "Started in " << std::filesystem::current_path() <<
" as: " << ss.str());
92static void InitFileAppender(std::filesystem::path
const& path) {
94 using log4cplus::FileAppender;
95 using log4cplus::PatternLayout;
96 auto appender = log4cplus::SharedAppenderPtr(
97 new FileAppender(path.string(), std::ios_base::out | std::ios_base::app));
98 appender->setLayout(std::make_unique<PatternLayout>(
"%D{%Y-%m-%dT%H:%M:%S.%q+0000} %-5p %m%n"));
99 log4cplus::Logger::getRoot().addAppender(appender);
102int main(
int argc,
char** argv) {
103 log4cplus::initialize();
105 log4cplus::BasicConfigurator config(log4cplus::Logger::getDefaultHierarchy(),
true);
108 namespace po = boost::program_options;
110 auto logger = log4cplus::Logger::getInstance(
"daq.dpmmerge");
112 const std::string synopsis(
"daqDpmMerge [options] <specification-file>");
114 po::positional_options_description positional;
115 positional.add(
"specification-file", 1);
117 std::string spec_file;
118 po::options_description options(synopsis);
120 options.add_options()
122 "produce help message (also use this option for each command to get relevant help)")
126 po::value<std::string>(),
127 "specifies root directory from which relative source file paths in specification-file will"
128 " be resolved as well as the default output directory. If not specified it will use the "
129 "root of the specification-file file or if "
130 "specification-file is not a regular file it will use current working directory.")
132 po::value<std::string>(),
133 "FITS output file name, e.g. `-o myfits.fits`. By default the output name will be "
134 "derived using the specification-file `fileId` property: `<fileId>.fits`. "
135 "Relative paths are relative the root directory.")
137 po::value<std::string>(),
138 "specifies optional FITS source resolver which resolves location of input FITS files "
139 "to their location on this host. If specification references FITS files this must be "
142 "output status messages to standard out in JSON format")
144 po::value<std::string>(),
145 "Write log output, in addition to the console, to the specified file.")
147 "skips operations with visible side-effects, useful for validating inputs")
149 po::options_description hidden(
"Hidden options");
151 (
"specification-file", po::value<std::string>(&spec_file),
"input file")
155 po::options_description all(
"All");
156 all.add(options).add(hidden);
158 po::variables_map vm;
160 auto parsed = po::command_line_parser(argc, argv)
162 .positional(positional)
163 .extra_parser(CustomParser)
165 po::store(parsed, vm);
166 if (vm.count(
"help")) {
167 std::cerr << options <<
'\n';
170 if (vm.count(
"version")) {
171 std::cerr <<
"daqDpmMerge " << VERSION
180 if (!vm.count(
"specification-file")) {
181 std::cerr <<
"argument error: specification-file not provided\n";
184 spec_file = vm[
"specification-file"].as<std::string>();
188 auto root_arg = vm.count(
"root") ? vm[
"root"].as<std::string>() : std::string();
189 auto root_path = std::filesystem::path();
190 if (!root_arg.empty()) {
193 std::filesystem::path(root_arg, std::filesystem::path::format::native_format);
194 if (!std::filesystem::is_directory(root_path)) {
195 std::cerr <<
"argument error: --root argument \"" << root_arg
196 <<
"\" is not a valid directory\n";
199 }
else if (std::filesystem::is_regular_file(spec_file)) {
201 root_path = std::filesystem::path(spec_file).parent_path();
204 root_path = std::filesystem::current_path();
207 auto log_file = vm.count(
"logfile") ? vm[
"logfile"].as<std::string>() : std::string();
208 if (!log_file.empty()) {
210 auto log_path = Resolve(std::filesystem::path(log_file), root_path);
211 InitFileAppender(log_path);
215 std::optional<std::filesystem::path> out_path;
216 if (vm.count(
"outfile")) {
217 auto out = Resolve(std::filesystem::path(vm[
"outfile"].as<std::string>()), root_path);
219 if (std::filesystem::exists(out)) {
220 std::cerr <<
"argument error: --outfile argument " << out <<
" exists\n";
228 if (vm.count(
"resolver")) {
230 Resolve(std::filesystem::path(vm[
"resolver"].as<std::string>()), root_path);
231 if (!std::filesystem::exists(resolver_path)) {
232 std::cerr <<
"argument error: --resolver argument " << resolver_path
236 std::ifstream file(resolver_path);
238 std::cerr <<
"error: failed to open specification with path '" << resolver_path
242 auto json = nlohmann::json::parse(file);
247 nlohmann::json json_spec;
248 if (spec_file ==
"-") {
250 json_spec = nlohmann::json::parse(std::cin);
253 std::ifstream file(spec_file);
255 std::cerr <<
"error: failed to open specification with path '" << spec_file <<
'\n';
258 json_spec = nlohmann::json::parse(file);
261 auto use_json = vm.count(
"json");
262 auto dry_run = vm.count(
"dry-run");
269 root_path, out_path, dp_spec, resolver, dry_run, use_json);
276 fmt::format(
"Failed to parse Data Product Specification '{}'", spec_file)));
281 }
catch (po::error
const& e) {
284 }
catch (nlohmann::json::parse_error
const& e) {
285 std::cerr <<
"error: json parsing failed: " << e.what() <<
'\n';
287 }
catch (nlohmann::json::exception
const& e) {
288 std::cerr <<
"error: json error: " << e.what() <<
'\n';
293 }
catch (std::exception
const& e) {
Provides location of fits source file.
void SetMapping(Mapping mapping) noexcept
std::map< SourceFile, std::string > Mapping
int main(int argc, char **argv)
int Entrypoint(fs::path const &root, std::optional< fs::path > const &opt_out_path, json::DpSpec const &spec, SourceResolver const &resolver, bool dry_run, bool use_json)
void ReportNestedExceptions(std::ostream &os) noexcept
DpSpec ParseDpSpec(Json const &json)
Parse JSON to construct the DpSpec structure.
@ InvalidProgramArgument
Invalid program arguments.
@ DpSpecNotFound
Data product specification file not found.
@ DpSpecInvalid
Invalid Data Product Specification.
@ DpSpecInvalidJson
Invalid Data Product Specification JSON
ExitWithErrorCode(int code)