perl code vs make like
                        meta rules
                        post depend 
                                what are they good for
                                warning about not using them too much
                                pure perl builder
                                        running shell commands
                                hosted builders
                                pure perl rules
                                        dependent matcher
                                        composit dependent matcher
                                simplified rules
                        Using rules dynamically
                default build
                                user check function
                        user defined build
                        functions available in Pbsfiles
                sub Pbs
                run (switches)
                debugging explained
                        command line switches
                        perl debugger
        add pod documentation for each function in the modules





subpbs { COMMAND_LINE_DEFINITIONS => { a => 1 , b=>2 } }


PBS - Perl Build System (PBS). - Front end to PBS.

DESCRIPTION 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 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 and PBS are still under development. No guaranties of any sort follow with this software. Your ideas, remarks and critics are very welcome.


perl all

perl -c -o --build_directory /somewhere/out --sd /sd1/ --sd /sd2 --bi -- all

perl -c --tree a.c all

INPUT takes switches and targets on the command line. -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 If no 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.

Adding a rule

Rules are the main way to control your build system. Two functions exist to define rules.

AddRule, which takes the following parameters:

1 Rule name
2 Depender
3 Builder (optional)
4 Arguments to builder (optional)
        Ex: AddRule 'exe',
                        [exe => undef],
                        [ 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:

2 Rule namespace
2 Rule name
3 Depender
4 Builder
5 Arguments to builder
        Ex: AddRuleTo 'Builtin', 'exe',
                        [exe => ],
                        [ 1, 2, 3 ] ;

AddRule[To] parameters explained

Rule namespace

The names space is a string describing the type of rules in it. The namespaces naming is just a convention.

Special namespaces

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:

An array

        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'], ...

A sub.

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:

1 The name to check for dependency
2 The current config.
3 A reference to the node being depended
4 A reference to all the nodes in the dependency tree

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:

1 an array reference containing:
1 1/true if the name to check matched
2 A list of dependency (which can be empty, in that case the special undef should be used). Each dependency must be encapsulated in an array reference!
2 A builder reference to override the buider defined in the rule
3 A scalar representing arguments to overide the arguments defined in the rule

Argument 2 and 3 are for advanced use, you can safely ignore them.

        AddRuleTo 'Builtin', 'C_to_H_H2', 
          my $file_to_depend = shift ;
          my $config         = shift ;
          if($file_to_depend =~ /(.*).c$/)
                       # 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, ...

pure perl rules

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/$']

[ sub{return($_[0] =~ qr <\.c$>)} => '$path/$']

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/>)) => ...]

An array with a sub

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:

1 The name to check for dependency
2 The current config.
3 A reference to an array with the dependencies defined in the rule.
4 A reference to the node being depended
5 A reference to all the nodes in the dependency tree

Argument 4 and 5 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:

1 an array reference containing:
1 1/true if the name to check matched
2 A list of dependency (which can be empty, in that case the special undef should be used). Each dependency must be encapsulated in an array reference!
2 A builder reference to override the buider defined in the rule
3 A scalar representing arguments to overide the arguments defined in the rule

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]) ; }

WARNING! All dependers are run!

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.


        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.

Cyclic dependencies

If PBS find a cyclic dependency in your system, it will stop and display a message.

        $ perl -Mblib -p -c -ns test_cyclic
        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.

The sub receives the following arguments:

1 A reference to a hash containing configuration data
2 The name of the node/file to build
3 What dependencies the node/file has
4 What dependencies are newer thant the node/file
5 The arguments for this node/file if any

and should return:

1 1/true if the build succeeded
2 A message string
        sub BuildAnExe {...} ;
        AddRuleTo 'Builtin', 'exe', ['all' => 'exe'], \&BuildAnExe ;

        AddRuleTo 'Builtin', 'exe',  ['all' => 'exe'],
          my $config        = shift ;
          my $file_to_build = shift ;
          #build steps
            return(1, 'Did this and that  ...') ,
            return(0, $error_that_occured_during_the_build) ;
          } ;

Shell commands

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.

        AddRuleTo 'Builtin', 'rule_name',  \&MyDepender,
          # 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.

        AddRule 'c_objects', [ '*.o' => '*.c' ],

'CC CFLAGS -c -o $file_to_build $dependency_list' is parsed by PBS and:

1 Upper case names are checked with the current configuration, if a match is found, the name is replaced by the value found in the configuration.
2 FILE_TO_BUILD is interpolated.
3 DEPENDENCY_LIST is interpolated (dependencies are joined with a space)

