This part provides a detailed description on the usage of PID methodology’s core concepts and tools to deal with packages.

Package definition

When developing a PID package one need to handle information such as:

  • meta-information : who is involved in its development ? what is its general purpose ? where to find this package on the network ? what is the license of the code ? etc.
  • build-related information : what are the components provided by the package and how to compile/link them from source code ? How components are tested ? what is the version of the package ? what are its functional dependencies ?
  • functional information : this is the source code of the components and other associated files like configuration files if any required.

The whole package development is organized and described with CMake, and the CMakeLists.txt files used contain the whole meta-information and build-related information used by the package. Each package contains several CMakeLists.txt files that are generally used to define components :

  • the root CMakeLists.txt file is used to define meta-information and dependencies of the package.
  • the CMakeLists.txt file contained in the src folder defines the library components.
  • the CMakeLists.txt file contained in the apps folder defines the application components.
  • the CMakeLists.txt file contained in the test folder defines the tests components and running tests (using these components or others).
  • the CMakeLists.txt file contained in the share folder defines additional files to install, like configuration files.

In the context of PID components are software artefacts generated and installed by a package. Each component may require other components from another package and so may explicitly define dependencies between packages. The primary role of PID is to manage these dependencies.

The CMakeLists.txt files are so used to describe 1) the way components are built and 2) dependencies between them. Each CMakeLists.txt uses the PID cmake API and follow a predefine pattern depending on its role in the project. The following subsections present examples on how to use the API together with more classical CMake code in order to completely define a functionnal PID package.

General description

The package general description takes place in the root CMakeLists.txt. This file contains the description of meta-information and dependencies of the package.

Meta-information

Let’s suppose we define a package with the name a-given-package, its root CMakeLists.txt could look like:

cmake_minimum_required(VERSION 3.0.2) #minimum version of CMake to use PID
set(WORKSPACE_DIR ${CMAKE_SOURCE_DIR}/../.. CACHE PATH "root of the packages workspace directory")
list(APPEND CMAKE_MODULE_PATH ${WORKSPACE_DIR}/cmake) #registereing PID CMake scripts
include(Package_Definition NO_POLICY_SCOPE) # use the PID API
project(a-given-package)

PID_Package(
		AUTHOR Robin Passama
		INSTITUTION LIRMM
		YEAR 2013
		LICENSE CeCILL
		ADDRESS git@gite.lirmm.fr:passama/a-given-package.git
 		DESCRIPTION an example PID package
		VERSION 1 2
)

PID_Category(example/simple)

# adding some binary packages references
PID_Reference(
 		BINARY VERSION 0 1 0 PLATFORM x86_64_linux_stdc++
 		URL 	http://gite.lirmm.fr/a-given-package/download/0.1.0/release
 			http://gite.lirmm.fr/a-given-package/download/0.1.0/debug
)

PID_Reference(
 		BINARY VERSION 1 1 PLATFORM x86_64_linux_stdc++
 		URL 	http://gite.lirmm.fr/a-given-package/download/1.1.0/release
 			http://gite.lirmm.fr/a-given-package/download/1.1.0/debug
)

