• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.57
/libs/full/command_line_handling/src/parse_command_line.cpp
1
//  Copyright (c) 2007-2025 Hartmut Kaiser
2
//
3
//  SPDX-License-Identifier: BSL-1.0
4
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
5
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6

7
#include <hpx/command_line_handling/parse_command_line.hpp>
8
#include <hpx/modules/command_line_handling_local.hpp>
9
#include <hpx/modules/errors.hpp>
10
#include <hpx/modules/filesystem.hpp>
11
#include <hpx/modules/format.hpp>
12
#include <hpx/modules/ini.hpp>
13

14
#include <cctype>
15
#include <cstddef>
16
#include <exception>
17
#include <iostream>
18
#include <string>
19
#include <utility>
20
#include <vector>
21

22
#include <hpx/config/warnings_prefix.hpp>
23

24
///////////////////////////////////////////////////////////////////////////////
25
namespace hpx::util {
26

27
    namespace detail {
28

29
        ///////////////////////////////////////////////////////////////////////
30
        // All command line options which are normally formatted as --hpx:foo
31
        // should be usable as --hpx:N:foo, where N is the node number this
×
32
        // option should be exclusively used for.
33
        bool handle_node_specific_option(std::string const& s, std::size_t node,
34
            std::pair<std::string, std::string>& opt)
35
        {
×
36
            // any option not starting with --hpx: will be handled elsewhere
37
            constexpr char const hpx_prefix[] = "--hpx:";
38
            constexpr std::string::size_type const hpx_prefix_len =
39
                sizeof(hpx_prefix) - 1;
×
40

×
41
            if (s.size() < hpx_prefix_len ||
×
42
                s.compare(0, hpx_prefix_len, hpx_prefix) != 0 ||
43
                !std::isdigit(s[hpx_prefix_len]))    // -V557
×
44
            {
45
                return false;
46
            }
47

48
            // any --hpx: option without a second ':' is handled elsewhere as
49
            // well
50
            std::string::size_type const p =
×
51
                s.find_first_of(':', hpx_prefix_len);
52
            if (p == std::string::npos)
53
                return false;
×
54

×
55
            if (hpx::util::from_string<std::size_t>(
×
56
                    s.substr(hpx_prefix_len, p - hpx_prefix_len),
57
                    static_cast<std::size_t>(-1)) == node)
58
            {
59
                using hpx::local::detail::trim_whitespace;
60

61
                // this option is for the current locality only
×
62
                std::string::size_type const p1 = s.find_first_of('=', p);
63
                if (p1 != std::string::npos)
64
                {
65
                    // the option has a value
×
66
                    std::string o(
×
67
                        "hpx:" + trim_whitespace(s.substr(p + 1, p1 - p - 1)));
×
68
                    std::string v(trim_whitespace(s.substr(p1 + 1)));
69
                    opt = std::make_pair(o, v);
70
                }
71
                else
72
                {
×
73
                    // no value
×
74
                    std::string o("hpx:" + trim_whitespace(s.substr(p + 1)));
75
                    opt = std::make_pair(o, std::string());
×
76
                }
77
                return true;
78
            }
79

80
            // This option is specifically not for us, so we return an option
×
81
            // which will be silently ignored.
×
82
            opt = std::make_pair(std::string("hpx:ignore"), std::string());
83
            return true;
84
        }
85

86
        ///////////////////////////////////////////////////////////////////////
87
        // Additional command line parser which interprets '@something' as an
88
        // option "options-file" with the value "something". Additionally we
89
        // resolve defined command line option aliases.
90
        struct option_parser : hpx::local::detail::option_parser
91
        {
92
            using base_type = hpx::local::detail::option_parser;
93

94
            option_parser(util::section const& ini, std::size_t node,
×
95
                bool ignore_aliases) noexcept
×
96
              : base_type(ini, ignore_aliases)
97
              , node_(node)
98
            {
99
            }
×
100

101
            std::pair<std::string, std::string> operator()(
102
                std::string const& s) const
103
            {
104
                // handle node specific options
×
105
                std::pair<std::string, std::string> opt;
106
                if (handle_node_specific_option(s, node_, opt))
107
                    return opt;
108

×
109
                // handle aliasing, if enabled
110
                return static_cast<base_type const&>(*this)(s);
111
            }
112

113
            std::size_t node_;
114
        };
115

116
        ///////////////////////////////////////////////////////////////////////
117
        // Handle all options from a given config file, parse and add them to
873✔
118
        // the given variables_map
119
        bool handle_config_file_options(std::vector<std::string> const& options,
120
            hpx::program_options::options_description const& desc,
121
            hpx::program_options::variables_map& vm, util::section const& rtcfg,
122
            std::size_t node, util::commandline_error_mode error_mode)
123
        {
873✔
124
            // add options to parsed settings
125
            if (!options.empty())
126
            {
127
                using hpx::program_options::command_line_parser;
128
                using hpx::program_options::store;
129
                using hpx::program_options::command_line_style::unix_style;
130

131
                util::commandline_error_mode const mode =
132
                    error_mode & util::commandline_error_mode::ignore_aliases;
133
                util::commandline_error_mode const notmode =
134
                    error_mode & ~util::commandline_error_mode::ignore_aliases;
×
135

×
136
                store(hpx::local::detail::get_commandline_parser(
137
                          command_line_parser(options)
138
                              .options(desc)
×
139
                              .style(unix_style)
140
                              .extra_parser(hpx::util::detail::option_parser(
141
                                  rtcfg, node, as_bool(mode))),
×
142
                          notmode)
143
                          .run(),
×
144
                    vm);
×
145
                notify(vm);
146
                return true;
147
            }
148
            return false;
149
        }
150

151
        // try to find a config file somewhere up the filesystem hierarchy
152
        // starting with the input file path. This allows to use a general
97✔
153
        // <app_name>.cfg file for all executables in a certain project.
154
        void handle_generic_config_options(std::string appname,
155
            hpx::program_options::variables_map& vm,
156
            hpx::program_options::options_description const& desc_cfgfile,
157
            util::section const& ini, std::size_t node,
158
            util::commandline_error_mode error_mode)
97✔
159
        {
×
160
            if (appname.empty())
161
                return;
97✔
162

97✔
163
            filesystem::path dir(filesystem::initial_path());
194✔
164
            filesystem::path const app(appname);
165
            appname = filesystem::basename(app.filename());
166

873✔
167
            // walk up the hierarchy, trying to find a file <appname>.cfg
168
            while (!dir.empty())
1,746✔
169
            {
170
                filesystem::path filename = dir / (appname + ".cfg");
171
                util::commandline_error_mode const mode = error_mode &
172
                    ~util::commandline_error_mode::report_missing_config_file;
173
                std::vector<std::string> options =
873✔
174
                    hpx::local::detail::read_config_file_options(
175
                        filename.string(), mode);
873✔
176

177
                if (handle_config_file_options(
178
                        options, desc_cfgfile, vm, ini, node, mode))
179
                {
180
                    break;    // break on the first options file found
181
                }
182

183
                // Boost filesystem and C++17 filesystem behave differently
184
                // here. Boost filesystem returns an empty path for
185
                // "/".parent_path() whereas C++17 filesystem will keep
186
                // returning "/".
873✔
187
#if !defined(HPX_FILESYSTEM_HAVE_BOOST_FILESYSTEM_COMPATIBILITY)
873✔
188
                auto dir_prev = dir;
873✔
189
                dir = dir.parent_path();    // chop off last directory part
190
                if (dir_prev == dir)
191
                    break;
192
#else
193
                dir = dir.parent_path();    // chop off last directory part
873✔
194
#endif
97✔
195
            }
196
        }
197

97✔
198
        // handle all --options-file found on the command line
199
        void handle_config_options(hpx::program_options::variables_map& vm,
200
            hpx::program_options::options_description const& desc_cfgfile,
201
            util::section const& ini, std::size_t node,
202
            util::commandline_error_mode error_mode)
203
        {
194✔
204
            using hpx::program_options::options_description;
205
            if (vm.count("hpx:options-file"))
206
            {
×
207
                auto const& cfg_files =
208
                    vm["hpx:options-file"].as<std::vector<std::string>>();
×
209

210
                for (std::string const& cfg_file : cfg_files)
211
                {
212
                    // parse a single config file and store the results
213
                    std::vector<std::string> options =
×
214
                        hpx::local::detail::read_config_file_options(
215
                            cfg_file, error_mode);
×
216

217
                    handle_config_file_options(
×
218
                        options, desc_cfgfile, vm, ini, node, error_mode);
219
                }
97✔
220
            }
221
        }
97✔
222

223
        hpx::local::detail::options_map compose_all_options(
224
            hpx::runtime_mode mode)
225
        {
226
            using hpx::local::detail::options_type;
227
            using hpx::program_options::value;
228

97✔
229
            hpx::local::detail::options_map all_options =
230
                hpx::local::detail::compose_local_options();
97✔
231

232
            switch (mode)
32✔
233
            {
234
            case runtime_mode::default_:
235
#if defined(HPX_HAVE_NETWORKING)
32✔
236
                // clang-format off
32✔
237
                all_options[options_type::hpx_options].add_options()
32✔
238
                    ("hpx:worker", "run this instance in worker mode")
32✔
239
                    ("hpx:console", "run this instance in console mode")
240
                    ("hpx:connect", "run this instance in worker mode, "
241
                         "but connecting late")
242
                ;
243
#else
244
                all_options[options_type::hpx_options].add_options()
245
                    ("hpx:console", "run this instance in console mode")
246
                ;
247
                // clang-format on
32✔
248
#endif
249
                break;
250

65✔
251
#if defined(HPX_HAVE_NETWORKING)
252
            case runtime_mode::worker:
253
            case runtime_mode::console:
254
                [[fallthrough]];
255
            case runtime_mode::connect:
256
                // If the runtime for this application is always run in worker
257
                // mode, silently ignore the worker option for hpx_pbs
258
                // compatibility.
259

65✔
260
                // clang-format off
65✔
261
                all_options[options_type::hidden_options].add_options()
65✔
262
                    ("hpx:worker", "run this instance in worker mode")
65✔
263
                    ("hpx:console", "run this instance in console mode")
264
                    ("hpx:connect", "run this instance in worker mode, "
265
                        "but connecting late")
266
                ;
65✔
267
                // clang-format on
268
                break;
269
#else
270
            case runtime_mode::console:
271
                // clang-format off
272
                all_options[options_type::hidden_options].add_options()
273
                    ("hpx:console", "run this instance in console mode")
274
                ;
275
                // clang-format on
276
                break;
277
#endif
278
            case runtime_mode::local:
279
                break;
×
280

281
            case runtime_mode::invalid:
282
                [[fallthrough]];
283
            default:
×
284
                throw hpx::detail::command_line_error(
285
                    "Invalid runtime mode specified");
286
            }
287

288
            // Always add the option to start the local runtime
97✔
289
            // clang-format off
97✔
290
            all_options[options_type::hpx_options].add_options()
291
                ("hpx:local",
292
                  "run this instance in local mode (experimental; certain "
293
                  "functionalities are not available at runtime)")
294
            ;
295
            // clang-format on
296

297
            // general options definitions
97✔
298
            // clang-format off
97✔
299
            all_options[options_type::hpx_options].add_options()
300
                ("hpx:run-hpx-main",
301
                  "run the hpx_main function, regardless of locality mode")
97✔
302
#if defined(HPX_HAVE_NETWORKING)
303
                ("hpx:agas", value<std::string>(),
304
                  "the IP address the AGAS root server is running on, "
305
                  "expected format: `address:port' (default: "
97✔
306
                  "127.0.0.1:7910)")
307
                ("hpx:hpx", value<std::string>(),
308
                  "the IP address the HPX parcelport is listening on, "
309
                  "expected format: `address:port' (default: "
97✔
310
                  "127.0.0.1:7910)")
311
                ("hpx:nodefile", value<std::string>(),
312
                  "the file name of a node file to use (list of nodes, one "
97✔
313
                  "node name per line and core)")
314
                ("hpx:nodes", value<std::vector<std::string> >()->multitoken(),
315
                  "the (space separated) list of the nodes to use (usually "
97✔
316
                  "this is extracted from a node file)")
317
                ("hpx:endnodes", "this can be used to end the list of nodes "
97✔
318
                  "specified using the option --hpx:nodes")
319
                ("hpx:ifsuffix", value<std::string>(),
320
                  "suffix to append to host names in order to resolve them "
97✔
321
                  "to the proper network interconnect")
322
                ("hpx:ifprefix", value<std::string>(),
323
                  "prefix to prepend to host names in order to resolve them "
97✔
324
                  "to the proper network interconnect")
325
                ("hpx:iftransform", value<std::string>(),
326
                  "sed-style search and replace (s/search/replace/) used to "
97✔
327
                  "transform host names to the proper network interconnect")
328
                ("hpx:force_ipv4", "Force ipv4 for resolving network hostnames")
329
#endif
97✔
330
#if defined(HPX_HAVE_DISTRIBUTED_RUNTIME)
331
                ("hpx:localities", value<std::size_t>(),
332
                  "the number of localities to wait for at application "
333
                  "startup (default: 1)")
334
#endif
97✔
335
#if defined(HPX_HAVE_NETWORKING)
336
                ("hpx:node", value<std::size_t>(),
337
                  "number of the node this locality is run on "
97✔
338
                  "(must be unique, alternatively: -0, -1, ..., -9)")
339
                ("hpx:ignore-batch-env", "ignore batch environment variables "
97✔
340
                 "(implied by --hpx:use-process-mask)")
341
                ("hpx:expect-connecting-localities",
342
                  "this locality expects other localities to dynamically connect "
343
                  "(default: false if the number of localities is equal to one, "
344
                  "true if the number of initial localities is larger than 1)")
345
#endif
346
            ;
97✔
347

97✔
348
            all_options[options_type::debugging_options].add_options()
349
                ("hpx:debug-agas-log", value<std::string>()->implicit_value("cout"),
350
                  "enable all messages on the AGAS log channel and send all "
194✔
351
                  "AGAS logs to the target destination")
352
                ("hpx:debug-parcel-log", value<std::string>()->implicit_value("cout"),
353
                  "enable all messages on the parcel transport log channel and send all "
354
                  "parcel transport logs to the target destination")
97✔
355
#if defined(HPX_HAVE_DISTRIBUTED_RUNTIME)
356
                ("hpx:list-symbolic-names", "list all registered symbolic "
97✔
357
                  "names after startup")
358
                ("hpx:list-component-types", "list all dynamic component types "
359
                  "after startup")
360
#endif
97✔
361
#if defined(HPX_HAVE_NETWORKING)
362
                ("hpx:list-parcel-ports", "list all available parcel-ports")
363
#endif
364
            ;
365

97✔
366
#if defined(HPX_HAVE_DISTRIBUTED_RUNTIME)
97✔
367
            all_options[options_type::counter_options].add_options()
368
                ("hpx:print-counter", value<std::vector<std::string> >()->composing(),
369
                  "print the specified performance counter either repeatedly "
370
                  "and/or at the times specified by --hpx:print-counter-at "
97✔
371
                    "(see also option --hpx:print-counter-interval)")
372
                ("hpx:print-counter-reset",
373
                        value<std::vector<std::string> >()->composing(),
374
                  "print the specified performance counter either repeatedly "
375
                  "and/or at the times specified by --hpx:print-counter-at, "
376
                    "reset the counter after the "
97✔
377
                    "value is queried (see also option --hpx:print-counter-interval)")
378
                ("hpx:print-counter-interval", value<std::size_t>(),
379
                  "print the performance counter(s) specified with --hpx:print-counter "
380
                  "repeatedly after the time interval (specified in milliseconds) "
97✔
381
                  "(default: 0, which means print once at shutdown)")
382
                ("hpx:print-counter-destination", value<std::string>(),
383
                  "print the performance counter(s) specified with --hpx:print-counter "
384
                  "to the given file (default: console (cout), "
385
                  "possible values: 'cout' (console), 'none' (no output), or "
97✔
386
                  "any file name")
387
                ("hpx:list-counters", value<std::string>()->implicit_value("minimal"),
388
                  "list the names of all registered performance counters, "
389
                  "possible values:\n"
390
                  "   'minimal' (prints counter name skeletons)\n"
97✔
391
                  "   'full' (prints all available counter names)")
194✔
392
                ("hpx:list-counter-infos",
393
                    value<std::string>()->implicit_value("minimal"),
394
                  "list the description of all registered performance counters, "
395
                  "possible values:\n"
396
                  "   'minimal' (prints infos for counter name skeletons)\n"
97✔
397
                  "   'full' (prints all available counter infos)")
398
                ("hpx:print-counter-format", value<std::string>(),
399
                  "print the performance counter(s) specified with --hpx:print-counter "
97✔
400
                  "in a given format (default: normal)")
401
                ("hpx:csv-header",
402
                  "print the performance counter(s) specified with --hpx:print-counter "
403
                  "with header when format specified with --hpx:print-counter-format"
97✔
404
                  "is csv or csv-short")
405
                ("hpx:no-csv-header",
406
                  "print the performance counter(s) specified with --hpx:print-counter "
407
                  "without header when format specified with --hpx:print-counter-format"
97✔
408
                  "is csv or csv-short")
409
                ("hpx:print-counter-at",
410
                    value<std::vector<std::string> >()->composing(),
411
                  "print the performance counter(s) specified with "
412
                  "--hpx:print-counter (or --hpx:print-counter-reset) at the given "
413
                  "point in time, possible "
97✔
414
                  "argument values: 'startup', 'shutdown' (default), 'noshutdown'")
415
                ("hpx:reset-counters",
416
                  "reset all performance counter(s) specified with --hpx:print-counter "
97✔
417
                  "after they have been evaluated")
418
                ("hpx:print-counters-locally",
97✔
419
                  "each locality prints only its own local counters")
420
                ("hpx:print-counter-types",
421
                  "append counter type description to generated output")
422
            ;
423
#endif
424
            // clang-format on
97✔
425

426
            return all_options;
427
        }
428
    }    // namespace detail
429

430
    ///////////////////////////////////////////////////////////////////////////
97✔
431
    // parse the command line
432
    bool parse_commandline(util::section const& rtcfg,
433
        hpx::program_options::options_description const& app_options,
434
        std::string const& arg0, std::vector<std::string> const& args,
435
        hpx::program_options::variables_map& vm, std::size_t node,
436
        util::commandline_error_mode error_mode, hpx::runtime_mode mode,
437
        hpx::program_options::options_description* visible,
438
        std::vector<std::string>* unregistered_options)
439
    {
440
        using hpx::local::detail::options_type;
441

442
        try
443
        {
444
            // construct the overall options description and parse the command
445
            // line
97✔
446
            hpx::local::detail::options_map all_options =
447
                detail::compose_all_options(mode);
97✔
448

449
            hpx::local::detail::compose_all_options(app_options, all_options);
450

194✔
451
#if defined(HPX_HAVE_DISTRIBUTED_RUNTIME)
97✔
452
            all_options[options_type::desc_cmdline].add(
194✔
453
                all_options[options_type::counter_options]);
97✔
454
            all_options[options_type::desc_cfgfile].add(
455
                all_options[options_type::counter_options]);
97✔
456
#endif
457
            bool const result = hpx::local::detail::parse_commandline(rtcfg,
458
                all_options, app_options, args, vm, error_mode, visible,
459
                unregistered_options);
97✔
460

461
            if (result && visible != nullptr)
32✔
462
            {
463
                (*visible).add(all_options[options_type::counter_options]);
464
            }
97✔
465

97✔
466
            detail::handle_generic_config_options(arg0, vm,
467
                all_options[options_type::desc_cfgfile], rtcfg, node,
97✔
468
                error_mode);
194✔
469
            detail::handle_config_options(vm,
470
                all_options[options_type::desc_cfgfile], rtcfg, node,
471
                error_mode);
472

473
            return result;
×
474
        }
475
        catch (std::exception const& e)
×
476
        {
477
            if (as_bool(error_mode &
478
                    util::commandline_error_mode::rethrow_on_error))
×
479
            {
480
                throw;
481
            }
×
482

483
            std::cerr << "hpx::init: exception caught: " << e.what()
×
484
                      << std::endl;
×
485
        }
486
        return false;
487
    }
488

489
    ///////////////////////////////////////////////////////////////////////////
490
    namespace detail {
33✔
491

492
        std::string extract_arg0(std::string const& cmdline)
33✔
493
        {
494
            if (std::string::size_type const p = cmdline.find_first_of(" \t");
495
                p != std::string::npos)
33✔
496
            {
497
                return cmdline.substr(0, p);
498
            }
499
            return cmdline;
500
        }
501
    }    // namespace detail
33✔
502

503
    bool parse_commandline(util::section const& rtcfg,
504
        hpx::program_options::options_description const& app_options,
505
        std::string const& cmdline, hpx::program_options::variables_map& vm,
506
        std::size_t node, util::commandline_error_mode error_mode,
507
        hpx::runtime_mode mode,
508
        hpx::program_options::options_description* visible,
509
        std::vector<std::string>* unregistered_options)
510
    {
511
        using namespace hpx::program_options;
512
#if defined(HPX_WINDOWS)
513
        std::vector<std::string> args = split_winmain(cmdline);
66✔
514
#else
515
        std::vector<std::string> args = split_unix(cmdline);
33✔
516
#endif
33✔
517
        return parse_commandline(rtcfg, app_options,
33✔
518
            detail::extract_arg0(cmdline), args, vm, node, error_mode, mode,
33✔
519
            visible, unregistered_options);
520
    }
521
}    // namespace hpx::util
522

523
#include <hpx/config/warnings_suffix.hpp>
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc