#################################################################################################################################### # Auto-Generate Command and Option Configuration Definition Enums, Constants and Data #################################################################################################################################### package pgBackRestBuild::Config::BuildDefine; 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 pgBackRest::Common::Log; use pgBackRest::Common::String; use pgBackRest::Version; use BackRestDoc::Common::DocConfig; use BackRestDoc::Common::DocRender; use pgBackRestBuild::Build::Common; use pgBackRestBuild::Config::Data; #################################################################################################################################### # Constants #################################################################################################################################### use constant BLDLCL_FILE_DEFINE => 'define'; use constant BLDLCL_DATA_COMMAND => '01-dataCommand'; use constant BLDLCL_DATA_OPTION => '02-dataOption'; use constant BLDLCL_ENUM_COMMAND => '01-enumCommand'; use constant BLDLCL_ENUM_OPTION_TYPE => '02-enumOptionType'; use constant BLDLCL_ENUM_OPTION => '03-enumOption'; #################################################################################################################################### # Definitions for constants and data to build #################################################################################################################################### my $strSummary = 'Command and Option Definition'; my $rhBuild = { &BLD_FILE => { &BLDLCL_FILE_DEFINE => { &BLD_SUMMARY => $strSummary, &BLD_ENUM => { &BLDLCL_ENUM_COMMAND => { &BLD_SUMMARY => 'Command define', &BLD_NAME => 'ConfigDefineCommand', }, &BLDLCL_ENUM_OPTION_TYPE => { &BLD_SUMMARY => 'Option type define', &BLD_NAME => 'ConfigDefineOptionType', }, &BLDLCL_ENUM_OPTION => { &BLD_SUMMARY => 'Option define', &BLD_NAME => 'ConfigDefineOption', }, }, &BLD_DATA => { &BLDLCL_DATA_COMMAND => { &BLD_SUMMARY => 'Command define data', }, &BLDLCL_DATA_OPTION => { &BLD_SUMMARY => 'Option define data', }, }, }, }, }; #################################################################################################################################### # Generate enum names #################################################################################################################################### sub buildConfigDefineCommandEnum { return bldEnum('cfgDefCmd', shift) } push @EXPORT, qw(buildConfigDefineCommandEnum); sub buildConfigDefineOptionTypeEnum { return bldEnum('cfgDefOptType', shift); } push @EXPORT, qw(buildConfigDefineOptionTypeEnum); sub buildConfigDefineOptionEnum { return bldEnum('cfgDefOpt', shift); } push @EXPORT, qw(buildConfigDefineOptionEnum); #################################################################################################################################### # Helper function to format help text #################################################################################################################################### sub helpFormatText { my $oManifest = shift; my $oDocRender = shift; my $oText = shift; my $iIndent = shift; my $iLength = shift; # Split the string into lines for processing my @stryText = split("\n", trim($oManifest->variableReplace($oDocRender->processText($oText)))); my $strText; my $iIndex = 0; foreach my $strLine (@stryText) { # Add a linefeed if this is not the first line if (defined($strText)) { $strText .= "\n"; } # Escape perl special characters $strLine =~ s/\"/\\"/g; my $strPart; my $bFirst = true; # Split the line for output if it's too long do { ($strPart, $strLine) = stringSplit($strLine, ' ', defined($strPart) ? $iLength - 4 : $iLength); $strText .= ' ' x $iIndent; if (!$bFirst) { $strText .= " "; } $strText .= "\"${strPart}"; if (defined($strLine)) { $strText .= "\"\n"; } else { $strText .= ($iIndex + 1 < @stryText ? '\n' : '') . '"'; } $bFirst = false; } while (defined($strLine)); $iIndex++; } return $strText; } #################################################################################################################################### # Helper functions for building optional option data #################################################################################################################################### sub renderAllowList { my $ryAllowList = shift; my $bCommandIndent = shift; my $strIndent = $bCommandIndent ? ' ' : ''; return "${strIndent} CFGDEFDATA_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} CFGDEFDATA_OPTION_OPTIONAL_DEPEND_LIST\n" . "${strIndent} (\n" . "${strIndent} " . buildConfigDefineOptionEnum($strDependOption) . ",\n" . "${strIndent} " . join(",\n${strIndent} ", bldQuoteList($ryDependList)) . "\n" . "${strIndent} )\n"; } return "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_DEPEND(" . buildConfigDefineOptionEnum($strDependOption) . ")\n"; } sub renderOptional { my $rhOptional = shift; my $bCommand = shift; my $rhOptionHelp = shift; my $oManifest = shift; my $oDocRender = 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}}; $strBuildSourceOptional .= (defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_ALLOW_RANGE(" . $fyRange[0] . ', ' . $fyRange[1] . ")\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} CFGDEFDATA_OPTION_OPTIONAL_DEFAULT(\"" . $rhOptional->{&CFGDEF_DEFAULT} . "\")\n"; $bSingleLine = true; } if (defined($rhOptional->{&CFGDEF_PREFIX})) { $strBuildSourceOptional .= (defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_PREFIX(\"" . $rhOptional->{&CFGDEF_PREFIX} . "\")\n"; $bSingleLine = true; } # Output alternate name if (!$bCommand && defined($rhOptionHelp->{&CONFIG_HELP_NAME_ALT})) { $strBuildSourceOptional .= (defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_HELP_NAME_ALT(" . join(', ', bldQuoteList($rhOptionHelp->{&CONFIG_HELP_NAME_ALT})) . ")\n"; $bSingleLine = true; } if ($bCommand && defined($rhOptional->{&CFGDEF_INTERNAL})) { $strBuildSourceOptional .= (defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_INTERNAL(" . ($rhOptional->{&CFGDEF_INTERNAL} ? 'true' : 'false') . ")\n"; $bSingleLine = true; } if ($bCommand && defined($rhOptional->{&CFGDEF_REQUIRED})) { $strBuildSourceOptional .= (defined($strBuildSourceOptional) && !$bSingleLine ? "\n" : '') . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_REQUIRED(" . ($rhOptional->{&CFGDEF_REQUIRED} ? 'true' : 'false') . ")\n"; $bSingleLine = true; } if ($bCommand && defined($rhOptionHelp) && defined($rhOptionHelp->{&CONFIG_HELP_SOURCE}) && $rhOptionHelp->{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_COMMAND) { my $strSummary = helpFormatText($oManifest, $oDocRender, $rhOptionHelp->{&CONFIG_HELP_SUMMARY}, 0, 72); if (length($strSummary) > 74) { confess("summary for command '${strCommand}', option '${strOption}' may not be greater than 72 characters"); } $strBuildSourceOptional .= (defined($strBuildSourceOptional) ? "\n" : '') . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_HELP_SUMMARY(${strSummary})\n" . "${strIndent} CFGDEFDATA_OPTION_OPTIONAL_HELP_DESCRIPTION\n" . "${strIndent} (\n" . helpFormatText($oManifest, $oDocRender, $rhOptionHelp->{&CONFIG_HELP_DESCRIPTION}, 20, 111) . "\n" . "${strIndent} )\n"; } return $strBuildSourceOptional; } #################################################################################################################################### # Build configuration constants and data #################################################################################################################################### sub buildConfigDefine { # Load help data #------------------------------------------------------------------------------------------------------------------------------- require BackRestDoc::Common::Doc; require BackRestDoc::Common::DocManifest; my $strDocPath = abs_path(dirname($0) . '/../doc'); my $oStorageDoc = new pgBackRestTest::Common::Storage( $strDocPath, new pgBackRestTest::Common::StoragePosix({bFileSync => false, bPathSync => false})); my @stryEmpty = []; my $oManifest = new BackRestDoc::Common::DocManifest( $oStorageDoc, \@stryEmpty, \@stryEmpty, \@stryEmpty, \@stryEmpty, undef, $strDocPath, false, false); my $oDocRender = new BackRestDoc::Common::DocRender('text', $oManifest, false); my $oDocConfig = new BackRestDoc::Common::DocConfig( new BackRestDoc::Common::Doc("${strDocPath}/xml/reference.xml"), $oDocRender); my $hConfigHelp = $oDocConfig->{oConfigHash}; # Build command constants and data #------------------------------------------------------------------------------------------------------------------------------- my $rhEnum = $rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_ENUM}{&BLDLCL_ENUM_COMMAND}; my $strBuildSource = "static ConfigDefineCommandData configDefineCommandData[] = CFGDEFDATA_COMMAND_LIST\n" . "("; foreach my $strCommand (cfgDefineCommandList()) { # Get command help my $hCommandHelp = $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand}; # Build C enum my $strCommandEnum = buildConfigDefineCommandEnum($strCommand); push(@{$rhEnum->{&BLD_LIST}}, $strCommandEnum); # Build command data $strBuildSource .= "\n" . " CFGDEFDATA_COMMAND\n" . " (\n" . " CFGDEFDATA_COMMAND_NAME(\"${strCommand}\")\n"; # Output help if (defined($hCommandHelp)) { $strBuildSource .= "\n"; # Output command summary my $strSummary = helpFormatText($oManifest, $oDocRender, $hCommandHelp->{&CONFIG_HELP_SUMMARY}, 0, 72); if (length($strSummary) > 74) { confess("summary for command '${strCommand}' may not be greater than 72 characters"); } $strBuildSource .= " CFGDEFDATA_COMMAND_HELP_SUMMARY(${strSummary})\n"; # Output description $strBuildSource .= " CFGDEFDATA_COMMAND_HELP_DESCRIPTION\n" . " (\n" . helpFormatText($oManifest, $oDocRender, $hCommandHelp->{&CONFIG_HELP_DESCRIPTION}, 12, 119) . "\n" . " )\n"; } $strBuildSource .= " )\n"; }; $strBuildSource .= ")\n"; $rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_COMMAND}{&BLD_SOURCE} = $strBuildSource; # Build option type constants #------------------------------------------------------------------------------------------------------------------------------- $rhEnum = $rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_ENUM}{&BLDLCL_ENUM_OPTION_TYPE}; foreach my $strOptionType (cfgDefineOptionTypeList()) { # Build C enum my $strOptionTypeEnum = buildConfigDefineOptionTypeEnum($strOptionType); push(@{$rhEnum->{&BLD_LIST}}, $strOptionTypeEnum); }; # Build option constants and data #------------------------------------------------------------------------------------------------------------------------------- my $rhConfigDefine = cfgDefine(); $rhEnum = $rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_ENUM}{&BLDLCL_ENUM_OPTION}; $strBuildSource = "static ConfigDefineOptionData configDefineOptionData[] = CFGDEFDATA_OPTION_LIST\n" . "("; foreach my $strOption (sort(keys(%{$rhConfigDefine}))) { # Get option help my $hOptionHelp = $hConfigHelp->{&CONFIG_HELP_OPTION}{$strOption}; # Build C enum my $strOptionEnum = buildConfigDefineOptionEnum($strOption); push(@{$rhEnum->{&BLD_LIST}}, $strOptionEnum); # Build option data my $rhOption = $rhConfigDefine->{$strOption}; my $strOptionPrefix = $rhOption->{&CFGDEF_PREFIX}; $strBuildSource .= "\n" . " // " . (qw{-} x 125) . "\n" . " CFGDEFDATA_OPTION\n" . " (\n"; my $bRequired = $rhOption->{&CFGDEF_REQUIRED}; $strBuildSource .= " CFGDEFDATA_OPTION_NAME(\"${strOption}\")\n" . " CFGDEFDATA_OPTION_REQUIRED(" . ($bRequired ? 'true' : 'false') . ")\n" . " CFGDEFDATA_OPTION_SECTION(cfgDefSection" . (defined($rhOption->{&CFGDEF_SECTION}) ? ucfirst($rhOption->{&CFGDEF_SECTION}) : 'CommandLine') . ")\n" . " CFGDEFDATA_OPTION_TYPE(" . buildConfigDefineOptionTypeEnum($rhOption->{&CFGDEF_TYPE}) . ")\n" . " CFGDEFDATA_OPTION_INTERNAL(" . ($rhOption->{&CFGDEF_INTERNAL} ? 'true' : 'false') . ")\n" . "\n" . " CFGDEFDATA_OPTION_INDEX_TOTAL(" . $rhOption->{&CFGDEF_INDEX_TOTAL} . ")\n" . " CFGDEFDATA_OPTION_SECURE(" . ($rhOption->{&CFGDEF_SECURE} ? 'true' : 'false') . ")\n"; if (defined($hOptionHelp)) { $strBuildSource .= "\n"; # Output section my $strSection = defined($hOptionHelp->{&CONFIG_HELP_SECTION}) ? $hOptionHelp->{&CONFIG_HELP_SECTION} : 'general'; if (length($strSection) > 72) { confess("section for option '${strOption}' may not be greater than 72 characters"); } $strBuildSource .= " CFGDEFDATA_OPTION_HELP_SECTION(\"${strSection}\")\n"; # Output summary my $strSummary = helpFormatText($oManifest, $oDocRender, $hOptionHelp->{&CONFIG_HELP_SUMMARY}, 0, 72); if (length($strSummary) > 74) { confess("summary for option '${strOption}' may not be greater than 72 characters"); } $strBuildSource .= " CFGDEFDATA_OPTION_HELP_SUMMARY(${strSummary})\n"; # Output description $strBuildSource .= " CFGDEFDATA_OPTION_HELP_DESCRIPTION\n" . " (\n" . helpFormatText($oManifest, $oDocRender, $hOptionHelp->{&CONFIG_HELP_DESCRIPTION}, 12, 119) . "\n" . " )\n"; } $strBuildSource .= "\n" . " CFGDEFDATA_OPTION_COMMAND_LIST\n" . " (\n"; foreach my $strCommand (cfgDefineCommandList()) { if (defined($rhOption->{&CFGDEF_COMMAND}{$strCommand})) { $strBuildSource .= " CFGDEFDATA_OPTION_COMMAND(" . buildConfigDefineCommandEnum($strCommand) . ")\n"; } } $strBuildSource .= " )\n"; # Render optional data my $strBuildSourceOptional = renderOptional($rhOption, false, $hOptionHelp, $oManifest, $oDocRender); # Render command overrides foreach my $strCommand (cfgDefineCommandList()) { my $strBuildSourceOptionalCommand; my $rhCommand = $rhOption->{&CFGDEF_COMMAND}{$strCommand}; if (defined($rhCommand)) { $strBuildSourceOptionalCommand = renderOptional( $rhCommand, true, $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption}, $oManifest, $oDocRender, $strCommand, $strOption); if (defined($strBuildSourceOptionalCommand)) { $strBuildSourceOptional .= (defined($strBuildSourceOptional) ? "\n" : '') . " CFGDEFDATA_OPTION_OPTIONAL_COMMAND_OVERRIDE\n" . " (\n" . " CFGDEFDATA_OPTION_OPTIONAL_COMMAND(" . buildConfigDefineCommandEnum($strCommand) . ")\n" . "\n" . $strBuildSourceOptionalCommand . " )\n"; } } }; if (defined($strBuildSourceOptional)) { $strBuildSource .= "\n" . " CFGDEFDATA_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; return $rhBuild; } push @EXPORT, qw(buildConfigDefine); 1;