Explanations:

  • Exactly as in any CMake project, the CMake package is defined with the PROJECT keyword. The CMake project’s name must be the same as the name of the git repository and must be the same as the project root folder name. The remaining lines until the PID_Package macro call must be let unchanged (initialization of the PID specific cmake API).

  • Then comes the PID_Package (or equivalent declare_PID_Package) macro, which is mandatory. This macro defines general meta-information on the package, and should not change a lot during package life cycle:

    • the main author (AUTHOR keyword) and its institution (INSTITUTION optional keyword), considered as the maintainer of the package.
    • the YEAR field helps defining the package’s life cycle range. For instance one can input a field such as “2009-2013”.
    • the LICENSE field is used to specify the license that applies to the code. This license must be defined in the workspace in the form of a license file.
    • the ADDRESS field is used to specify the address of the official GIT repository of the package.
    • the DESCRIPTION field must be filled with a short description of the package usage/utility.
    • the VERSION argument (equivalent to using set_PID_Package_Version) is used to set the currently developed version of the package. It takes as arguments at least a major and minor numbers and optionally a patch number (default value for patch number is 0). The version number thus follows the same pattern as git release versions. Before a version is released with a git tag this version number must be set adequately so that git tag matches cmake package version. Generally, the best way to do is to set the version number used in the CMakeLists.txt with the number of the next version to release. In the example we consider that the version 1.1 of the package has been released so the version number has been set to 1.2.
  • Then the user fill other meta-information that may evolve during project life cycle:

    • the PID_Reference function is used to register a downloadable binary version of the package. The VERSION keyword specify the version with major.minor.patch pattern. The PLATFORM keyword specifies the target platform for which the binaries have been built. The two addresses after the URL keyword specify where the binary package version can be downloaded either in release (first address) and debug (second address) modes.
    • the PID_Category function is used to specify to which categories the package belongs to. A Category is nothing more than a text identifier that tells what concerns are treated by the package. For instance a package implementing a robot driver could belong to the “drivers/robot” category. Many categories can be defined for the same package. This information can be use at the workspace level (using workspace specific commands) to know which packages are bound to a given concern. This information just helps classifying packages and does not impact the build process.

Dependencies

The last part of the root CMakeLists.txt is used to manage dependencies between the current package and other packages it depends on. It could look like:

# finding used packages
PID_Dependency(boost VERSION 1.55.0)
PID_Dependency(a-given-package 1.0 COMPONENTS lib-first-sh lib-second-st)

#declare a dependency over another PID package

declare_PID_Package_Dependency ( # same as using PID_Dependency
 			PACKAGE another-package NATIVE VERSION 1.0
 			COMPONENTS lib-other-sh
)

build_PID_Package()

Explanations:

  • The PID development process imposes to declare dependencies of the current package. Indeed this is not because you try to find other packages that you will use them, even if obviously this assumption will be right most of time. A dependency simply means that components of the package are using components from other packages. For instance, the current package uses the package another-package with minimum version 1.0. To make this declaration possible PID API provides the PID_Dependency (or equivalent declare_PID_Package_Dependency) function. This function can be used in two different ways:

    • it is used to specify a dependency to an external package (optionnaly using the keyword EXTERNAL after the package name but not required as PID detect dependency nature). An external package is a “light” PID package that is not available as a repository: it is just a kind of port of an existing project into PID system in order to ease its deployment process. Installed external packages version can be found in the install folder of the workspace as for native packages. The idea behind external packages is to allow to import existing projects and wrap them as specific PID packages.
    • it is used to specify a dependency to a native package (optionnaly using NATIVE keyword). Then version and component requirement informations can be used exactly as in the example.
  • The last command called by the CMakeLists.txt must the build_PID_Package macro. This line is mandatory in order to allow the build of the package: compilation, installation, deployment, API doc generation, etc. Without this call, the CMake process would simply do nothing.

Dealing with conditional dependencies

The previous example is quite simple since it directly deals with required dependencies. Nevertheless, when using cmake one sometimes have to deal with conditional dependencies. Indeed, conditional dependencies allow to configure the build according to the OS requirements or to the configuration of user’s station. This conditional dependencies are managed nearly the same way as previously:

PID_Dependency(another-package OPTIONAL VERSION 1.0)

if(NOT another-package_AVAILABLE)
	PID_Dependency(yet-another-one VERSION 5.0)
endif()

Explanations:

  • This technic is used to describe dependencies that are either optional or alternative requirements when multiple different packages can be used for the same purpose. This later case is shown in the previous code. Defining such conditional dependencies is made using PID_Dependency as previously but using the OPTIONAL keyword.

  • In the previous example, there is an implicit priority of required packages: another-package will be used prior to yet-another-one if it is found (information given by another-package_AVAILABLE).

  • The only mandatory descriptive elements is to use PID_Dependency function to tell to PID system which package is required or optional.

Defining library components

Once package dependencies have been defined, the package developers can then declare the components of the package and their relationship with these dependencies. Most common components are library components : they are used by developers to define reusable functionalities. All libraries are defined in the CMakeLists.txt contained in the src folder of the package repository.

