Promote special CLI rules to flags (#495)

* Promote special CLI rules to flags.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Improve argument parsing logging and error messages.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Remove some short form CLI flags.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
This commit is contained in:
Michel Hidalgo 2019-09-11 17:00:51 -07:00 committed by GitHub
parent 40a276b6f4
commit e8bbb752df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 246 additions and 194 deletions

View file

@ -39,15 +39,16 @@ typedef struct rcl_arguments_t
#define RCL_ROS_ARGS_EXPLICIT_END_TOKEN "--" #define RCL_ROS_ARGS_EXPLICIT_END_TOKEN "--"
#define RCL_PARAM_FLAG "--param" #define RCL_PARAM_FLAG "--param"
#define RCL_SHORT_PARAM_FLAG "-p" #define RCL_SHORT_PARAM_FLAG "-p"
#define RCL_PARAM_FILE_FLAG "--params-file"
#define RCL_REMAP_FLAG "--remap" #define RCL_REMAP_FLAG "--remap"
#define RCL_SHORT_REMAP_FLAG "-r" #define RCL_SHORT_REMAP_FLAG "-r"
#define RCL_LOG_LEVEL_FLAG "--log-level"
#define RCL_EXTERNAL_LOG_CONFIG_FLAG "--log-config-file"
// To be prefixed with --enable- or --disable-
#define RCL_LOG_STDOUT_FLAG_SUFFIX "stdout-logs"
#define RCL_LOG_ROSOUT_FLAG_SUFFIX "rosout-logs"
#define RCL_LOG_EXT_LIB_FLAG_SUFFIX "external-lib-logs"
#define RCL_LOG_LEVEL_ARG_RULE "__log_level:="
#define RCL_EXTERNAL_LOG_CONFIG_ARG_RULE "__log_config_file:="
#define RCL_LOG_DISABLE_STDOUT_ARG_RULE "__log_disable_stdout:="
#define RCL_LOG_DISABLE_ROSOUT_ARG_RULE "__log_disable_rosout:="
#define RCL_LOG_DISABLE_EXT_LIB_ARG_RULE "__log_disable_external_lib:="
#define RCL_PARAM_FILE_ARG_RULE "__params:="
/// Return a rcl_arguments_t struct with members initialized to `NULL`. /// Return a rcl_arguments_t struct with members initialized to `NULL`.
RCL_PUBLIC RCL_PUBLIC
@ -75,7 +76,7 @@ rcl_get_zero_initialized_arguments(void);
* Parameter override rule parsing is supported via `-p/--param` flags e.g. `--param name:=value` * Parameter override rule parsing is supported via `-p/--param` flags e.g. `--param name:=value`
* or `-p name:=value`. * or `-p name:=value`.
* *
* The default log level will be parsed as `__log_level:=level`, where `level` is a name * The default log level will be parsed as `--log-level level`, where `level` is a name
* representing one of the log levels in the `RCUTILS_LOG_SEVERITY` enum, e.g. `info`, `debug`, * representing one of the log levels in the `RCUTILS_LOG_SEVERITY` enum, e.g. `info`, `debug`,
* `warn`, not case sensitive. * `warn`, not case sensitive.
* If multiple of these rules are found, the last one parsed will be used. * If multiple of these rules are found, the last one parsed will be used.

View file

@ -101,7 +101,7 @@ typedef rmw_ret_t rcl_ret_t;
/// Argument is not a valid parameter rule /// Argument is not a valid parameter rule
#define RCL_RET_INVALID_PARAM_RULE 1010 #define RCL_RET_INVALID_PARAM_RULE 1010
/// Argument is not a valid log level rule /// Argument is not a valid log level rule
#define RCL_RET_INVALID_LOG_LEVEL_RULE 1020 #define RCL_RET_INVALID_LOG_LEVEL 1020
// rcl event specific ret codes in 20XX // rcl event specific ret codes in 20XX
/// Invalid rcl_event_t given return code. /// Invalid rcl_event_t given return code.

View file

@ -57,24 +57,17 @@ _rcl_parse_remap_rule(
rcl_allocator_t allocator, rcl_allocator_t allocator,
rcl_remap_t * output_rule); rcl_remap_t * output_rule);
/// Parse an argument that may or may not be a parameter file rule. /// Parse an argument that may or may not be a param rule.
/** /**
* The syntax of the file name is not validated.
* \param[in] arg the argument to parse * \param[in] arg the argument to parse
* \param[in] allocator an allocator to use * \param[in,out] params param overrides structure to populate.
* \param[in,out] param_file string that could be a parameter file name * This structure must have been initialized by the caller.
* \return RCL_RET_OK if the rule was parsed correctly, or * \return RCL_RET_OK if a valid rule was parsed, or
* \return RCL_RET_INVALID_ARGUMENT if an argument is invalid, or
* \return RCL_RET_INVALID_PARAM_RULE if the argument is not a valid rule, or * \return RCL_RET_INVALID_PARAM_RULE if the argument is not a valid rule, or
* \return RCL_RET_BAD_ALLOC if an allocation failed, or * \return RCL_RET_BAD_ALLOC if an allocation failed, or
* \return RLC_RET_ERROR if an unspecified error occurred. * \return RLC_RET_ERROR if an unspecified error occurred.
*/ */
RCL_LOCAL
rcl_ret_t
_rcl_parse_param_file_rule(
const char * arg,
rcl_allocator_t allocator,
char ** param_file);
rcl_ret_t rcl_ret_t
_rcl_parse_param_rule( _rcl_parse_param_rule(
const char * arg, const char * arg,
@ -148,18 +141,18 @@ rcl_arguments_get_param_overrides(
* \param[in] allocator an allocator to use * \param[in] allocator an allocator to use
* \param[in,out] log_level parsed log level represented by `RCUTILS_LOG_SEVERITY` enum * \param[in,out] log_level parsed log level represented by `RCUTILS_LOG_SEVERITY` enum
* \return RCL_RET_OK if a valid log level was parsed, or * \return RCL_RET_OK if a valid log level was parsed, or
* \return RCL_RET_INVALID_LOG_LEVEL_RULE if the argument is not a valid rule, or * \return RCL_RET_INVALID_LOG_LEVEL if the argument is not a valid rule, or
* \return RCL_RET_BAD_ALLOC if an allocation failed, or * \return RCL_RET_BAD_ALLOC if an allocation failed, or
* \return RLC_RET_ERROR if an unspecified error occurred. * \return RLC_RET_ERROR if an unspecified error occurred.
*/ */
RCL_LOCAL RCL_LOCAL
rcl_ret_t rcl_ret_t
_rcl_parse_log_level_rule( _rcl_parse_log_level(
const char * arg, const char * arg,
rcl_allocator_t allocator, rcl_allocator_t allocator,
int * log_level); int * log_level);
/// Parse an argument that may or may not be a log file rule. /// Parse an argument that may or may not be a log configuration file.
/** /**
* \param[in] arg the argument to parse * \param[in] arg the argument to parse
* \param[in] allocator an allocator to use * \param[in] allocator an allocator to use
@ -175,6 +168,23 @@ _rcl_parse_external_log_config_file(
rcl_allocator_t allocator, rcl_allocator_t allocator,
char ** log_config_file); char ** log_config_file);
/// Parse an argument that may or may not be a parameter file.
/**
* The syntax of the file name is not validated.
* \param[in] arg the argument to parse
* \param[in] allocator an allocator to use
* \param[in,out] param_file string that could be a parameter file name
* \return RCL_RET_OK if the rule was parsed correctly, or
* \return RCL_RET_BAD_ALLOC if an allocation failed, or
* \return RLC_RET_ERROR if an unspecified error occurred.
*/
RCL_LOCAL
rcl_ret_t
_rcl_parse_param_file(
const char * arg,
rcl_allocator_t allocator,
char ** param_file);
/// Parse a bool argument that may or may not be for the provided key rule. /// Parse a bool argument that may or may not be for the provided key rule.
/** /**
* \param[in] arg the argument to parse * \param[in] arg the argument to parse
@ -185,30 +195,11 @@ _rcl_parse_external_log_config_file(
*/ */
RCL_LOCAL RCL_LOCAL
rcl_ret_t rcl_ret_t
_rcl_parse_bool_arg( _rcl_parse_disabling_flag(
const char * arg, const char * arg,
const char * key, const char * key,
bool * value); bool * value);
/// Parse a null terminated string to a boolean value.
/**
* The case sensitive values: "T", "t", "True", "true", "Y", "y", "Yes", "yes",
* and "1" will all map to true.
* The case sensitive values: "F", "f", "False", "false", "N", "n", "No", "no",
* and "0" will all map to false.
*
* \param[in] str a null terminated string to be parsed into a boolean
* \param[in,out] val the boolean value parsed from the string.
* Left unchanged if string cannot be parsed to a valid bool.
* \return RCL_RET_OK if a valid boolean parsed, or
* \return RLC_RET_ERROR if an unspecified error occurred.
*/
RCL_LOCAL
rcl_ret_t
_atob(
const char * str,
bool * val);
rcl_ret_t rcl_ret_t
rcl_parse_arguments( rcl_parse_arguments(
int argc, int argc,
@ -307,8 +298,9 @@ rcl_parse_arguments(
if (i + 1 < argc) { if (i + 1 < argc) {
// Attempt to parse next argument as parameter override rule // Attempt to parse next argument as parameter override rule
if (RCL_RET_OK == _rcl_parse_param_rule(argv[i + 1], args_impl->parameter_overrides)) { if (RCL_RET_OK == _rcl_parse_param_rule(argv[i + 1], args_impl->parameter_overrides)) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "param override rule : %s\n", argv[i + 1]); RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
i += 1; // Skip flag here, for loop will skip rule. "Got param override rule : %s\n", argv[i + 1]);
++i; // Skip flag here, for loop will skip rule.
continue; continue;
} }
rcl_error_string_t prev_error_string = rcl_get_error_string(); rcl_error_string_t prev_error_string = rcl_get_error_string();
@ -324,8 +316,8 @@ rcl_parse_arguments(
goto fail; goto fail;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as parameter override rule. Not a '%s' or '%s' flag.", i, "Couldn't parse arg %d (%s) as parameter override rule. Not a %s nor a %s flag.",
argv[i], RCL_PARAM_FLAG, RCL_SHORT_PARAM_FLAG); i, argv[i], RCL_PARAM_FLAG, RCL_SHORT_PARAM_FLAG);
// Attempt to parse argument as remap rule flag // Attempt to parse argument as remap rule flag
if (strcmp(RCL_REMAP_FLAG, argv[i]) == 0 || strcmp(RCL_SHORT_REMAP_FLAG, argv[i]) == 0) { if (strcmp(RCL_REMAP_FLAG, argv[i]) == 0 || strcmp(RCL_SHORT_REMAP_FLAG, argv[i]) == 0) {
@ -334,9 +326,9 @@ rcl_parse_arguments(
rcl_remap_t * rule = &(args_impl->remap_rules[args_impl->num_remap_rules]); rcl_remap_t * rule = &(args_impl->remap_rules[args_impl->num_remap_rules]);
*rule = rcl_get_zero_initialized_remap(); *rule = rcl_get_zero_initialized_remap();
if (RCL_RET_OK == _rcl_parse_remap_rule(argv[i + 1], allocator, rule)) { if (RCL_RET_OK == _rcl_parse_remap_rule(argv[i + 1], allocator, rule)) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "remap rule : %s\n", argv[i + 1]);
++(args_impl->num_remap_rules); ++(args_impl->num_remap_rules);
i += 1; // Skip flag here, for loop will skip rule. RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Got remap rule : %s\n", argv[i + 1]);
++i; // Skip flag here, for loop will skip rule.
continue; continue;
} }
rcl_error_string_t prev_error_string = rcl_get_error_string(); rcl_error_string_t prev_error_string = rcl_get_error_string();
@ -352,80 +344,142 @@ rcl_parse_arguments(
goto fail; goto fail;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as remap rule. Not a '%s' or '%s' flag.", i, "Couldn't parse arg %d (%s) as remap rule. Not a %s' nor a %s flag.", i,
argv[i], RCL_REMAP_FLAG, RCL_SHORT_REMAP_FLAG); argv[i], RCL_REMAP_FLAG, RCL_SHORT_REMAP_FLAG);
// Attempt to parse argument as parameter file rule // Attempt to parse argument as parameter file rule
if (strcmp(RCL_PARAM_FILE_FLAG, argv[i]) == 0) {
if (i + 1 < argc) {
// Attempt to parse next argument as remap rule
args_impl->parameter_files[args_impl->num_param_files_args] = NULL; args_impl->parameter_files[args_impl->num_param_files_args] = NULL;
if ( if (
RCL_RET_OK == _rcl_parse_param_file_rule( RCL_RET_OK == _rcl_parse_param_file(
argv[i], allocator, &(args_impl->parameter_files[args_impl->num_param_files_args]))) argv[i + 1], allocator,
&args_impl->parameter_files[args_impl->num_param_files_args]))
{ {
++(args_impl->num_param_files_args); ++(args_impl->num_param_files_args);
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"params rule : %s\n total num param rules %d", "Got params file : %s\ntotal num param files %d",
args_impl->parameter_files[args_impl->num_param_files_args - 1], args_impl->parameter_files[args_impl->num_param_files_args - 1],
args_impl->num_param_files_args); args_impl->num_param_files_args);
++i; // Skip flag here, for loop will skip rule.
continue; continue;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, rcl_error_string_t prev_error_string = rcl_get_error_string();
"Couldn't parse arg %d (%s) as parameter file rule. Error: %s", i, argv[i],
rcl_get_error_string().str);
rcl_reset_error(); rcl_reset_error();
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse params file: '%s %s'. Error: %s", argv[i], argv[i + 1],
prev_error_string.str);
} else {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse trailing params file flag: '%s'. No file path provided.", argv[i]);
}
ret = RCL_RET_INVALID_ROS_ARGS;
goto fail;
}
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as params file flag. Not a %s flag.", i,
argv[i], RCL_PARAM_FILE_FLAG);
// Attempt to parse argument as log level configuration // Attempt to parse argument as log level configuration
if (strcmp(RCL_LOG_LEVEL_FLAG, argv[i]) == 0) {
if (i + 1 < argc) {
int log_level; int log_level;
if (RCL_RET_OK == _rcl_parse_log_level_rule(argv[i], allocator, &log_level)) { if (RCL_RET_OK == _rcl_parse_log_level(argv[i + 1], allocator, &log_level)) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Got log level: %s\n", argv[i + 1]);
args_impl->log_level = log_level; args_impl->log_level = log_level;
++i; // Skip flag here, for loop will skip value.
continue; continue;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, rcl_error_string_t prev_error_string = rcl_get_error_string();
"Couldn't parse arg %d (%s) as log level rule. Error: %s", i, argv[i],
rcl_get_error_string().str);
rcl_reset_error(); rcl_reset_error();
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse log level: '%s %s'. Error: %s", argv[i], argv[i + 1],
prev_error_string.str);
} else {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse trailing log level flag: '%s'. No log level provided.", argv[i]);
}
ret = RCL_RET_INVALID_ROS_ARGS;
goto fail;
}
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as log level flag. Not a %s flag.",
i, argv[i], RCL_LOG_LEVEL_FLAG);
// Attempt to parse argument as log configuration file // Attempt to parse argument as log configuration file
rcl_ret_t ret = _rcl_parse_external_log_config_file( if (strcmp(RCL_EXTERNAL_LOG_CONFIG_FLAG, argv[i]) == 0) {
argv[i], allocator, &args_impl->external_log_config_file); if (i + 1 < argc) {
if (NULL != args_impl->external_log_config_file) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Overriding log configuration file : %s\n",
args_impl->external_log_config_file);
allocator.deallocate(args_impl->external_log_config_file, allocator.state);
args_impl->external_log_config_file = NULL;
}
if (RCL_RET_OK == _rcl_parse_external_log_config_file(
argv[i + 1], allocator, &args_impl->external_log_config_file))
{
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Got log configuration file : %s\n",
args_impl->external_log_config_file);
++i; // Skip flag here, for loop will skip value.
continue;
}
rcl_error_string_t prev_error_string = rcl_get_error_string();
rcl_reset_error();
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse log configuration file: '%s %s'. Error: %s", argv[i], argv[i + 1],
prev_error_string.str);
} else {
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Couldn't parse trailing log configuration file flag: '%s'."
" No file path provided.", argv[i]);
}
ret = RCL_RET_INVALID_ROS_ARGS;
goto fail;
}
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as log configuration file flag. Not a %s flag.",
i, argv[i], RCL_EXTERNAL_LOG_CONFIG_FLAG);
// Attempt to parse --enable/disable-stdout-logs flag
ret = _rcl_parse_disabling_flag(
argv[i], RCL_LOG_STDOUT_FLAG_SUFFIX, &args_impl->log_stdout_disabled);
if (RCL_RET_OK == ret) { if (RCL_RET_OK == ret) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Disable log stdout ? %s\n", args_impl->log_stdout_disabled ? "true" : "false");
continue; continue;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as log config rule. Error: %s", i, argv[i], "Couldn't parse arg %d (%s) as ...%s flag. Error: %s",
rcl_get_error_string().str); i, argv[i], RCL_LOG_STDOUT_FLAG_SUFFIX, rcl_get_error_string().str);
rcl_reset_error(); rcl_reset_error();
// Attempt to parse argument as log_stdout_disabled // Attempt to parse --enable/disable-rosout-logs flag
ret = _rcl_parse_bool_arg( ret = _rcl_parse_disabling_flag(
argv[i], RCL_LOG_DISABLE_STDOUT_ARG_RULE, &args_impl->log_stdout_disabled); argv[i], RCL_LOG_ROSOUT_FLAG_SUFFIX, &args_impl->log_rosout_disabled);
if (RCL_RET_OK == ret) { if (RCL_RET_OK == ret) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Disable log rosout ? %s\n", args_impl->log_rosout_disabled ? "true" : "false");
continue; continue;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as log_stdout_disabled rule. Error: %s", i, argv[i], "Couldn't parse arg %d (%s) as ...%s flag. Error: %s",
rcl_get_error_string().str); i, argv[i], RCL_LOG_ROSOUT_FLAG_SUFFIX, rcl_get_error_string().str);
rcl_reset_error(); rcl_reset_error();
// Attempt to parse argument as log_rosout_disabled // Attempt to parse --enable/disable-external-lib-logs flag
ret = _rcl_parse_bool_arg( ret = _rcl_parse_disabling_flag(
argv[i], RCL_LOG_DISABLE_ROSOUT_ARG_RULE, &args_impl->log_rosout_disabled); argv[i], RCL_LOG_EXT_LIB_FLAG_SUFFIX, &args_impl->log_ext_lib_disabled);
if (RCL_RET_OK == ret) { if (RCL_RET_OK == ret) {
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Disable log external lib ? %s\n", args_impl->log_ext_lib_disabled ? "true" : "false");
continue; continue;
} }
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as log_rosout_disabled rule. Error: %s", i, argv[i], "Couldn't parse arg %d (%s) as ...%s flag. Error: %s",
rcl_get_error_string().str); i, argv[i], RCL_LOG_EXT_LIB_FLAG_SUFFIX, rcl_get_error_string().str);
rcl_reset_error();
// Attempt to parse argument as log_ext_lib_disabled
ret = _rcl_parse_bool_arg(
argv[i], RCL_LOG_DISABLE_EXT_LIB_ARG_RULE, &args_impl->log_ext_lib_disabled);
if (RCL_RET_OK == ret) {
continue;
}
RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME,
"Couldn't parse arg %d (%s) as log_ext_lib_disabled rule. Error: %s", i, argv[i],
rcl_get_error_string().str);
rcl_reset_error(); rcl_reset_error();
// Argument is an unknown ROS specific argument // Argument is an unknown ROS specific argument
@ -985,7 +1039,6 @@ _rcl_parse_resource_match_token(rcl_lexer_lookahead2_t * lex_lookahead)
/// Parse a resource name match side of a rule (ex: `rostopic://foo`) /// Parse a resource name match side of a rule (ex: `rostopic://foo`)
/** /**
* \sa _rcl_parse_param_rule()
* \sa _rcl_parse_remap_match_name() * \sa _rcl_parse_remap_match_name()
*/ */
RCL_LOCAL RCL_LOCAL
@ -1378,7 +1431,7 @@ _rcl_parse_remap_begin_remap_rule(
} }
rcl_ret_t rcl_ret_t
_rcl_parse_log_level_rule( _rcl_parse_log_level(
const char * arg, const char * arg,
rcl_allocator_t allocator, rcl_allocator_t allocator,
int * log_level) int * log_level)
@ -1386,17 +1439,12 @@ _rcl_parse_log_level_rule(
RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(log_level, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(log_level, RCL_RET_INVALID_ARGUMENT);
if (strncmp(RCL_LOG_LEVEL_ARG_RULE, arg, strlen(RCL_LOG_LEVEL_ARG_RULE)) != 0) { rcutils_ret_t ret = rcutils_logging_severity_level_from_string(arg, allocator, log_level);
RCL_SET_ERROR_MSG("Argument does not start with '" RCL_LOG_LEVEL_ARG_RULE "'");
return RCL_RET_INVALID_LOG_LEVEL_RULE;
}
rcutils_ret_t ret = rcutils_logging_severity_level_from_string(
arg + strlen(RCL_LOG_LEVEL_ARG_RULE), allocator, log_level);
if (RCUTILS_RET_OK == ret) { if (RCUTILS_RET_OK == ret) {
return RCL_RET_OK; return RCL_RET_OK;
} }
RCL_SET_ERROR_MSG("Argument does not use a valid severity level"); RCL_SET_ERROR_MSG("Argument does not use a valid severity level");
return RCL_RET_INVALID_LOG_LEVEL_RULE; return RCL_RET_INVALID_LOG_LEVEL;
} }
rcl_ret_t rcl_ret_t
@ -1519,26 +1567,20 @@ cleanup:
} }
rcl_ret_t rcl_ret_t
_rcl_parse_param_file_rule( _rcl_parse_param_file(
const char * arg, const char * arg,
rcl_allocator_t allocator, rcl_allocator_t allocator,
char ** param_file) char ** param_file)
{ {
RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(param_file, RCL_RET_INVALID_ARGUMENT);
const size_t param_prefix_len = strlen(RCL_PARAM_FILE_ARG_RULE); *param_file = rcutils_strdup(arg, allocator);
if (strncmp(RCL_PARAM_FILE_ARG_RULE, arg, param_prefix_len) == 0) {
size_t outlen = strlen(arg) - param_prefix_len;
*param_file = allocator.allocate(sizeof(char) * (outlen + 1), allocator.state);
if (NULL == *param_file) { if (NULL == *param_file) {
RCL_SET_ERROR_MSG("Failed to allocate memory for parameters file path"); RCL_SET_ERROR_MSG("Failed to allocate memory for parameters file path");
return RCL_RET_BAD_ALLOC; return RCL_RET_BAD_ALLOC;
} }
snprintf(*param_file, outlen + 1, "%s", arg + param_prefix_len);
return RCL_RET_OK; return RCL_RET_OK;
}
RCL_SET_ERROR_MSG("Argument does not start with '" RCL_PARAM_FILE_ARG_RULE "'");
return RCL_RET_INVALID_PARAM_RULE;
} }
rcl_ret_t rcl_ret_t
@ -1550,65 +1592,48 @@ _rcl_parse_external_log_config_file(
RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(log_config_file, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(log_config_file, RCL_RET_INVALID_ARGUMENT);
const size_t param_prefix_len = sizeof(RCL_EXTERNAL_LOG_CONFIG_ARG_RULE) - 1; *log_config_file = rcutils_strdup(arg, allocator);
if (strncmp(RCL_EXTERNAL_LOG_CONFIG_ARG_RULE, arg, param_prefix_len) == 0) { // TODO(hidmic): add file checks
size_t outlen = strlen(arg) - param_prefix_len;
*log_config_file = rcutils_format_string_limit(allocator, outlen, "%s", arg + param_prefix_len);
if (NULL == *log_config_file) { if (NULL == *log_config_file) {
RCL_SET_ERROR_MSG("Failed to allocate memory for external log config file"); RCL_SET_ERROR_MSG("Failed to allocate memory for external log config file");
return RCL_RET_BAD_ALLOC; return RCL_RET_BAD_ALLOC;
} }
return RCL_RET_OK; return RCL_RET_OK;
}
RCL_SET_ERROR_MSG("Argument does not start with '" RCL_EXTERNAL_LOG_CONFIG_ARG_RULE "'");
return RCL_RET_INVALID_PARAM_RULE;
} }
RCL_LOCAL RCL_LOCAL
rcl_ret_t rcl_ret_t
_rcl_parse_bool_arg( _rcl_parse_disabling_flag(
const char * arg, const char * arg,
const char * key, const char * suffix,
bool * value) bool * disable)
{ {
RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(arg, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(key, RCL_RET_INVALID_ARGUMENT); RCL_CHECK_ARGUMENT_FOR_NULL(suffix, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(disable, RCL_RET_INVALID_ARGUMENT);
const size_t param_prefix_len = strlen(key); const char * true_prefix = "--enable-";
if (strncmp(key, arg, param_prefix_len) == 0) { const size_t true_prefix_len = strlen(true_prefix);
return _atob(arg + param_prefix_len, value); if (
} strncmp(true_prefix, arg, true_prefix_len) == 0 &&
strcmp(suffix, arg + true_prefix_len) == 0)
RCL_SET_ERROR_MSG("Argument does not start with key"); {
return RCL_RET_INVALID_PARAM_RULE; *disable = false;
}
RCL_LOCAL
rcl_ret_t
_atob(
const char * str,
bool * val)
{
RCL_CHECK_ARGUMENT_FOR_NULL(str, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(val, RCL_RET_INVALID_ARGUMENT);
const char * true_values[] = {"y", "Y", "yes", "Yes", "t", "T", "true", "True", "1"};
const char * false_values[] = {"n", "N", "no", "No", "f", "F", "false", "False", "0"};
for (size_t idx = 0; idx < sizeof(true_values) / sizeof(char *); idx++) {
if (0 == strncmp(true_values[idx], str, strlen(true_values[idx]))) {
*val = true;
return RCL_RET_OK; return RCL_RET_OK;
} }
}
for (size_t idx = 0; idx < sizeof(false_values) / sizeof(char *); idx++) { const char * false_prefix = "--disable-";
if (0 == strncmp(false_values[idx], str, strlen(false_values[idx]))) { const size_t false_prefix_len = strlen(false_prefix);
*val = false; if (
strncmp(false_prefix, arg, false_prefix_len) == 0 &&
strcmp(suffix, arg + false_prefix_len) == 0)
{
*disable = true;
return RCL_RET_OK; return RCL_RET_OK;
} }
}
RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
"Argument does not match %s%s nor %s%s", true_prefix, suffix, false_prefix, suffix);
return RCL_RET_ERROR; return RCL_RET_ERROR;
} }

View file

@ -143,29 +143,42 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_known_vs_unkno
EXPECT_TRUE(are_known_ros_args({"--ros-args", "-p", "/foo/bar:=bar"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-p", "/foo/bar:=bar"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "-p", "foo:=/bar"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-p", "foo:=/bar"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "-p", "/foo123:=/bar123"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-p", "/foo123:=/bar123"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__params:=file_name.yaml"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--params-file", "file_name.yaml"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "--custom-ros-arg"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "--custom-ros-arg"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "__node:=node_name"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "__node:=node_name"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "old_name:__node:=node_name"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "old_name:__node:=node_name"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "/foo/bar:=bar"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "/foo/bar:=bar"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "foo:=/bar"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "foo:=/bar"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "__param:=file_name.yaml"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "file_name.yaml"}));
// Setting config logging file
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-config-file", "file.config"}));
// Setting logger level // Setting logger level
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=UNSET"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "UNSET"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=DEBUG"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "DEBUG"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=INFO"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "INFO"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=WARN"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "WARN"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=ERROR"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "ERROR"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=FATAL"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "FATAL"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=debug"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "debug"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "__log_level:=Info"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "--log-level", "Info"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "__log:=foo"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "--log", "foo"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "__loglevel:=foo"})); EXPECT_FALSE(are_known_ros_args({"--ros-args", "--loglevel", "foo"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "__log_level:="}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "__log_level:=foo"})); // Disabling logging
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--enable-rosout-logs"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--disable-rosout-logs"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--enable-stdout-logs"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--disable-stdout-logs"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--enable-external-lib-logs"}));
EXPECT_TRUE(are_known_ros_args({"--ros-args", "--disable-external-lib-logs"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "stdout-logs"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "external-lib-logs"}));
EXPECT_FALSE(are_known_ros_args({"--ros-args", "external-lib-logs"}));
} }
bool bool
@ -185,7 +198,11 @@ are_valid_ros_args(std::vector<const char *> argv)
} }
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_invalid_args) { TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_invalid_args) {
EXPECT_TRUE(are_valid_ros_args({"--ros-args", "-p", "foo:=bar", "-r", "__node:=node_name"})); EXPECT_TRUE(are_valid_ros_args({
"--ros-args", "-p", "foo:=bar", "-r", "__node:=node_name",
"--params-file", "file_name.yaml", "--log-level", "INFO",
"--log-config-file", "file.config"
}));
// ROS args unknown to rcl are not (necessarily) invalid // ROS args unknown to rcl are not (necessarily) invalid
EXPECT_TRUE(are_valid_ros_args({"--ros-args", "--custom-ros-arg"})); EXPECT_TRUE(are_valid_ros_args({"--ros-args", "--custom-ros-arg"}));
@ -198,7 +215,7 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_inval
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", ":=bar"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", ":=bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--param"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--params-file"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", ":="})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", ":="}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", "foo:="})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", "foo:="}));
@ -216,11 +233,20 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_valid_vs_inval
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "f{oo:=/bar"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "f{oo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "foo:=/b}ar"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "foo:=/b}ar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", "}foo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", "f oo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "rostopic://:=rosservice"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "rostopic://:=rosservice"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "rostopic::=rosservice"})); EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-r", "rostopic::=rosservice"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", "}foo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--param", "}foo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "-p", "f oo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--param", "f oo:=/bar"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--params-file"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--log-config-file"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--log-level"}));
EXPECT_FALSE(are_valid_ros_args({"--ros-args", "--log-level", "foo"}));
} }
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_no_args) { TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_no_args) {
@ -566,7 +592,7 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_param_argument_
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_param_argument_single) { TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_param_argument_single) {
const char * argv[] = { const char * argv[] = {
"process_name", "--ros-args", "-r", "__ns:=/namespace", "random:=arg", "process_name", "--ros-args", "-r", "__ns:=/namespace", "random:=arg",
"__params:=parameter_filepath" "--params-file", "parameter_filepath"
}; };
int argc = sizeof(argv) / sizeof(const char *); int argc = sizeof(argv) / sizeof(const char *);
rcl_ret_t ret; rcl_ret_t ret;
@ -593,8 +619,8 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_param_argument_
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_param_argument_multiple) { TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_param_argument_multiple) {
const char * argv[] = { const char * argv[] = {
"process_name", "--ros-args", "__params:=parameter_filepath1", "-r", "__ns:=/namespace", "process_name", "--ros-args", "--params-file", "parameter_filepath1",
"random:=arg", "__params:=parameter_filepath2" "-r", "__ns:=/namespace", "random:=arg", "--params-file", "parameter_filepath2"
}; };
int argc = sizeof(argv) / sizeof(const char *); int argc = sizeof(argv) / sizeof(const char *);
rcl_ret_t ret; rcl_ret_t ret;