Task execution order

Running tasks in parallel is a simple problem, but in practice it is more complicated:

To make the problem more simple, it is divided by the different concerns, and the ordering constraints can be given on three different levels:

  1. groups of tasks may run only after another group of tasks has finished to run, this represents a strict sequential order between groups of tasks, for example a compiler is produced and used to compile the tasks in the next group
  2. task types to indicate the instance will run after other task type instances, for example linking object files may only occur after compiling the source files
  3. specific constraints for task instances that can only run after a few other task instances

Task groups

In some circumstances it is necessary to build a compiler and all its dependencies before using it for executing some other tasks (bootstrapping). The following demonstrates how declare groups of tasks to be executed after other groups of tasks:

def build(bld):
	bld.new_task_gen(features='cc cprogram', source='main.c', target='mycompiler')
	bld.add_group()
	bld.new_task_gen(features='cc cprogram', source='user.c', target='someotherapp')
					

The effect of task groups when running tasks in parallel is illustrated by the following diagram. Three groups of tasks have been added, and the execution of the next group only starts when the execution of the tasks in the previous group is complete.

It is possible to create groups at any point in the scripts, and to add the task generators to any group previously created. Adding groups for specific folders or scripts enables a behaviour similar to projects organized in recursive Makefiles.

def build(bld):

	bld.add_group('test1')
	bld.add_group('test2')
	bld.add_group('test3')
	bld.add_group('test4')

	print('adding task generators')

	bld.set_group('test3')
	bld.new_task_gen(features='cxx cprogram', source='main3.c', target='g3')

	bld.set_group('test1')
	bld.new_task_gen(features='cxx cprogram', source='main1.c', target='g1')

	bld.set_group('test2')
	obj2 = bld.new_task_gen(features='cxx cprogram', source='main2.c', target='g2')

	bld.set_group('test4')
	obj2.clone('debug')
					

Because task groups prevent parallelization, they reduce performance. On the other hand, they make projects more structured and improve the maintainance.

Precedence constraints

The attributes before and after are used to declare ordering constraints between tasks:

import Task
class task_test_a(Task.TaskBase):
	before = 'task_test_b'
class task_test_b(Task.TaskBase):
	after = 'task_test_a'
					

Another way to declare precedence constraints is to declare a file extension production, for example:

import Task
class task_test_a(Task.TaskBase):
	ext_in = '.c'
class task_test_b(Task.TaskBase):
	ext_out = '.c'
					

The extensions have to match to add a valid precedence constraint, but they are only annotations, they do not mean the tasks actually have to produce files of that type.

Precedence constraints on task instances

The method set_run_after is used to declare ordering constraints between tasks:

task1.set_run_after(task2)
					

unlike the previous constraints, it is used on the instances of class Task which is a subclass of class TaskBase