PID defines three types of libraries, matching the three classical types available in C/C++. It provides a systematic way to define these libraries and automate their use, avoiding the user to know precisely how to deal with each type and their relative properties. Following subsections explain how to declare each of these types. These definitions rely on cmake functions provided within PID: declare_PID_Component and declare_PID_Component_Dependency.

Header libraries

Header libraries are not compiled to produce a binary object. They are just made of a set of header files that defines an API. This kind of library is often used for template libraries definitions, for instance the Boost framework essentially contains lots of header libraries. A header library is never used at link time (never linked to another library or executable using a linker) but only at compile time (when including header files in code). The definition of a header library should look like:

declare_PID_Component(HEADER_LIB NAME my-given-lib DIRECTORY my_lib)
declare_PID_Component_Dependency(COMPONENT my-given-lib
				EXPORT NATIVE lib-other-sh PACKAGE another-package
				EXPORTED_DEFINITIONS USE_SPECIFIC_COMPILE_OPTION)

or equivalent short signature:

PID_Component(my-given-lib HEADER DIRECTORY my_lib)
PID_Component_Dependency(COMPONENT my-given-lib
				EXPORT lib-other-sh PACKAGE another-package
				EXPORTED_DEFINITIONS USE_SPECIFIC_COMPILE_OPTION)

Explanations:

  • The first thing to do is to declare the header library by using the function declare_PID_Component (or PID_Component):

    • the HEADER_LIB (or HEADER) keyword is used to declare a header library component.
    • the NAME keyword is used to define the identifier of the component in PID, whose unicity must be preserved. In the example the name is my-given-lib. NAME keyword can be ommitted if the name if the first argument (see short signature).
    • the DIRECTORY keyword is used to specify in which sub-directory of the include folder the header files are found (in the example the my_lib sub-folder). The direct consequence is that all headers of a given library must be placed in a unique folder. DIRECTORY argument can be ommitted if the name of the folder is the same as the name of the component (not the case here).
  • Then, depending on the library’s content, some dependencies can be attached to the library, using declare_PID_Component_Dependency (or PID_Component_Dependency). Indeed a header library can depend on other libraries (either header, static or shared):
  • the COMPONENT keyword is used to specify for which component a dependency is defined, in the example the previously defined my-given-lib header library.
  • the EXPORT keyword specifies that my-given-lib exports the required dependency. Exporting means that the reference to the required component is defined in the interface of the library. Since an header library is only made of an interface, it must export each of its dependencies.
  • the NATIVE and PACKAGE keywords are used to specify the dependency itself: my-given-lib depends on the component lib-other-sh defined in the PID package another-package. Declaring an external or system dependency or even an internal dependency is slightly different, but follows the same logic.
  • the EXPORTED_DEFINITIONS is used to specify values of C preprocessor definitions that are exported by the library. In the example the exported dedinition is USE_SPECIFIC_COMPILE_OPTION. Exported definition are used by components that will use my-given-lib to configure its code adequately.

One interesting property of PID is to be able to declare different components from the same code. For instance:

declare_PID_Component(HEADER_LIB NAME my-given-lib-bis DIRECTORY my_lib)

In this example, a new component named my-given-lib-bis is declared and created from the same source code contained in the my_lib folder. The differences with the previous component are that my-given-lib-bis has no dependencies and it does not define USE_SPECIFIC_COMPILE_OPTION. This is useful to declare many alternatives from the same code.

Static libraries

Static libraries are binary archives that provides some functionalities through an API defined by a set of headers. A static library is made of: * a set of header files that define its interface (i.e. what functionnalities is available for library users). * a set of (compiled) binary objects that implement its behaviour. Its interface is used at compile time (when its header are included) and its contained objects are linked to executables and shared libraries at link time, so they no more exist at run time. The definition of a static lib should look like:

