mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-18 04:58:51 +02:00
Migrate command-line help generation to C.
Command-line help is now generated at build time so it does not need to be committed. This reduces churn on commits that add configuration and/or update the help. Since churn is no longer an issue, help.auto.c is bzip2 compressed to save space in the binary. The Perl config parser (Data.pm) has been moved to doc/lib since the Perl build path is no longer required. Likewise doc/xml/reference.xml has been moved to src/build/help/help.xml since it is required at build time.
This commit is contained in:
parent
def7d513cd
commit
f4e1babf6b
@ -570,7 +570,7 @@ To add an option, two files need be to be modified:
|
||||
|
||||
- `src/build/config/config.yaml`
|
||||
|
||||
- `doc/xml/reference.xml`
|
||||
- `src/build/config/help.xml`
|
||||
|
||||
These files are discussed in the following sections along with how to verify the `help` command output.
|
||||
|
||||
@ -657,7 +657,7 @@ repo-test-type:
|
||||
|
||||
At compile time, the `config.auto.h` file will be generated to contain the constants used for options in the code. For the C enums, any dashes in the option name will be removed, camel-cased and prefixed with `cfgOpt`, e.g. `repo-path` becomes `cfgOptRepoPath`.
|
||||
|
||||
### reference.xml
|
||||
### help.xml
|
||||
|
||||
All options must be documented or the system will error during the build. To add an option, find the command section identified by `command id="COMMAND"` section where `COMMAND` is the name of the command (e.g. `expire`) or, if the option is used by more than one command and the definition for the option is the same for all of the commands, the `operation-general title="General Options"` section.
|
||||
|
||||
|
@ -1,195 +0,0 @@
|
||||
####################################################################################################################################
|
||||
# Auto-Generate C Files Required for Build
|
||||
####################################################################################################################################
|
||||
package pgBackRestBuild::Build;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
use English '-no_match_vars';
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(basename);
|
||||
use Storable qw(dclone);
|
||||
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
use pgBackRestTest::Common::Storage;
|
||||
use pgBackRestTest::Common::StoragePosix;
|
||||
|
||||
use pgBackRestBuild::Build::Common;
|
||||
|
||||
####################################################################################################################################
|
||||
# Define generator used for auto generated warning messages
|
||||
####################################################################################################################################
|
||||
use constant GENERATOR => 'Build.pm';
|
||||
|
||||
####################################################################################################################################
|
||||
# buildAll - execute all build functions and generate C source code
|
||||
####################################################################################################################################
|
||||
sub buildAll
|
||||
{
|
||||
my $strBuildPath = shift;
|
||||
my $rhBuild = shift;
|
||||
my $strFileExt = shift;
|
||||
|
||||
# Storage object
|
||||
my $oStorage = new pgBackRestTest::Common::Storage(
|
||||
$strBuildPath, new pgBackRestTest::Common::StoragePosix({bFileSync => false, bPathSync => false}));
|
||||
|
||||
# List of files actually built
|
||||
my @stryBuilt;
|
||||
|
||||
# Build and output source code
|
||||
#-------------------------------------------------------------------------------------------------------------------------------
|
||||
foreach my $strBuild (sort(keys(%{$rhBuild})))
|
||||
{
|
||||
my $strPath = $rhBuild->{$strBuild}{&BLD_PATH};
|
||||
|
||||
foreach my $strFile (sort(keys(%{$rhBuild->{$strBuild}{&BLD_DATA}{&BLD_FILE}})))
|
||||
{
|
||||
my $rhFile = $rhBuild->{$strBuild}{&BLD_DATA}{&BLD_FILE}{$strFile};
|
||||
my $rhFileConstant = $rhFile->{&BLD_CONSTANT_GROUP};
|
||||
my $rhFileEnum = $rhFile->{&BLD_ENUM};
|
||||
my $rhFileDeclare = $rhFile->{&BLD_DECLARE};
|
||||
my $rhFileData = $rhFile->{&BLD_DATA};
|
||||
my $rhSource;
|
||||
|
||||
# Build general banner
|
||||
#-------------------------------------------------------------------------------------------------------------------------------
|
||||
my $strBanner = bldBanner($rhFile->{&BLD_SUMMARY}, GENERATOR);
|
||||
|
||||
# Build header file
|
||||
#-------------------------------------------------------------------------------------------------------------------------------
|
||||
if (defined($rhFileEnum) || defined($rhFileConstant) || defined($rhFileDeclare))
|
||||
{
|
||||
my $strHeaderDefine = uc("${strPath}/${strFile}") . '_AUTO_H';
|
||||
$strHeaderDefine =~ s/\//_/g;
|
||||
|
||||
my $strHeader =
|
||||
$strBanner .
|
||||
"#ifndef ${strHeaderDefine}\n" .
|
||||
"#define ${strHeaderDefine}\n";
|
||||
|
||||
# Iterate constant groups
|
||||
foreach my $strConstantGroup (sort(keys(%{$rhFileConstant})))
|
||||
{
|
||||
my $rhConstantGroup = $rhFileConstant->{$strConstantGroup};
|
||||
|
||||
$strHeader .= "\n" . bldBanner($rhConstantGroup->{&BLD_SUMMARY} . ' constants');
|
||||
|
||||
# Iterate constants
|
||||
foreach my $strConstant (sort(keys(%{$rhConstantGroup->{&BLD_CONSTANT}})))
|
||||
{
|
||||
my $rhConstant = $rhConstantGroup->{&BLD_CONSTANT}{$strConstant};
|
||||
|
||||
$strHeader .=
|
||||
"#define ${strConstant} " . (' ' x (69 - length($strConstant) - 10)) .
|
||||
$rhConstant->{&BLD_CONSTANT_VALUE} . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Iterate enum groups
|
||||
foreach my $strEnum (sort(keys(%{$rhFileEnum})))
|
||||
{
|
||||
my $rhEnum = $rhFileEnum->{$strEnum};
|
||||
|
||||
$strHeader .=
|
||||
"\n" . bldBanner($rhEnum->{&BLD_SUMMARY} . ' enum');
|
||||
|
||||
$strHeader .=
|
||||
"typedef enum\n" .
|
||||
"{\n";
|
||||
|
||||
my $iExpectedValue = 0;
|
||||
|
||||
# Iterate enums
|
||||
foreach my $strEnumItem (@{$rhEnum->{&BLD_LIST}})
|
||||
{
|
||||
$strHeader .=
|
||||
" ${strEnumItem}";
|
||||
|
||||
if (defined($rhEnum->{&BLD_VALUE}{$strEnumItem}) && $rhEnum->{&BLD_VALUE}{$strEnumItem} != $iExpectedValue)
|
||||
{
|
||||
$strHeader .= ' = ' . (' ' x (61 - length($strEnumItem))) . $rhEnum->{&BLD_VALUE}{$strEnumItem};
|
||||
$iExpectedValue = $rhEnum->{&BLD_VALUE}{$strEnumItem};
|
||||
}
|
||||
|
||||
$strHeader .= ",\n";
|
||||
$iExpectedValue++;
|
||||
}
|
||||
|
||||
$strHeader .=
|
||||
"} " . $rhEnum->{&BLD_NAME} . ";\n";
|
||||
}
|
||||
|
||||
foreach my $strDeclare (sort(keys(%{$rhFileDeclare})))
|
||||
{
|
||||
my $rhDeclare = $rhFileDeclare->{$strDeclare};
|
||||
|
||||
$strHeader .= "\n" . bldBanner($rhDeclare->{&BLD_SUMMARY});
|
||||
$strHeader .= $rhDeclare->{&BLD_SOURCE};
|
||||
}
|
||||
|
||||
$strHeader .=
|
||||
"\n#endif";
|
||||
|
||||
$rhSource->{&BLD_HEADER} = $strHeader;
|
||||
}
|
||||
|
||||
# Build C file
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
if (defined($rhFileData))
|
||||
{
|
||||
my $strCode;
|
||||
|
||||
foreach my $strData (sort(keys(%{$rhFileData})))
|
||||
{
|
||||
my $rhData = $rhFileData->{$strData};
|
||||
|
||||
$strCode .= "\n" . bldBanner($rhData->{&BLD_SUMMARY});
|
||||
$strCode .= $rhData->{&BLD_SOURCE};
|
||||
}
|
||||
|
||||
$rhSource->{&BLD_C} = "${strBanner}${strCode}";
|
||||
}
|
||||
|
||||
# Output files
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
foreach my $strFileType (sort(keys(%{$rhSource})))
|
||||
{
|
||||
my $strExt = $strFileType;
|
||||
|
||||
if (defined($strFileExt))
|
||||
{
|
||||
$strExt = $strFileType eq BLD_C ? $strFileExt : "${strFileExt}h";
|
||||
}
|
||||
|
||||
# Save the file if it has not changed
|
||||
my $strBuilt = "${strPath}/${strFile}.auto.${strExt}";
|
||||
my $bSave = true;
|
||||
my $oFile = $oStorage->openRead($strBuilt, {bIgnoreMissing => true});
|
||||
|
||||
if (defined($oFile) && ${$oStorage->get($oFile)} eq (trim($rhSource->{$strFileType}) . "\n"))
|
||||
{
|
||||
$bSave = false;
|
||||
}
|
||||
|
||||
if ($bSave)
|
||||
{
|
||||
$oStorage->put($strBuilt, trim($rhSource->{$strFileType}) . "\n");
|
||||
push(@stryBuilt, basename($strBuildPath) . "/${strBuilt}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Return list of files built
|
||||
return @stryBuilt;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(buildAll);
|
||||
|
||||
1;
|
@ -1,144 +0,0 @@
|
||||
####################################################################################################################################
|
||||
# Build Constants and Functions
|
||||
####################################################################################################################################
|
||||
package pgBackRestBuild::Build::Common;
|
||||
|
||||
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 Storable qw(dclone);
|
||||
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
|
||||
####################################################################################################################################
|
||||
# Constants
|
||||
####################################################################################################################################
|
||||
use constant BLD_PATH => 'path';
|
||||
push @EXPORT, qw(BLD_PATH);
|
||||
use constant BLD_FILE => 'file';
|
||||
push @EXPORT, qw(BLD_FILE);
|
||||
|
||||
use constant BLD_C => 'c';
|
||||
push @EXPORT, qw(BLD_C);
|
||||
use constant BLD_EXT => 'ext';
|
||||
push @EXPORT, qw(BLD_EXT);
|
||||
use constant BLD_HEADER => 'h';
|
||||
push @EXPORT, qw(BLD_HEADER);
|
||||
|
||||
use constant BLD_CONSTANT => 'constant';
|
||||
push @EXPORT, qw(BLD_CONSTANT);
|
||||
use constant BLD_CONSTANT_GROUP => 'constantGroup';
|
||||
push @EXPORT, qw(BLD_CONSTANT_GROUP);
|
||||
use constant BLD_CONSTANT_VALUE => 'constantValue';
|
||||
push @EXPORT, qw(BLD_CONSTANT_VALUE);
|
||||
|
||||
use constant BLD_DATA => 'data';
|
||||
push @EXPORT, qw(BLD_DATA);
|
||||
use constant BLD_DECLARE => 'declare';
|
||||
push @EXPORT, qw(BLD_DECLARE);
|
||||
use constant BLD_ENUM => 'enum';
|
||||
push @EXPORT, qw(BLD_ENUM);
|
||||
use constant BLD_LIST => 'list';
|
||||
push @EXPORT, qw(BLD_LIST);
|
||||
use constant BLD_NAME => 'name';
|
||||
push @EXPORT, qw(BLD_NAME);
|
||||
use constant BLD_PATH => 'path';
|
||||
push @EXPORT, qw(BLD_PATH);
|
||||
use constant BLD_SOURCE => 'buildSource';
|
||||
push @EXPORT, qw(BLD_SOURCE);
|
||||
use constant BLD_SUMMARY => 'summary';
|
||||
push @EXPORT, qw(BLD_SUMMARY);
|
||||
use constant BLD_VALUE => 'value';
|
||||
push @EXPORT, qw(BLD_VALUE);
|
||||
|
||||
####################################################################################################################################
|
||||
# bldAutoWarning - warning not to modify automatically generated files directly
|
||||
####################################################################################################################################
|
||||
sub bldAutoWarning
|
||||
{
|
||||
my $strGenerator = shift;
|
||||
|
||||
return "Automatically generated by ${strGenerator} -- do not modify directly.";
|
||||
}
|
||||
|
||||
push @EXPORT, qw(bldAutoWarning);
|
||||
|
||||
####################################################################################################################################
|
||||
# bldBanner - build general banner
|
||||
####################################################################################################################################
|
||||
sub bldBanner
|
||||
{
|
||||
my $strContent = shift;
|
||||
my $strGenerator = shift;
|
||||
|
||||
my $strBanner =
|
||||
qw{/} . (qw{*} x 131) . "\n" .
|
||||
trim($strContent) . "\n";
|
||||
|
||||
if (defined($strGenerator))
|
||||
{
|
||||
$strBanner .=
|
||||
"\n" .
|
||||
bldAutoWarning($strGenerator) . "\n";
|
||||
}
|
||||
|
||||
$strBanner .=
|
||||
(qw{*} x 131) . qw{/} . "\n";
|
||||
|
||||
return $strBanner;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(bldBanner);
|
||||
|
||||
####################################################################################################################################
|
||||
# Generate an enum name from a prefix and - separated name
|
||||
####################################################################################################################################
|
||||
sub bldEnum
|
||||
{
|
||||
my $strPrefix = shift;
|
||||
my $strName = shift;
|
||||
my $bInitCapFirst = shift;
|
||||
|
||||
$bInitCapFirst = defined($bInitCapFirst) ? $bInitCapFirst : true;
|
||||
my $bFirst = true;
|
||||
|
||||
my @stryName = split('\-', $strName);
|
||||
$strName = undef;
|
||||
|
||||
foreach my $strPart (@stryName)
|
||||
{
|
||||
$strName .= ($bFirst && $bInitCapFirst) || !$bFirst ? ucfirst($strPart) : $strPart;
|
||||
$bFirst = false;
|
||||
}
|
||||
|
||||
return "${strPrefix}${strName}";
|
||||
}
|
||||
|
||||
push @EXPORT, qw(bldEnum);
|
||||
|
||||
####################################################################################################################################
|
||||
# Quote a list of strings
|
||||
####################################################################################################################################
|
||||
sub bldQuoteList
|
||||
{
|
||||
my $ryList = shift;
|
||||
|
||||
my @stryQuoteList;
|
||||
|
||||
foreach my $strItem (@{$ryList})
|
||||
{
|
||||
push(@stryQuoteList, "\"${strItem}\"");
|
||||
}
|
||||
|
||||
return @stryQuoteList;
|
||||
}
|
||||
|
||||
push @EXPORT, qw(bldQuoteList);
|
||||
|
||||
1;
|
@ -1,490 +0,0 @@
|
||||
####################################################################################################################################
|
||||
# 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;
|
@ -317,7 +317,7 @@ eval
|
||||
my $oRender = new pgBackRestDoc::Common::DocRender('text', $oManifest, !$bNoExe);
|
||||
my $oDocConfig =
|
||||
new pgBackRestDoc::Common::DocConfig(
|
||||
new pgBackRestDoc::Common::Doc("${strBasePath}/xml/reference.xml"), $oRender);
|
||||
new pgBackRestDoc::Common::Doc("${strBasePath}/../src/build/help/help.xml"), $oRender);
|
||||
|
||||
$oStorageDoc->pathCreate(
|
||||
"${strBasePath}/output/man", {strMode => '0770', bIgnoreExists => true, bCreateParent => true});
|
||||
|
@ -11,10 +11,9 @@ use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
|
||||
use pgBackRestBuild::Config::Data;
|
||||
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
use pgBackRestDoc::Custom::DocConfigData;
|
||||
use pgBackRestDoc::ProjectInfo;
|
||||
|
||||
####################################################################################################################################
|
||||
|
@ -15,8 +15,6 @@ use Exporter qw(import);
|
||||
use File::Basename qw(dirname);
|
||||
use Storable qw(dclone);
|
||||
|
||||
use pgBackRestBuild::Config::Data;
|
||||
|
||||
use pgBackRestTest::Common::ExecuteTest;
|
||||
use pgBackRestTest::Common::HostTest;
|
||||
use pgBackRestTest::Common::HostGroupTest;
|
||||
@ -26,6 +24,7 @@ use pgBackRestDoc::Common::Exception;
|
||||
use pgBackRestDoc::Common::Ini;
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
use pgBackRestDoc::Custom::DocConfigData;
|
||||
use pgBackRestDoc::ProjectInfo;
|
||||
|
||||
####################################################################################################################################
|
||||
|
@ -119,7 +119,16 @@ sub new
|
||||
next;
|
||||
}
|
||||
|
||||
$$oSourceHash{doc} = new pgBackRestDoc::Common::Doc("$self->{strDocPath}/xml/${strKey}.xml");
|
||||
# Help is in src/build/help
|
||||
if ($strKey eq 'help')
|
||||
{
|
||||
$oSourceHash->{doc} = new pgBackRestDoc::Common::Doc("$self->{strDocPath}/../src/build/help/${strKey}.xml");
|
||||
}
|
||||
# Else should be in doc/xml
|
||||
else
|
||||
{
|
||||
$$oSourceHash{doc} = new pgBackRestDoc::Common::Doc("$self->{strDocPath}/xml/${strKey}.xml");
|
||||
}
|
||||
|
||||
# Read variables from source
|
||||
$self->variableListParse($$oSourceHash{doc}->nodeGet('variable-list', false), $rhVariableOverride);
|
||||
|
@ -193,14 +193,14 @@ sub new
|
||||
my $oRenderOut =
|
||||
$self->{oManifest}->renderOutGet($self->{strType} eq 'latex' ? 'pdf' : $self->{strType}, $self->{strRenderOutKey});
|
||||
|
||||
# If these are the backrest docs then load the reference
|
||||
# If these are the backrest docs then load the help
|
||||
if ($self->{oManifest}->isBackRest())
|
||||
{
|
||||
$self->{oReference} =
|
||||
new pgBackRestDoc::Common::DocConfig(${$self->{oManifest}->sourceGet('reference')}{doc}, $self);
|
||||
new pgBackRestDoc::Common::DocConfig(${$self->{oManifest}->sourceGet('help')}{doc}, $self);
|
||||
}
|
||||
|
||||
if (defined($$oRenderOut{source}) && $$oRenderOut{source} eq 'reference' && $self->{oManifest}->isBackRest())
|
||||
if (defined($$oRenderOut{source}) && $$oRenderOut{source} eq 'help' && $self->{oManifest}->isBackRest())
|
||||
{
|
||||
if ($self->{strRenderOutKey} eq 'configuration')
|
||||
{
|
||||
@ -843,7 +843,7 @@ sub processTag
|
||||
|
||||
$strBuffer .= $strStart;
|
||||
|
||||
# Admonitions in the reference materials are tags of the text element rather than field elements of the document so special
|
||||
# Admonitions in the help materials are tags of the text element rather than field elements of the document so special
|
||||
# handling is required
|
||||
if ($strTag eq 'admonition')
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
# The configuration is defined in src/build/config/config.yaml, which also contains the documentation.
|
||||
####################################################################################################################################
|
||||
package pgBackRestBuild::Config::Data;
|
||||
package pgBackRestDoc::Custom::DocConfigData;
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
@ -12,11 +12,10 @@ use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
|
||||
use pgBackRestBuild::Config::Data;
|
||||
|
||||
use pgBackRestDoc::Common::DocRender;
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
use pgBackRestDoc::Custom::DocConfigData;
|
||||
use pgBackRestDoc::ProjectInfo;
|
||||
|
||||
####################################################################################################################################
|
||||
|
@ -83,7 +83,7 @@
|
||||
<source key="index"/>
|
||||
<source key="user-guide-index"/>
|
||||
<source key="user-guide"/>
|
||||
<source key="reference" type="custom"/>
|
||||
<source key="help" type="custom"/>
|
||||
<source key="release" type="custom"/>
|
||||
<source key="faq"/>
|
||||
<source key="metric"/>
|
||||
@ -98,8 +98,8 @@
|
||||
<render-source key="user-guide-index" menu="User Guides"/>
|
||||
<render-source key="user-guide"/>
|
||||
<render-source key="release" menu="Releases"/>
|
||||
<render-source key="configuration" source="reference" menu="Configuration"/>
|
||||
<render-source key="command" source="reference" menu="Commands"/>
|
||||
<render-source key="configuration" source="help" menu="Configuration"/>
|
||||
<render-source key="command" source="help" menu="Commands"/>
|
||||
<render-source key="faq" menu="FAQ"/>
|
||||
<render-source key="metric" menu="Metrics"/>
|
||||
</render>
|
||||
|
@ -631,7 +631,7 @@ run 8/1 ------------- L2285 no current backups
|
||||
<p>To add an option, two files need be to be modified:</p>
|
||||
<list>
|
||||
<list-item><file>src/build/config/config.yaml</file></list-item>
|
||||
<list-item><file>doc/xml/reference.xml</file></list-item>
|
||||
<list-item><file>src/build/help/help.xml</file></list-item>
|
||||
</list>
|
||||
|
||||
<p>These files are discussed in the following sections along with how to verify the <code>help</code> command output.</p>
|
||||
@ -720,8 +720,8 @@ run 8/1 ------------- L2285 no current backups
|
||||
<p>At compile time, the <file>config.auto.h</file> file will be generated to contain the constants used for options in the code. For the C enums, any dashes in the option name will be removed, camel-cased and prefixed with <code>cfgOpt</code>, e.g. <code>repo-path</code> becomes <code>cfgOptRepoPath</code>.</p>
|
||||
</section>
|
||||
|
||||
<section id="reference-file">
|
||||
<title>reference.xml</title>
|
||||
<section id="help-file">
|
||||
<title>help.xml</title>
|
||||
|
||||
<p>All options must be documented or the system will error during the build. To add an option, find the command section identified by <code>command id="COMMAND"</code> section where <id>COMMAND</id> is the name of the command (e.g. <cmd>expire</cmd>) or, if the option is used by more than one command and the definition for the option is the same for all of the commands, the <code>operation-general title="General Options"</code> section.</p>
|
||||
|
||||
|
1
src/.gitignore
vendored
1
src/.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
/.build
|
||||
autom4te.cache
|
||||
/command/help/help.auto.c
|
||||
/config.log
|
||||
/config.status
|
||||
/Makefile
|
||||
|
@ -6,6 +6,8 @@
|
||||
# List of required source files. main.c should always be listed last and the rest in alpha order.
|
||||
####################################################################################################################################
|
||||
SRCS_BUILD = \
|
||||
common/compress/bz2/common.c \
|
||||
common/compress/bz2/compress.c \
|
||||
common/debug.c \
|
||||
common/encode.c \
|
||||
common/error.c \
|
||||
@ -13,6 +15,8 @@ SRCS_BUILD = \
|
||||
common/io/filter/filter.c \
|
||||
common/io/filter/group.c \
|
||||
common/io/filter/sink.c \
|
||||
common/io/bufferRead.c \
|
||||
common/io/bufferWrite.c \
|
||||
common/io/io.c \
|
||||
common/io/read.c \
|
||||
common/io/write.c \
|
||||
@ -32,6 +36,7 @@ SRCS_BUILD = \
|
||||
common/type/stringList.c \
|
||||
common/type/variant.c \
|
||||
common/type/variantList.c \
|
||||
common/type/xml.c \
|
||||
common/user.c \
|
||||
common/wait.c \
|
||||
storage/posix/read.c \
|
||||
@ -81,8 +86,6 @@ SRCS = \
|
||||
command/verify/protocol.c \
|
||||
command/verify/verify.c \
|
||||
common/compress/helper.c \
|
||||
common/compress/bz2/common.c \
|
||||
common/compress/bz2/compress.c \
|
||||
common/compress/bz2/decompress.c \
|
||||
common/compress/gz/common.c \
|
||||
common/compress/gz/compress.c \
|
||||
@ -100,8 +103,6 @@ SRCS = \
|
||||
common/exit.c \
|
||||
common/fork.c \
|
||||
common/ini.c \
|
||||
common/io/bufferRead.c \
|
||||
common/io/bufferWrite.c \
|
||||
common/io/client.c \
|
||||
common/io/fd.c \
|
||||
common/io/fdRead.c \
|
||||
@ -125,7 +126,6 @@ SRCS = \
|
||||
common/stat.c \
|
||||
common/type/json.c \
|
||||
common/type/mcv.c \
|
||||
common/type/xml.c \
|
||||
config/config.c \
|
||||
config/exec.c \
|
||||
config/load.c \
|
||||
@ -183,7 +183,7 @@ SRCS = \
|
||||
####################################################################################################################################
|
||||
CC = @CC@
|
||||
CFLAGS = $(CFLAGS_EXTRA) @CFLAGS@
|
||||
CPPFLAGS = @CPPFLAGS@ -I@srcdir@
|
||||
CPPFLAGS = @CPPFLAGS@ -I. -I@srcdir@
|
||||
LDFLAGS = $(LDFLAGS_EXTRA) @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
LIBS_BUILD = @LIBS_BUILD@
|
||||
@ -237,6 +237,23 @@ build-error: $(OBJS_BUILD_ERROR) build/error/error.yaml
|
||||
$(CC) -o build-error $(OBJS_BUILD_ERROR) $(LDFLAGS) $(LIBS) $(LIBS_BUILD)
|
||||
./build-error $(VPATH)
|
||||
|
||||
####################################################################################################################################
|
||||
# Compile and link help generator
|
||||
####################################################################################################################################
|
||||
SRCS_BUILD_HELP = \
|
||||
build/common/render.c \
|
||||
build/common/yaml.c \
|
||||
build/config/parse.c \
|
||||
build/help/parse.c \
|
||||
build/help/render.c \
|
||||
build/help/main.c
|
||||
|
||||
OBJS_BUILD_HELP = $(patsubst %.c,$(BUILDDIR)/%.o,$(SRCS_BUILD) $(SRCS_BUILD_HELP))
|
||||
|
||||
build-help: $(OBJS_BUILD_HELP) build/config/config.yaml build/help/help.xml
|
||||
$(CC) -o build-help $(OBJS_BUILD_HELP) $(LDFLAGS) $(LIBS) $(LIBS_BUILD)
|
||||
./build-help $(VPATH)
|
||||
|
||||
####################################################################################################################################
|
||||
# Installation. DESTDIR can be used to modify the install location.
|
||||
####################################################################################################################################
|
||||
@ -258,7 +275,7 @@ uninstall:
|
||||
# Clean build files and executable created by make
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
rm -f pgbackrest build-config
|
||||
rm -f pgbackrest build-config build-error build-help command/help/help.auto.c
|
||||
|
||||
.PHONY = clean-all
|
||||
|
||||
@ -270,6 +287,7 @@ clean-all: clean
|
||||
# Special per-object flags
|
||||
####################################################################################################################################
|
||||
$(BUILDDIR)/postgres/interface/page.o: CFLAGS += @CFLAGS_PAGE_CHECKSUM@
|
||||
$(BUILDDIR)/main.o: build-help
|
||||
|
||||
####################################################################################################################################
|
||||
# Compile and generate dependencies
|
||||
|
@ -53,6 +53,7 @@ Parse command list
|
||||
typedef struct BldCfgCommandRaw
|
||||
{
|
||||
const String *const name; // See BldCfgCommand for comments
|
||||
bool internal;
|
||||
bool logFile;
|
||||
const String *logLevelDefault;
|
||||
bool lockRequired;
|
||||
@ -138,6 +139,7 @@ bldCfgParseCommandList(Yaml *const yaml)
|
||||
|
||||
if (strEqZ(cmdDef.value, "internal"))
|
||||
{
|
||||
cmdRaw.internal = yamlBoolParse(cmdDefVal);
|
||||
}
|
||||
else if (strEqZ(cmdDef.value, "lock-type"))
|
||||
{
|
||||
@ -189,6 +191,7 @@ bldCfgParseCommandList(Yaml *const yaml)
|
||||
&(BldCfgCommand)
|
||||
{
|
||||
.name = strDup(cmdRaw.name),
|
||||
.internal = cmdRaw.internal,
|
||||
.logFile = cmdRaw.logFile,
|
||||
.logLevelDefault = strDup(cmdRaw.logLevelDefault),
|
||||
.lockRequired = cmdRaw.lockRequired,
|
||||
@ -275,6 +278,7 @@ typedef struct BldCfgOptionDeprecateRaw
|
||||
typedef struct BldCfgOptionCommandRaw
|
||||
{
|
||||
const String *name; // See BldCfgOptionCommand for comments
|
||||
const Variant *internal;
|
||||
const Variant *required;
|
||||
const String *defaultValue;
|
||||
const BldCfgOptionDependRaw *depend;
|
||||
@ -287,6 +291,7 @@ typedef struct BldCfgOptionRaw
|
||||
const String *name; // See BldCfgOption for comments
|
||||
const String *type;
|
||||
const String *section;
|
||||
bool internal;
|
||||
const Variant *required;
|
||||
const Variant *negate;
|
||||
bool reset;
|
||||
@ -646,6 +651,7 @@ bldCfgParseOptionCommandList(Yaml *const yaml, const List *const optList)
|
||||
}
|
||||
else if (strEqZ(optCmdDef.value, "internal"))
|
||||
{
|
||||
optCmdRaw.internal = varNewBool(yamlBoolParse(optCmdDefVal));
|
||||
}
|
||||
else if (strEqZ(optCmdDef.value, "required"))
|
||||
{
|
||||
@ -669,6 +675,7 @@ bldCfgParseOptionCommandList(Yaml *const yaml, const List *const optList)
|
||||
&(BldCfgOptionCommandRaw)
|
||||
{
|
||||
.name = strDup(optCmdRaw.name),
|
||||
.internal = varDup(optCmdRaw.internal),
|
||||
.required = varDup(optCmdRaw.required),
|
||||
.defaultValue = strDup(optCmdRaw.defaultValue),
|
||||
.depend = optCmdRaw.depend,
|
||||
@ -798,6 +805,7 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
|
||||
}
|
||||
else if (strEqZ(optDef.value, "internal"))
|
||||
{
|
||||
optRaw.internal = yamlBoolParse(optDefVal);
|
||||
}
|
||||
else if (strEqZ(optDef.value, "negate"))
|
||||
{
|
||||
@ -889,6 +897,7 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
|
||||
.name = strDup(optRaw->name),
|
||||
.type = strDup(optRaw->type),
|
||||
.section = strDup(optRaw->section),
|
||||
.internal = optRaw->internal,
|
||||
.required = varBool(optRaw->required),
|
||||
.negate = varBool(optRaw->negate),
|
||||
.reset = optRaw->reset,
|
||||
@ -931,6 +940,10 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
|
||||
if (optCmd.required == NULL)
|
||||
optCmd.required = optRaw->required;
|
||||
|
||||
// Default internal to option internal if not defined
|
||||
if (optCmd.internal == NULL)
|
||||
optCmd.internal = varNewBool(optRaw->internal);
|
||||
|
||||
// Default command role list if not defined
|
||||
if (optCmd.roleList == NULL)
|
||||
{
|
||||
@ -963,6 +976,7 @@ bldCfgParseOptionList(Yaml *const yaml, const List *const cmdList, const List *c
|
||||
&(BldCfgOptionCommand)
|
||||
{
|
||||
.name = strDup(optCmd.name),
|
||||
.internal = varBool(optCmd.internal),
|
||||
.required = varBool(optCmd.required),
|
||||
.defaultValue = strDup(optCmd.defaultValue),
|
||||
.depend = bldCfgParseDependReconcile(optCmd.depend, result),
|
||||
|
@ -62,6 +62,7 @@ Types
|
||||
typedef struct BldCfgCommand
|
||||
{
|
||||
const String *const name; // Name
|
||||
const bool internal; // Is the command internal?
|
||||
const bool logFile; // Does the command write automatically to a log file?
|
||||
const String *const logLevelDefault; // Default log level
|
||||
const bool lockRequired; // Is a lock required
|
||||
@ -94,6 +95,7 @@ typedef struct BldCfgOptionDeprecate
|
||||
typedef struct BldCfgOptionCommand
|
||||
{
|
||||
const String *const name; // Name
|
||||
const bool internal; // Is the option internal?
|
||||
const bool required; // Is the option required?
|
||||
const String *const defaultValue; // Default value, if any
|
||||
const BldCfgOptionDepend *const depend; // Dependency, if any
|
||||
@ -106,6 +108,7 @@ struct BldCfgOption
|
||||
const String *const name; // Name
|
||||
const String *const type; // Option type, e.g. integer
|
||||
const String *const section; // Option section, i.e. stanza or global
|
||||
const bool internal; // Is the option internal?
|
||||
const bool required; // Is the option required?
|
||||
const bool negate; // Can the option be negated?
|
||||
const bool reset; // Can the option be reset?
|
||||
|
36
src/build/help/main.c
Normal file
36
src/build/help/main.c
Normal file
@ -0,0 +1,36 @@
|
||||
/***********************************************************************************************************************************
|
||||
Auto-Generate Help
|
||||
***********************************************************************************************************************************/
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/log.h"
|
||||
#include "storage/posix/storage.h"
|
||||
|
||||
#include "build/config/parse.h"
|
||||
#include "build/help/parse.h"
|
||||
#include "build/help/render.h"
|
||||
|
||||
int
|
||||
main(int argListSize, const char *argList[])
|
||||
{
|
||||
// Check parameters
|
||||
CHECK(argListSize <= 2);
|
||||
|
||||
// Initialize logging
|
||||
logInit(logLevelWarn, logLevelError, logLevelOff, false, 0, 1, false);
|
||||
|
||||
// Get current working directory
|
||||
char currentWorkDir[1024];
|
||||
THROW_ON_SYS_ERROR(getcwd(currentWorkDir, sizeof(currentWorkDir)) == NULL, FormatError, "unable to get cwd");
|
||||
|
||||
// Get repo path (cwd if it was not passed)
|
||||
const String *pathRepo = argListSize >= 2 ? strPath(STR(argList[1])) : strPath(STR(currentWorkDir));
|
||||
|
||||
// Render config
|
||||
const Storage *const storageRepo = storagePosixNewP(pathRepo);
|
||||
const Storage *const storageBuild = storagePosixNewP(STR(currentWorkDir), .write = true);
|
||||
const BldCfg bldCfg = bldCfgParse(storageRepo);
|
||||
bldHlpRender(storageBuild, bldCfg, bldHlpParse(storageRepo, bldCfg));
|
||||
|
||||
return 0;
|
||||
}
|
191
src/build/help/parse.c
Normal file
191
src/build/help/parse.c
Normal file
@ -0,0 +1,191 @@
|
||||
/***********************************************************************************************************************************
|
||||
Parse Help Xml
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "common/type/xml.h"
|
||||
#include "storage/storage.h"
|
||||
|
||||
#include "build/config/parse.h"
|
||||
#include "build/help/parse.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse option help
|
||||
***********************************************************************************************************************************/
|
||||
// Helper to parse options
|
||||
static void
|
||||
bldHlpParseOption(XmlNodeList *const xmlOptList, List *const optList, const String *const sectionDefault)
|
||||
{
|
||||
ASSERT(xmlOptList != NULL);
|
||||
ASSERT(optList != NULL);
|
||||
|
||||
for (unsigned int optIdx = 0; optIdx < xmlNodeLstSize(xmlOptList); optIdx++)
|
||||
{
|
||||
const XmlNode *const xmlOpt = xmlNodeLstGet(xmlOptList, optIdx);
|
||||
|
||||
// Get section or use default
|
||||
const String *section = xmlNodeAttribute(xmlOpt, STRDEF("section"));
|
||||
|
||||
if (section == NULL)
|
||||
section = sectionDefault;
|
||||
|
||||
// Add option to list
|
||||
MEM_CONTEXT_BEGIN(lstMemContext(optList))
|
||||
{
|
||||
lstAdd(
|
||||
optList,
|
||||
&(BldHlpOption)
|
||||
{
|
||||
.name = xmlNodeAttribute(xmlOpt, STRDEF("id")),
|
||||
.section = strDup(section),
|
||||
.summary = xmlNodeChild(xmlOpt, STRDEF("summary"), true),
|
||||
.description = xmlNodeChild(xmlOpt, STRDEF("text"), true),
|
||||
});
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
lstSort(optList, sortOrderAsc);
|
||||
}
|
||||
|
||||
static List *
|
||||
bldHlpParseOptionList(XmlNode *const xml)
|
||||
{
|
||||
List *const result = lstNewP(sizeof(BldHlpOption), .comparator = lstComparatorStr);
|
||||
|
||||
// Parse config options
|
||||
const XmlNodeList *xmlSectionList = xmlNodeChildList(
|
||||
xmlNodeChild(xmlNodeChild(xml, STRDEF("config"), true), STRDEF("config-section-list"), true), STRDEF("config-section"));
|
||||
|
||||
for (unsigned int sectionIdx = 0; sectionIdx < xmlNodeLstSize(xmlSectionList); sectionIdx++)
|
||||
{
|
||||
const XmlNode *const xmlSection = xmlNodeLstGet(xmlSectionList, sectionIdx);
|
||||
|
||||
bldHlpParseOption(
|
||||
xmlNodeChildList(xmlNodeChild(xmlSection, STRDEF("config-key-list"), true), STRDEF("config-key")), result,
|
||||
xmlNodeAttribute(xmlSection, STRDEF("id")));
|
||||
}
|
||||
|
||||
// Parse command-line only options
|
||||
bldHlpParseOption(
|
||||
xmlNodeChildList(
|
||||
xmlNodeChild(
|
||||
xmlNodeChild(xmlNodeChild(xml, STRDEF("operation"), true), STRDEF("operation-general"), true),
|
||||
STRDEF("option-list"), true),
|
||||
STRDEF("option")),
|
||||
result, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Parse command help
|
||||
***********************************************************************************************************************************/
|
||||
static List *
|
||||
bldHlpParseCommandList(XmlNode *const xml)
|
||||
{
|
||||
List *const result = lstNewP(sizeof(BldHlpCommand), .comparator = lstComparatorStr);
|
||||
|
||||
// Parse commands
|
||||
const XmlNodeList *xmlCmdList = xmlNodeChildList(xml, STRDEF("command"));
|
||||
|
||||
for (unsigned int cmdIdx = 0; cmdIdx < xmlNodeLstSize(xmlCmdList); cmdIdx++)
|
||||
{
|
||||
const XmlNode *const xmlCmd = xmlNodeLstGet(xmlCmdList, cmdIdx);
|
||||
|
||||
// Parse option list if any
|
||||
List *cmdOptList = NULL;
|
||||
const XmlNode *const xmlCmdOptListParent = xmlNodeChild(xmlCmd, STRDEF("option-list"), false);
|
||||
|
||||
if (xmlCmdOptListParent != NULL)
|
||||
{
|
||||
cmdOptList = lstNewP(sizeof(BldHlpOption), .comparator = lstComparatorStr);
|
||||
|
||||
bldHlpParseOption(xmlNodeChildList(xmlCmdOptListParent, STRDEF("option")), cmdOptList, NULL);
|
||||
}
|
||||
|
||||
// Add command to list
|
||||
MEM_CONTEXT_BEGIN(lstMemContext(result))
|
||||
{
|
||||
lstAdd(
|
||||
result,
|
||||
&(BldHlpCommand)
|
||||
{
|
||||
.name = xmlNodeAttribute(xmlCmd, STRDEF("id")),
|
||||
.summary = xmlNodeChild(xmlCmd, STRDEF("summary"), true),
|
||||
.description = xmlNodeChild(xmlCmd, STRDEF("text"), true),
|
||||
.optList = lstMove(cmdOptList, memContextCurrent()),
|
||||
});
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
lstSort(result, sortOrderAsc);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Reconcile help
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
bldHlpValidate(const BldHlp bldHlp, const BldCfg bldCfg)
|
||||
{
|
||||
// Validate command help
|
||||
for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldCfg.cmdList); cmdIdx++)
|
||||
{
|
||||
const BldCfgCommand *const cmd = lstGet(bldCfg.cmdList, cmdIdx);
|
||||
const BldHlpCommand *const cmdHlp = lstFind(bldHlp.cmdList, &cmd->name);
|
||||
|
||||
if (cmdHlp == NULL)
|
||||
THROW_FMT(FormatError, "command '%s' must have help", strZ(cmd->name));
|
||||
}
|
||||
|
||||
// Validate option help
|
||||
for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++)
|
||||
{
|
||||
const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx);
|
||||
const BldHlpOption *const optHlp = lstFind(bldHlp.optList, &opt->name);
|
||||
|
||||
// If help was not found in general command-line or config options then check command overrides
|
||||
if (optHlp == NULL)
|
||||
{
|
||||
for (unsigned int optCmdListIdx = 0; optCmdListIdx < lstSize(opt->cmdList); optCmdListIdx++)
|
||||
{
|
||||
const BldCfgOptionCommand *const optCmd = lstGet(opt->cmdList, optCmdListIdx);
|
||||
const BldHlpCommand *const cmdHlp = lstFind(bldHlp.cmdList, &optCmd->name);
|
||||
CHECK(cmdHlp != NULL);
|
||||
|
||||
// Only options with a command role of main require help
|
||||
if (!strLstExists(optCmd->roleList, CMD_ROLE_MAIN_STR))
|
||||
continue;
|
||||
|
||||
const BldHlpOption *const cmdOptHlp = cmdHlp->optList != NULL ? lstFind(cmdHlp->optList, &opt->name) : NULL;
|
||||
|
||||
if (cmdOptHlp == NULL)
|
||||
THROW_FMT(FormatError, "option '%s' must have help for command '%s'", strZ(opt->name), strZ(optCmd->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
BldHlp
|
||||
bldHlpParse(const Storage *const storageRepo, const BldCfg bldCfg)
|
||||
{
|
||||
// Initialize xml
|
||||
XmlNode *const xml = xmlDocumentRoot(
|
||||
xmlDocumentNewBuf(storageGetP(storageNewReadP(storageRepo, STRDEF("src/build/help/help.xml")))));
|
||||
|
||||
// Parse help
|
||||
BldHlp result =
|
||||
{
|
||||
.cmdList = bldHlpParseCommandList(
|
||||
xmlNodeChild(xmlNodeChild(xml, STRDEF("operation"), true), STRDEF("command-list"), true)),
|
||||
.optList = bldHlpParseOptionList(xml)
|
||||
};
|
||||
|
||||
bldHlpValidate(result, bldCfg);
|
||||
|
||||
return result;
|
||||
}
|
41
src/build/help/parse.h
Normal file
41
src/build/help/parse.h
Normal file
@ -0,0 +1,41 @@
|
||||
/***********************************************************************************************************************************
|
||||
Parse Help Xml
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef BUILD_HELP_PARSE_H
|
||||
#define BUILD_HELP_PARSE_H
|
||||
|
||||
#include "build/config/parse.h"
|
||||
#include "common/type/xml.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Types
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct BldHlpCommand
|
||||
{
|
||||
const String *name; // Name
|
||||
const XmlNode *summary; // Summary
|
||||
const XmlNode *description; // Description
|
||||
const List *optList; // Option list
|
||||
} BldHlpCommand;
|
||||
|
||||
typedef struct BldHlpOption
|
||||
{
|
||||
const String *name; // Name
|
||||
const String *section; // Section
|
||||
const XmlNode *summary; // Summary
|
||||
const XmlNode *description; // Description
|
||||
} BldHlpOption;
|
||||
|
||||
typedef struct BldHlp
|
||||
{
|
||||
const List *cmdList; // Command list
|
||||
const List *optList; // Option list
|
||||
} BldHlp;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
// Parse help.xml
|
||||
BldHlp bldHlpParse(const Storage *const storageRepo, const BldCfg bldCfg);
|
||||
|
||||
#endif
|
336
src/build/help/render.c
Normal file
336
src/build/help/render.c
Normal file
@ -0,0 +1,336 @@
|
||||
/***********************************************************************************************************************************
|
||||
Render Help
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "common/compress/bz2/compress.h"
|
||||
#include "common/io/bufferRead.h"
|
||||
#include "common/io/bufferWrite.h"
|
||||
#include "common/log.h"
|
||||
#include "common/type/pack.h"
|
||||
|
||||
#include "build/common/render.h"
|
||||
#include "build/config/parse.h"
|
||||
#include "build/help/parse.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Render xml as text
|
||||
***********************************************************************************************************************************/
|
||||
static String *
|
||||
bldHlpRenderReplace(const String *const string, const char *const replace, const char *const with)
|
||||
{
|
||||
String *result = strNew();
|
||||
|
||||
StringList *const stringList = strLstNewSplitZ(string, replace);
|
||||
|
||||
for (unsigned int stringIdx = 0; stringIdx < strLstSize(stringList); stringIdx++)
|
||||
{
|
||||
if (stringIdx != 0)
|
||||
strCatZ(result, with);
|
||||
|
||||
strCat(result, strLstGet(stringList, stringIdx));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static String *
|
||||
bldHlpRenderXmlNode(const xmlNodePtr xml)
|
||||
{
|
||||
String *const result = strNew();
|
||||
|
||||
for (xmlNodePtr currentNode = xml->children; currentNode != NULL; currentNode = currentNode->next)
|
||||
{
|
||||
const String *const name = STR((char *)currentNode->name);
|
||||
|
||||
if (currentNode->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
if (strEq(name, STRDEF("admonition")))
|
||||
{
|
||||
strCatZ(result, "NOTE: ");
|
||||
strCat(result, bldHlpRenderXmlNode(currentNode));
|
||||
strCatZ(result, "\n\n");
|
||||
}
|
||||
else if (strEq(name, STRDEF("backrest")))
|
||||
strCatZ(result, "pgBackRest");
|
||||
else if (strEq(name, STRDEF("list")))
|
||||
{
|
||||
strCat(result, bldHlpRenderXmlNode(currentNode));
|
||||
strCatChr(result, '\n');
|
||||
}
|
||||
else if (strEq(name, STRDEF("list-item")))
|
||||
{
|
||||
strCatZ(result, "* ");
|
||||
strCat(result, bldHlpRenderXmlNode(currentNode));
|
||||
strCatChr(result, '\n');
|
||||
}
|
||||
else if (strEq(name, STRDEF("p")))
|
||||
{
|
||||
strCat(result, bldHlpRenderXmlNode(currentNode));
|
||||
strCatZ(result, "\n\n");
|
||||
}
|
||||
else if (strEq(name, STRDEF("postgres")))
|
||||
strCatZ(result, "PostgreSQL");
|
||||
else if (
|
||||
strEq(name, STRDEF("id")) || strEq(name, STRDEF("br-option")) || strEq(name, STRDEF("cmd")) ||
|
||||
strEq(name, STRDEF("link")) || strEq(name, STRDEF("setting")) || strEq(name, STRDEF("pg-setting")) ||
|
||||
strEq(name, STRDEF("code")) || strEq(name, STRDEF("i")) || strEq(name, STRDEF("file")) ||
|
||||
strEq(name, STRDEF("path")) || strEq(name, STRDEF("b")) || strEq(name, STRDEF("host")) ||
|
||||
strEq(name, STRDEF("exe")) || strEq(name, STRDEF("proper")))
|
||||
{
|
||||
strCat(result, bldHlpRenderXmlNode(currentNode));
|
||||
}
|
||||
else
|
||||
THROW_FMT(FormatError, "unknown tag '%s'", strZ(name));
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlChar *content = xmlNodeGetContent(currentNode);
|
||||
String *text = strNewZ((char *)content);
|
||||
xmlFree(content);
|
||||
|
||||
if (strchr(strZ(text), '\n') != NULL)
|
||||
{
|
||||
if (!strEmpty(strTrim(strDup(text))))
|
||||
THROW_FMT(FormatError, "text '%s' is invalid", strZ(text));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
text = bldHlpRenderReplace(text, "{[dash]}", "-");
|
||||
|
||||
strCat(result, text);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static String *
|
||||
bldHlpRenderXml(const XmlNode *const xml)
|
||||
{
|
||||
return strTrim(bldHlpRenderXmlNode(*(const xmlNodePtr *)xml));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Render help to a pack
|
||||
***********************************************************************************************************************************/
|
||||
static PackWrite *
|
||||
bldHlpRenderHelpAutoCPack(const BldCfg bldCfg, const BldHlp bldHlp)
|
||||
{
|
||||
PackWrite *const pack = pckWriteNewBuf(bufNew(65 * 1024));
|
||||
|
||||
// Command help
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
pckWriteArrayBeginP(pack);
|
||||
|
||||
for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldCfg.cmdList); cmdIdx++)
|
||||
{
|
||||
const BldCfgCommand *const cmd = lstGet(bldCfg.cmdList, cmdIdx);
|
||||
const BldHlpCommand *const cmdHlp = lstFind(bldHlp.cmdList, &cmd->name);
|
||||
CHECK(cmdHlp != NULL);
|
||||
|
||||
pckWriteBoolP(pack, cmd->internal);
|
||||
pckWriteStrP(pack, bldHlpRenderXml(cmdHlp->summary));
|
||||
pckWriteStrP(pack, bldHlpRenderXml(cmdHlp->description));
|
||||
}
|
||||
|
||||
pckWriteArrayEndP(pack);
|
||||
|
||||
// Option help
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
pckWriteArrayBeginP(pack);
|
||||
|
||||
for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++)
|
||||
{
|
||||
const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx);
|
||||
const BldHlpOption *const optHlp = lstFind(bldHlp.optList, &opt->name);
|
||||
|
||||
// Internal
|
||||
pckWriteBoolP(pack, opt->internal);
|
||||
|
||||
// Section
|
||||
if (optHlp != NULL)
|
||||
pckWriteStrP(pack, optHlp->section);
|
||||
else
|
||||
pckWriteNullP(pack);
|
||||
|
||||
// Summary
|
||||
if (optHlp != NULL)
|
||||
pckWriteStrP(pack, bldHlpRenderXml(optHlp->summary));
|
||||
else
|
||||
pckWriteNullP(pack);
|
||||
|
||||
// Description
|
||||
if (optHlp != NULL)
|
||||
pckWriteStrP(pack, bldHlpRenderXml(optHlp->description));
|
||||
else
|
||||
pckWriteNullP(pack);
|
||||
|
||||
// Deprecations
|
||||
StringList *const deprecateList = strLstNew();
|
||||
|
||||
if (opt->deprecateList != NULL)
|
||||
{
|
||||
for (unsigned int deprecateIdx = 0; deprecateIdx < lstSize(opt->deprecateList); deprecateIdx++)
|
||||
{
|
||||
const BldCfgOptionDeprecate *const deprecate = lstGet(opt->deprecateList, deprecateIdx);
|
||||
|
||||
if (!strEq(deprecate->name, opt->name))
|
||||
strLstAdd(deprecateList, deprecate->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!strLstEmpty(deprecateList))
|
||||
{
|
||||
pckWriteArrayBeginP(pack);
|
||||
|
||||
for (unsigned int deprecateIdx = 0; deprecateIdx < strLstSize(deprecateList); deprecateIdx++)
|
||||
pckWriteStrP(pack, strLstGet(deprecateList, deprecateIdx));
|
||||
|
||||
pckWriteArrayEndP(pack);
|
||||
}
|
||||
else
|
||||
pckWriteNullP(pack);
|
||||
|
||||
// Command overrides
|
||||
bool found = false;
|
||||
|
||||
for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldCfg.cmdList); cmdIdx++)
|
||||
{
|
||||
const BldCfgCommand *const cmd = lstGet(bldCfg.cmdList, cmdIdx);
|
||||
const BldCfgCommand *const optCmd = lstFind(opt->cmdList, &cmd->name);
|
||||
|
||||
if (optCmd != NULL)
|
||||
{
|
||||
const BldHlpCommand *const cmdHlp = lstFind(bldHlp.cmdList, &cmd->name);
|
||||
CHECK(cmdHlp != NULL);
|
||||
const BldHlpOption *const cmdOptHlp = cmdHlp->optList != NULL ? lstFind(cmdHlp->optList, &opt->name) : NULL;
|
||||
|
||||
if (opt->internal != optCmd->internal || cmdOptHlp != NULL)
|
||||
{
|
||||
if (!found)
|
||||
{
|
||||
pckWriteArrayBeginP(pack);
|
||||
found = true;
|
||||
}
|
||||
|
||||
pckWriteObjBeginP(pack, .id = cmdIdx + 1);
|
||||
|
||||
if (opt->internal != optCmd->internal)
|
||||
pckWriteBoolP(pack, optCmd->internal);
|
||||
else
|
||||
pckWriteNullP(pack);
|
||||
|
||||
if (cmdOptHlp != NULL)
|
||||
{
|
||||
pckWriteStrP(pack, bldHlpRenderXml(cmdOptHlp->summary));
|
||||
pckWriteStrP(pack, bldHlpRenderXml(cmdOptHlp->description));
|
||||
}
|
||||
|
||||
pckWriteObjEndP(pack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
pckWriteArrayEndP(pack);
|
||||
else
|
||||
pckWriteNullP(pack);
|
||||
}
|
||||
|
||||
pckWriteArrayEndP(pack);
|
||||
pckWriteEnd(pack);
|
||||
|
||||
return pack;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Compress pack to a buffer
|
||||
***********************************************************************************************************************************/
|
||||
static Buffer *
|
||||
bldHlpRenderHelpAutoCCmp(const BldCfg bldCfg, const BldHlp bldHlp)
|
||||
{
|
||||
// Get pack buffer
|
||||
const Buffer *const packBuf = pckWriteBuf(bldHlpRenderHelpAutoCPack(bldCfg, bldHlp));
|
||||
Buffer *const result = bufNew(bufSize(packBuf));
|
||||
|
||||
// Open source/destination
|
||||
IoRead *const source = ioBufferReadNew(packBuf);
|
||||
IoWrite *const destination = ioBufferWriteNew(result);
|
||||
|
||||
ioFilterGroupAdd(ioWriteFilterGroup(destination), bz2CompressNew(9));
|
||||
|
||||
ioReadOpen(source);
|
||||
ioWriteOpen(destination);
|
||||
|
||||
// Copy data from source to destination
|
||||
Buffer *read = bufNew(bufUsed(packBuf) + 1);
|
||||
|
||||
ioRead(source, read);
|
||||
ASSERT(ioReadEof(source));
|
||||
|
||||
ioWrite(destination, read);
|
||||
ioWriteClose(destination);
|
||||
|
||||
// Return compressed buffer
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Output buffer to a file as a byte array
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
bldHlpRenderHelpAutoC(const Storage *const storageRepo, const BldCfg bldCfg, const BldHlp bldHlp)
|
||||
{
|
||||
// Convert pack to bytes
|
||||
String *const help = strNewFmt(
|
||||
"%s"
|
||||
"static const unsigned char helpData[] =\n"
|
||||
"{\n",
|
||||
strZ(bldHeader("help", "Help Data")));
|
||||
|
||||
const Buffer *const buffer = bldHlpRenderHelpAutoCCmp(bldCfg, bldHlp);
|
||||
bool first = true;
|
||||
size_t lineSize = 0;
|
||||
char byteZ[4];
|
||||
|
||||
for (unsigned int bufferIdx = 0; bufferIdx < bufUsed(buffer); bufferIdx++)
|
||||
{
|
||||
snprintf(byteZ, sizeof(byteZ), "%u", bufPtrConst(buffer)[bufferIdx]);
|
||||
|
||||
if (strlen(byteZ) + 1 + (first ? 0 : 1) + lineSize > 128)
|
||||
{
|
||||
strCatChr(help, '\n');
|
||||
first = true;
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
strCatFmt(help, " ");
|
||||
lineSize = 0;
|
||||
}
|
||||
|
||||
strCatFmt(help, "%s%s,", first ? "" : " ", byteZ);
|
||||
|
||||
lineSize += strlen(byteZ) + 1 + (first ? 0 : 1);
|
||||
first = false;
|
||||
}
|
||||
|
||||
strCatZ(help, "\n};\n");
|
||||
|
||||
// Write to storage
|
||||
bldPut(storageRepo, "command/help/help.auto.c", BUFSTR(help));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
bldHlpRender(const Storage *const storageRepo, const BldCfg bldCfg, const BldHlp bldHlp)
|
||||
{
|
||||
bldHlpRenderHelpAutoC(storageRepo, bldCfg, bldHlp);
|
||||
}
|
16
src/build/help/render.h
Normal file
16
src/build/help/render.h
Normal file
@ -0,0 +1,16 @@
|
||||
/***********************************************************************************************************************************
|
||||
Render Help
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef BUILD_HELP_RENDER_H
|
||||
#define BUILD_HELP_RENDER_H
|
||||
|
||||
#include "build/config/parse.h"
|
||||
#include "build/help/parse.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
// Render help
|
||||
void bldHlpRender(const Storage *const storageRepo, const BldCfg bldCfg, const BldHlp bldHlp);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -8,19 +8,17 @@ Help Command
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/compress/bz2/decompress.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io/bufferRead.h"
|
||||
#include "common/io/fdWrite.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/pack.h"
|
||||
#include "config/config.intern.h"
|
||||
#include "config/parse.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include automatically generated help data pack
|
||||
***********************************************************************************************************************************/
|
||||
#include "command/help/help.auto.c"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define the console width - use a fixed with of 80 since this should be safe on virtually all consoles
|
||||
***********************************************************************************************************************************/
|
||||
@ -234,16 +232,27 @@ typedef struct HelpOptionData
|
||||
} HelpOptionData;
|
||||
|
||||
static String *
|
||||
helpRender(void)
|
||||
helpRender(const Buffer *const helpData)
|
||||
{
|
||||
FUNCTION_LOG_VOID(logLevelDebug);
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(BUFFER, helpData);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
String *result = strNewZ(PROJECT_NAME " " PROJECT_VERSION);
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Set a small buffer size to minimize memory usage
|
||||
ioBufferSizeSet(8192);
|
||||
|
||||
// Read pack from compressed buffer
|
||||
IoRead *const helpRead = ioBufferReadNew(helpData);
|
||||
ioFilterGroupAdd(ioReadFilterGroup(helpRead), bz2DecompressNew());
|
||||
ioReadOpen(helpRead);
|
||||
|
||||
PackRead *pckHelp = pckReadNew(helpRead);
|
||||
|
||||
// Unpack command data
|
||||
PackRead *pckHelp = pckReadNewBuf(BUF(helpDataPack, sizeof(helpDataPack)));
|
||||
HelpCommandData *commandData = memNew(sizeof(HelpCommandData) * CFG_COMMAND_TOTAL);
|
||||
|
||||
pckReadArrayBeginP(pckHelp);
|
||||
@ -544,13 +553,15 @@ helpRender(void)
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
cmdHelp(void)
|
||||
cmdHelp(const Buffer *const helpData)
|
||||
{
|
||||
FUNCTION_LOG_VOID(logLevelDebug);
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(BUFFER, helpData);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
ioFdWriteOneStr(STDOUT_FILENO, helpRender());
|
||||
ioFdWriteOneStr(STDOUT_FILENO, helpRender(helpData));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
|
@ -4,10 +4,12 @@ Help Command
|
||||
#ifndef COMMAND_HELP_HELP_H
|
||||
#define COMMAND_HELP_HELP_H
|
||||
|
||||
#include "common/type/buffer.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
// Render help and output to stdout
|
||||
void cmdHelp(void);
|
||||
void cmdHelp(const Buffer *const helpData);
|
||||
|
||||
#endif
|
||||
|
@ -42,6 +42,11 @@ Main
|
||||
#include "storage/helper.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include automatically generated help data
|
||||
***********************************************************************************************************************************/
|
||||
#include "command/help/help.auto.c"
|
||||
|
||||
int
|
||||
main(int argListSize, const char *argList[])
|
||||
{
|
||||
@ -82,7 +87,7 @@ main(int argListSize, const char *argList[])
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
if (cfgCommandHelp())
|
||||
{
|
||||
cmdHelp();
|
||||
cmdHelp(BUF(helpData, sizeof(helpData)));
|
||||
}
|
||||
// Local role
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -1,20 +1,4 @@
|
||||
# File types for source files in the project
|
||||
build/lib/pgBackRestBuild/Build.pm:
|
||||
class: build
|
||||
type: perl
|
||||
|
||||
build/lib/pgBackRestBuild/Build/Common.pm:
|
||||
class: build
|
||||
type: perl
|
||||
|
||||
build/lib/pgBackRestBuild/Config/BuildHelp.pm:
|
||||
class: build
|
||||
type: perl
|
||||
|
||||
build/lib/pgBackRestBuild/Config/Data.pm:
|
||||
class: build
|
||||
type: perl
|
||||
|
||||
doc/doc.pl:
|
||||
class: doc/core
|
||||
type: perl
|
||||
@ -55,6 +39,10 @@ doc/lib/pgBackRestDoc/Common/String.pm:
|
||||
class: doc/core
|
||||
type: perl
|
||||
|
||||
doc/lib/pgBackRestDoc/Custom/DocConfigData.pm:
|
||||
class: doc/core
|
||||
type: perl
|
||||
|
||||
doc/lib/pgBackRestDoc/Custom/DocCustomRelease.pm:
|
||||
class: doc/core
|
||||
type: perl
|
||||
@ -147,10 +135,6 @@ doc/xml/metric.xml:
|
||||
class: doc/source
|
||||
type: xml
|
||||
|
||||
doc/xml/reference.xml:
|
||||
class: doc/source
|
||||
type: xml
|
||||
|
||||
doc/xml/release.xml:
|
||||
class: doc/source
|
||||
type: xml
|
||||
@ -251,6 +235,30 @@ src/build/error/render.h:
|
||||
class: build
|
||||
type: c/h
|
||||
|
||||
src/build/help/help.xml:
|
||||
class: build
|
||||
type: make
|
||||
|
||||
src/build/help/main.c:
|
||||
class: build
|
||||
type: c
|
||||
|
||||
src/build/help/parse.c:
|
||||
class: build
|
||||
type: c
|
||||
|
||||
src/build/help/parse.h:
|
||||
class: build
|
||||
type: c/h
|
||||
|
||||
src/build/help/render.c:
|
||||
class: build
|
||||
type: c
|
||||
|
||||
src/build/help/render.h:
|
||||
class: build
|
||||
type: c/h
|
||||
|
||||
src/build/install-sh:
|
||||
class: build
|
||||
type: make
|
||||
@ -407,10 +415,6 @@ src/command/expire/expire.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/command/help/help.auto.c:
|
||||
class: core/auto
|
||||
type: c
|
||||
|
||||
src/command/help/help.c:
|
||||
class: core
|
||||
type: c
|
||||
@ -2067,6 +2071,10 @@ test/src/module/build/errorTest.c:
|
||||
class: test/module
|
||||
type: c
|
||||
|
||||
test/src/module/build/helpTest.c:
|
||||
class: test/module
|
||||
type: c
|
||||
|
||||
test/src/module/command/archiveCommonTest.c:
|
||||
class: test/module
|
||||
type: c
|
||||
|
@ -631,6 +631,14 @@ unit:
|
||||
- build/error/parse
|
||||
- build/error/render
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: help
|
||||
total: 2
|
||||
|
||||
coverage:
|
||||
- build/help/parse
|
||||
- build/help/render
|
||||
|
||||
# ********************************************************************************************************************************
|
||||
- name: info
|
||||
|
||||
@ -778,6 +786,9 @@ unit:
|
||||
- command/help/help
|
||||
- command/help/help.auto: noCode
|
||||
|
||||
include:
|
||||
- build/help/render
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: info
|
||||
total: 3
|
||||
|
408
test/src/module/build/helpTest.c
Normal file
408
test/src/module/build/helpTest.c
Normal file
@ -0,0 +1,408 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Build Help
|
||||
***********************************************************************************************************************************/
|
||||
#include "storage/posix/storage.h"
|
||||
|
||||
#include "common/harnessPack.h"
|
||||
#include "common/harnessStorage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
testRun(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
// Create default storage object for testing
|
||||
Storage *storageTest = storagePosixNewP(TEST_PATH_STR, .write = true);
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("bldHlpRenderXml()"))
|
||||
{
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("format errors");
|
||||
|
||||
TEST_ERROR(
|
||||
bldHlpRenderXml(xmlDocumentRoot(xmlDocumentNewBuf(BUFSTRDEF("<doc><bogus/></doc>")))), FormatError,
|
||||
"unknown tag 'bogus'");
|
||||
|
||||
TEST_ERROR(
|
||||
bldHlpRenderXml(xmlDocumentRoot(xmlDocumentNewBuf(BUFSTRDEF("<doc>bogus\n</doc>")))), FormatError,
|
||||
"text 'bogus\n' is invalid");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("xml to text");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
bldHlpRenderXml(xmlDocumentRoot(xmlDocumentNewBuf(BUFSTRDEF(
|
||||
"<doc>"
|
||||
"<p><backrest/> <postgres/> {[dash]} "
|
||||
"<b><br-option><cmd><code><exe><file><host><i><id><link><path><pg-setting><proper><setting>"
|
||||
"info"
|
||||
"</setting></proper></pg-setting></path></link></id></i></host></file></exe></code></cmd></br-option></b></p>\n"
|
||||
"\n"
|
||||
"<admonition>think about it</admonition>\n"
|
||||
"\n"
|
||||
"<p>List:</p>\n"
|
||||
"\n"
|
||||
"<list>\n"
|
||||
"<list-item>item1</list-item>\n"
|
||||
"<list-item>item2</list-item>\n"
|
||||
"</list>\n"
|
||||
"\n"
|
||||
"<p>last para</p>"
|
||||
"</doc>")))),
|
||||
"pgBackRest PostgreSQL - info\n"
|
||||
"\n"
|
||||
"NOTE: think about it\n"
|
||||
"\n"
|
||||
"List:\n"
|
||||
"\n"
|
||||
"* item1\n"
|
||||
"* item2\n"
|
||||
"\n"
|
||||
"last para",
|
||||
"render");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("bldHlpParse() and bldHlpRender()"))
|
||||
{
|
||||
TEST_TITLE("error on missing command");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/config/config.yaml",
|
||||
"command:\n"
|
||||
" backup: {}\n"
|
||||
"\n"
|
||||
"optionGroup:\n"
|
||||
" pg: {}\n"
|
||||
"\n"
|
||||
"option:\n"
|
||||
" buffer:\n"
|
||||
" section: general\n"
|
||||
" type: size\n"
|
||||
"\n"
|
||||
" pg:\n"
|
||||
" type: string\n"
|
||||
" command-role:\n"
|
||||
" local: {}"
|
||||
"\n"
|
||||
" stanza:\n"
|
||||
" type: string\n"
|
||||
"\n"
|
||||
" xfer:\n"
|
||||
" type: bool\n"
|
||||
"\n");
|
||||
|
||||
BldCfg bldCfgErr = bldCfgParse(storageTest);
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/help/help.xml",
|
||||
"<doc>\n"
|
||||
" <config>\n"
|
||||
" <config-section-list>\n"
|
||||
" </config-section-list>\n"
|
||||
" </config>\n"
|
||||
"\n"
|
||||
" <operation>\n"
|
||||
" <operation-general>\n"
|
||||
" <option-list>\n"
|
||||
" </option-list>\n"
|
||||
" </operation-general>\n"
|
||||
"\n"
|
||||
" <command-list>\n"
|
||||
" </command-list>\n"
|
||||
" </operation>\n"
|
||||
"</doc>\n");
|
||||
|
||||
TEST_ERROR(bldHlpParse(storageTest, bldCfgErr), FormatError, "command 'backup' must have help");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on missing config option");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/help/help.xml",
|
||||
"<doc>\n"
|
||||
" <config>\n"
|
||||
" <config-section-list>\n"
|
||||
" </config-section-list>\n"
|
||||
" </config>\n"
|
||||
"\n"
|
||||
" <operation>\n"
|
||||
" <operation-general>\n"
|
||||
" <option-list>\n"
|
||||
" </option-list>\n"
|
||||
" </operation-general>\n"
|
||||
"\n"
|
||||
" <command-list>\n"
|
||||
" <command id=\"backup\" name=\"Backup\">\n"
|
||||
" <summary>Backup.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Backup.</p>\n"
|
||||
" </text>\n"
|
||||
" </command>\n"
|
||||
" </command-list>\n"
|
||||
" </operation>\n"
|
||||
"</doc>\n");
|
||||
|
||||
TEST_ERROR(bldHlpParse(storageTest, bldCfgErr), FormatError, "option 'buffer' must have help for command 'backup'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("error on missing command-line option");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/help/help.xml",
|
||||
"<doc>\n"
|
||||
" <config>\n"
|
||||
" <config-section-list>\n"
|
||||
" <config-section id=\"general\" name=\"General\">\n"
|
||||
" <config-key-list>\n"
|
||||
" <config-key id=\"buffer\" name=\"Buffer\">\n"
|
||||
" <summary>Buffer.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Buffer.</p>\n"
|
||||
" </text>\n"
|
||||
" </config-key>\n"
|
||||
" </config-key-list>\n"
|
||||
" </config-section>\n"
|
||||
" </config-section-list>\n"
|
||||
" </config>\n"
|
||||
"\n"
|
||||
" <operation>\n"
|
||||
" <operation-general>\n"
|
||||
" <option-list>\n"
|
||||
" </option-list>\n"
|
||||
" </operation-general>\n"
|
||||
"\n"
|
||||
" <command-list>\n"
|
||||
" <command id=\"backup\" name=\"Backup\">\n"
|
||||
" <summary>Backup.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Backup.</p>\n"
|
||||
" </text>\n"
|
||||
" </command>\n"
|
||||
" </command-list>\n"
|
||||
" </operation>\n"
|
||||
"</doc>\n");
|
||||
|
||||
TEST_ERROR(bldHlpParse(storageTest, bldCfgErr), FormatError, "option 'stanza' must have help for command 'backup'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("parse and render");
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/config/config.yaml",
|
||||
"command:\n"
|
||||
" backup:\n"
|
||||
" command-role:\n"
|
||||
" async: {}\n"
|
||||
" local: {}\n"
|
||||
" remote: {}\n"
|
||||
"\n"
|
||||
" check: {}\n"
|
||||
"\n"
|
||||
" restore:\n"
|
||||
" internal: true\n"
|
||||
" command-role:\n"
|
||||
" local: {}\n"
|
||||
" remote: {}\n"
|
||||
"\n"
|
||||
"optionGroup:\n"
|
||||
" pg: {}\n"
|
||||
" repo: {}\n"
|
||||
"\n"
|
||||
"option:\n"
|
||||
" config:\n"
|
||||
" type: string\n"
|
||||
" required: false\n"
|
||||
" command:\n"
|
||||
" backup:\n"
|
||||
" internal: true\n"
|
||||
" restore: {}\n"
|
||||
"\n"
|
||||
" buffer-size:\n"
|
||||
" section: global\n"
|
||||
" type: integer\n"
|
||||
" default: 1024\n"
|
||||
" allow-list: [512, 1024, 2048, 4096]\n"
|
||||
"\n"
|
||||
" force:\n"
|
||||
" type: boolean\n"
|
||||
" command:\n"
|
||||
" check: {}\n"
|
||||
" restore: {}\n"
|
||||
"\n"
|
||||
" stanza:\n"
|
||||
" type: string\n"
|
||||
" required: false\n"
|
||||
" deprecate:\n"
|
||||
" stanza: {}\n"
|
||||
" stanza1: {}\n"
|
||||
" stanza2: {}\n"
|
||||
"\n");
|
||||
|
||||
BldCfg bldCfg = bldCfgParse(storageTest);
|
||||
|
||||
HRN_STORAGE_PUT_Z(
|
||||
storageTest, "src/build/help/help.xml",
|
||||
"<doc>\n"
|
||||
" <config>\n"
|
||||
" <config-section-list>\n"
|
||||
" <config-section id=\"general\" name=\"General\">\n"
|
||||
" <config-key-list>\n"
|
||||
" <config-key id=\"buffer-size\" name=\"Buffer Size\">\n"
|
||||
" <summary>Buffer size for file operations.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Buffer.</p>\n"
|
||||
" </text>\n"
|
||||
" </config-key>\n"
|
||||
"\n"
|
||||
" <config-key id=\"stanza\" name=\"Stanza\">\n"
|
||||
" <summary>Defines the stanza.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Stanza.</p>\n"
|
||||
" </text>\n"
|
||||
" </config-key>\n"
|
||||
" </config-key-list>\n"
|
||||
" </config-section>\n"
|
||||
" </config-section-list>\n"
|
||||
" </config>\n"
|
||||
"\n"
|
||||
" <operation>\n"
|
||||
" <operation-general>\n"
|
||||
" <option-list>\n"
|
||||
" <option id=\"config\" section=\"stanza\" name=\"Config\">\n"
|
||||
" <summary><backrest/> configuration file.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Use this option to specify a different configuration file than the default.</p>\n"
|
||||
" </text>\n"
|
||||
" </option>\n"
|
||||
" </option-list>\n"
|
||||
" </operation-general>\n"
|
||||
"\n"
|
||||
" <command-list>\n"
|
||||
" <command id=\"backup\" name=\"Backup\">\n"
|
||||
" <summary>Backup cluster.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p><backrest/> backup.</p>\n"
|
||||
" </text>\n"
|
||||
" </command>\n"
|
||||
"\n"
|
||||
" <command id=\"check\" name=\"Check\">\n"
|
||||
" <summary>Check cluster.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p><backrest/> check.</p>\n"
|
||||
" </text>\n"
|
||||
"\n"
|
||||
" <option-list>\n"
|
||||
" <option id=\"force\" name=\"Force\">\n"
|
||||
" <summary>Force delete.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Longer description.</p>\n"
|
||||
" </text>\n"
|
||||
" </option>\n"
|
||||
" </option-list>\n"
|
||||
" </command>\n"
|
||||
"\n"
|
||||
" <command id=\"restore\" name=\"Restore\">\n"
|
||||
" <summary>Restore cluster.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p><backrest/> restore.</p>\n"
|
||||
" </text>\n"
|
||||
"\n"
|
||||
" <option-list>\n"
|
||||
" <option id=\"force\" name=\"Force\">\n"
|
||||
" <summary>Force delete.</summary>\n"
|
||||
"\n"
|
||||
" <text>\n"
|
||||
" <p>Longer description.</p>\n"
|
||||
" </text>\n"
|
||||
" </option>\n"
|
||||
" </option-list>\n"
|
||||
" </command>\n"
|
||||
" </command-list>\n"
|
||||
" </operation>\n"
|
||||
"</doc>\n");
|
||||
|
||||
TEST_RESULT_STR_Z(
|
||||
hrnPackToStr(pckReadNewBuf(pckWriteBuf(bldHlpRenderHelpAutoCPack(bldCfg, bldHlpParse(storageTest, bldCfg))))),
|
||||
"1:array:"
|
||||
"["
|
||||
// backup command
|
||||
"2:str:Backup cluster."
|
||||
", 3:str:pgBackRest backup."
|
||||
// check command
|
||||
", 5:str:Check cluster."
|
||||
", 6:str:pgBackRest check."
|
||||
// restore command
|
||||
", 7:bool:true"
|
||||
", 8:str:Restore cluster."
|
||||
", 9:str:pgBackRest restore."
|
||||
"]"
|
||||
", 2:array:"
|
||||
"["
|
||||
// buffer-size option
|
||||
"2:str:general"
|
||||
", 3:str:Buffer size for file operations."
|
||||
", 4:str:Buffer."
|
||||
// config option
|
||||
", 8:str:stanza"
|
||||
", 9:str:pgBackRest configuration file."
|
||||
", 10:str:Use this option to specify a different configuration file than the default."
|
||||
", 12:array:"
|
||||
"["
|
||||
// backup command override
|
||||
"1:obj:"
|
||||
"{"
|
||||
"1:bool:true"
|
||||
"}"
|
||||
"]"
|
||||
// force option
|
||||
", 18:array:"
|
||||
"["
|
||||
// check command override
|
||||
"2:obj:"
|
||||
"{"
|
||||
"2:str:Force delete."
|
||||
", 3:str:Longer description."
|
||||
"}"
|
||||
// restore command override
|
||||
", 3:obj:"
|
||||
"{"
|
||||
"2:str:Force delete."
|
||||
", 3:str:Longer description."
|
||||
"}"
|
||||
"]"
|
||||
// stanza option
|
||||
", 20:str:general"
|
||||
", 21:str:Defines the stanza."
|
||||
", 22:str:Stanza."
|
||||
", 23:array:"
|
||||
"["
|
||||
"1:str:stanza1"
|
||||
", 2:str:stanza2"
|
||||
"]"
|
||||
"]",
|
||||
"parse and render");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("check help file");
|
||||
|
||||
TEST_RESULT_VOID(bldHlpRender(storageTest, bldCfg, bldHlpParse(storageTest, bldCfg)), "write file");
|
||||
TEST_STORAGE_EXISTS(storageTest, "command/help/help.auto.c");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RETURN_VOID();
|
||||
}
|
@ -37,6 +37,10 @@ testRun(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
// Create help data
|
||||
const BldCfg bldCfg = bldCfgParse(storagePosixNewP(HRN_PATH_REPO_STR));
|
||||
const Buffer *const helpData = bldHlpRenderHelpAutoCCmp(bldCfg, bldHlpParse(storagePosixNewP(HRN_PATH_REPO_STR), bldCfg));
|
||||
|
||||
// Program name a version are used multiple times
|
||||
const char *helpVersion = PROJECT_NAME " " PROJECT_VERSION;
|
||||
|
||||
@ -136,13 +140,13 @@ testRun(void)
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help from empty command line");
|
||||
TEST_RESULT_STR_Z(helpRender(), generalHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), generalHelp, "check text");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
strLstAddZ(argList, "help");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help from help command");
|
||||
TEST_RESULT_STR_Z(helpRender(), generalHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), generalHelp, "check text");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("version command");
|
||||
@ -161,7 +165,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "help");
|
||||
strLstAddZ(argList, "version");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for version command");
|
||||
TEST_RESULT_STR_Z(helpRender(), commandHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), commandHelp, "check text");
|
||||
|
||||
// This test is broken up into multiple strings because C99 does not require compilers to support const strings > 4095 bytes
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -321,7 +325,7 @@ testRun(void)
|
||||
hrnCfgArgRawZ(argList, cfgOptDbInclude, "db1");
|
||||
hrnCfgArgRawZ(argList, cfgOptDbInclude, "db2");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for restore command");
|
||||
TEST_RESULT_STR_Z(helpRender(), commandHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), commandHelp, "check text");
|
||||
hrnCfgEnvRemoveRaw(cfgOptRepoCipherPass);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -334,7 +338,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "parse too many options");
|
||||
TEST_ERROR(helpRender(), ParamInvalidError, "only one option allowed for option help");
|
||||
TEST_ERROR(helpRender(helpData), ParamInvalidError, "only one option allowed for option help");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
@ -342,7 +346,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, BOGUS_STR);
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "parse bogus option");
|
||||
TEST_ERROR(helpRender(), OptionInvalidError, "option 'BOGUS' is not valid for command 'archive-push'");
|
||||
TEST_ERROR(helpRender(helpData), OptionInvalidError, "option 'BOGUS' is not valid for command 'archive-push'");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
@ -350,7 +354,7 @@ testRun(void)
|
||||
strLstAddZ(argList, CFGCMD_ARCHIVE_PUSH);
|
||||
strLstAddZ(argList, CFGOPT_PROCESS);
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "parse option invalid for command");
|
||||
TEST_ERROR(helpRender(), OptionInvalidError, "option 'process' is not valid for command 'archive-push'");
|
||||
TEST_ERROR(helpRender(helpData), OptionInvalidError, "option 'process' is not valid for command 'archive-push'");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("default and current option value");
|
||||
@ -378,12 +382,13 @@ testRun(void)
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "buffer-size");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for archive-push command, buffer-size option");
|
||||
TEST_RESULT_STR(helpRender(), strNewFmt("%s\ndefault: 1048576\n", optionHelp), "check text");
|
||||
TEST_RESULT_STR(helpRender(helpData), strNewFmt("%s\ndefault: 1048576\n", optionHelp), "check text");
|
||||
|
||||
// Set a current value
|
||||
hrnCfgArgRawZ(argList, cfgOptBufferSize, "32768");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for archive-push command, buffer-size option");
|
||||
TEST_RESULT_STR(helpRender(), strNewFmt("%s\ncurrent: 32768\ndefault: 1048576\n", optionHelp), "check text, current value");
|
||||
TEST_RESULT_STR(
|
||||
helpRender(helpData), strNewFmt("%s\ncurrent: 32768\ndefault: 1048576\n", optionHelp), "check text, current value");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("deprecated host option names");
|
||||
@ -411,7 +416,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "repo1-s3-host");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for archive-push command, repo1-s3-host option");
|
||||
TEST_RESULT_STR_Z(helpRender(), optionHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), optionHelp, "check text");
|
||||
|
||||
optionHelp = strZ(strNewFmt(
|
||||
HELP_OPTION
|
||||
@ -424,7 +429,7 @@ testRun(void)
|
||||
hrnCfgArgRawZ(argList, cfgOptRepoType, "s3");
|
||||
strLstAddZ(argList, "--repo1-s3-host=s3-host");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for archive-push command, repo1-s3-host option");
|
||||
TEST_RESULT_STR_Z(helpRender(), optionHelp, "check text, current value");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), optionHelp, "check text, current value");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("cipher pass redacted");
|
||||
@ -447,7 +452,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "archive-push");
|
||||
strLstAddZ(argList, "repo-cipher-pass");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for archive-push command, repo1-s3-host option");
|
||||
TEST_RESULT_STR_Z(helpRender(), optionHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), optionHelp, "check text");
|
||||
hrnCfgEnvRemoveRaw(cfgOptRepoCipherPass);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -474,7 +479,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "backup");
|
||||
strLstAddZ(argList, "repo-hardlink");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for backup command, repo-hardlink option");
|
||||
TEST_RESULT_STR_Z(helpRender(), optionHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), optionHelp, "check text");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "/path/to/pgbackrest");
|
||||
@ -482,7 +487,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "backup");
|
||||
strLstAddZ(argList, "hardlink");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for backup command, deprecated hardlink option");
|
||||
TEST_RESULT_STR_Z(helpRender(), optionHelp, "check text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), optionHelp, "check text");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("check admonition");
|
||||
@ -518,7 +523,7 @@ testRun(void)
|
||||
strLstAddZ(argList, "backup");
|
||||
strLstAddZ(argList, "repo-retention-archive");
|
||||
TEST_RESULT_VOID(testCfgLoad(argList), "help for backup command, repo-retention-archive option");
|
||||
TEST_RESULT_STR_Z(helpRender(), optionHelp, "check admonition text");
|
||||
TEST_RESULT_STR_Z(helpRender(helpData), optionHelp, "check admonition text");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
@ -534,7 +539,7 @@ testRun(void)
|
||||
THROW_ON_SYS_ERROR(freopen(TEST_PATH "/stdout.help", "w", stdout) == NULL, FileWriteError, "unable to reopen stdout");
|
||||
|
||||
// Not in a test wrapper to avoid writing to stdout
|
||||
cmdHelp();
|
||||
cmdHelp(helpData);
|
||||
|
||||
// Restore normal stdout
|
||||
dup2(stdoutSave, STDOUT_FILENO);
|
||||
|
30
test/test.pl
30
test/test.pl
@ -34,10 +34,6 @@ use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::Common::String;
|
||||
use pgBackRestDoc::ProjectInfo;
|
||||
|
||||
use pgBackRestBuild::Build;
|
||||
use pgBackRestBuild::Build::Common;
|
||||
use pgBackRestBuild::Config::BuildHelp;
|
||||
|
||||
use pgBackRestTest::Common::BuildTest;
|
||||
use pgBackRestTest::Common::CodeCountTest;
|
||||
use pgBackRestTest::Common::ContainerTest;
|
||||
@ -534,32 +530,8 @@ eval
|
||||
{
|
||||
&log(INFO, "autogenerate code");
|
||||
|
||||
# Auto-generate C files
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
my $rhBuild =
|
||||
{
|
||||
'configHelp' =>
|
||||
{
|
||||
&BLD_DATA => buildConfigHelp(),
|
||||
&BLD_PATH => 'command/help',
|
||||
},
|
||||
};
|
||||
|
||||
my @stryBuilt = buildAll("${strBackRestBase}/src", $rhBuild);
|
||||
|
||||
# Error when checking that files have already been generated but they change
|
||||
if ($bGenCheck && @stryBuilt)
|
||||
{
|
||||
confess &log(
|
||||
ERROR,
|
||||
'unexpected autogeneration of C code: ' . join(', ', @stryBuilt) . ":\n" .
|
||||
trim(executeTest("git -C ${strBackRestBase} diff")));
|
||||
}
|
||||
|
||||
&log(INFO, " autogenerated C code: " . (@stryBuilt ? join(', ', @stryBuilt) : 'no changes'));
|
||||
|
||||
# Build code
|
||||
executeTest("make -C ${strBuildPath} build-config build-error");
|
||||
executeTest("make -C ${strBuildPath} build-config build-error build-help");
|
||||
|
||||
if ($bGenOnly)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user