1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/build/lib/pgBackRestBuild/Config/BuildParse.pm
David Steele a76cb57c80 Remove parse.auto.h.
At one time there was a lot more in this header but it got merged with the enums and constants in config.auto.c, leaving only the ConfigOptionType enum.

Auto-generating the ConfigOptionType enum is not very useful since new option types require code in parse.c.
2021-06-07 08:48:09 -04:00

609 lines
22 KiB
Perl

####################################################################################################################################
# Auto-Generate Option Definition for Parsing with getopt_long().
####################################################################################################################################
package pgBackRestBuild::Config::BuildParse;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Cwd qw(abs_path);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);
use Storable qw(dclone);
use pgBackRestDoc::Common::Log;
use pgBackRestDoc::Common::String;
use pgBackRestDoc::ProjectInfo;
use pgBackRestBuild::Build::Common;
use pgBackRestBuild::Config::Build;
use pgBackRestBuild::Config::Data;
####################################################################################################################################
# Constants
####################################################################################################################################
use constant BLDLCL_FILE_DEFINE => 'parse';
use constant BLDLCL_DATA_COMMAND => '01-dataCommand';
use constant BLDLCL_DATA_OPTION_GROUP => '01-dataOptionGroup';
use constant BLDLCL_DATA_OPTION => '02-dataOption';
use constant BLDLCL_DATA_OPTION_GETOPT => '03-dataOptionGetOpt';
use constant BLDLCL_DATA_OPTION_RESOLVE => '04-dataOptionResolve';
####################################################################################################################################
# Definitions for constants and data to build
####################################################################################################################################
my $strSummary = 'Config Parse Rules';
my $rhBuild =
{
&BLD_FILE =>
{
&BLDLCL_FILE_DEFINE =>
{
&BLD_SUMMARY => $strSummary,
&BLD_DATA =>
{
&BLDLCL_DATA_COMMAND =>
{
&BLD_SUMMARY => 'Command parse data',
},
&BLDLCL_DATA_OPTION_GROUP =>
{
&BLD_SUMMARY => 'Option group parse data',
},
&BLDLCL_DATA_OPTION =>
{
&BLD_SUMMARY => 'Option parse data',
},
&BLDLCL_DATA_OPTION_GETOPT =>
{
&BLD_SUMMARY => 'Option data for getopt_long()',
},
&BLDLCL_DATA_OPTION_RESOLVE =>
{
&BLD_SUMMARY => 'Order for option parse resolution',
},
},
},
},
};
####################################################################################################################################
# Generate enum names
####################################################################################################################################
sub buildConfigDefineOptionTypeEnum
{
return bldEnum('cfgOptType', shift);
}
push @EXPORT, qw(buildConfigDefineOptionTypeEnum);
sub buildConfigCommandRoleEnum
{
return bldEnum('cfgCmdRole', shift);
}
####################################################################################################################################
# Helper functions for building optional option data
####################################################################################################################################
sub renderAllowList
{
my $ryAllowList = shift;
my $bCommandIndent = shift;
my $strIndent = $bCommandIndent ? ' ' : '';
return
"${strIndent} PARSE_RULE_OPTION_OPTIONAL_ALLOW_LIST\n" .
"${strIndent} (\n" .
"${strIndent} " . join(",\n${strIndent} ", bldQuoteList($ryAllowList)) .
"\n" .
"${strIndent} ),\n";
}
sub renderDepend
{
my $rhDepend = shift;
my $bCommandIndent = shift;
my $strIndent = $bCommandIndent ? ' ' : '';
my $strDependOption = $rhDepend->{&CFGDEF_DEPEND_OPTION};
my $ryDependList = $rhDepend->{&CFGDEF_DEPEND_LIST};
if (defined($ryDependList))
{
my @stryQuoteList;
foreach my $strItem (@{$ryDependList})
{
push(@stryQuoteList, "\"${strItem}\"");
}
return
"${strIndent} PARSE_RULE_OPTION_OPTIONAL_DEPEND_LIST\n" .
"${strIndent} (\n" .
"${strIndent} " . buildConfigOptionEnum($strDependOption) . ",\n" .
"${strIndent} " . join(",\n${strIndent} ", bldQuoteList($ryDependList)) .
"\n" .
"${strIndent} ),\n";
}
return
"${strIndent} PARSE_RULE_OPTION_OPTIONAL_DEPEND(" . buildConfigOptionEnum($strDependOption) . "),\n";
}
sub renderOptional
{
my $rhOptional = shift;
my $bCommand = shift;
my $strCommand = shift;
my $strOption = shift;
my $strIndent = $bCommand ? ' ' : '';
my $strBuildSourceOptional;
my $bSingleLine = false;
if (defined($rhOptional->{&CFGDEF_ALLOW_LIST}))
{
$strBuildSourceOptional .=
(defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') .
renderAllowList($rhOptional->{&CFGDEF_ALLOW_LIST}, $bCommand);
$bSingleLine = false;
}
if (defined($rhOptional->{&CFGDEF_ALLOW_RANGE}))
{
my @fyRange = @{$rhOptional->{&CFGDEF_ALLOW_RANGE}};
my $iMultiplier = $rhOptional->{&CFGDEF_TYPE} eq CFGDEF_TYPE_TIME ? 1000 : 1;
$strBuildSourceOptional =
(defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') .
"${strIndent} PARSE_RULE_OPTION_OPTIONAL_ALLOW_RANGE(" . ($fyRange[0] * $iMultiplier) . ', ' .
($fyRange[1] * $iMultiplier) . "),\n";
$bSingleLine = true;
}
if (defined($rhOptional->{&CFGDEF_DEPEND}))
{
$strBuildSourceOptional .=
(defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') .
renderDepend($rhOptional->{&CFGDEF_DEPEND}, $bCommand);
$bSingleLine = defined($rhOptional->{&CFGDEF_DEPEND}{&CFGDEF_DEPEND_LIST}) ? false : true;
}
if (defined($rhOptional->{&CFGDEF_DEFAULT}))
{
$strBuildSourceOptional .=
(defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') .
"${strIndent} PARSE_RULE_OPTION_OPTIONAL_DEFAULT(" .
($rhOptional->{&CFGDEF_DEFAULT_LITERAL} ? '' : '"') .
(defined($rhOptional->{&CFGDEF_TYPE}) && $rhOptional->{&CFGDEF_TYPE} eq CFGDEF_TYPE_TIME ?
$rhOptional->{&CFGDEF_DEFAULT} * 1000 : $rhOptional->{&CFGDEF_DEFAULT}) .
($rhOptional->{&CFGDEF_DEFAULT_LITERAL} ? '' : '"') .
"),\n";
$bSingleLine = true;
}
if ($bCommand && defined($rhOptional->{&CFGDEF_REQUIRED}))
{
$strBuildSourceOptional .=
(defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') .
"${strIndent} PARSE_RULE_OPTION_OPTIONAL_REQUIRED(" .
($rhOptional->{&CFGDEF_REQUIRED} ? 'true' : 'false') . "),\n";
$bSingleLine = true;
}
return $strBuildSourceOptional;
}
####################################################################################################################################
# Build configuration constants and data
####################################################################################################################################
sub buildConfigParse
{
# Build command parse data
# ------------------------------------------------------------------------------------------------------------------------------
my $rhCommandDefine = cfgDefineCommand();
my $strBuildSource =
"static const ParseRuleCommand parseRuleCommand[CFG_COMMAND_TOTAL] =\n" .
"{";
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
{
my $rhCommand = $rhCommandDefine->{$strCommand};
# Build command data
$strBuildSource .=
"\n" .
" //" . (qw{-} x 126) . "\n" .
" PARSE_RULE_COMMAND\n" .
" (\n" .
" PARSE_RULE_COMMAND_NAME(\"${strCommand}\"),\n";
if ($rhCommand->{&CFGDEF_PARAMETER_ALLOWED})
{
$strBuildSource .=
" PARSE_RULE_COMMAND_PARAMETER_ALLOWED(true),\n";
}
$strBuildSource .=
"\n" .
" PARSE_RULE_COMMAND_ROLE_VALID_LIST\n" .
" (\n";
foreach my $strCommandRole (sort(keys(%{$rhCommand->{&CFGDEF_COMMAND_ROLE}})))
{
$strBuildSource .=
" PARSE_RULE_COMMAND_ROLE(" . buildConfigCommandRoleEnum($strCommandRole) . ")\n";
}
$strBuildSource .=
" ),\n";
$strBuildSource .=
" ),\n";
};
$strBuildSource .=
"};\n";
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_COMMAND}{&BLD_SOURCE} = $strBuildSource;
# Build option group parse data
# ------------------------------------------------------------------------------------------------------------------------------
my $rhOptionGroupDefine = cfgDefineOptionGroup();
$strBuildSource =
"static const ParseRuleOptionGroup parseRuleOptionGroup[CFG_OPTION_GROUP_TOTAL] =\n" .
"{";
foreach my $strGroup (sort(keys(%{$rhOptionGroupDefine})))
{
$strBuildSource .=
"\n" .
" //" . (qw{-} x 126) . "\n" .
" PARSE_RULE_OPTION_GROUP\n" .
" (\n" .
" PARSE_RULE_OPTION_GROUP_NAME(\"" . $strGroup . "\"),\n" .
" ),\n";
}
$strBuildSource .=
"};\n";
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_OPTION_GROUP}{&BLD_SOURCE} = $strBuildSource;
# Build option parse data
# ------------------------------------------------------------------------------------------------------------------------------
my $rhConfigDefine = cfgDefine();
$strBuildSource =
"static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =\n" .
"{";
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
{
my $rhOption = $rhConfigDefine->{$strOption};
$strBuildSource .=
"\n" .
" // " . (qw{-} x 125) . "\n" .
" PARSE_RULE_OPTION\n" .
" (\n" .
" PARSE_RULE_OPTION_NAME(\"${strOption}\"),\n" .
" PARSE_RULE_OPTION_TYPE(" . buildConfigDefineOptionTypeEnum($rhOption->{&CFGDEF_TYPE}) . "),\n" .
" PARSE_RULE_OPTION_REQUIRED(" . ($rhOption->{&CFGDEF_REQUIRED} ? 'true' : 'false') . "),\n" .
" PARSE_RULE_OPTION_SECTION(cfgSection" .
(defined($rhOption->{&CFGDEF_SECTION}) ? ucfirst($rhOption->{&CFGDEF_SECTION}) : 'CommandLine') .
"),\n";
if ($rhOption->{&CFGDEF_SECURE})
{
$strBuildSource .=
" PARSE_RULE_OPTION_SECURE(true),\n";
}
if ($rhOption->{&CFGDEF_TYPE} eq CFGDEF_TYPE_HASH || $rhOption->{&CFGDEF_TYPE} eq CFGDEF_TYPE_LIST)
{
$strBuildSource .=
" PARSE_RULE_OPTION_MULTI(true),\n";
}
# Build group info
# --------------------------------------------------------------------------------------------------------------------------
if ($rhOption->{&CFGDEF_GROUP})
{
$strBuildSource .=
" PARSE_RULE_OPTION_GROUP_MEMBER(true),\n" .
" PARSE_RULE_OPTION_GROUP_ID(" . buildConfigOptionGroupEnum($rhOption->{&CFGDEF_GROUP}) . "),\n";
}
# Build command role valid lists
#---------------------------------------------------------------------------------------------------------------------------
my $strBuildSourceSub = "";
foreach my $strCommandRole (CFGCMD_ROLE_MAIN, CFGCMD_ROLE_ASYNC, CFGCMD_ROLE_LOCAL, CFGCMD_ROLE_REMOTE)
{
$strBuildSourceSub = "";
foreach my $strCommand (cfgDefineCommandList())
{
if (defined($rhOption->{&CFGDEF_COMMAND}{$strCommand}))
{
if (defined($rhOption->{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_COMMAND_ROLE}{$strCommandRole}))
{
$strBuildSourceSub .=
" PARSE_RULE_OPTION_COMMAND(" . buildConfigCommandEnum($strCommand) . ")\n";
}
}
}
if ($strBuildSourceSub ne "")
{
$strBuildSource .=
"\n" .
" PARSE_RULE_OPTION_COMMAND_ROLE_" . uc($strCommandRole) . "_VALID_LIST\n" .
" (\n" .
$strBuildSourceSub .
" ),\n";
}
}
# Render optional data and command overrides
# --------------------------------------------------------------------------------------------------------------------------
my $strBuildSourceOptional = renderOptional($rhOption, false);
foreach my $strCommand (cfgDefineCommandList())
{
my $strBuildSourceOptionalCommand;
my $rhCommand = $rhOption->{&CFGDEF_COMMAND}{$strCommand};
if (defined($rhCommand))
{
$strBuildSourceOptionalCommand = renderOptional($rhCommand, true, $strCommand, $strOption);
if (defined($strBuildSourceOptionalCommand))
{
$strBuildSourceOptional .=
(defined($strBuildSourceOptional) ? "\n" : '') .
" PARSE_RULE_OPTION_OPTIONAL_COMMAND_OVERRIDE\n" .
" (\n" .
" PARSE_RULE_OPTION_OPTIONAL_COMMAND(" . buildConfigCommandEnum($strCommand) . "),\n" .
"\n" .
$strBuildSourceOptionalCommand .
" )\n";
}
}
}
if (defined($strBuildSourceOptional))
{
$strBuildSource .=
"\n" .
" PARSE_RULE_OPTION_OPTIONAL_LIST\n" .
" (\n" .
$strBuildSourceOptional .
" ),\n";
}
$strBuildSource .=
" ),\n";
}
$strBuildSource .=
"};\n";
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_OPTION}{&BLD_SOURCE} = $strBuildSource;
# Build option list for getopt_long()
#-------------------------------------------------------------------------------------------------------------------------------
$strBuildSource =
"static const struct option optionList[] =\n" .
"{";
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
{
my $rhOption = $rhConfigDefine->{$strOption};
my $strOptionEnum = buildConfigOptionEnum($strOption);
my $strOptionArg = ($rhOption->{&CFGDEF_TYPE} ne CFGDEF_TYPE_BOOLEAN ? " .has_arg = required_argument,\n" : '');
my $strOptionPrefix = $rhConfigDefine->{$strOption}{&CFGDEF_PREFIX};
my @stryOptionName = ($strOption);
if (defined($rhOption->{&CFGDEF_DEPRECATE}))
{
foreach my $strOptionNameAlt (sort(keys(%{$rhOption->{&CFGDEF_DEPRECATE}})))
{
push(@stryOptionName, $strOptionNameAlt);
}
}
$strBuildSource .=
"\n" .
" // ${strOption} option" . (@stryOptionName > 1 ? ' and deprecations' : '') . "\n" .
" // " . (qw{-} x 125) . "\n";
for (my $iOptionIdx = 1; $iOptionIdx <= $rhOption->{&CFGDEF_INDEX_TOTAL}; $iOptionIdx++)
{
for (my $iOptionNameIdx = 0; $iOptionNameIdx < @stryOptionName; $iOptionNameIdx++)
{
my $strOptionName = $stryOptionName[$iOptionNameIdx];
my $rhNameAlt = $rhOption->{&CFGDEF_DEPRECATE}{$strOptionName};
# Skip alt name if it is not valid for this option index
if ($iOptionNameIdx > 0 && defined($rhNameAlt->{&CFGDEF_INDEX}) && $rhNameAlt->{&CFGDEF_INDEX} != $iOptionIdx)
{
next;
}
# Generate output name
my $strOptionNameOut = $strOptionName;
my $strOptionConst;
if (defined($strOptionPrefix))
{
if ($iOptionNameIdx == 0)
{
$strOptionNameOut =
"${strOptionPrefix}${iOptionIdx}-" . substr($strOptionName, length($strOptionPrefix) + 1);
}
else
{
$strOptionNameOut =~ s/\?/$iOptionIdx/g;
}
}
# Generate option value used for parsing (offset is added so options don't conflict with getopt_long return values)
my $strOptionFlag = 'PARSE_OPTION_FLAG |';
# Build option constant name if this is the current name for the option
if ($iOptionNameIdx == 0)
{
if (!$rhConfigDefine->{$strOption}{&CFGDEF_GROUP})
{
$strOptionConst = "CFGOPT_" . uc($strOptionNameOut);
$strOptionConst =~ s/\-/_/g;
}
}
# Else use bare string and mark as deprecated
else
{
$strOptionFlag .= ' PARSE_DEPRECATE_FLAG |';
}
my $strOptionVal =
($rhOption->{&CFGDEF_GROUP} ? "(" . ($iOptionIdx - 1) . " << PARSE_KEY_IDX_SHIFT) | " : '') . $strOptionEnum;
# Add option
$strBuildSource .=
" {\n" .
" .name = \"${strOptionNameOut}\",\n" .
$strOptionArg .
" .val = ${strOptionFlag} ${strOptionVal},\n" .
" },\n";
# Add negation when defined
if ($rhOption->{&CFGDEF_NEGATE} &&
!($iOptionNameIdx > 0 && defined($rhNameAlt->{&CFGDEF_NEGATE}) && !$rhNameAlt->{&CFGDEF_NEGATE}))
{
$strBuildSource .=
" {\n" .
" .name = \"no-${strOptionNameOut}\",\n" .
" .val = ${strOptionFlag} PARSE_NEGATE_FLAG | ${strOptionVal},\n" .
" },\n";
}
# Add reset when defined
if ($rhOption->{&CFGDEF_RESET} &&
!($iOptionNameIdx > 0 && defined($rhNameAlt->{&CFGDEF_RESET}) && !$rhNameAlt->{&CFGDEF_RESET}))
{
$strBuildSource .=
" {\n" .
" .name = \"reset-${strOptionNameOut}\",\n" .
" .val = ${strOptionFlag} PARSE_RESET_FLAG | ${strOptionVal},\n" .
" },\n";
}
}
}
}
# The option list needs to be terminated or getopt_long will just keep on reading
$strBuildSource .=
" // Terminate option list\n" .
" {\n" .
" .name = NULL\n" .
" }\n" .
"};\n";
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_OPTION_GETOPT}{&BLD_SOURCE} = $strBuildSource;
# Build option resolve order list. This allows the option validation in C to take place in a single pass.
#
# Always process the stanza option first since confusing error message are produced if it is missing.
#-------------------------------------------------------------------------------------------------------------------------------
my @stryResolveOrder = (buildConfigOptionEnum(CFGOPT_STANZA));
my $rhResolved = {&CFGOPT_STANZA => true};
my $bAllResolved;
do
{
# Assume that we will finish on this loop
$bAllResolved = true;
# Loop through all options
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
{
my $bSkip = false;
# Check the default depend
my $strOptionDepend =
ref($rhConfigDefine->{$strOption}{&CFGDEF_DEPEND}) eq 'HASH' ?
$rhConfigDefine->{$strOption}{&CFGDEF_DEPEND}{&CFGDEF_DEPEND_OPTION} :
$rhConfigDefine->{$strOption}{&CFGDEF_DEPEND};
if (defined($strOptionDepend) && !$rhResolved->{$strOptionDepend})
{
# &log(WARN, "$strOptionDepend is not resolved");
$bSkip = true;
}
# Check the command depends
foreach my $strCommand (sort(keys(%{$rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}})))
{
my $strOptionDepend =
ref($rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_DEPEND}) eq 'HASH' ?
$rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_DEPEND}{&CFGDEF_DEPEND_OPTION} :
$rhConfigDefine->{$strOption}{&CFGDEF_COMMAND}{$strCommand}{&CFGDEF_DEPEND};
if (defined($strOptionDepend) && !$rhResolved->{$strOptionDepend})
{
$bSkip = true;
}
}
# If dependency was not found try again on the next loop
if ($bSkip)
{
$bAllResolved = false;
}
# Else add option to resolve order list
elsif (!$rhResolved->{$strOption})
{
push(@stryResolveOrder, buildConfigOptionEnum($strOption));
$rhResolved->{$strOption} = true;
}
}
}
while (!$bAllResolved);
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_OPTION_RESOLVE}{&BLD_SOURCE} =
"static const ConfigOption optionResolveOrder[] =\n" .
"{\n" .
" " . join(",\n ", @stryResolveOrder) . ",\n" .
"};\n";
return $rhBuild;
}
push @EXPORT, qw(buildConfigParse);
1;