declare_PID_Component(STATIC_LIB NAME my-static-lib DIRECTORY binary_lib
			INTERNAL DEFINITIONS A_VERY_SPECIFIC_IMPLEM
)
declare_PID_Component_Dependency(COMPONENT my-static-lib
				EXTERNAL boost INCLUDE_DIRS <boost>/include
)
declare_PID_Component_Dependency(COMPONENT my-static-lib
				EXPORT NATIVE my-given-lib-bis
)

or with short signature:

PID_Component(my-static-lib STATIC DIRECTORY binary_lib
			INTERNAL DEFINITIONS A_VERY_SPECIFIC_IMPLEM
			EXPORT boost/boost-headers my-given-lib-bis
)

Explanations:

As for any component, the first thing to do is to declare it by using declare_PID_Component (equivalent to PID_Component):

  • the STATIC_LIB (or STATIC) keyword is used to declare a static library.
  • the NAME keyword is used to define the identifier of the library, my-static-lib in the example. NAME keyword can be ommitted if the name if the first argument.
  • the DIRECTORY keyword is used to say in which sub-directory of the include folder the header files of the static library are found, in the example the binary_lib sub-folder. The same folder name is used to specify in which subdirectory of the src folder the source and non-public header files of the library are found. For a same library, this constraints the user to use same folder names between include and src directories. DIRECTORY argument can be ommitted if the name of the folder is the same as the name of the component (not the case here).
  • the INTERNAL DEFINITIONS is used to specify definitions that affect only the implementation (i.e. that is not used in any header file of the library). In the example my-static-lib defines the preprocessor definition A_VERY_SPECIFIC_IMPLEM.

As readers may notice, the declaration is quite the same as for header libraries. Note also that static libraries can dedine exported definitions (same as header libraries) for those which are used in their header files. The declaration of dependencies also follows the exact same pattern. In the example: * my-static-lib uses an external package named boost. As boost is a pure header library it only needs to specify where to find its header files, using the INCLUDE_DIRS keyword. The include path specified is relative to the boost external package root folder (using the specifier). * `my-static-lib` is also using (keyword `NATIVE`) `my-given-lib-bis` that is defined in the same package (no `PACKAGE` keyword used). It exports `my-given-lib-bis` meaning that its headers contain `#include` directive over headers of `my-given-lib-bis`.

Shared libraries

Shared libraries are binary objects that provides some functionalities through an API defined by a set of headers. A shared library is made of: * a set of header files that define its interface (i.e. what class/functions are available for library users). * a binary object (file with .so extension on linux) that implements its behaviour.

Its interface is used at compile time (when including its headers) and its binary object is checked at link time and truly used at run time, either when the dependent executable is loaded (load time) or when it explicitly loads the library at run time. The definition of a shared library is more or less the same as for static libraries and should look like:

declare_PID_Component(SHARED_LIB NAME my-shared-lib DIRECTORY binary_lib
			INTERNAL DEFINITIONS ANOTHER_SPECIFIC_IMPLEM
)
declare_PID_Component_Dependency(COMPONENT my-shared-lib
				EXTERNAL boost INCLUDE_DIRS <boost>/include
)
declare_PID_Component_Dependency(COMPONENT my-shared-lib
				EXPORT NATIVE my-given-lib
)

or with short signature:

PID_Component(my-shared-lib SHARED DIRECTORY binary_lib
			INTERNAL DEFINITIONS ANOTHER_SPECIFIC_IMPLEM
			EXPORT boost/boost-headers my-given-lib
)

Explanations:

In this example, the function declare_PID_Component (equivalent to PID_Component) is used the common way: * the SHARED_LIB (or SHARED) keyword declares the type of component as a shared library. * the NAME keyword is used to declare my-shared-lib. NAME keyword can be ommitted if the name if the first argument. * the DIRECTORY keyword defines include and src sub folders where to find code. DIRECTORY argument can be ommitted if the name of teh flder is the same as the name of the component (not the case here). * the INTERNAL DEFINITIONS is used to define the preprocessor variable ANOTHER_SPECIFIC_IMPLEM contrarily to my-static-lib.

