Since its V4 release, PID provides a way to specify requirements on the host configuration directly in packages. This is usefull to:

  • check that a language is available and check its specific features.
  • use specific compiler toolchains.
  • explicitly require the use of third party tools.

Step 1: Management of languages used in packages

1.1: Changing toolchains

To understand how to specifcy constraints on build system, let’s start with a simple example. We suppose we have a package, called test-host:

pid cd
pid create package=test-host
pid cd test-host

Now we simply want to enforce the use of a specific compiler, for instance clang with a minimal version, let’s say 3.8. Its CMake description looks like:

...
project(test-host)

PID_Package(
	...
)
check_PID_Environment(LANGUAGE CXX TOOLSET clang_toolchain[version=3.8])#need either a Fortran compiler of f2C to be able to link C code
...
build_PID_Package()

All requirements on host configuartion are performed using the check_PID_Environment function. The LANGUAGE keyword specifcies which language we want to configure, here c++ denoted by its CMake language identifier CXX. To target a language you must use a CMake standard language identifiers C, CXX, ASM, Fortran or CUDA.

The TOOLSET keyword is then used to specifcy which toolchain you want to use. In this example we use the clang_toolchain. This name must match an existing environment that defines a toolchain for the target language. Furthermore you can use constraints expressions (like for target platform checks) to specifcy more constraints on the environment. Here we say that we want the minimum version 3.8 to be used. This is possible since the clang_toolchain environment defines such constraint.

When tha package is configured, the PID system will look into current profile if this environment, with matching constraints, is defined. If not, then it will:

  • try to deploy the clang_toolchain in workspace if not already available.
  • then evaluate it to generate a configuration that matches the requirements (i.e.g a clang compiler toolchain with version 3.8 or more).

If everything works well, the package will be configured to use the clang compiler anytime the package needs to build c++ code, instead of default compiler toolchain defined in current profile.

Remarks:

  • The current profile will not be modified by this actions, so if it does not define alternative toolchains that match the constraints, the package will need to reevaluate the clang_toolchain any time the package is configured. So in the end it is a good approach to add clang_toolchain[version=3.8] to current profile “by hand” so that it will be evaluated once and for all. The package will then simply reuse this compatible host configuration without reevaluating the environment. Have a look at the tutorial on profiles management to understand how to do that.
  • As far as possible, you should avoid forcing the use of a specific toolchain. It should be reserved to the casse when you use specific compiler features that are provided only by the given toolchain.

1.2: Specifying constraints on languages

Another way to configure language is to define constraints directly on languages, using constraint expressions.

For instance, let’s suppose you wrote c++17 code inside the test-host package, so you defined components that explicitly use the C++ 17 standard (using CXX_STANDARD argument of PID_Component). But nothing ensure that the default compiler is trully capable of building c++17 code ! One way to ensure that is to enforce a constraint on host configuration:

...
project(test-host)

PID_Package(
	...
)
check_PID_Environment(LANGUAGE CXX[std=17])
...
build_PID_Package()

Again the LANGUAGE keyword is used to tell that we want to check the configruation of a specific language, here c++. But now we use a constraint expression, [std=17], to specificy that the package needs a compiler with full support to c++17 standard.

We could also want to use available processor optimizations when building c++ code, so we can write:

...
check_PID_Environment(LANGUAGE CXX[std=17:optimization=native])
...

The use of native value for optimization simply tells the system to use every possible optimization, so it is not a hard constraint. But if the code requires a specific optimization, which obviously is a very rare situation, we could have written:

...
check_PID_Environment(LANGUAGE CXX[std=17:optimization=AVX_512F,AVX512_SKX])
...

The package now explicitly requires optimizations AVX_512F and AVX512_SKX. This could be the case for instance if the code is written for a specific family of processors. Possible values of the optimization constraints are given anytime the workspace is configured:

pid cd
pid configure

Have a look at the line processor family:

...
[PID] INFO : Target platform in use is x86_64_linux_stdc++11:
 + processor family = x86 (optimizations: SSE, SSE2, SSE3, SSSE3, SSE4_1, POPCNT, SSE4_2, FP16, FMA3, AVX, AVX2, AVX_512F, AVX512_SKX)
...

Remark:

  • At the time this documentation is written, C and CXX both support optimization and std constraints. std supports different set of values for C and CXX (depending on the available standards for the two languages) while optimization support same set of values for both.
  • To know possible constraints implemented for each language use the info workspace command:
pid cd
pid info language=CXX

Output looks like:

CXX available, possible CONSTRAINTS:
+ soname
+ symbol
+ optimization
+ std

Step 2: Management of specific tools

Now let’s reuse the description of the eigen-qp package. You can deploy this package this way:

pid cd
pid deploy package=eigen-qp

Its main CMakeLists.txt looks like:

...
project(eigen-qp)

PID_Package(
	...
		)
...

check_PID_Environment(OPTIONAL LANGUAGE Fortran)#need either a Fortran compiler of f2C to be able to link C code
if(NOT Fortran_ENVIRONMENT)#none available
	check_PID_Environment(TOOL f2c)#if no fortran language available then need a f2c generator
endif()

PID_Dependency(eigen)
if(BUILD_AND_RUN_TESTS)
	PID_Dependency(boost)
endif()

build_PID_Package()

The important part here is the use of the check_PID_Environment function.

We first specify that the package uses the Fortran language by default:

check_PID_Environment(OPTIONAL LANGUAGE Fortran)

We need to use such expression simply because the eigen-qp package contains Fortran source code.

The call is straightforward: the LANGUAGE keyword is used to tell that we want to check a language and OPTIONAL is used to avoid to generate an error if Fortran is not available for target platform. The call generates the variable Fortran_ENVIRONMENT that is TRUE if Fortran language has been found and FALSE otherwise.

In case it is not available, we want to use an alternative: generate C code using the f2c tool and then compile it. We so need to check that the extra too f2c is available:

if(NOT Fortran_ENVIRONMENT)#none available
	check_PID_Environment(TOOL f2c)#if no fortran language available then need a f2c generator
endif()

Here we use the TOOL keyword to specify that we want to use a tool (a code generator) that is not natively supported by CMake. OPTIONAL is not used so if f2c is not usable on host platform then an error is generated and the package configuration stops. This is normal since we have no other alternative.

Everything in this situation is automatic: either the Fortran compiler is directly used to generate binaries or f2c generates C code and then this code is compiled to generate binaries. In the end, the description of components does not change whatever the compilation process is, for instance:

PID_Component(eigen-quadprog SHARED CXX_STANDARD 11
  DIRECTORY eigen_quadprog
  INTERNAL COMPILER_OPTIONS -Wall -Wextra -pedantic
           DEFINITIONS EIGEN_QUADPROG_EXPORT
  EXPORT eigen/eigen)

As you can see there is no specific CMake code to deal with the two alternatives. This is mainly due to the fact that the f2c plugin behavior, when used, automatically adds a dependency to the f2c library. To understand how these plugin behavior can be written you can refer to this tutorial.