This wiki page provides a quite complete example of the usage of PID API. Several packages and their dependencies are described in following sections.

Package the-testpack-a

Root CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)
set(WORKSPACE_DIR ${CMAKE_SOURCE_DIR}/../.. CACHE PATH "root of the packages workspace directory")
list(APPEND CMAKE_MODULE_PATH ${WORKSPACE_DIR}/cmake) # using generic scripts/modules of the workspace
include(Package_Definition NO_POLICY_SCOPE)

PROJECT(the-testpack-a)

declare_PID_Package(
	AUTHOR Robin Passama
	INSTITUTION LIRMM
	YEAR 2013
	LICENSE CeCILL
	ADDRESS git@gite.lirmm.fr:passama/the-testpack-a.git
 	DESCRIPTION test package for PID
)
set_PID_Package_Version(1 6)
# adding some binary packages references
add_PID_Package_Reference(BINARY VERSION 0 4 2 SYSTEM x86_64_linux_stdc++
			URL http://lirmm.lirmm.fr/FileX/get?auto=1&k=E5UWg8sZ08bN8HhVCY2
			http://lirmm.lirmm.fr/FileX/get?auto=1&k=RtFNxqdfYemEaGRqxdw)
#now finding packages
find_package (Threads REQUIRED)
build_PID_Package()

The root CMakeLists.txt of the package the-testpack-a declares main meta-data: main author, date when people have been working on the package, description, etc. Most important fields are LICENSE and ADDRESS:

  • LICENSE specifies the software license that applies to all elements of the package. In this case this is the CeCILL license. This meta-data is used to generate a license.txt file for the package according to the license used.
  • ADDRESS specifies the address of the official git repository for the package. It is used to retrieve the package repository, download and install it. This address can be shadowed by a private repository address using the add_PID_Package_Reference macro with the SOURCE signature.

After declaration the set_PID_Package_Version macro sets the current version currently built by the user. May be otherwise replaced by using the VERSION argument of declare_PID_Package.

The add_PID_Package_Reference macro is used to register a binary package version relocatable archive previously generated. Finally the find_package command is used to register a system dependency with the Threads system package. This is a kind of “virtual” CMake package used to find threading libraries in a cross platform way. Finally the package is built and CMakeLists.txt of subdirectories are called in order to declare components generated by the package.

Libraries

Libraries source code is dispatched in include and src sub-folders of the package. The CMakeLists.txt file of the src folder declares available libraries:

declare_PID_Component(STATIC_LIB NAME lib-a DIRECTORY lib_a
 			INTERNAL DEFINITIONS LIBA_FUNC2_NO_MULTIPLY_VERSION
 			EXPORTED DEFINITIONS DOUBLE_IS_AVAILABLE)

declare_PID_Component(HEADER_LIB NAME lib-x DIRECTORY lib_x)
declare_PID_Component_Dependency(COMPONENT lib-x EXPORT DEPEND lib-a
 				IMPORTED_DEFINITIONS USE_LIBA_FUNC1
				EXPORTED_DEFINITIONS USE_LIBA_FOR_COMPUTING)

declare_PID_Component(SHARED_LIB NAME lib-b-sh DIRECTORY lib_b
			INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/common_code)
declare_PID_Component_Dependency(COMPONENT lib-b-sh
 				EXPORT DEPEND lib-x
 				EXPORTED_DEFINITIONS USE_LIBB_FUNC1_ALT_VERSION)

The declared libraries are: * lib-a, a static library, whose headers are located in the lib_a sub-folder of the include folder, and whose source files are in the lib_a sub-folder of the src folder. * lib-x a header library, whose header files are located in the lib_a sub-folder of the include folder. * lib-b-sh a shared library, whose header files are located in the lib_b sub-folder of the include folder, and whose source files are in the lib_b sub-folder of the src folder.

Source code of lib-a library

The folder <package root>/include/lib_a contains a header file named libA.h whose content is:

#ifndef LIBA_INCLUDE
#define LIBA_INCLUDE
#ifdef __cplusplus
extern "C"{
#endif
#ifdef USE_LIBA_FUNC1
void liba_function1(int * data);
#endif
void liba_function2(int);
#ifdef DOUBLE_IS_AVAILABLE
void liba_function3(double);
#endif
#ifdef __cplusplus
}
#endif
#endif

The folder <package root>/src/lib_a contains a source file named code1.c whose content is:

#include <libA.h>
#include <stdio.h>
#ifndef LIBA_FUNC2_NO_MULTIPLY_VERSION
int MULTIPLY_FACTOR = 5;
#endif
void liba_function1(int * data){
	printf("liba_function1 pointer is %p, data is %d\n",data,*data);
}
void liba_function2(int data){
#ifdef LIBA_FUNC2_NO_MULTIPLY_VERSION
	printf("liba_function2 (1) data is %d\n",data);
#else
	printf("liba_function2 (2) data is %d\n",data*MULTIPLY_FACTOR);
#endif
}
#ifdef DOUBLE_IS_AVAILABLE
void liba_function3(double data){
	printf("liba_function3 data is %lf\n",data);
}
#endif

The declare_PID_Component macro defines some of the preprocessor variables in the source of lib-a library:

  • INTERNAL DEFINITIONS arguments are used to define some of the preprocessor variables that are found ONLY inside the implementation. For instance, LIBA_FUNC2_NO_MULTIPLY_VERSION preprocessor variable, that is only located in code1.c, is defined for lib-a library.

  • EXPORTED_DEFINITIONS arguments are used to define some of the preprocessor variables that are found inside header files. These definitions must be exported when the library is used. For instance DOUBLE_IS_AVAILABLE is defined inside libA.h. When a preprocessor variable is found both inside the implementation and in the interface, then it must be defined with the EXPORTED_DEFINITIONS keyword.

From this source code we so define a specific component and it would be possible to define others that would be alternative versions built from the same source code.

Source code of lib-x library

The folder <package root>/include/lib_x contains a header named libX.h whose content is:

#ifndef LIBX_INCLUDE
#define LIBX_INCLUDE
#ifdef __cplusplus
extern "C"{
#endif
#include <stdio.h>
//some definition and global variables
typedef struct{
	int one;
	double two;
} LibXType;
#ifdef USE_LIBX_PRINT
void libx_print(LibXType var){
	printf("libX print one = %d, two = %lf\n",var.one, var.two);
}
#endif
#ifdef USE_LIBA_FOR_COMPUTING
#include <libA.h>
void libx_print_with_liba(LibXType * var){
	liba_function1(&var->one);
	liba_function3(var->two);
}
void libx_print_with_liba_again(LibXType * var){
	liba_function2(var->one);
	liba_function3(var->two);
}
#endif
#ifdef __cplusplus
}
#endif
#endif

The lib-x library is a pure header library so no source file is bound to it and no binary generated from it. The interesting thing as regard of previous library is that lib-x depends on on the previously defined lib-a static library. This dependency is expressed in the code via the #include<libA.h> directive.

As readers can see this dependency is conditional: it depends on the definition of the preprocessor variable USE_LIBA_FOR_COMPUTING. lib-x is supposed to depend on lib-a so this variable must be defined when the dependency between lib-x and lib-a is declared: the call to declare_PID_Component_Dependency macro is made with argument EXPORTED_DEFINITIONS that define the preprocessor variable USE_LIBA_FOR_COMPUTING. USE_LIBA_FOR_COMPUTING must be set as “exported” by lib-x because:

  • it belongs to the interface of lib-x. lib-x being a header library, all its definitions are necessarily exported.
  • its value must be set in caller’s context in order to use lib-x adequately.

lib-x dependency declaration also defines USE_LIBA_FUNC1 with the keyword IMPORTED_DEFINITIONS, which means that this preprocessor variable : * is bound to the dependency (here lib-a) not to the declared component itself. “Bound to a dependency” simply means that the preprocessor variable is used in the interface (headers) of the required library. In the current example, USE_LIBA_FUNC1 is used in the interface of lib-a. * must be defined, when using lib-x. * will be exported if the dependency is exported. In current example, lib-x exports lib-a so the definitions of USE_LIBA_FUNC1 will be exported so that code using lib-x library can be configured adequately.

Source code of lib-b-sh library

The folder <package root>/include/lib_b contains a header named libB.h whose content is:

#ifndef LIBB_INCLUDE
#define LIBB_INCLUDE
#ifdef __cplusplus
extern "C"{
#endif
#ifdef USE_LIBB_FUNC1_ALT_VERSION
#include <libX.h>
void libb_function1(LibXType * var);
LibXType * libb_function2();
void libb_function3();
#else
void libb_function4();
#endif
#ifdef __cplusplus
}
#endif
#endif

The folder <package root>/src/lib_b contains a source file named code2.c whose content is:

#include <libB.h>
#include <libX.h>
#include <stdlib.h>
#include "commonDefs.h"
LibXType * curr_allocated = 0;
void libb_function4(){
	LibXType varlibx = {.one= 1234, .two=21.4578};
#ifdef USE_LIBA_FOR_COMPUTING
	libx_print_with_liba_again(&varlibx);
#else
#ifdef USE_LIBX_PRINT
	libx_print(varlibx);
#else
#error "NO AVAILABLE IMPLEMENTATION FOR FUNCTION 4"
#endif
#endif
}
void libb_function1(LibXType * p){
	printf("using alternative version !!\n");
#if defined USE_LIBA_FOR_COMPUTING
	libx_print_with_liba(p);
#elif defined USE_LIBX_PRINT
	libx_print(*p);
#else
#error "NO AVAILABLE IMPLEMENTATION FOR FUNCTION 1"
#endif
}
LibXType* libb_function2(){
	if(curr_allocated != 0) free(curr_allocated);
 	curr_allocated = (LibXType*) malloc(sizeof(LibXType));
 	curr_allocated->one = 4567;
 	curr_allocated->two = 8.91011;
 	return curr_allocated;
}
void libb_function3(){
	if(curr_allocated != 0) free(curr_allocated);
}

lib-b-sh is a shared library whose declaration follows the same rules as previously presented. The only original point is that it uses an additional include folder, using the INTERNAL INCLUDE_DIRS keywords. Many include folder can be added this way. Their main concern is to allow to define some code shared by components of the same package. The difference with sub-folders of the include folder are: * these additional included folders are placed in the src folder not in the include folder. For instance commonDefs.h included in libB.h is placed in the folder src/common_code (itself considered as an internal include folder). * headers contained in these additional included folders will never be installed after package has been built. The direct constraint is that these files **cannot be used in header files contained in the sub-folders of include. * headers contained in these additional included folders can be included in source code located inside any sub-folder of the src folder. In other word, it is shared between very different source code. Most of time it is used to provide common definition and constants between different unrelated (or loosely related) components.

Applications

Applications source code is contained in apps folder of the package. The CMakeLists.txt of the apps folder declares available applications:

declare_PID_Component(APPLICATION NAME app-a-dyn DIRECTORY app_a)
declare_PID_Component_Dependency (COMPONENT app-a-dyn DEPEND lib-b-sh
					INTERNAL_DEFINITIONS APP_A_CASE1)
declare_PID_Component_Dependency(COMPONENT app-a-dyn
					LINKS SHARED ${CMAKE_THREAD_LIBS_INIT})

It defines a single standard application named app-a-dyn whose source code is placed in the app_a sub folder of the apps folder. This later contains a unique source file named app-a.c that implements the executable:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <libB.h>
pthread_t _thread;
void* thread_function(void * args){
#ifndef APP_A_CASE1
	libb_function4();
#else
	libb_function1(libb_function2());
	libb_function3();
#endif
pthread_exit(NULL);
}
int main(int argc, char* argv[]){
if (pthread_create(&_thread, NULL, thread_function, NULL) < 0) {
	printf("[ERROR] impossible to create the thread\n");
	exit(0);
}
pthread_join(_thread, NULL);
return 0;
}

app-a-dyn has two dependencies: * it depends on lib-b-sh. In the code, this is represented by the #include <libB.h> directive. As readers can notice there is an alternative implementation in the implementation of thread_function. app-a-dyn is built with the alternative selected when APP_A_CASE1 is defined (see INTERNAL_DEFINITIONS in CMakeLists.txt). Another application could use the other alternative by letting APP_A_CASE1 undefined. * it depends on system. The dependency declaration simply defines the shared libraries linked using LINK SHARED keywords followed by libraries used. In the present example $CMAKE_THREAD_LIBS_INIT is a CMake variable generate by the call to find_package containing something like -lpthread.

Package the-testpack-b

Root CMakeLists.txt

CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)
set(WORKSPACE_DIR ${CMAKE_SOURCE_DIR}/../.. CACHE PATH "root of the packages workspace directory")
list(APPEND CMAKE_MODULE_PATH ${WORKSPACE_DIR}/cmake) # using generic scripts/modules of the workspace
include(Package_Definition NO_POLICY_SCOPE)