This example shows how shared and static libraries can be built from the same source code and how developers can define alternative implementation for part of their code using preprocessor definitions. In the example reader can notice that the shared library is built from the same code as static library my-static-lib but with different compile flags (ANOTHER_SPECIFIC_IMPLEM instead of A_VERY_SPECIFIC_IMPLEM). Their dependencies can also vary depending on the way they are built: * my-shared-lib uses the Boost external package the same way as my-static-lib. * my-shared-lib uses the library my-given-lib instead of my-given-lib-bis used by my-static-lib.

Defining application components

In order to produce programs a package can also contains application components. Application components designed to be used by end-users are defined in the CMakeLists.txt contained in the apps folder of the package. Test applications are specifically used to test package libraries or applications and are placed in the test folder of the package repository.

PID defines three types of applications explained in following subsections. These definitions rely on same cmake API already presented in defining library component section.

Standard applications

By standard applications we mean applications that are intended to be used by end-users or a run-time software component that can be deployed using a specific middleware/framework. The definition of a standard application should look like:

declare_PID_Component(APPLICATION NAME my-app DIRECTORY my_app_dir
			INTERNAL INCLUDE_DIRS common_defs common_types
)
declare_PID_Component_Dependency(COMPONENT my-app
				EXPORT NATIVE lib-other-sh  PACKAGE another-package
)

or with short signature:

PID_Component(my-app APP DIRECTORY my_app_dir
			INTERNAL INCLUDE_DIRS common_defs common_types
			EXPORT another-package/lib-other-sh
)

Explanations:

As for library components, the first thing to do is to declare the application by using the macro declare_PID_Component (or PID_Component): * the APPLICATION (or APP) keyword defines the type of the component as a standard application. * the NAME keyword defines the unique identifier of the application, my-app in the example. NAME keyword can be ommitted if the name if the first argument. * the DIRECTORY specifies in which sub-directory of the app folder the source files of the application are found, in the example the my_app_dir sub-folder. DIRECTORY argument can be ommitted if the name of the folder is the same as the name of the component (not the case here). * the INTERNAL INCLUDE_DIRS specifies additional directories (sub folders of the apps folder) where to find non-public header files, in the examples folders common_defs and common_types.

Then developers can add dependencies for the application, exactly the same way as for libraries using the declare_PID_Component_Dependency (or PID_Component_Dependency) function: * the COMPONENT specifies the application that declares a dependency, my-app in the example. * NATIVE and PACKAGE keyword are used to target a specific component from another package, here the shared library lib-other-sh of the package another-package that is a native package. NATIVE keyword is optional. Dependencies management work the same way as for libraries.

Example applications

Example applications are little pieces of executable code whose only purpose is to provide to developers examples and tutorials on the way of using other components (most of time libraries) defined in the package. The definition of an example application should look like:

declare_PID_Component(EXAMPLE_APPLICATION NAME my-example DIRECTORY my_example_dir)
declare_PID_Component_Dependency(COMPONENT my-example NATIVE my-shared-lib)

or short signature:

PID_Component(my-example EXAMPLE DIRECTORY my_example_dir
							DEPEND my-shared-lib)

Explanations:

From a strict C/C++ point of view example application are just like standard applications, in other word an executable binary object. From PID point of view this is also nearly the same: * Example application are developed with same rules as standard applications except that we have to use the EXAMPLE (or EXAMPLE_APPLICATION) keyword within PID_Component (or equivalent declare_PID_Component) function. * Developers can decide (using dedicated CMake option in cache) to avoid compiling example applications since most of time they are not really useful. * Example application code may be referenced into the API documentation.

Test applications

The test folder of the package contains a CMakeLists.txt file that builds (and run) test units. The organization into subdirectories follows the same logic as for libraries and applications. The first step when playing test is to define test applications, by doing something like this in the CMakeLists.txt of test:

declare_PID_Component(TEST_APPLICATION NAME my-test DIRECTORY my_test_dir)
declare_PID_Component_Dependency(COMPONENT my-test NATIVE my-shared-lib)

or short signature:

PID_Component(my-test TEST DIRECTORY my_test_dir
							DEPEND my-shared-lib)

