1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-03 00:26:59 +02:00
Files
pgbackrest/build/lib/pgBackRestBuild/Config/BuildHelp.pm
David Steele f3f0c64a78 Complete migration of config code generation to C.
This was started in c5ae047e but did not include generation of parse.auto.c.

The parser has also been improved with better errors and multiple passes to reduce dependency on ordering and produce and cleaner output.

Option order resolution now includes cycle detection.
2021-07-28 19:59:23 -04:00

491 lines
16 KiB
Perl

####################################################################################################################################
# Auto-Generate Command and Option Help Pack
####################################################################################################################################
package pgBackRestBuild::Config::BuildHelp;
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 pgBackRestDoc::Common::DocConfig;
use pgBackRestDoc::Common::DocRender;
use pgBackRestDoc::Common::Log;
use pgBackRestDoc::Common::String;
use pgBackRestDoc::ProjectInfo;
use pgBackRestBuild::Build::Common;
use pgBackRestBuild::Config::Data;
####################################################################################################################################
# Constants
####################################################################################################################################
use constant BLDLCL_FILE_DEFINE => 'help';
use constant BLDLCL_DATA_COMMAND => '01-command';
####################################################################################################################################
# Definitions for data to build
####################################################################################################################################
my $strSummary = 'Help Definition Pack';
my $rhBuild =
{
&BLD_FILE =>
{
&BLDLCL_FILE_DEFINE =>
{
&BLD_SUMMARY => $strSummary,
&BLD_DATA =>
{
&BLDLCL_DATA_COMMAND =>
{
&BLD_SUMMARY => 'Command help',
},
},
},
},
};
####################################################################################################################################
# Format pack tag
####################################################################################################################################
use constant PCK_TYPE_ARRAY => 1;
use constant PCK_TYPE_BOOL => 2;
use constant PCK_TYPE_OBJ => 5;
use constant PCK_TYPE_STR => 7;
# Pack an unsigned 64-bit integer to base-128 varint encoding and output to hex. This is a simplified version of
# pckWriteUInt64Internal() so see that function for more information.
sub packIntFormat
{
my $iValue = shift;
my $strResult = '';
while ($iValue >= 0x80)
{
# Encode the lower order 7 bits, adding the continuation bit to indicate there is more data
$strResult .= sprintf(" 0x%02X,", ($iValue & 0x7f) | 0x80);
# Shift the value to remove bits that have been encoded
$iValue >>= 7;
}
return $strResult . sprintf(" 0x%02X,", $iValue);
}
# Write pack field tag and data. This is a cut down version of pckWriteTag() so see that function for more information.
sub packTagFormat
{
my $strName = shift;
my $iType = shift;
my $iDelta = shift;
my $xData = shift;
my $iIndent = shift;
my $strIndent = ' ' x $iIndent;
# Pack delta bits and determine value for various pack. See pckWriteTag() for more detailed information.
my $iValue = undef;
my $iBits = undef;
if ($iType == PCK_TYPE_STR || $iType == PCK_TYPE_BOOL)
{
$iBits = $iDelta & 0x3;
$iDelta >>= 2;
if ($iDelta != 0)
{
$iBits |= 0x4;
}
if ($iType == PCK_TYPE_STR)
{
$iBits |= 0x8;
$iValue = length($xData);
}
else
{
$iBits |= $xData ? 0x8 : 0;
undef($xData);
}
}
elsif ($iType == PCK_TYPE_ARRAY || $iType == PCK_TYPE_OBJ)
{
$iBits |= $iDelta & 0x7;
$iDelta >>= 3;
if ($iDelta != 0)
{
$iBits |= 0x8;
}
}
# Output pack type and bits
my $strResult = sprintf("${strIndent}0x%02X,", ($iType << 4) | $iBits);
# Output additional id delta when present
if ($iDelta > 0)
{
$strResult .= packIntFormat($iDelta);
}
# Output value when present
if (defined($iValue))
{
$strResult .= packIntFormat($iValue);
}
# Output pack name
$strResult .= " // ${strName}";
# Output data in hex format
if (defined($xData) && length($xData) > 0)
{
$strResult .= "\n${strIndent} ";
my $iLength = length($strIndent) + 4;
my $bLastLF = false;
my $bFirst = true;
# Loop through all chars
foreach my $iChar (unpack("W*", $xData))
{
# Encode char to hex
my $strOut = sprintf("0x%02X,", $iChar);
# Break on linefeeds to prevent diffs within a paragraph of text from cascading through all the data
if ($bLastLF && $iChar != 0xA)
{
$strResult .= "\n${strIndent} ";
$iLength = length($strIndent) + 4;
$bFirst = true;
}
$bLastLF = $iChar == 0xA;
# If this hex would exceed the line length then break and write on the next line
if ($iLength + length($strOut) + 1 > 132)
{
$strResult .= "\n${strIndent} ${strOut}";
$iLength = length($strIndent) + 4 + length($strOut);
}
# Else append the hex
else
{
$strResult .= ($bFirst ? '' : ' ') . "${strOut}";
$iLength += length($strOut) + ($bFirst ? 0 : 1);
$bFirst = false;
}
}
}
return $strResult . "\n";
}
####################################################################################################################################
# Build help data
####################################################################################################################################
sub buildConfigHelp
{
# Load help data
#-------------------------------------------------------------------------------------------------------------------------------
require pgBackRestDoc::Common::Doc;
require pgBackRestDoc::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 pgBackRestDoc::Common::DocManifest(
$oStorageDoc, \@stryEmpty, \@stryEmpty, \@stryEmpty, \@stryEmpty, undef, $strDocPath, false, false);
my $oDocRender = new pgBackRestDoc::Common::DocRender('text', $oManifest, false);
my $oDocConfig =
new pgBackRestDoc::Common::DocConfig(
new pgBackRestDoc::Common::Doc("${strDocPath}/xml/reference.xml"), $oDocRender);
my $hConfigHelp = $oDocConfig->{oConfigHash};
# Build command help
#-------------------------------------------------------------------------------------------------------------------------------
my $rhCommandDefine = cfgDefineCommand();
my $strBuildSource =
"static const unsigned char helpDataPack[] =\n" .
"{\n" .
" // Commands\n" .
" // " . (qw{-} x 125) . "\n";
$strBuildSource .= packTagFormat("Commands begin", PCK_TYPE_ARRAY, 0, undef, 4);
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
{
my $rhCommand = $rhCommandDefine->{$strCommand};
my $iDelta = 0;
# Get command help
my $rhCommandHelp = $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand};
if (!defined($rhCommandHelp))
{
confess "no help for command ${strCommand}"
}
# Build command data
$strBuildSource .=
"\n" .
" // ${strCommand} command\n" .
" // " . (qw{-} x 121) . "\n";
if ($rhCommand->{&CFGDEF_INTERNAL})
{
$strBuildSource .= packTagFormat("Internal", PCK_TYPE_BOOL, 0, true, 8);
}
else
{
$iDelta++;
}
my $strSummary = trim($oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_SUMMARY})));
if (length($strSummary) > 72)
{
confess("summary for command '${strCommand}' may not be greater than 72 characters");
}
$strBuildSource .= packTagFormat("Summary", PCK_TYPE_STR, $iDelta, $strSummary, 8);
$iDelta = 0;
$strBuildSource .= packTagFormat(
"Description", PCK_TYPE_STR, 0,
trim($oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_DESCRIPTION}))), 8);
};
$strBuildSource .=
"\n" .
" 0x00, // Commands end\n";
# Build option help
#-------------------------------------------------------------------------------------------------------------------------------
my $rhConfigDefine = cfgDefine();
$strBuildSource .=
"\n" .
" // Options\n" .
" // " . (qw{-} x 125) . "\n";
$strBuildSource .= packTagFormat("Options begin", PCK_TYPE_ARRAY, 0, undef, 4);
my $iDelta = 0;
foreach my $strOption (sort(keys(%{$rhConfigDefine})))
{
my $bFirst = true;
my $bInternal = false;
# Build option data
my $rhOption = $rhConfigDefine->{$strOption};
# Get option help
my $rhOptionHelp = $hConfigHelp->{&CONFIG_HELP_OPTION}{$strOption};
# Build command data
$strBuildSource .=
"\n" .
" // ${strOption} option\n" .
" // " . (qw{-} x 121) . "\n";
# Internal
if ($rhOption->{&CFGDEF_INTERNAL})
{
$strBuildSource .= packTagFormat("Internal", PCK_TYPE_BOOL, $iDelta, true, 8);
$iDelta = 0;
$bInternal = true;
}
else
{
$iDelta++;
}
if (defined($rhOptionHelp))
{
# Section
my $strSection = $rhOptionHelp->{&CONFIG_HELP_SECTION};
if (defined($strSection))
{
if (length($strSection) > 72)
{
confess("section for option '${strOption}' may not be greater than 72 characters");
}
$strBuildSource .= packTagFormat("Section", PCK_TYPE_STR, $iDelta, $strSection, 8);
$iDelta = 0;
}
else
{
$iDelta++;
}
# Summary
my $strSummary = trim($oManifest->variableReplace($oDocRender->processText($rhOptionHelp->{&CONFIG_HELP_SUMMARY})));
if (length($strSummary) > 72)
{
confess("summary for option '${strOption}' may not be greater than 72 characters");
}
$strBuildSource .= packTagFormat("Summary", PCK_TYPE_STR, $iDelta, $strSummary, 8);
$iDelta = 0;
# Description
$strBuildSource .= packTagFormat(
"Description", PCK_TYPE_STR, $iDelta,
trim($oManifest->variableReplace($oDocRender->processText($rhOptionHelp->{&CONFIG_HELP_DESCRIPTION}))), 8);
$bFirst = false;
}
else
{
$iDelta += 3;
}
# Output deprecated names
my $stryDeprecatedName = $rhOptionHelp->{&CONFIG_HELP_NAME_ALT};
if (defined($stryDeprecatedName))
{
$strBuildSource .=
($bFirst ? '' : "\n") .
packTagFormat("Deprecated names begin", PCK_TYPE_ARRAY, $iDelta, undef, 8);
$iDelta = 0;
foreach my $strDeprecatedName (@{$stryDeprecatedName})
{
$strBuildSource .= packTagFormat($strDeprecatedName, PCK_TYPE_STR, 0, $strDeprecatedName, 12);
}
$strBuildSource .=
" 0x00, // Deprecated names end\n";
$bFirst = false;
}
else
{
$iDelta++;
}
# Command overrides
my $strBuildSourceCommands;
my $iCommandId = 0;
my $iLastCommandId = 0;
foreach my $strCommand (sort(keys(%{$rhCommandDefine})))
{
my $rhCommand = $rhOption->{&CFGDEF_COMMAND}{$strCommand};
my $iDeltaCommand = 0;
my $strBuildSourceCommand;
if (defined($rhCommand))
{
if ($bInternal && defined($rhCommand->{&CFGDEF_INTERNAL}) && !$rhCommand->{&CFGDEF_INTERNAL})
{
confess("option '${strOption}' is internal but command '${strCommand}' override is not");
}
# Internal
if (defined($rhCommand->{&CFGDEF_INTERNAL}) && $bInternal != $rhCommand->{&CFGDEF_INTERNAL})
{
$strBuildSourceCommand .=
packTagFormat("Internal", PCK_TYPE_BOOL, $iDeltaCommand, true, 16);
$iDeltaCommand = 0;
}
else
{
$iDeltaCommand++;
}
my $rhCommandHelp = $hConfigHelp->{&CONFIG_HELP_COMMAND}{$strCommand}{&CONFIG_HELP_OPTION}{$strOption};
if (defined($rhCommandHelp->{&CONFIG_HELP_SOURCE}) &&
$rhCommandHelp->{&CONFIG_HELP_SOURCE} eq CONFIG_HELP_SOURCE_COMMAND)
{
# Summary
my $strSummary = trim(
$oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_SUMMARY})));
if (length($strSummary) > 72)
{
confess("summary for command '${strCommand}' option '${strOption}' may not be greater than 72 characters");
}
$strBuildSourceCommand .=
packTagFormat("Summary", PCK_TYPE_STR, $iDeltaCommand, $strSummary, 16);
$iDeltaCommand = 0;
# Description
$strBuildSourceCommand .= packTagFormat(
"Description", PCK_TYPE_STR, $iDeltaCommand,
trim($oManifest->variableReplace($oDocRender->processText($rhCommandHelp->{&CONFIG_HELP_DESCRIPTION}))),
16);
}
if (defined($strBuildSourceCommand))
{
$strBuildSourceCommands .=
"\n" .
packTagFormat(
"Command ${strCommand} override begin", PCK_TYPE_OBJ, $iCommandId - $iLastCommandId, undef, 12) .
$strBuildSourceCommand .
" 0x00, // Command ${strCommand} override end\n";
$iLastCommandId = $iCommandId + 1;
}
}
$iCommandId++;
}
if (defined($strBuildSourceCommands))
{
$strBuildSource .=
($bFirst ? '' : "\n") .
packTagFormat("Command overrides begin", PCK_TYPE_ARRAY, $iDelta, undef, 8) .
$strBuildSourceCommands . "\n" .
" 0x00, // Command overrides end\n";
$iDelta = 0;
$bFirst = false;
}
else
{
$iDelta++;
}
}
$strBuildSource .=
"\n" .
" 0x00, // Options end\n";
$strBuildSource .=
"\n" .
" 0x00, // Pack end\n" .
"};\n";
$rhBuild->{&BLD_FILE}{&BLDLCL_FILE_DEFINE}{&BLD_DATA}{&BLDLCL_DATA_COMMAND}{&BLD_SOURCE} = $strBuildSource;
return $rhBuild;
}
push @EXPORT, qw(buildConfigHelp);
1;