Rules perl code vs make like meta rules post depend what are they good for warning about not using them too much Builders pure perl builder running shell commands hosted builders AddRule creator pure perl rules regex dependent matcher composit dependent matcher simplified rules dependers post_depender RemoveRule Using rules dynamically Triggers Definition Importing Digest default build Depend Check user check function digest user defined build Pbsfile functions available in Pbsfiles override sub Pbs overridding NON_RELOCATABLE_PBSFILE functions wizards run (switches) debugging explained command line switches log regex breakpoint perl debugger examples add pod documentation for each function in the modules
IMMEDIATE_BUILD
ChildPbs
PBSFILE_CONTENT
subpbs { COMMAND_LINE_DEFINITIONS => { a => 1 , b=>2 } }
PBS - Perl Build System (PBS).
pbs.pl - Front end to PBS.
pbs.pl is a script that handles command line switches and starts PBS. It runs under Linux and in Cygwin. If using Cygwin be careful to use the Perl that comes with Cygwin. No port to the Windows is planned but it should be trivial.
This is version '.01' of this document. Please help us to make it better.
PBS/pbs.pl (pbs from this point) is a build utility in the same spirit as 'make'. The main difference is it implementation language and a different rule definition language. pbs uses Perl exclusively for defining rules. If you have used 'make', pbs should be easy to understand and use.
PBS is not compatible with 'make' and works completely differently!
Not for simple projects, the examples in this documentation should get you going. A working knowledge of perl will allow you to use the advanced functionality of PBS
pbs.pl and PBS are still under development. No guaranties of any sort follow with this software. Your ideas, remarks and critics are very welcome.
perl pbsl.pl all
perl pbs.pl -c -o --build_directory /somewhere/out --sd /sd1/ --sd /sd2 --bi -- all
perl pbsl.pl -c --tree a.c all
pbsl.pl takes switches and targets on the command line. pbsl.pl -h displays all the accepted switches (60+) and a short description of what the switch does.
To drive PBS you write a Pbsfile (in analogy to a Makefile). Pbsfiles are package less perl scripts. Pbsfile can have any name you operating system supports. If no Pbsfile is given, pbs will load a file called Pbsfile.pl. If no Pbsfile.pl is found, pbs exits with an error message.
Unlike other build systems, PBS doesn't define any Built-in rule. It is very easy to define and use libraries of rules. Those become your Built-in rules. pbs comes with some examples for such libraries. In fact pbs has most of the rules you need to build your C projects. Those rules are simply not used by default.
Rules are the main way to control your build system. Two functions exist to define rules.
AddRule, which takes the following parameters:
Ex: AddRule 'exe', [exe => undef], \&BuildAnExe, [ 1, 2, 3 ] ;
AddRule will add this rule to a rule namespace (explained bellow) called 'User'. PBS will know this rule as: PBS::'User'::'Exe'. To specify the rule name space, you use AddRuleTo, which takes the following parameters:
Ex: AddRuleTo 'Builtin', 'exe', [exe => ], \&BuildAnExe, [ 1, 2, 3 ] ;
The names space is a string describing the type of rules in it. The namespaces naming is just a convention.
All the rules added through AddRule are added to this namespace.
This is a namespace that the Default Build (explained bellow) uses in conjonction to the 'User' namespace. The namespaces are conventions. Except for the default Build using them, they are in no diffrent from other namespaces.
All the definitions made through the -D switch on the command line are stored in this namespace. All the definitions throught the command line are merged in sub Pbs even if those have use LockConfigMerge().
This is the name of the rule, a string. The name will be used by PBS when displaying information about the build. The name must be unique.
AddRule and AddRuleTo will generate a depender from the argument you give it.
This argument can be on of the following:
Ex: AddRule 'a.o_rule name', [a.o => 'a.c', '/somewhere/a.c2', '[path]/subdir/prefix_[basename].c3'], ...
For '/path_to_a/a.o', the depender will generate these dependencies :
'a.c' => '/path_to_a/a.c' # relativ path '/somewhere/a.c2' => '/somewhere/a.c2'# full path [path]/subdir/prefix_[basename].c3' => '/path_to_a/subdir/prefix_a.c3'
if your node has no dependencies use undef as in the example bellow,.
AddRuleTo 'Builtin', [VIRTUAL], 'install', [install => undef], \&Installer ;
AddRuleTo 'BuiltIn', 'o_c', ['*.o' => '*.c'] ;
This is equivalent to
['*.o' => '[path]/[basename].c'], ...
If you know perl, you can write your own depender as a perl sub. The argument is a sub reference so you can define your sub inline or refer to an already existing sub. You get total control of the dependency list generation.
The sub receives the following arguments:
Argument 3 and 4 are for advanced use, you can safely ignore them, their are used by PBS to handle certain types of rules.
The depender sub should return:
Argument 2 and 3 are for advanced use, you can safely ignore them.
Ex: AddRuleTo 'Builtin', 'C_to_H_H2', sub { my $file_to_depend = shift ; my $config = shift ; if($file_to_depend =~ /(.*).c$/) { return ( # argument 0: match flag and dependency list [ 1 # matched '.c' file , (["$1.h"], ["$1.h2"]) # note the encapsulation into arrays ] # argument 1: optional builder override # argument 2: optional arguments override ) ; } } Ex2: AddRuleTo 'Builtin', 'exe', \&SomePerlPackage::SomeDepender, ...
if the dependent regex is a regex or a sub, PBS considers the rule to be a pure perl rule.
The dependencies can then be defined in term of: $path $basename $name $ext
[qr<\.c$> => '$path/$name.xxx']
[ sub{return($_[0] =~ qr <\.c$>)} => '$path/$name.xxx']
the sub is passed the dependent name as first argument and %TARGET_PATH as a second argument. it returns true if the depender matched
in Dependers/Matchers, the following rules are defined:
AnyMach: takes a list of regexes, returns true if any regex matches [AnyMatch(qr<\.c$>, qr<\.s$>) => ...]
NoMatch: takes a list of regexes, returns false if any matches [NoMatch(qr<\.c$>) => ...]
AndMatch: takes a list of regexes of dependent matcher subs. Ands (&&) all the results of the matching.
[AndMatch(qr<\.c$>, NoMatch(qr<^somepath/>)) => ...]
PBS allows you to give a reference to a sub instead for defining the dependencies inline with the rules. The sub will be called only if the dependent part matches (in the example bellow, only if the node is named 'a.o').
Ex: AddRule 'a.o_rule name', ['a.o' => \&SpecialDepender], ...
You can also pass dependencies to the sub, simply define them in the array.
Ex: AddRule 'a.o_rule name', ['a.o' => 'a.c', '/path/file', \&SpecialDepender], ...
Only one sub is allowed within the dependency definition. The sub receives the following arguments:
Argument 4 and 5 are for advanced use, you can safely ignore them, their are used by PBS to handle certain types of rules.
retrn variables 2 and 3 are for advanced use, you can safely ignore them (return only the first variable).
This construct allow us to handle the case of generated c files quite easily. Say we have a c file that is generated from a flex file. We need to generate the c file before the 'c depender' kicks in. The 'c depender' is a POST_DEPEND rule so we know it is going to be runafter all the non POST_DEPEND dependers.
Uncomplete example!!!
# flex example
AddRule 'a.flex', ['a.c' => 'a.flex', \&GenerateWithFlex], BuildOk("flex to C generator") ;
sub GenerateWithFlex { my ($dependent, $config, $all_dependencies, $tree, $inserted_file, $arguments) = @_ ; PrintInfo "GenWithFlex: $dependent\n" ;
# check if file exists in a source dir # check if flex file exists # should do this in the build directory
my $flex_command = "flex -.... -.... $dependent" ;
if(system($flex_command)) { die ERROR "Error runnning flex command: '$flex_command'.\n" ; }
ForceDigestGeneration($dependent) ;
return([1, @$all_dependencies]) ; }
If multiple rules match a node/file, the sum of the dependencies returned by matching dependers will become the node/file dependencies. Unlike gmake PBS dependers match only on one side of the rules. if the following rules:
AddRule 'o_c', ['*.o' => '*.c'] ; AddRule 'o_s', ['*.o' => '*.s'] ;
are used on file compress.c, the dependers would generate the following dependencies: compress.c and compress.s.
gmake is IMO too magical in its way of handling your rules. I don't mean it is wrong but that it simply doesn't fit the pbs way of generating dependencies.
EX: AddRuleTo 'BuiltIn', 'o_c', ['*.o' => '*.c'] ; AddRuleTo 'BuiltIn', 'o_s', ['*.o' => '*.s'] ; AddRuleTo 'BuiltIn', [META_RULE], 'o_meta', ['o_c', 'o_s'], \&FirstAndOnlyOneOnDisk ;
When you define the above 'o_meta' rule, PBS removes rule 'o_c' and rule 'o_s' from it's rule list (in the current package only). FirstAndOnlyOneOnDisk will be called with a reference to the slaves rules as arguments. This allows you to define your own 'magic'. FirstAndOnlyOneOnDisk source code can be found in the distribution.
If PBS find a cyclic dependency in your system, it will stop and display a message.
$ perl -Mblib pbs.pl -p test.pl -c -ns test_cyclic ... **Depending** **Checking** Cyclic dependency detected on './a'! '__NAME' => './a', '__NAME' => './xx', '__NAME' => './nadim', '__NAME' => './nadim2', '__NAME' => './nadim3', './a' => $Cyclic dependency,
A builder is a sub called to build a node/file. You define the builder in the same rule as the depender or you can define it elsewhere and give a reference instead.
and should return:
Ex: sub BuildAnExe {...} ; AddRuleTo 'Builtin', 'exe', ['all' => 'exe'], \&BuildAnExe ; Ex2: AddRuleTo 'Builtin', 'exe', ['all' => 'exe'], sub { my $config = shift ; my $file_to_build = shift ; #build steps ... if($success) { return(1, 'Did this and that ...') , } else { return(0, $error_that_occured_during_the_build) ; } } ;
You might just want to run shell commands in your builder. PBS::Shell::Shell is a function that helps you write those commands in a simple way. PBS::Shell::Shell handles the mechanics of calling the command and returning from the Builder if an error occurred.
Ex: AddRuleTo 'Builtin', 'rule_name', \&MyDepender, sub { ... # do an 'ls' PBS::Shell::Shell('ls -lsa') ; # generates an error and return to PBS. PBS::Shell::Shell('non existing_application') ; # this point is never reached because of the error above. return( ...) ; } ;
PBS allows you to define simple shell commands directly in the rule definition. If the rule argument is an array (reference), PBS will consider each element of the array to be a shell command.
Ex: AddRule 'c_objects', [ '*.o' => '*.c' ], ['CC CFLAGS -c -o FILE_TO_BUILD DEPENDENCY_LIST'] ;
'CC CFLAGS -c -o $file_to_build $dependency_list' is parsed by PBS and:
If multiple rules match the node/file, the last defined builder will be used to build the node
The last argument passed to AddRules is passed as an argument to the builder.
ex: AddRuleTo 'Builtin', 'o_c', ['*.o' => '*.c'], \&BuildAnObject ;
It is up to the builder to interpret the argument (a scalar that can point to other perl types if you want it to). The argument to builder allows to do something special on a specific node. For example, you'd like to compile a specific C file with a -O2 switch to the compiler. You could use the following rules:
# General rule to build object file AddRuleTo 'Builtin', 'o_c', ['*.o' => '*.c'], \&BuildAnObject ; #specific rule AddRuleTo 'Builtin', 'special_o_file', ['special_file.o' => 'special_file.c'], undef, '-O2';
All the .o files will be generated by BuildAnObject builder. When building 'special_file.o', BuildAnObject will be passed the argument '-O2'.
The last defined argument for a node/file is passed to the builder. PBS will warn you if multiple arguments are selected.
You can give attributes to the nodes, the attribute is declare by following the node name with a colon and a textual attribute (see the example bellow). When PBS finds such an attribute, it calls a user sub registrated via RegisterUserCheckSub. The user sub receives the following arguments:
The sub should return a file full name (path/name) this is most often the first argument it receives, or die with an error message. The node attribute could be used to, for example, verify the version of a node.
Ex: PBS::Rules::AddRule 'all_lib',['all' => 'x.lib:1.0', 'y.lib'] RegisterUserCheckSub ( sub { my ($full_name, $user_attribute) = @_ ; # open file and extract version from it # die if the version is no equal to $user_attribute return($_[0]) ; # must return a file name } ) ;
!! extract from Philip make system.
PBS::Config::AddConfigTo and PBS::Config::AddConfig let you add configuration variable in PBS. PBS::Config::AddConfigTo accepts the following arguments :
PBS::Config::AddConfig takes an initialization list only and stores the configuration in namespace 'User'.
Ex: AddConfigTo 'BuiltIn', ( cc => 'gcc' , ld => 'ld' ) ; AddConfig (perl => '/usr/local/bin/perl') ; # added to the User namespace
The current configuration is automatically passed to the builders. What the 'current' configuration means depends on what Build() sub you are using? If you are using the default Build(), your configuration should be in the 'Builtin' or 'User' namespace. If you defined your own Build() sub, it is up to you to choose where to store your configuration; you can access it directly through PBS::Config::GetConfig(). It is still a good idea to use the 'Builtin' and 'User' namespace.
See also PBS:Config.pm documentation.
Yes, through the %ENV hash. Ex: PBS::Config::AddConfig 'BuiltIn', (cc => $ENV{COMPILER}, 'outdir' => "/somewhere/...") ;
Use the configuration variable passed to the builder.
Ex: AddRuleTo 'Builtin', 'O_C', '.o:.c', sub { my $config = shift ; my $file_to_build = shift ; my $dependencies = shift ; my $triggering_dependencies = shift ; my $arguments = shift || '' ; my $compiler = $config->{cc} ; my $, = ' ' ; Shell("$compiler -c @$dependencies -o $file_to_build $arguments") ; } ;
Remember that your Pbsfile is a program an not a static rule description. When defining a rule or adding a configuration variable, you can use any of the constructs perl allows. Remember only that Pbsfile are package less perl scripts.
PBS builds a dependency tree from the top level target and your rules. Each node in the tree is recursively traversed to check if it has dependencies itself. The depender you define return a dependency list. PBS doesn't specially treat the dependencies as files. For PBS, they are just nodes with names.
When PBS checks the dependency tree, it uses a function to check if a node is older than its dependencies. The default build uses a timestamp comparison (thus considering the node as a file).
A problem can occur if the node name is also an existing file. You consider it to be VIRTUAL but when PBS checks the file tree, it see a physical file and might not Build your VIRTUAL node. You can force a node to be virtual. The node will only be build if one of its dependencies triggers. Unlike other build systems, PBS will display a warning message if a file with the same name as you node exists in the file system. To make a node virtual, declare one of the rules generating the node as VIRTUAL.
If a node exists in one of your source directories and has no dependencies that trigger its rebuild, PBS will _not_ copy it in your build directory. Instead it will reference it from the directory where it is located. If you want to force a node/file to be present in your build directory, declare it as LOCAL.
You can also foce the build of a node by declaring one of the rules generating it as FORCED. The node will always be build (if it exists in the dependency tree). This is used, for example, when you need a 'test' target; you don't want any file to be called test so you declare the node to be 'VIRTUAL' but if none of the 'test 'target triggers, the node will not be build, you have to for its build with the 'FORCED' keyword
Ex: AddRuleTo 'Builtin', [VIRTUAL], 'install', [install => undef], \&Installer ; AddRule [LOCAL], 'os_binary', ['os.bin' => ....] ; AddRule [FORCED], 'O_C', '.o:.c', ... AddRule [VIRTUAL, FORCED], 'test', ...
See also switch --bi.
You can also use POST_DEPEND to force a dependers to be run last. In the dependency list, you can insert __REMOVE_DEPENDENCIES_AND_BUILDERS or __ERROR_IF_DEPENDENCIES. __REMOVE_DEPENDENCIES_AND_BUILDERS removes all the dependencies that were added by the other dependers. __ERROR_IF_DEPENDENCIES will stop the build with an error message if any dependency was added to the node.
POST_DEPEND and __REMOVE_DEPENDENCIES_AND_BUILDERS / __ERROR_IF_DEPENDENCIES were implemented while doing a test and they are left in PBS in case they are helpfull.
Ex: AddRule 'all', [all => 'a.o'] ; AddRule [POST_DEPEND], 'post_all', [all => '__REMOVE_DEPENDENCIES_AND_BUILDERS', 'z.o'] ; AddRule 'all2', [all => 'b.o'] ;
the above rules give the following display when run with the --dd switch:
$ perl -Mblib pbs.pl -p test.pl -dd all **Depending** ./all has dependencies [./a.o], rule 2:all ./all has dependencies [./b.o], rule 3:all2 POST_DEPEND rule: 'post_all' at ./test.pl:22 is Running __REMOVE_DEPENDENCIES_AND_BUILDERS command for node './all' Removing dependency: './a.o' Removing dependency: './b.o' ./all has dependencies [./z.o], rule 5:post_all ./z.o has no locally defined dependencies
PBS having no built-in rules, it would be cumbersome to have to redefine the rules you use in all the Pbsfiles. PBS support in include mechanism that resembles '#include' in C or 'use' in perl. PbsUse takes the name of a file which contains rules or configurations definitions.
Ex: If Rules/C.pm contains: AddRuleTo 'BuiltIn', 'exe', [exe => undef], \&BuildAnExe ; AddRuleTo 'BuiltIn', 'O_to_C', '.o:.c' ; AddRuleTo 'BuiltIn', 'C_to_H', '.c:.h' ;
You can then include it in you Pbsfile.
Ex: # this is Pebsfile.pl PbsUse('Rules/C') ;
PbsUse will automatically append '.pm' at the end of the file name. If the file can't be found in the same directory as the Pbsfile, the -plp option will be used to point to the root directory where the files are to be searched.
I recommend that you keep all your rules in the library directory. It is possible to have rules and configuration definitions in the same lib file but it is better to keep them separated.
Please contribute your rules to PBS.
Using --build_directory, you can have PBS place the generated files in a directory different from the current directory. This allows you to separate your source files from the generated files.
Using --source_directory or --sd, you can direct PBS to search for files in other source directories than the current directory. You can specify multiple --sd switches. PBS will search the directories in the order you specify them.
See Digest bellow.
If no build or source directory is specified, PBS will use the current directory. If you specify a source directories, PBS will search exclusively in the specified directories. The curren directory is not searched. If you want the current directory and other directories to be searched, you must specify the current directory too.
When no default build and/or source directory is given, pbs will issue a warning.
$> pwd /temp/PerlBuildSystem-0.05 $> perl pbs.pl -o -sd /d1 -sd /d2 -b -dsi -dd -fb -- T No Build directory! Using '/temp/PerlBuildSystem-0.05'. No user defined [PBS] Build, using default Build() with [BuiltIn, User] rules and config. **Depending** ./T has dependencies [./test], rule 6:T : User : PBS : './Pbsfile.pl' : 22 ./test has dependencies [], rule 7:test : User : PBS : './Pbsfile.pl' : 23 **Checking** Trying ./T @ /d1/T: not found. Trying ./T @ /d2/T: not found. Trying ./test @ /d1/test: not found. Trying ./test @ /d2/test: not found. Final Location for ./test @ /temp/PerlBuildSystem-0.05/test Final Location for ./T @ /temp/PerlBuildSystem-0.05/T Final Location for __./Pbsfile.pl @ /temp/PerlBuildSystem-0.05/__./Pbsfile.pl **Building** Building /temp/PerlBuildSystem-0.05/test : Building /temp/PerlBuildSystem-0.05/test : BUILD_FAILED : No builder.
See switches --dsd --dsi --daa.
If you Organize your source code (or whatever you want to build) in a hierarchy, you can use PBS to build your system. Let take an example.
You have the following files:
/source/lib/lib.c /source/lib/lib2.c /source/lib/test.c /source/application.c
You wan to:
You want to write a /source/Pbsfile that uses /lib/Pbsfile to generate the library.
This is very easy to do with PBS and was one of the goals of the project. /source/Pbsfile could look like this.
AddRule 'application',['a.o' => './lib/lib.lib', 'a.c'], \&Builder ; AddRule 'lib', {NODE_REGEX => 'lib.lib', PBSFILE => '/lib/Pbsfile.pl', PACKAGE => 'LIB'} ;
When you build your application, PBS sees you are using 'lib/lib.lib', it will load '/lib/Pbsfile.pl' and run it.
Some points are worth noting.
PBS will automatically push your script in a package. This is done to separate rules and configurations when doing a hierarchical build. If your build system isn't hierarchical, your Pbsfile will be placed in the PBS package.
Within a pair of matching curly braquets'{}', list:
Ex: AddRule 'sub_depend', { NODE_REGEX => 'x.lib' , PBSFILE => './P2.pl' , PACKAGE => 'LIB' , BUILD_DIRECTORY => '/bd_P2' , SOURCE_DIRECTORIES=> ['/sd_P2_2', '/sd_P2_1'] } ;
/lib/Pbsfile will use it's own rules, as if it was not part of a sub build.
When PBS starts a sub Pbsfile, PBS pushes (merges) the the parent configuration in the child Pbs. This is done automatically by PBS just before calling the child Build() sub or the default Build() sub.
By calling LockConfigMerge() in your Pbsfile, you disable the configuration merging that PBS does when loading your Pbsfile. PBS will display a warning message. Definitions done throught the -D switch are always available in the sub Pbs configuration even if locked.
See also PBS.html.
Must show an output here !!
See --dpl --dds --dd
ImportTriggers('./Pbsfiles/sub_trigger.pl') ;
sub ExportTriggers { AddTrigger 'T2', ['Y' => 'z2'] ; AddRule 'sub_trigger_Y', { NODE_REGEX => 'Y' , PBSFILE => './Pbsfiles/sub_trigger.pl' , PACKAGE => 'Y' } ; } # end ExportTriggers
We have learned how to define rules and set configurations. We have also see how to write 'Builder' in perl. PBS uses those definitions and 'builders' to build your system. In 'PBS Program flow', I introduced the inner working of PBS ('depend - check - build'). The function that runs the 'depend - check - build' steps is called the default Build().
Your rules and configuration are stored by PBS in namespaces. The default Build() uses the rules and configurations that _you_ store in the 'User' and 'Builtin' namespace. If you store your definitions in other namespaces, PBS will _not_ use them. Most of the build system you will define will use the default Build() so remember to use the 'User' and 'Builtin' namespaces. All the rules added by AddRule automatically end in the 'User' namespace.
When PBS can't find a user defined Build(), it displays the following message:
No user defined [PBS] Build, using default Build() with [BuiltIn, User] rules and config.
This tells us that the Pbsfile we use doesn't define any Build() and that it was automatically pushed in the package [PBS]. When running hierarchical builds, PBS will show you if sub Pbsfiles define a Build() or not. Ex:
$> perl pbs.pl all No user defined [PBS] Build, using default Build() with [BuiltIn, User] rules and config. ... No user defined [PBS::LIBS] Build, using default Build() with [BuiltIn, User] rules and config. ... No user defined [PBS::LIBS] Build, using default Build() with [BuiltIn, User] rules and config.
Here some sub Pbsfile, without user defined Build() is used twice (it's loaded in memory in memory only once. See switch --dpl).
See also switches --dur --dar --dc --dac --dr.
In this section and sections bellow, a working knowledge of perl is needed. I think any programmer with some experience can experiment with perl.
Sometimes you just can seem to get your build system to do what you want, however intricate rules you write! PBS Let's you take control by defining a user Build() function. When such a function is found, b<PBS> calls it instead for the default Build() function. You are now in control and PBS will not interfere any more if not called from your function. if you define an empty Build() .
sub Build {}
running pbs would give the following.
$> perl pbs.pl --dpl -- all No Build directory! Using '.....'. No source directory! Using '....'. =>Loading Pbsfile: ./Pbsfile.pl [PBS] Found user defined Build. $>
By defining your own Build() function, you state your will to take control over all the part of the build. You are not using B>PBS> as a build system any more but using it as a library. I will now explain how to do some routine work within your Build() then I will give an example or two. Please contribute your examples to PBS.
There are two ways of programming your own build.
A reference to an array containing the names of the target to build.
A string containing the name of the build directory
A reference to a list of source directories.
A reference to hash that contains the files already inserted in your dependency tree.
A reference to a hash, that is to be used as your dependency tree.
A boolean Flag that tells you if you should run the build step or only depend the targets. See "Behave correctly as a sub build.".
__PACKAGE__ (see perl documentation) will contain your running package. You should normally not have to take of what 'package' you are running inside. PBS lets you peak into other packages rules and configuration variables but you should consider them to be read-only.
The first part of your Build() function should handle your build configuration. This is different from case to case. Look at the 'Examples' section. This might be the right place to check for new source code availability or get a unique build number from a database.
PBS lets you pass variables to your Build() through the command line and switch -u.
Ex: perl pbs.pl -c -u clean='yes' -u heap_size=16 all Inside your Build() you can access those variables: if(defined $PBS::user_options{clean}) { # do some cleaning } my $heap_size = $PBS::user_options{heap_size} || 32 ;
You can call AddRule from inside your Build() function. PBS is extremely flexible because it is programmed and programmable with perl. AddRule accepts functions references as depender, builder or argument. Those can be dynamically created at run time if you so wish. Rules are often added depending on the command line arguments.
In most cases you want to manipulate the rules and config and then call the default Build(). Here is an example.
Ex: sub Build { my ($Pbsfile, $package, $package_config, $targets, $inserted_files, $dependency_tree, $depend_and_build) = @_ ; ... PBS::DefaultBuild::DefaultBuild ( $Pbsfile , $package , $package_config , ['BuiltIn', 'User'] , ['BuiltIn', 'User', 'CommandLineDefinitions'] # target will be inserted by rule 'BuiltIn::__ROOT , $inserted_files , $dependency_tree , $depend_and_build ) ; }
If pbs is called as: perl pbs.pl -u extra_objects all, The Build above will add a.o, b.o and c.o to 'exe' dependencies. 'a.o' and the other object files will be recursively depended with the rules define in your system. PBS::DefaultBuild::DefaultBuild takes three (3) extra arguments compared with the user build.
at position zero (0), the names of the rules namespaces to use during the default build.
at position zero (1), the names of the configuration namespaces to use during the default build.
at last position, the names the package to build. This should always be __PACKAGE__.
You can write advanced Build() but you must know what PBS data structures look like and how to use them. You are now navigating the darker waters of PBS development.
You can query PBS for the rules and config that are defined when you Build() is run.
PBS::Rules::GetRules takes 2 arguments.
PBS::Rules::GetRules will return a list of rule structures used internaly by PBS that match your request.
PBS::Config::GetConfig takes 2 arguments.
PBS::Config::GetConfig will return a list of all the configuration variables.
PBS::Rules::RemoveRule allows you to remove a rule if you know its name and namespace.
PBS::Rules::RemoveRule takes 3 arguments.
PBS::Config::Removeconfig takes 3 arguments.
PBS functionality is build around 3 low level functions:
PBS::Depend::CreateDependencyTree
PBS::Check::CheckDependencyTree
You are also allowed to define another trigger rule instead for the built-in time stamp checking.
PBS::Build::BuildSequence
Those 3 functions are used in PBS::DefaultBuild::DefaultBuild(). which serves as a good example on how to use them. The functions are further documented in their respective modules.
When writing a user Build() function that doesn't call the default Build(), make sure the function behavior is right when being used as a sub build. The last argument passed to the Build() function tell it if it should run the check and build step or not. If you need to access the low level functionality, make sure you have the latest version of PBS and use PBS::DefaultBuild::DefaultBuild() as a template for your function.
Document your Pbsfile! Pod is the favorite documentation format for perl scripts and modules and for Pbsfiles. Check your perl documentation for how to use pod. By documenting your Pbsfiles and build system you will help making your system maintainable.
pbs.pl handle two switches that help you when you document your Pbsfiles.
The --hu switch will extract the section that starts with =head1 PBSFILE USER HELP
Comments that you put in a =pod or =comment sections are not extracted. The help is dumped on STDERR.
$>perl pbs.pl -hu ==== PBSFILE USER HELP ==== == *Pbsfile.pl* == *Pbsfile.pl* is to be used with the Perl Build System (``PBS''). == Description == This *Pbsfile* come with PBS distribution. It contains some examples of what you can do with *pbs.pl* and ``PBS'' == Uses rules and Config from == * Rules/C * Configs/ShellCommands * Configs/gcc == Uses sub *Pbsfile* == * ./P2.pl == Documentations for User Build == Example ... blah ... User options: -u something to do something -u something_else ....
If you write Pbsfiles for a lot of users, you might want to distribute a html (or other format) documentation. pbs.pl will dump raw pod for section 'PBSFILE USER HELP' if you use the --hur switch
$>perl pbs.pl -hur | pod2html > document_name.html
When PBS check if a node is to be rebuild, it first checks if any dependency is newer, in that case the node is rebuild. If no dependencies force a rebuild, PBS checks if the node corresponds to a file on disk. If the file is found, PBS verifies if the file found on disk is compatible with the current build. To do this checking, PBSW reads a digest corresponding to the file from the disk. If no digest is found, the node is rebuild. The digest is generated automatically when a node is build. The digest contains the following elements:
If all the elements needed to build the node are found in the file's digest, PBS uses it, otherwise the node is rebuild.
Items 1 and 2 are automatically inserted in the digest by PBS (this might change if we feel we want to give you even more control). Sometimes, you know of dependencies that PBS can't find out or you want to mark the build node with the versions of some tools. PBS let you define such dependencies throught the following functions:
AddFileDependencies() : PBS will compute an md5 for each file in the list you pass as argument and add it to the digest.
AddEnvironmentDependencies(): PBS will add each environement variable you name in the list passed as argument. If the environnement variable is not set, PBS will add the variable to the digest and give it the empty string value.
AddSwitchDependencies() : : PBS will add the veriables and their values to the digest. Only Defined (-D) and User Defines (-u) can be added.
Ex: AddSwitchDependencies('-D*') ; # depend on all command line defines AddSwitchDependencies('-u*') ; # depend on all user variables from the command line AddSwitchDependencies('-u something', '-D debug', -D clean) ; # add only the given variables to the digest
AddVariableDependency() : This allows you to insert a variable name and it's value into the digest. For example, this could be used if you are cross compiling for an embeded platform from diffrent OSes. The cross compilers would have diffrent md5 on the OSes, so you can't add the cross compiler throught AddFileDependencies().
Ex: my $compiler_version = GetCompilerNameAndVersion(...) ; AddVariableDependency('compiler_version' => $compiler_version) ;
PBS will expects a digest for all nodes/files. We have to tell PBS how to make the diffrence between a generated file and a source file (or any file for which a digest makes no sense).
ExcludeFromDigestGeneration() allows you to exampt a certain type of files from the digest.
Ex: ExcludeFromDigestGeneration('c_files' => qr/\.c$/) ;
The first argument is a description string, the second one a reference to a regex object. Node names matching the regex will be exampted.
Some source files are automatically generated (ex by flex, yacc, your own generators, ...), you can selectively re-impose a digest on a certain file that would have been exampted by ExcludeFromDigestGeneration. ForceDigestGeneration() lets you do that.
Ex: ForceDigestGeneration( 'a.c is generated' => qr/a\.c$/) ;
The first argument is a description string, the second argument is a reference to a regex object.
The best example is pbs.pl. You should read it source code and start from there. Feel free to mail me you questions. I also recommend to run PBS inside an eval block as it 'die's when a serious error occurs..
The source code is best read with tab size = 3. All lines should match /^\t*[^\s]/.
Look at PBS.POD for a quick introduction to how PBS works. All the modules are documented. The Author welcomes your remarks, suggestions and critics. Html or man page documentation should be installed automatically when you install the Perl Build System.
use strict ; use warnings ;
perl has a built debugger, t=you can use it to look at how your build system is working.
pbs -p xx b@all
On-line documentation for the PBS switches is available through --hs.
Some switches are considered by PBS to be debug flags. When a debug flag is set, only the depend phase is run. To force a complete build us e the --fb switch
Khemir Nadim ibn Hamouda. <nadim@khemir.net>
1 POD Error
The following errors were encountered while parsing the POD:
Unknown directive: =comment
To install PBS::Log, copy and paste the appropriate command in to your terminal.
cpanm
cpanm PBS::Log
CPAN shell
perl -MCPAN -e shell install PBS::Log
For more information on module installation, please visit the detailed CPAN module installation guide.