1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/build/lib/pgBackRestBuild/Config/BuildHelp.pm
David Steele f520ecc89a Move help data from define.auto.c/config.auto.c to a pack.
The help data can be represented more compactly in a pack and this separates data needed for help from data needed for parsing, freeing each to have a more appropriate representation.
2020-12-16 15:59:36 -05:00

499 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::Build;
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 => 'pckTypeArray';
use constant PCK_TYPE_BOOL => 'pckTypeBool';
use constant PCK_TYPE_OBJ => 'pckTypeObj';
use constant PCK_TYPE_STR => 'pckTypeStr';
# 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 $strType = 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 ($strType eq PCK_TYPE_STR || $strType eq PCK_TYPE_BOOL)
{
$iBits = $iDelta & 0x3;
$iDelta >>= 2;
if ($iDelta != 0)
{
$iBits |= 0x4;
}
if ($strType eq PCK_TYPE_STR)
{
$iBits |= 0x8;
$iValue = length($xData);
}
else
{
$iBits |= $xData ? 0x8 : 0;
undef($xData);
}
}
elsif ($strType eq PCK_TYPE_ARRAY || $strType eq PCK_TYPE_OBJ)
{
$iBits |= $iDelta & 0x7;
$iDelta >>= 3;
if ($iDelta != 0)
{
$iBits |= 0x8;
}
}
# Output pack type and bits
my $strResult = "${strIndent}${strType} << 4";
if ($iBits != 0)
{
$strResult .= sprintf(" | 0x%02X", $iBits);
}
$strResult .= ',';
# 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;