PROJECT(the-testpack-b)

declare_PID_Package(
	AUTHOR Robin Passama
	INSTITUTION LIRMM
	YEAR 2013
	LICENSE CeCILL
	ADDRESS git@gite.lirmm.fr:passama/the-testpack-b.git
	DESCRIPTION test package B for PID
)
set_PID_Package_Version(1 1)
# adding some binary packages references
add_PID_Package_Reference(BINARY VERSION 0 1 0 PLATFORM x86_64_linux_stdc++ URL
				http://lirmm.lirmm.fr/FileX/get?auto=1&k=rfYTf1gkpI5XtEpQWVA
				http://lirmm.lirmm.fr/FileX/get?auto=1&k=oMyg4JVeeKYWpqwEFxE)

add_PID_Package_Reference(BINARY VERSION 1 1 PLATFORM x86_64_linux_stdc++ URL
				http://lirmm.lirmm.fr/FileX/get?auto=1&k=DYt2j35Kw8ozOgHfoVA
				http://lirmm.lirmm.fr/FileX/get?auto=1&k=zEx02N4KWfzDWPTxiO)
# from here we manage packages dependencies #
find_package (Boost REQUIRED) #boost as a system library
find_package (the-testpack-a 1.0 REQUIRED lib-b-sh lib-x)
if(Boost_DIR-NOTFOUND)
	set(BOOST_ROOT /usr)
endif()
declare_PID_Package_Dependency(PACKAGE boost EXTERNAL
			VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}")
declare_PID_Package_Dependency (PACKAGE the-testpack-a NATIVE
			VERSION 1 0 COMPONENTS lib-b-sh lib-x)
build_PID_Package()

The package the-testpack-b (version 1.1) requires the external package boost and the native package the-testpack-a (version 1.0 or compatible). Whether the-testpack-a has been found or not it declares this package as a required dependency. In that case the-testpack-a may be downloaded, installed and then configured automatically by PID system.

It defines URL where to find binary package version relocatable archives using the add_PID_Package_Reference macro.

Libraries

Libraries source code is contained in include and src folders. The CMakeLists.txt of the src folder declare available libraries:

#libY -> dependency with libX
declare_PID_Component(HEADER_LIB NAME lib-y DIRECTORY lib_y)
declare_PID_Component_Dependency (COMPONENT lib-y EXPORT NATIVE lib-x PACKAGE the-testpack-a
			EXPORTED_DEFINITIONS USE_EXTERNAL_LIB_X)
#libYBis -> dependency with libX
declare_PID_Component(HEADER_LIB NAME lib-y-bis DIRECTORY lib_y)

#libC shared -> dependency with libY
declare_PID_Component(SHARED_LIB NAME lib-c-sh DIRECTORY lib_c
				INTERNAL DEFINITIONS A_VERY_SPECIFIC_IMPLEM)
declare_PID_Component_Dependency(COMPONENT lib-c-sh EXTERNAL boost INCLUDE_DIRS <boost>/include)
declare_PID_Component_Dependency(COMPONENT lib-c-sh EXPORT NATIVE lib-y)

#libC static -> dependency with libY
declare_PID_Component(STATIC_LIB NAME lib-c-st DIRECTORY lib_c)
declare_PID_Component_Dependency(COMPONENT lib-c-st EXTERNAL boost INCLUDE_DIRS <boost>/include)
declare_PID_Component_Dependency(COMPONENT lib-c-st EXPORT NATIVE lib-y-bis)

The declared libraries are: * lib-y a header library, whose headers are in the lib_y folder of the include folder. * lib-y-bis a header library, whose headers are the same as those of lib-y (same folder used), but with other definitions (thus modifying the behaviour of the library). * lib-c-sh a shared library, whose headers are in the lib_c of include, and whose source files are in the lib_c folder of src. * lib-c-st a static library based on the same source code than lib-c-sh library (same folders) but with different definitions and dependencies and built a diffrent way (as an archive instead of a dynamic object).

Shared code between lib-y and lib-y-bis libraries

The folder <package root>/include/lib_y contains a header named libY.h whose content is:

#ifndef LIBY_INCLUDE
#define LIBY_INCLUDE
#include <stdio.h>
#ifdef USE_EXTERNAL_LIB_X
#include <libX.h>
typedef struct{
	LibXType onetwo;
	unsigned char three;
	char four;
	unsigned long five;
} LibYType;
#else
typedef struct{
	char tutu [256];
	unsigned char tata[12];
	unsigned char three;
	char four;
	unsigned long five;
} LibYType;
#endif
typedef LibYType YBuffer[10];
void liby_print(YBuffer yb){
>	for (unsigned int i = 0; i < 10;i++){
#ifdef USE_EXTERNAL_LIB_X
#ifdef USE_LIBX_PRINT
>		libx_print(yb[i].onetwo);
#else
#ifdef USE_LIBA_FOR_COMPUTING
>		libx_print_with_liba(&yb[i].onetwo);
#else
#error "NO IMPLEMENTATION AVAILABLE FOR LIBX !!!"
#endif
#endif
		printf("three : %d, four = %c, five : %lu\n", yb[i].three, yb[i].four, yb[i].five);
#else
		printf("tutu : %s, tata : %s, three : %d, four = %c, five : %lu\n", yb[i].tutu, yb[i].tata, yb[i].three, yb[i].four, yb[i].five);
#endif
>	}
}
#endif

As reader can notice, depending on the value of the USE_EXTERNAL_LIB_X preprocessor variable, this code will have different behaviours. It will notably conditionate the use of another library lib-x supposed to be defined in another package, because it conditionates the #include<libX.h> directive. In CMakeLists.txt, the lib-y header library component defines this preprocessor variable while the  lib-y-bis header library component does not: * when USE_EXTERNAL_LIB_X is defined it forces the use of a dependency to another component lib-x of another package the-testpack-a. When this dependency is declared the EXPORTED_DEFINITION keyword is used to say that the preprocessor variable is defined in the interface (i.e. a header) of lib-y. In this case the lib-x component is exported (use of EXPORT keyword in dependency declaration) since it is contained in the interface of the lib-y component. * when USE_EXTERNAL_LIB_X is not defined the dependency to lib-x component is not defined (no #include<libX.h> directive). Consequently the EXPORTED_DEFINITION keyword is not used into declare_PID_Component (see declaration of lib-y-bis). Indeed, this preprocessor definition cannot be attached to a particular dependency (no dependency declared for lib-y-bis).

This way we can associate different components to the same source code by playing on preprocessor variables. Since these definition are exported they can be used by libraries that are using either version of this code (lib-y or lib-y-bis). For instance, we suppose that the lib-x library also exports some preprocessor variable definitions, the same way as lib-y : USE_LIBX_PRINT and USE_LIBA_FOR_COMPUTING. The behaviour of lib-y is conditional whether the preprocessor variable of lib-x that has been defined by the component.

Shared code between lib-c-sh and lib-c-st libraries

The <package root>/include/lib_c folder contains a header named libC.h whose content is:

#ifndef LIBC_INCLUDE
#define LIBC_INCLUDE
#define LIB_C_CONST_VALUE 10.0256
extern "C"{
#include <libY.h>
}
void libc_function1(YBuffer);
void libc_function2(YBuffer);
#endif
```

The folder `<package root>/src/lib_c` contains a source file named `code1.cc` whose content is:

```cpp
#include <libC.h>
#include <stdio.h>
#include <boost/math/special_functions/factorials.hpp>
void libc_function1(YBuffer data){
	liby_print(data);
	double res = boost::math::factorial<double>(data[0].five);
	printf("factorial = %lf, with factor = %lf\n", res, res*LIB_C_CONST_VALUE);
}
void libc_function2(YBuffer data){
#ifdef A_VERY_SPECIFIC_IMPLEM
	printf("SPECIFIC libc_function2 !!!\n");
	liby_print(data);
#else
	printf("STANDARD libc_function2 !!!\n");
	liby_print(data);
#endif
}

As readers can notice the interface (headers) is the same for both libraries lib-c-sh and lib-c-st : there is no use of a preprocessor variable in libC.h to differentiate alternative signatures. Apart from the nature of their resulting binary (archive of objects or dynamic object) their main difference resides in an alternative implementation of the function libc_function2, using preprocessor variable A_VERY_SPECIFIC_IMPLEM (in code1.cc). This variable is defined by lib-c-sh but not by lib-c-st. lib-c-sh defines it with the INTERNAL keyword (see CMakeLists.txt) because it applies only to the implementation of both component, not their interface (otherwise it would be defined with the EXPORTED_DEFINITION keyword).

Both libraries have a dependency with the external boost package (they need to know where is the include folders of that package) and they each define an internal dependency:

  • lib-c-sh depends on lib-y, consequently USE_EXTERNAL_LIB_X will be defined (by transitivity).
  • lib-c-st depends on lib-y-bis, consequently USE_EXTERNAL_LIB_X will not be defined (by transitivity).

Finally both libraries will have slightly different behaviours depending on either (1) their local implementation and (2) their dependencies. Finally readers can also notice that dependencies are exported or not:

  • Dependency to external boost package is not exported since the #include <boost/math/special_functions/factorials.hpp> directive is only in the code1.cc source file.
  • Dependencies to lib-y or lib-y-bis are exported since the #include <libY.h> directive is in the interface (libC.h) of both libraries.

Applications

Applications source code is contained in the apps sub-folder of the package. The CMakeLists.txt of the apps folder declares available applications:

#1 first version (example) of application
declare_PID_Component(EXAMPLE_APPLICATION NAME app-b1 DIRECTORY app_B1)
declare_PID_Component_Dependency(COMPONENT app-b1 NATIVE lib-b-sh PACKAGE the-testpack-a)
#2 second version of application
declare_PID_Component(APPLICATION NAME app-b1-evolved DIRECTORY app_B1)
declare_PID_Component_Dependency (COMPONENT app-b1-evolved NATIVE lib-b-sh PACKAGE the-testpack-a)
declare_PID_Component_Dependency (COMPONENT app-b1-evolved NATIVE lib-c-sh INTERNAL_DEFINITIONS EVOLVED_IMPLEM)

The declared applications are: * app-b1, an example application, whose code is placed into the app_b1 sub-folder of the apps folder. * app-b1-evolved a standard application which share its source code with app-b1 (its source code is also in `app_b1 folder).

The folder <package root>/apps/app_b1 contains a source file named app_b1.cc whose content is:

#include <stdlib.h>
extern "C"{
#include <libB.h>
}
#ifdef EVOLVED_IMPLEM
#include <libC.h>
#endif
int main(int argc, char* argv[]){
#ifdef EVOLVED_IMPLEM
	libb_function1(libb_function2());
	YBuffer buff;
	for (unsigned int i = 0;i < 10 ; i++){
		buff[i].three = i+1;
		buff[i].four = 'c';
		buff[i].five = (unsigned long) buff[i].three * 2;
>	}
	libc_function1(buff);
>	libc_function2(buff);
#else
	libb_function1(libb_function2());
#endif
return 0;
}

As readers can notice source code contains alternative implementations, the same way as for lib-c-sh and lib-c-st libraries: * app-b1 is the simple version, EVOLVED_IMPLEM is let undefined. It is just a simple example of usage of the library lib-b-sh. * app-b1-evolved is a more complex version. Readers have to notice that application having no interface they cannot export any definition. Unlike libraries, applications dependencies can so only be defined with INTERNAL_DEFINITIONS and IMPORTED_DEFINITIONS keywords.

Both applications define a dependency to lib-b-sh library of the package the-testpack-a. app-b1-evolved defines an additional dependency to lib-c-sh. The usage of this library is conditional (in source code) depending on the EVOLVED_IMPLEM preprocessor variable (that guards the #include<libC.h> directive). For app-b1-evolved, call to declare_PID_Component_Dependency so uses the INTERNAL_DEFINITIONS keyword to define EVOLVED_IMPLEM.

Discussion on the example

As reader can notice with previous examples, the complexity to declare components can be very high notably due to preprocessor variables definitions according to code. This would be even worst if PID was not rationalizing the declaration of components notably at the moment when developers would use these libraries. For instance, PID completely automates the management of code dependencies transitivity, at compile-time, link time and run-time.

Nevertheless we discourage to overuse preprocessor variables inside library interfaces, their use should be avoided as often as possible. Shortly, keep it as simple as possible.

To clarify the use of preprocessor denitions we propose the following strategy:

  • when a preprocessor variable is to be used only in the implementation (source code) of the component (does not appear in its interface):
    • if it conditionates the use of a library (e.g. if it is used in #ifndef a guard for a #include directive whose target is a header file of another library), then it should be declared using INTERNAL_DEFINITIONS keyword within a call to declare_PID_Component_Dependency.
    • otherwise it should be declared also using INTERNAL DEFINITIONS keywords within a call to declare_PID_Component.
  • when a preprocessor variable is used in the interface (header) of the component (whether it appears in its implementation or not):
    • if it conditionates the use of a library, it is declared using the keyword EXPORTED_DEFINITIONS in declare_PID_Component_Dependency.
    • otherwise it should be declared using EXPORTED DEFINITIONS keyword but within a call to declare_PID_Component.

We also propose additional good practices:

  • By default, in the interface of a library (its headers), all preprocessor variables must be explicitely defined. Applying this simple strategy tends to minimize the amount of peprocessor variable to manage when using this library.
  • As an exception of previous rule, preprocessor variables that the calling context is allowed to define/undefine, must be let undefined in component declaration function. They will be defined or undefined by components using this library thanks to the IMPORTED_DEFINITIONS keyword. Take care at this later option since it may cause a lot of problems if misused. Notably, such a preprocessor variable should never be used to modify the interface of the library, except if the implementation of the modified part is in the interface (always the case for header libraries).