What builder is run?

If multiple rules match the node/file, the last defined builder will be used to build the node

Argument to builder

The last argument passed to AddRules is passed as an argument to the builder.

        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'.

What arguments are passed to the builders?

The last defined argument for a node/file is passed to the builder. PBS will warn you if multiple arguments are selected.

Node Attribute Checking

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:

1 The full name for the node (path and name)
2 The attribute declared in the rule

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.

        PBS::Rules::AddRule 'all_lib',['all' => 'x.lib:1.0', 'y.lib']

                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
        ) ;

Complete rules example

        !! 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 :

1 a namespace name.
2 an initialization list

PBS::Config::AddConfig takes an initialization list only and stores the configuration in namespace 'User'.

        AddConfigTo 'BuiltIn',
            cc => 'gcc'
          , ld => 'ld'
          ) ;   
        AddConfig (perl => '/usr/local/bin/perl') ; # added to the User namespace

How do I defined my compiler, archiver, etc... so my builders can user them?

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 documentation.

Can I acces environement variables?

 Yes, through the %ENV hash.
        PBS::Config::AddConfig 'BuiltIn', (cc => $ENV{COMPILER}, 'outdir' => "/somewhere/...") ;

How do I access my config variables in my builders?

Use the configuration variable passed to the builder.

        AddRuleTo 'Builtin', 'O_C',  '.o:.c',
          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") ;
          } ;

Pbsfiles are dynamic!

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

        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.

        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 -p -dd all
        ./all has dependencies [./a.o], rule 2:all
        ./all has dependencies [./b.o], rule 3:all2
        POST_DEPEND rule: 'post_all' at ./ 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/ 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.

        # this is
        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.


Build directory

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.

Source directory

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.

Default directories

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
        $> perl -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.
        ./T has dependencies [./test], rule 6:T : User : PBS : './' : 22
        ./test has dependencies [], rule 7:test : User : PBS : './' : 23
        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 __./ @ /temp/PerlBuildSystem-0.05/__./
        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:


You wan to:

1 Build a library from lib.c
2 Build a test for the library
3 Build an application using the library

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/', PACKAGE => 'LIB'} ;

When you build your application, PBS sees you are using 'lib/lib.lib', it will load '/lib/' and run it.

Some points are worth noting.

1 No new process is started when using /lib/Pbsfile
2 '/lib/Pbsfile' is not special in any way (See bellow in USER DEFINED BUILD for an explanation).


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.

Rule definition for sub depend

Within a pair of matching curly braquets'{}', list:

1 Mandatory. The node name => the Pbsfile name.
2 Mandatory. Package => the sub package in which to run the sub build.
3 Optional. Extra variables you would like to set in the sub depend
        AddRule 'sub_depend',
                   NODE_REGEX => 'x.lib'
                 , PBSFILE  => './'
                 , PACKAGE => 'LIB'
                 , BUILD_DIRECTORY => '/bd_P2'
                 , SOURCE_DIRECTORIES=> ['/sd_P2_2', '/sd_P2_1']
                 } ;

Which rules are used in the child depend step?

/lib/Pbsfile will use it's own rules, as if it was not part of a sub build.

Which configuration is used in the child dependers?

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.

Locking your config

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.

PBS Build flow for the above example

1 Load the top level Pbsfile.
2 Set the top level configuration data.
3 Run the dependency step.
4 If during, the dependency step, a sub Pbsfile is to be loaded
1 Load the sub Pbsfile
2 Set the sub Pbsfile configuration data
3 Inject the parent's configuration data, possibly overwriting some or all of the sub build configuration data.
4 run the sub Pbsfile Build() sub if defined or the default Build().
4 return from the sub PbsFile
5 run the check step (in the top PBS).
6 run the build step (in the top PBS).

See also PBS.html.

Test run

Must show an output here !!

Switches that help you understand what is going on.

See --dpl --dds --dd

Node Triggers

Rule definition

Importing and Exporting Triggers

ImportTriggers('./Pbsfiles/') ;