Explanations:

  • Test applications are developed with same rules as standard or example applications except that we have to use the TEST (or equivalent TEST_APPLICATION) keyword within PID_Component (or equivalent declare_PID_Component) function.
  • DEPEND keyword is used to say that my-test uses my-shared-lib but that not export its symbols (a test never export symbols since it has no interface).
  • Tests are specific components because they will not be installed with package version binary. They are just used to test the validity of the codes, for instance to be sure it behaves properly or respects some backward compatibility constraints.

Defining tests

PID uses the CMake basic API to provide basic testing capabilities to packages. The previous section shows my-test that is in charge of testing the library my-shared-lib. Then, in the CMakeLists.txt of the test folder, this test application can be used to launch series of tests, using standard CTest tooling integrated in CMake:

run_PID_Test (NAME correctness_of_my-shared-lib_step1 COMPONENT my-test ARGUMENTS "first" "124" "12")
run_PID_Test (NAME correctness_of_my-shared-lib_step2 COMPONENT my-test ARGUMENTS "second" "12" "46")
run_PID_Test (NAME correctness_of_my-shared-lib_step3 COMPONENT my-test ARGUMENTS "first" "0" "87")

In the previous example, one can see that the same test application my-shared-lib may be used to run series of tests, simply by changing its input parameters. Of course different test applications may be used to test same libraries if needed (for instance to discriminate unit testing, backward compatibility testing, non-regression testing, etc.). Another option is to use generic test tools (e.g. Valgrind, Purify, etc.) to check for validity of general properties (e.g. runtime memory errors, analysis of code metrics), but this is far beyond the topic of this document.

A simple and standard way to proceed is to define test applications that take different arguments: * an argument represents the tested functionality (e.g. “first” in the previous example). A functionality can be seen as a particular use of the testes library’s API in order to obtain a given behaviour. It is implemented as a particular block of code inside the test application. * one or more arguments represent input parameters (e.g. “124” in first test). * one or more arguments represent the expected output parameters (e.g. “12” in first test). * the test program (e.g. my-test) calls the adequate target functionality (e.g. “first”) of the tested library (e.g. my-shared-lib) with adequate input parameters (e.g. “124”) and check if the result is the expected one (e.g. “12”). If successful it returns 0, otherwise it returns an error code (something else than 0).

The previous code will automatically generate a sequence of tests whose result is PASSED or FAILED according to the result returned by the test program. The package cannot be installed in the wrkspace until all tests have PASSED.

Remark: If you want to use some program as test but also as an example for instance, then always declare it as an EXAMPLE_APPLICATION. Indeed, this way your test application will be installed and so will be usable by a third party user.

Generating API documentation

When a library is defined, it is intended to be used by third party developers. To this end, it is always useful to have a clear way to consult the API provided by this library. The API documentation has to be as close as possible to the source code, that is why the best way is to use an API documentation generation tool like doxygen and to automate its use directly during the build process.

PID automatically manages the generation of API documentation with doxygen. The generated documentation is installed in the binary package version folder in the share/doc/html sub-folder. If latex is installed on the building host, it is also possible to generate an equivalent pdf document that will placed in the share/doc/latex sub-folder.

The API documentation requires that the users document the header files contained in each sub-folder of include. Indeed, these headers constitue the interface of libraries defined in the package and this is the only part to document from a API user point of view. The way to document headers is defined by doxygen tooling. Generally speaking, it consists in defining comments with specific annotations in header files code. You can report to the wiki explaining how to document source code.

When developers have documented their headers, they have to do nothing more to get a standard html or pdf document automatically generated, API generation with CMake is completely managed by PID. The doxygen compiler generates a raw API documentation, that may be customize by following the next requirements:

  • add some content in the doc sub-folder of the package’s share folder. Typically a set of images can be put in a img sub-folder of the doc folder.
  • modify the doxygen configuration file (Doxyfile.in) that can be found in the doxygen sub-folder of the package’s share folder. This file is used by doxygen to know how to generate the documentation. For instance, one can modify the IMAGE_PATH contained in this file to make it reference the new img folder.
  • Then images can be referenced directly into doxygen headers comments using a specific keyword (@image).

