Tutorial - Defining requirements on host configuration
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 addclang_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
andCXX
both supportoptimization
andstd
constraints.std
supports different set of values forC
andCXX
(depending on the available standards for the two languages) whileoptimization
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.