sub ExportTriggers { AddTrigger 'T2', ['Y' => 'z2'] ; AddRule 'sub_trigger_Y', { NODE_REGEX => 'Y' , PBSFILE => './Pbsfiles/' , 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().

'User' and 'Builtin' names spaces.

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 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 --dpl -- all
        No Build directory! Using '.....'.
        No source directory! Using '....'.
        =>Loading Pbsfile: ./ [PBS]
        Found user defined Build.

Taking control of the 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.

1 Manipulate the rules and config, eventually generating rules and interacting with the outside world, and then call the default Build(). You can also manipulated the default Build() arguments. This is quite easy to do and will certainly answer 90% of your advanced needs.
2 Call the low level functionality exposed by PBS instead for calling the default Build(). This is quite advanced, requires that you understand perl and PBS architecture well. The low level functions receive a config argument, that argument should be a snapshot of the config (a copy) for the package at the moment the user defined build is run.

Arguments passed to your Build() function

1 $targets

A reference to an array containing the names of the target to build.

2 $build_directory

A string containing the name of the build directory

3 $source_directories

A reference to a list of source directories.

4 $inserted_files

A reference to hash that contains the files already inserted in your dependency tree.

5 $dependency_tree

A reference to a hash, that is to be used as your dependency tree.

6 $depend_and_build

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.".

What Package is my Build() running in?

__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.

Configuring your system and other management tasks

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.

User switches

PBS lets you pass variables to your Build() through the command line and switch -u.

        perl -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 ;

Adding rules dynamically

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.

Calling the default Build()

In most cases you want to manipulate the rules and config and then call the default Build(). Here is an example.

        sub Build
        my ($Pbsfile, $package, $package_config, $targets, $inserted_files, $dependency_tree, $depend_and_build) = @_ ;
                , $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 -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__.


User interaction

Depending source files

Version control


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.

Rules and config

Getting rules and config.

You can query PBS for the rules and config that are defined when you Build() is run.

PBS::Rules::GetRules takes 2 arguments.

1 A package name, this should be '__PACKAGE__'
2 A list of rule namespaces

PBS::Rules::GetRules will return a list of rule structures used internaly by PBS that match your request.

PBS::Config::GetConfig takes 2 arguments.

1 A package name, this should be '__PACKAGE__'
2 A list of config namespaces

PBS::Config::GetConfig will return a list of all the configuration variables.

Removing rules

PBS::Rules::RemoveRule allows you to remove a rule if you know its name and namespace.

PBS::Rules::RemoveRule takes 3 arguments.

1 The package name (use __PACKAGE__ for the current package name)
2 The namespace (ex: 'Builtin', 'User')
3 The name of the rule to remove, if no name is given, all the rules are removed.

Removing configuration

PBS::Config::Removeconfig takes 3 arguments.

1 A package name, this should be '__PACKAGE__'
2 A namespace name
3 A configuration variable name

Calling low level PBS functions.

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.

Behaving correctly as a sub build.

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.

Advanced Example

Documenting your Pbsfiles

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.

Helping your users. handle two switches that help you when you document your Pbsfiles.

Online help

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 -hu

        ==== PBSFILE USER HELP ====
        ==   **   ==
            ** 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 ** and ``PBS''
        ==   Uses rules and Config from   ==
        * Rules/C
        * Configs/ShellCommands
        * Configs/gcc
        ==   Uses sub *Pbsfile*   ==
        * ./
        ==   Documentations for User Build   ==
                Example ...
                blah ...
                User options:
                        -u something to do something
                        -u something_else ....

Html help

If you write Pbsfiles for a lot of users, you might want to distribute a html (or other format) documentation. will dump raw pod for section 'PBSFILE USER HELP' if you use the --hur switch

        $>perl -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:

1 md5 of the Pbsfile that generated it
2 md5 of the files that have been included through PbsUse
3 Any element you have specified through the Add...Dependencies functions

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.

        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().

        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.

INTEGRATING PBS in another application

The best example is 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]/.

Reading the source

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 ;

The ultimate debugger.

perl has a built debugger, t=you can use it to look at how your build system is working.



composite targets

pbs -p xx b@all


On-line documentation for the PBS switches is available through --hs.

Debug Switches

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. <>


10 POD Errors

The following errors were encountered while parsing the POD:

Around line 1:

Unknown directive: =comment

Around line 188:

Unknown directive: =head5

Around line 192:

Unknown directive: =head5

Around line 197:

Unknown directive: =head5

Around line 229:

Unknown directive: =head5

Around line 314:

Unknown directive: =head5

Around line 318:

Unknown directive: =head5

Around line 324:

Unknown directive: =head5

Around line 526:

Unknown directive: =head5

Around line 547:

Unknown directive: =head5