The main constraint Configuring doxygen behaviour is far beyond the scope of this document. The only thing that is absolutely required is to let some variables of the Doxyfile.in unchanged: all variables whose value is surrounded by the @ symbol must be let unchanged. These variables are automatically fill by PID cmake scripts, for instance:

...
# The PROJECT_NAME tag is a single word
PROJECT_NAME = "@DOXYFILE_PROJECT_NAME@"
# The PROJECT_NUMBER tag can be used to enter a version.
PROJECT_NUMBER = "@DOXYFILE_PROJECT_VERSION@"
# The OUTPUT_DIRECTORY tag is used to specify the (relative or
# absolute) base path where the generated documentation will
# be put.
OUTPUT_DIRECTORY = "@DOXYFILE_OUTPUT_DIR@"
...
# If the GENERATE_HTML tag is set to YES (the default) Doxygen
# will generate HTML output.
GENERATE_HTML= @DOXYFILE_GENERATE_HTML@
# The HTML_OUTPUT tag is used to specify where the HTML docs
# will be put.
HTML_OUTPUT= "@DOXYFILE_HTML_DIR@"

When PID API generates the doxygen configuration file, it uses the Doxyfile.in pattern and automatically fills all fields surrounded by the @ symbol. Modifying these fields would provoke unexpected behaviours.

Adding package-specific content

The CMakeLists.txt of the share folder does not explicitly manage installation of the API documentation (it is automatically managed by PID). If developers add resources to the share folder like for instance images, these resources may be needed when the package binary is installed. This is the case when some components of the package require these resources at runtime. In such a case they have to be put in the share/resources folder (or any subfolder created by the user). The whole content of the share/resources folder will be automatically installed.

Nevertheless, you may need to install more resources (e.g. README files or folders containing technical documents) that are not used by components, then the CMakeLists.txt has to manage the installation of these resources, using the classical CMake install command. These resources have to be “manually” placed in the binary package’s share folder with a cmake command like:

install(DIRECTORY doc/documents DESTINATION ${${PROJECT_NAME}_INSTALL_SHARE_PATH})

This later command will install the documents folder (that is in the share/doc folder) and all its content into the adequate share folder of the installed binary package. For simple files use the install(FILE ...) command.

Package development process control

PID packages provide a set of cache variables that are usd to control the build process of the package. The configuration of these CMake cache variables is basically made using ccmake .. command in the build directory of the package or by using Cmake configuration GUI. Depending on this configuration there will build command available or not, and the whole build process will behaves differently.

All these options and related behaviors are explained here.

Understanding the result

From the complete build/install process an “installed version” of the package is generated and put into the adequate folder of the workspace. The install process is managed by PID system so developer should not worry about how it takes place. The only exception is for documents and other resources (like images) placed into the source package repository’s share folder that must be installed “by hand”.

The figure provides an example of the workspace’s install folder containing two installed packages for a given platform (x86_64_linux_stdc++11). There can be many versions of the same package installed in the workspace for teh same platform, as for the package another-package, that has two versions installed. The installers folder of this package may contains many binary package version relocatable archive (quickly called “package installers”), for instance two for each installed version. Indeed each binary version is associated with two package installers: one for Release mode and one for Debug mode.

Package installers are tar.gz archives (cmake is able to compress and extract those archives in a cross-platform way) with following pattern for their name: <package name>-<package version number>[-dbg]-<platform>.tar.gz. Package installers for Debug mode have an additional -dbg postfix notation appended to their name and version.

Each binary package version folder also have a .rpath folder which is a PID specific folder used for configuring runtime links of PID components. It contains, for each binary executable component (shared or module libraries binaries, applications binaries) of the package a set of symbolic links that points to the adequate shared libraries or executable. PID system can reconfigure “on demand” run-time dependencies depending on the shared libraries versions installed and used. This system allows to create relocatable and reconfigurable binary code without using system mechanisms.

Generally speaking users should never change the content of any of those folders “by hand”, otherwise it could cause troubles.