Rev 6133 | Blame | Last modification | View Log | RSS feed
######################################################################### COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.## Module name : jats_generate_deployable.pl# Module type : Makefile system# Compiler(s) : Perl# Environment(s): jats build system## Description : Extracts current package version list from release manager# based on the 'IS_DEPOLYABLE' flag in a given Release# and copies resultant packages to release specific# directory.## Based on jats_update_release.pl and jats_gen_bom.pl but it is# intended to be used by the PULSE digital distribution process#......................................................................#require 5.008_002;use File::Basename;use File::Copy;use File::Path;use strict;use warnings;use JatsEnv;use JatsError;use JatsRmApi;use ArrayHashUtils;use FileUtils;use DBI;use Getopt::Long;use Pod::Usage; # required for help supportuse JSON;## Config Options#my $VERSION = "1.0.0"; # Update thismy $opt_help = 0;my $opt_verbose = $ENV{'GBE_VERBOSE'}; # Allow global verbosemy $opt_rtagid;my $opt_rootdir = '.';my $opt_test;my $opt_showFilters;my @opt_addFilters;my @opt_delFilters;my $opt_showFiles;my @opt_addFiles;my @opt_delFiles;## Constants#my $CONFFILE = '.bomGen';my $BOMFILE = '.bomCots';my $MANIFEST = 'MANIFEST.json';my $TFVARS = 'MANIFEST.tf';## Globals#my $DM_DB; # Data Base Interfacemy %bomList; # All files in the BOMmy $bomInfo; # Sbom meta datamy %baseList; # List of files in bin## Configuration file vars#my @confFilters;my @confFiles;my %filtersUsed;#-------------------------------------------------------------------------------# Function : Main## Description : Main entry point# Parse user options## Inputs :## Returns :#my $result = GetOptions ("help:+" => \$opt_help, # flag, multiple use allowed"manual:3" => \$opt_help, # flag, multiple use allowed"verbose:+" => \$opt_verbose, # flag"rtagid|rtag_id=s" => \$opt_rtagid, # Number"rootdir=s" => \$opt_rootdir, # string"addfilter=s" => \@opt_addFilters, # multiple strings"delfilter=s" => \@opt_delFilters, # multiple strings"showfilters" => \$opt_showFilters, # flag"addfiles=s" => \@opt_addFiles, # multiple strings"delfiles=s" => \@opt_delFiles, # multiple strings"showfiles" => \$opt_showFiles, # flag"test" => \$opt_test, # flag);## Process help and manual options#pod2usage(-verbose => 0, -message => "Version: $VERSION") if ($opt_help == 1 || ! $result);pod2usage(-verbose => 1) if ($opt_help == 2 );pod2usage(-verbose => 2) if ($opt_help > 2);ErrorConfig( 'name' => 'GenDeploy','verbose' => $opt_verbose );## Sanity tests## Supplied rootdir must exists as a directoryError("Root dir not specified")unless defined $opt_rootdir;Error("Root dir not a valid directory: ", $opt_rootdir )unless( -d $opt_rootdir );# Environment var GBE_DPKG must exists as a directoryError("GBE_DPKG Environment var is not a directory")unless ( -d $ENV{GBE_DPKG} );LoadFilterConfig();ProcessFilterArgs();# Non Filter operations# Must supply an rtagidError("Need --rtagid", "Example: -rtagid=2362" )unless ($opt_rtagid);Error("No Filters defined.", "Add filters before creating BOM")unless ( @confFilters );## This command is destined to be used in a directory where group permissions# are important. Ensure that the user is not killing group access#umask 0002;## Body of the processing# Save generation time into the meta datamy $now = time;$bomInfo->{version} = "2.0.0";$bomInfo->{timestamp}{epoch} = $now;$bomInfo->{timestamp}{utc} = gmtime($now);Message("Copying packages from $ENV{GBE_DPKG} to $opt_rootdir");## Processing#connectRM(\$DM_DB);GetReleaseInfo(); # Get Release MetadataGetPackageData(); # Get RM DataRemoveDuplicates(); # Need P or D, but not bothCopyInNew(); # Copy new filesRemoveExcess(); # Remove files no longer requiredGenFileData(); # Generate file metadataWriteManifest(); # Save out meta dataexit 0;#-------------------------------------------------------------------------------# Function : GenFileData## Description : Generate meta data on each file# Much of this is a guess.# Assume files look like:## VIXcryptoKeyManager-1.0.2061.cr-WIN32.exe# erg-pkgmnt_1.0.3010.cr_UBUNTU16_P.deb# erg-pkgmnt_1.0.3010.cr_RHEL7_P.rpm# xxxxxx.sh - bit trickier## Inputs : None## Returns : Populates $bomInfo#sub GenFileData{my @elist;my @edup;foreach my $file (sort keys %bomList){my $data;my $alias;$bomList{$file}{version} =~ m~(.*)\.([a-z]+)$~;my $pvfull = $1;my $proj = $2;my $pv = $pvfull;$pv =~ s~\.\d+$~~;if ($file =~ m~^(.*)-(.*)\.(.*)-(WIN.*)\.(exe)$~i){$data->{name} = $1;$data->{version} = $2;$data->{prj} = $3;$data->{arch} = $4;$data->{type} = $5;}elsif ( $file =~ m~^(.*)_(.*)\.([^_]+)_(.*)\.(deb|tgz|rpm)$~i){$data->{name} = $1;$data->{version} = $2;$data->{prj} = $3;$data->{arch} = $4;$data->{type} = $5;$data->{arch} =~ s~_[PD]~~;}elsif ( $file =~ m~^(.*)-($pv)\.(.*)\.(rpm)$~i){# COTS package$data->{name} = $1;$data->{version} = $2;$data->{arch} = $3;$data->{type} = $4;$data->{prj} = $proj;}elsif ( $file =~ m~^(.*)_($pv)\.(tgz)$~i){# COTS package$data->{name} = $1;$data->{version} = $2;$data->{arch} = 'UNKNOWN';$data->{type} = $3;$data->{prj} = $proj;}elsif ( $file =~ m~^(.*)-($pv)\.(.*)\.(deb)$~i){# COTS package$data->{name} = $1;$data->{version} = $2;$data->{arch} = $3;$data->{type} = $4;$data->{prj} = $proj;}elsif ( $file =~ m~^(.*)\.(sh|zip|msi|tar\.gz)$~i){$data->{name} = $1;$data->{arch} = 'NOARCH';$data->{type} = $2;$data->{version} = $pvfull;$data->{prj} = $proj;}unless ($data && $data->{name} && $data->{prj} && $data->{type}) {push @elist, $file;next;}$data->{fullname} = $file;## Create a nice alias# ERG -> VIX (not done)# All lowercase#$alias = join ('.', $data->{name}, $data->{prj}, $data->{type});$alias = lc ($alias);#$alias =~ s~^erg~vix~;#$alias =~ s~^vix~vix-~;#$alias =~ s~^vix--~vix-~;push (@edup, join( ' : ', $alias, $file ,$bomInfo->{files}{$alias}{fullname}) ) if exists $bomInfo->{files}{$alias};delete $data->{type};$bomInfo->{files}{$alias} = $data;}ReportError ("Cannot extract file metadata from:", @elist) if (@elist);ReportError ("Duplicate aliases for:", @edup) if (@edup);ErrorDoExit();}#-------------------------------------------------------------------------------# Function : CopyInNew## Description : Copy in new files# Don't copy in files that already exist - assume that the# files don't chnage without a chnage to the file name## Inputs :## Returns :#sub CopyInNew{## Ensure the output directory exists#if ( ! -d $opt_rootdir ){if ( defined($opt_test) ){Message("mkdir $opt_rootdir");}else{eval { mkpath($opt_rootdir) };Error("Failed to make project directory tree $opt_rootdir") if ( $@ || ! -d $opt_rootdir );}}## Generate a list of all files in the directory#foreach my $file ( glob("$opt_rootdir/*") ) {$baseList{$file}{data} = 1;}## Determine the files to be transferred#my @filelist;foreach my $file ( keys %bomList){push (@filelist, $file) unless ( -f "$opt_rootdir/$file" );}## Perform the actual copy#if ( @filelist ){#Message("Copying files for package $PKG_NAME version $PKG_VERSION");if ( defined($opt_test) ){Message( map("$_...", @filelist) );}else{eval { mkpath($opt_rootdir) };Error("Failed to make destination directory") if ( $@ || ! -d $opt_rootdir );foreach my $file ( @filelist ){Verbose("Copy: $file...");my $srcFile = $bomList{$file}{path};if ( ! copy($srcFile, $opt_rootdir) ){Warning("Failed to copy $file ($!)");}}}}}#-------------------------------------------------------------------------------# Function : RemoveExcess## Description : Remove excess files from the output directory## Inputs :## Returns :#sub RemoveExcess{my @filelist;my %keepList = map { $_ => 1 } @confFiles;## Find all files in the output directory# Use the 'keepList' so that we don't pickup files that should# be in the directory. README.md, MANIFEST ...#foreach my $srcPath ( glob("$opt_rootdir/*") ){my $dstFile = basename($srcPath);next if exists $keepList{$dstFile};next unless ( -f $srcPath );push (@filelist, $dstFile) unless (exists $bomList{$dstFile} );}if ( @filelist){#Message ("Delete execess files", @filelist );unless ( defined($opt_test) ){foreach my $file ( @filelist ){Verbose("Delete: $file...");if ( unlink("$opt_rootdir/$file") ne 1 ){Warning("Failed to delete: $file. ($!)");}}}}## Report changed files# Generate a list of all files in the directory#foreach my $file ( glob("$opt_rootdir/*") ) {$baseList{$file}{data} |= 2;}## Determined added, removed and replaced##my (@replaced, @added, @removed, @unchanged, %newList);foreach my $entry ( keys %baseList ){(my $key = $entry) =~ s~\d+~z~g;$baseList{$entry}{key} = $key;(my $name = $entry) =~ s~^\./~~;$newList{$key}{$baseList{$entry}{data}} = $entry;}foreach my $key ( sort keys %newList ){if (exists $newList{$key}{1} && exists $newList{$key}{2} ) {push @replaced, "$newList{$key}{1} => $newList{$key}{2}";} elsif (exists $newList{$key}{1}) {push @removed, $newList{$key}{1};} elsif (exists $newList{$key}{2}) {push @added, $newList{$key}{2};} elsif (exists $newList{$key}{3}) {push @unchanged, $newList{$key}{3};}}Message ("Unchanged: " .(@unchanged ? scalar(@unchanged ) : 'None') );Message ("Replaced: " . (@replaced ? scalar(@replaced ) : 'None'), @replaced);Message ("Added: " . (@added ? scalar(@added ) : 'None'), @added);Message ("Removed: " . (@removed ? scalar(@removed ) : 'None'), @removed);}#-------------------------------------------------------------------------------# Function : RemoveDuplicates## Description : Scan the BOM file list and remove duplicate installers# Duplicate installers are that that have both a P and a D# flavor of the installer## This test has some nasty built-in knowledge (assumtions)# It assumes that:# Windows installers are only created for one flavor# Don't need to worry about windoes installers# Non windows installers are of the form:# Name_Architecture_Type.deb## Inputs :## Returns :#sub RemoveDuplicates{my %baseNames;foreach my $file ( keys %bomList){## Only process files that are of the expected form# ie: erg-udcrypt_1.0.3043.vss_UBUNTU16_P.deb#if( $file =~ m~(.*)_([PD])(\.(deb|rpm|tgz))$~ ){my $base=$1;my $type=$2;my $suf=$3;if (exists $baseNames{$base} ){my $debugName = $base . '_D' . $suf;Verbose("Remove debug installer: $file. Kill: $debugName");delete $bomList{$debugName};}$baseNames{$base} = $type;}}}#-------------------------------------------------------------------------------# Function : LoadFilterConfig## Description : Load Filter Config# Retain filter config for future reference## Inputs :## Returns :#sub LoadFilterConfig{if ( -f "$opt_rootdir/$CONFFILE" ){Message("Loading Config File");my $perl_scalar = ReadJsonFile("$opt_rootdir/$CONFFILE");Error ("Invalid format in Config file")unless (ref($perl_scalar->{filters}) eq 'ARRAY');push (@confFilters, @{$perl_scalar->{filters}});push (@confFiles, @{$perl_scalar->{keptfiles}}) if exists ($perl_scalar->{keptfiles});}}#-------------------------------------------------------------------------------# Function : ReadJsonFile## Description : Read a JSON file and return the data## Inputs : $fname - Name of the file to read## Returns : Ref to the JSON#sub ReadJsonFile{my ($fname) = @_;local $/;open(my $fh, $fname ) || Error("Failed to open $fname. $!");my $json_text = <$fh>;my $perl_scalar = decode_json( $json_text );close($fh);return $perl_scalar;}#-------------------------------------------------------------------------------# Function : WriteJsonFile## Description : Write data into a file as JSON## Inputs : $fname - Name of file to write# $data - Ref to data to write## Returns : Will not return on error#sub WriteJsonFile{my ($fname, $data) = @_;FileCreate ($fname, to_json( $data, { ascii => 1, pretty => 1 }));}#-------------------------------------------------------------------------------# Function : ProcessFilterArgs## Description : Process the filter based arguments## Inputs :## Returns :#sub ProcessFilterArgs{my $filterArgSeen;my $writeConf;if ( @opt_addFilters ){Message ("Adding command line filters to the release config file");foreach my $element (@opt_addFilters) {UniquePush (\@confFilters, $_ ) foreach ( split(/,/, $element));}$writeConf = 1;}if ( @opt_delFilters ){Message ("Deleting command line filters to the release config file");foreach my $element (@opt_delFilters) {ArrayDelete (\@confFilters, $_ ) foreach ( split(/,/, $element));}$writeConf = 1;}if ( @opt_addFiles ){Message ("Adding command line files to the release config file");foreach my $element (@opt_addFiles) {UniquePush (\@confFiles, $_ ) foreach ( split(/,/, $element));}$writeConf = 1;}if ( @opt_delFiles ){Message ("Deleting command line files to the release config file");foreach my $element (@opt_delFiles) {ArrayDelete (\@confFiles, $_ ) foreach ( split(/,/, $element));}$writeConf = 1;}## Save filter information#if ( $writeConf && ! defined($opt_test) ){Verbose ("Write config file");## Add known files#UniquePush (\@confFiles, $CONFFILE, $BOMFILE, $MANIFEST, $TFVARS);my $config;push @{$config->{filters}},@confFilters;push @{$config->{keptfiles}},@confFiles;WriteJsonFile("$opt_rootdir/$CONFFILE",$config);}## Display information to the user#if ($opt_showFilters){Message ("Configured Filters",@confFilters );$filterArgSeen = 1;}if ($opt_showFiles){Message ("Configured Files. Keep:",@confFiles );$filterArgSeen = 1;}## Terminate program on any filter operations#exit 0 if ( $writeConf || $filterArgSeen);}#-------------------------------------------------------------------------------# Function : WriteManifest## Description : Save the filter config file if required## Inputs :## Returns :#sub WriteManifest{return if defined($opt_test);## Create JSON metadata#Verbose ("Write JSON Manifest");my $jsonString = to_json( $bomInfo, { ascii => 1, pretty => 1, canonical => 1 } );FileCreate ($opt_rootdir . '/' . $MANIFEST, $jsonString);## Create Terraform data# Note: Terraform variable cannot have a '.' in them#my @tfData2;push @tfData2, "// Terraform variable definitions to map clean package name to full file name";push @tfData2, "variable vixFileName {";push @tfData2, " type = \"map\"";push @tfData2, " default = {" ;foreach my $item ( sort keys %{$bomInfo->{files}} ){push @tfData2, " \"". $item ."\" = \"" .$bomInfo->{files}{$item}{fullname} ."\"";}push @tfData2, " }" ;push @tfData2, "}" ;FileCreate ($opt_rootdir . '/' . $TFVARS, @tfData2);}#-------------------------------------------------------------------------------# Function : GetReleaseInfo## Description : Get Release Meta Data## Inputs :## Returns : Will exit on error#sub GetReleaseInfo{my $m_sqlstr = "SELECT p.PROJ_ID, rt.rtag_id, p.PROJ_NAME, rt.RTAG_NAME" ." FROM release_tags rt, PROJECTS p" ." WHERE p.PROJ_ID = rt.PROJ_ID" ." and rt.RTAG_ID = " . $opt_rtagid;my $sth = $DM_DB->prepare($m_sqlstr);if ( defined($sth) ){if ( $sth->execute( ) ){if ( $sth->rows ){while ( my ( $proj_id, $xx, $pname, $rname ) = $sth->fetchrow_array ){my $data;$data->{product_id} = $proj_id;$data->{product_name} = $pname;$data->{release_rtagid} = $opt_rtagid;$data->{release_name} = $rname;push @{$bomInfo->{release}}, $data;}}else{Error("GetReleaseInfo: No rtagid found for " . $opt_rtagid);}$sth->finish();}else{Error("GetReleaseInfo: Execute failure", $sth->errstr(), $m_sqlstr );}}else{Error("GetReleaseInfo: Prepare failure", $sth->errstr(), $m_sqlstr );}}#-------------------------------------------------------------------------------# Function : GetPackageData## Description : Extract data from RM based on the provided rtag_id## Inputs :## Returns :#sub GetPackageData{my $m_sqlstr = "SELECT p.PKG_NAME, " ." pv.PKG_VERSION, " ." l.name " ."FROM package_versions pv, " ." RELEASE_MANAGER.RELEASE_CONTENT rc, " ." RELEASE_MANAGER.PACKAGES p, " ." RELEASE_MANAGER.LICENCING pl, " ." RELEASE_MANAGER.LICENCES l " ."WHERE rc.rtag_id = $opt_rtagid " ." AND rc.pv_id = pv.pv_id " ." AND p.PKG_ID = pv.pkg_id " ." AND pv.IS_DEPLOYABLE = 'Y' " ." AND pl.PV_ID(+) = pv.pv_id " ." AND pl.LICENCE = l.LICENCE(+)" ;# " and ( pv.IS_DEPLOYABLE = 'Y' or upper( p.PKG_NAME) like 'ERG%' or upper( p.PKG_NAME) like 'VIX%' )";my ( $PKG_NAME, $PKG_VERSION, $LICENSE );my $sth = $DM_DB->prepare($m_sqlstr);if ( defined($sth) ){if ( $sth->execute( ) ){if ( $sth->rows ){while ( ( $PKG_NAME, $PKG_VERSION, $LICENSE ) = $sth->fetchrow_array ){Verbose ("Deployable: $PKG_NAME, $PKG_VERSION");my $pkgDir = "$ENV{GBE_DPKG}/$PKG_NAME";my $srcDir = "$ENV{GBE_DPKG}/$PKG_NAME/$PKG_VERSION";my $dstDir = $opt_rootdir;if ( -d "$srcDir" ){my $foundFiltered = 0;# for each of the filter rules we glob the rule in the src pkg/version dir# and if any of the globbed files dont exist in the dst dir add it to the# the filelist array of files to copyforeach my $filter ( @confFilters ){foreach my $srcPath ( glob("$srcDir/$filter") ){next unless ( -f $srcPath );$foundFiltered = 1;$filtersUsed{$filter} = 1;my $dstFile = basename($srcPath);my $srcFile = $srcPath;$srcFile =~ s~^$srcDir/~~;$bomList{$srcFile}{path} = $srcPath;$bomList{$srcFile}{package} = $PKG_NAME;$bomList{$srcFile}{version} = $PKG_VERSION;$bomList{$srcFile}{license} = $LICENSE || '';}}# if no files found using filters then issue warningWarning("No Files found for Package Version $PKG_NAME/$PKG_VERSION using supplied filters")unless ( $foundFiltered );if ($foundFiltered){$bomInfo->{packages}{$PKG_NAME} = $PKG_VERSION;}}elsif ( ! -d "$pkgDir" ){# if srcDir and pkgDir dont exist then package is not in dpkg_archive so display messageWarning("Skipping Package $PKG_NAME/$PKG_VERSION as it does not exist in dpkg_archive");}else{# However if srcDir does not exist but pkgDir does exist then the package version is missing which maybe an issueWarning("Missing Version $PKG_VERSION for Package $PKG_NAME in dpkg_archive");}}## Report filter elements that where not used.#my @notUsed;foreach my $filter ( @confFilters ){next if ( exists $filtersUsed{$filter} );push @notUsed, $filter}Warning ("Unused filter rules:", @notUsed )if ( @notUsed );}else{Error("No Packages for rtagid: $opt_rtagid");}$sth->finish();}else{Error("Execute failure", $sth->errstr(), $m_sqlstr );}}else{Error("Prepare failure", $sth->errstr(), $m_sqlstr );}## Report Commercial packages# Write out a file to contain the list of COTS files#my @Commercial;foreach my $file (sort keys %bomList){next unless ($bomList{$file}{license} =~ m ~^Commercial~);$bomList{$file}{cots} = 1;push @Commercial, $file;}Message ("Commercial software packages:", @Commercial);my $data;$data->{COTS} = \@Commercial;WriteJsonFile ("$opt_rootdir/$BOMFILE", $data);}#-------------------------------------------------------------------------------# Documentation#=pod=for htmltoc DEPLOY::generate_deployable=head1 NAMEjats_generate_deployable - Extracts current package version list from Release Manager RtagIdand copy resultant packages to a specific directory.=head1 SYNOPSISjats generate_deployable [options]Options:-help - Brief help message-help -help - Detailed help message-man - Full documentation-rtagid=xxx - Specify the Release Manager RtagId to process-rootdir=xxx - Specifies the root of the releases directory-showfilters - Display current filter set and exit-addfilter=xxx[,yyy] - Add a new filter to the existing filter set-delfilter=xxx[,yyy] - Delete a filter from the existing filter set-showfiles - Display current kept file set and exit-addfiles=xxx[,yyy] - Add a new file to the kept file set-delfiles=xxx[,yyy] - Delete a file from the kept file set-test - Just log actions without copying files.-verbose - Enable verbose output=head1 OPTIONS=over 8=item B<-help>Print a brief help message and exits.=item B<-help -help>Print a detailed help message with an explanation for each option.=item B<-man>Prints the manual page and exits.=item B<-rtagid=xxx>This option specifies one or more RTAG_ID's to use as the source of packages that will be copied.The ID will be used to get a unique list of package/versions that can be copied from dpkg_archive.This option is Mandatory, for non-filter command.=item B<-rootdir=xxx>This option specifies the root directory where the packages will be copied to.The specified directory must exist.The default value is the current directory.=item B<-showfilters>This option will display the current filter set. If it is combined with another filter operationthen the other operations will be performed before the display.=item B<-addFilter=xxx[,yyy]>This option allows new filters to be added to the set of filters. Thisoption can be specified multiple times.This option specifies a comma separated list of shell wildcard filter rule thatwill be used to determine which files are copied from package version directory inGBE_DPKG to the release directory. This can be supplied multiple times tospecify rules for copying.Filters must be added the first time this command is run against a releaseand packages are copied to the project/release directory. These values are then written to aconfig file in the release directory so the same values can be used on subsequent runs.In these subsequent runs this option need not be specified as the config items will be used, howeverthey can be changed by specifying them again on the command line and the config will be re-written.The values of these will depend on what builds are required for each project. Some examples are--filter='*-WIN32.exe,*.deb'=item B<-delFilter=xxx[,yyy]>This option deletes one or more filter rules from an existing set of filters. Thisoption can be specified multiple times.=item B<-showfiles>This option will display the current file set. If it is combined with another file operationsthen the other operations will be performed before the display.=item B<-addFile=xxx[,yyy]>This option allows new files to be added to the set of kept files. Thisoption can be specified multiple times.This option specifies a comma separated list of file names (No wild cards) thatwill be used to specify a list of files that shold be kept in the directory. Thesefiles do not form a part of the manifest, but are not deleted by the tool.=item B<-delFile=xxx[,yyy]>This option deletes one or more files from the set of kept files. Thisoption can be specified multiple times.=item B<-test>This option will display what would be copied without actually copying anything=item B<-verbose>This option will display progress information as the program executes.=back=head1 DESCRIPTIONThis program is used to update a Distribution 'bin' directory with the versions ofpackages as indicated by the specified Deployment Manager SBoms.There are two modes of operation: Filter modification operations and BOM creation.In 'Filter modification' mode the current filter set will be updated and the program willexit.In BOM creation mode an sbomid must be provided.The rtagid is used to get all the required information from Release Manager aboutwhich package version are required, as well as the project name and release name.In addition to using Release Manager information to determine which Package/Versions arerequired to be copied this script also uses a set of shell wildcard filters that areused to determine which files are actually copied when invoked.The filter rules can be supplied on the command line if available read from aconfiguration file saved in the output diretory the last time the script was runon this release directory.One or more filter rules must be specified on the command line the first time this commandis run against a project release directory. These filter values are then written to a configfile in the output directory so the same values can be used on subsequent runs.In subsequent runs the filter rules will be loaded from the config file and need not be specifiedon the command line, however the filter rules in the config file can be changed by specifyingthem again on the command line and the config will be re-written.=cut