Copyright 2021 Huawei Technologies Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
- Introduction to ALP/GraphBLAS Building and Testing Infrastructure:
- The Building Infrastructure
- Test Categories and modes
- Reproducible Builds
- The coverage infrastructure
The current building infrastructure is based on CMake and on what is commonly defined "Modern CMake". If you are new to this technology, here are some useful materials:
- CMake official tutorial
- an introduction do Modern CMake like Effective Modern CMake
- control structures and in particular basic expressions
- scoping rules
- properties of targets
The current testing infrastructure is composed of several scripts invoking the test binaries. These scripts can be invoked either manually or via the building infrastructure.
In the following, the main steps to use the building and testing infrastructure are discussed, together with more advanced topics.
To create the building infrastructure, cmake version 3.13 or higher is
required (https://cmake.org/download/), together with GNU make (or ninja).
Not that ALP/GraphBLAS supports only Linux and there is no current plan to
support other operating systems.
This section details various aspects of the building infrastructure, from its generation to its expansion with more tests or more backends.
In general, the building infrastructure is designed to generate multiple ALP/GraphBLAS backends at once, according to the passed configuration options. Some backends have their own corresponding binary library (like the bsp1d and the hybrid backend), while other backends may be grouped into the same library; for example, the reference and reference_omp backends live together in the so-called shared memory backend library. The building infrastructure allows users to select which backends are to be built together with the relevant build options (dependencies, additional compilation/optimization flags, ...).
There are two ways to create the building infrastructure, depending on the level of control you want over the build options.
The easiest way to initialize the building infrastructure is via the
bootstrap.sh script in the project root. This script allows for the convenient
setting of common build options for end-users of ALP/GraphBLAS. To invoke it,
create an empty directory for the build, move into it, and then invoke the
bootstrap.sh script from there. For example:
cd <ALP/GraphBLAS root>
mkdir build
cd build
../bootstrap.sh --prefix=../install <other options>The bootstrap.sh script should be invoked from an empty directory; if the
directory is not empty, it asks to delete its contents. Invoking it from the
ALP/GraphBLAS source directory is disallowed. The script accepts the following
arguments:
--prefix=<path/to/install/directory/>(mandatory) specifies the directory to install ALP/GraphBLAS consumable targets (binaries, headers, wrapper scripts and configuration files for consumption via CMake); if the directory does not exist it is built at the moment, but its parent directory must exist--with-lpf[=<path/>]enables LPF and passes its installation directory; if only--with-lpfis given without the=<path/>information, LPF binaries are assumed to be available in the standard search paths and automatically read from there (e.g., viacommand -v lpfrun)--with-banshee=<path/>to pass the tools to compile the banshee backend (required together with--with-snitch=<path/>)--with-snitch=<path/>to pass the tools for Snitch support for the banshee backend (required together with--with-banshee=<path/>)--no-referencedisables the reference and reference_omp backends--debug-buildbuild ALP/GraphBLAS with debug-suitable options, both backends and tests; note that this causes tests to run much slower than with standard options (corresponding to CMake'sReleasebuild type)--generator=<value>sets the generator for CMake, otherwise CMake's default is used; example values areUnix Makefiles(usually CMake's default on UNIX systems) andNinja-- for more information, see the official documentation--showshows generation commands instead of running them; useful for dry runs of the script--delete-filesdeletes all files in the current directory without asking for confirmation; it is iseful, for example, for scripted builds--with-datasets=<path/>allows passing the path to the directory with the datasets required to run some tests (otherwise skipped)--spblas-prefix=<prefix>to indicate a custom prefix for the SpBLAS API and library; symbols will be named as in the following example: "<prefix>dcsrgemv" for double-precision sparse matrix--vector multiplication using the standard CRS. The corresponding libraries will be named "<prefix>sequential" and "<prefix>shmem_parallel". The correspondingmaketargets will be called "<prefix>_sequential" and "<prefix>_shmem_parallel"--no-solver-libto disable generating solver library compiled against the reference and nonblocking backends--enable-extra-solver-libto enable the solver library compiled against the reference OMP backend--helpshows all available options and skips directory checks.
For a dry run, just add the --show option to inspect the building command on
the terminal.
If you want more control over the building options, you may want to invoke
cmake manually and choose each option.
This allows choosing the build type (e.g., Release or Debug), which
backends and dependencies are enabled and many more aspects.
Since CMake encourages out-of-tree builds, you should first create a dedicated
build directory, from which you may run cmake.
This way you can experiment with multiple building options and have separate
build-trees, where in case of changes you need to recompile only what is really
needed: for example, you may have a directory build_release to compile with
release-suitable options (cmake flag -DCMAKE_BUILD_TYPE=Release) and another
called build_debug to compile with debug-suitable options (cmake flag
-DCMAKE_BUILD_TYPE=Debug); if you change a single file, you must recompile
only that one inside the directory corresponding to the build type you want to
test (e.g., build_release if you want to assess the performance or
build_debug to run with a debugger).
As from above, a convenient way to start even for a custom build is from the
bootstrap.sh script, which can be invoked with the --show option to inspect
the building command and start from there with the custom options.
For example:
mkdir build_release
cd build_release
cmake -DCMAKE_INSTALL_PREFIX=/path/to/install/dir -DCMAKE_BUILD_TYPE=Release \
-DLPF_INSTALL_PATH='/path/to/lpf/install' <other options prefixed with -D ...> \
../
make -j$(nproc) unittestsThe following section describes all available options.
Currently, the CMake infrastructure supports several options, grouped in the following according to their scope.
To control the backends to build, the following options are available:
WITH_REFERENCE_BACKENDto build the reference backend (default:ON)WITH_OMP_BACKENDto build the OMP backend (default:ON)WITH_NUMAto enable NUMA support (default:ON)LPF_INSTALL_PATHpath to the LPF tools for the bsp1d and hybrid backends (default:OFF, no LPF backend)WITH_BSP1D_BACKENDbuild the bsp1d backend (needsLPF_INSTALL_PATHset, otherwiseOFF)WITH_HYBRID_BACKENDbuild the Hybrid backend (needsLPF_INSTALL_PATHset, otherwiseOFF)WITH_NONBLOCKING_BACKENDbuild the non-blocking backend (default:ON)
When choosing, keep in mind that several constraints apply:
- the bsp1d and and hybrid backends both need LPF and NUMA support
- the hybrid backend needs the OMP backend
- the bsp1d backend requires either the reference or the OMP backend
Passing incompatible options will cause error messages and the build to stop.
The ALP/GraphBLAS building infrastructure currently supports Release,
Debug and Coverage builds, on which several compilation flags depend that
are defined by default in the main CMakeLists.txt file
inside the section SETTINGS FOR COMPILATION.
The build type can be chosen via the standard
CMAKE_BUILD_TYPE
option; if none is passed, Release is automatically set.
In particular, Release optimizes all targets aggressively and disables non-
mandatory sanity and run-time checks, which are enabled in the Debug build
and can hence result in much slower code.
Finally, the Coverage build type instruments backend and test binaries to
extract coverage information after running them and, to this aim, may disable
certain performance optimizations; for more information, see the
dedicated section.
The following options control compile definitions and options for backends and tests and are by default empty:
COMMON_COMPILE_DEFINITIONScompile definitions common to both backends and tests; setting this option overrides the default definitionsCOMMON_COMPILE_OPTIONScompile options common to both backends and tests; setting this option overrides the default optionsADDITIONAL_BACKEND_DEFINITIONScompiler definitions only for backends that are appended to default definitions, or appended to common definitions if the defaults were overriddenADDITIONAL_BACKEND_OPTIONScompiler options only for backends that are appended to default options, or appended to common options if the defaults were overriddenADDITIONAL_TEST_DEFINITIONScompiler definitions only for tests that are appended to default definitions, or to common definitions if the default were overriddenADDITIONAL_TEST_OPTIONScompiler options applicable only to tests. These options are appended to the default options, or appended to common options if the defaults were overriddenTEST_PERFORMANCE_DEFINITIONScompiler definitions for performance, applicable only to tests that do not explicitly disable performance flags. Setting this option overrides the default performance definitions, leaving any other definitions intactTEST_PERFORMANCE_OPTIONScompiler options for performance applicable only to tests that do not explicitly disable performance flags. Setting this option overrides the default performance options, leaving any other options intact
Since ALP/GraphBLAS is mostly template-based, most of the code being compiled
for a test belongs to the executable itself, rather than to the backend library
the test is linked against.
This motivates the presence of performance flags for performance tests as well as
for excessively slow unit and smoke tests. The backend libraries also use
performance flags, though most crucially, the end-user should take care to
compile ALP/GraphBLAS programs or libraries with similar performance flags-- in
particular, the -DNDEBUG flag should not be forgotten.
In case a user wants to control performance optimizations for both tests and
backends explicitly, she may set the COMMON_COMPILE_{DEFINITIONS|OPTIONS}
options to override the global flags and the ADDITIONAL_BACKEND_{DEFINITIONS|OPTIONS}
to append flags.
For example, one may add the _FORTIFY_SOURCE=1 definition for backends only,
e.g. for production purposes, via
cmake -DADDITIONAL_BACKEND_DEFINITIONS="_FORTIFY_SOURCE=1" <other flags as usual> ../or similarly for tests by adding the flag
-DADDITIONAL_TEST_DEFINITIONS="_FORTIFY_SOURCE=1" to the same command.
During the CMake configuration step a report summarizes all the flags for the
various kinds of targets and for each test category.
To manually tweak the various compilation flags, one can edit the
CMakeCache.txt file, where the related variables are stored for backends and
for the various categories of tests.
Then a cmake . run regenerates the compilation infrastructure and the flags
report should correctly reflect the new compilation settings.
Furthermore, one can use
CMake Environment Variables for Languages (like
CXXFLAGS) to
"inject" flags from the command line that are directly applied; the only caveat
here is that these flags may conflict with the default ones or be ignored
depending on the order they are passed to the compiler; hence, this apprach is
discouraged.
For a deep inspection of all the commands being run, the user can build the
desired target(s) via make and pass the VERBOSE=3 option, for example
make test_matrixIterator_bsp1d VERBOSE=3Finally, another set of options store the directories with test inputs:
DATASETS_DIRDirectory with datasets for tests, used for most tests requiring an input (default:<ALP/GraphBLAS root>/datasets)GNN_DATASET_PATHdirectory with the GNN dataset, for the tests requiring it
The names of targets defined inside the infrastructure follow these conventions:
- targets needed (directly or indirectly) to build backends start with
backend_, in particular:- targets referring to headers include paths (in
<ALP/GraphBLAS root>/include/CMakeLists.txt) end
with
_headers, following the patternbackend_<backend>_headers - targets to build whole backends are named
backend_<backend>_<static|shared>, for examplebackend_reference_static - targets for backends to be linked to tests are named
backend_<backend>and are usually an alias of a corresponding targetbackend_<backend>_<static|shared>; this naming scheme should not be changed, otherwise tests would not link to backends anymore and errors would occur
- targets referring to headers include paths (in
<ALP/GraphBLAS root>/include/CMakeLists.txt) end
with
- targets to build and run single tests start with
test_, then have the test name, the category name, the mode name (if any) and end with the backend name, following the patterntest_<test name>_<category>_<mode>_<backend>, such astest_hpcg_unit_ndebug_reference_omp; note that the actual binaries do not have thetest_prefix and the category name the for simplicity; for more information about categories and modes, you may read the Testing Infrastructure section - targets to build and run a group of tests start with the prefix
tests_, for exampletests_<category>build and runs the tests for the given category (unit,knn,pagerank, ...)- some target names referring to the same per-category tests are kept for
backward compatibility with the previous infrastructure, such as
unittests,smoketests,test, ...; these targets are listed in the main documentation
- targets to just build (without running) groups of tests (for example to test
the building infrastructure) start with
build_, for example:build_tests_category_<category>builds all test of the given categorybuild_tests_backend_<backend>builds all test of the given categorybuild_tests_allbuilds all tests
- certain targets that just list existing tests for each category start with
list, for example:list_tests_category_<category>for the tests of a given categorylist_tests_backend_<backend>for the tests of a given backendlist_tests_categoriesto list the actual test categorieslist_tests_allto list all tests
Other targets:
libsbuild the binary libraries for all backendsdocsbuilds all HTML ALP/GraphBLAS documentation in<ALP/GraphBLAS root>/docs/code/html/index.htmland the LaTeX source files in<ALP/GraphBLAS root>/docs/code/latex; ifpdflatex,graphviz, and other standard tools are available, they are compiled into a PDF found at<ALP/GraphBLAS root>/docs/code/latex/refman.pdf.
Test sources are split in categories, whose purpose is explained in the Testing
Infrastructure section.
For each category, the tests are compiled and run according to the file
tests/<category>/CMakeLists.txt, for example
tests/unit/CMakeLists.txt.
Adding a test thus requires adding the relevant command to the CMakeLists.txt
file that corresponding to the test's category, for example
add_grb_executables( my_test
my_test_source_1.cpp
my_test_source_2.hpp
my_test_source_3.hpp
BACKENDS reference reference_omp bsp1d hybrid
ADDITIONAL_LINK_LIBRARIES test_utils
COMPILE_DEFINITIONS MY_TEST_KEY=VALUE ANOTHER_TEST_DEFINITION
)In this example:
add_grb_executablesis a command similar in spirit to CMake'sadd_executable, which adds multiple tests, one per backendmy_testis the test base namemy_test_source_1.cpp,my_test_source_2.cppandmy_test_source_3.cppare the test source files (at least one is required)BACKENDS reference reference_omp bsp1d hybridis the list of all backends the test should be compiled against (at least one is required); for each backend, an executable target is created following the naming conventions in Naming conventions for targetsADDITIONAL_LINK_LIBRARIES test_utils(optional) lists additional libraries to link (the backend library is linked by default)COMPILE_DEFINITIONS MY_TEST_KEY=VALUE ANOTHER_TEST_DEFINITION(optional) lists additional compile definitions (corresponding to, e.g., gcc's definitions-DMY_TEST_KEY=VALUE -DANOTHER_TEST_DEFINITION)
More options are available for the function add_grb_executables, which are
documented in cmake/AddGRBTests.cmake.
Tests may have different categories; presently, one of:
- unit,
- performance,
- smoke.
All tests belonging to each category are run via the related script in the
project root: unit tests are run via the script unittests.sh, while the
performance classes are run as part of the perftests.sh script.
When a new test is added, its invocation(s) must be manually added to the
relevant script.
Each script is sub-divided in several sections depending on the backend that is
assumed to run and on relevant options: hence, you should place your test
invocation in the relevant section.
Furthermore, you can achieve more control over the test target generation, i.e.,
the building of tests, by using the function add_grb_executable_custom, also
defined in cmake/AddGRBTests.cmake, which requires
to specify dependencies manually (thus, building against multiple backends needs
correspondingly multiple calls of the same function) and is therefore used only
in special cases.
Adding a new backend requires multiple changes to the building infrastructure, which are discussed here. For the sake of this example, we assume that the new backend:
- is named
example - has several headers stored inside
<ALP/GraphBLAS root>/include/graphblas/example - has several implementation files stored inside
<ALP/GraphBLAS root>/src/graphblas/example - should produce a separate static library, to be linked to each test/application
Hence, the steps are as follows.
The new backend may need some configuration options from the user, to be added
at the beginning of
the main configuration file <ALP/GraphBLAS root>/CMakeLists.txt
with the proper validation steps; for example, you may add an option to enable
it
option( WITH_EXAMPLE_BACKEND "Enable building New Backend" OFF )Similarly, you should also add the relevant logic to check for dependencies (if any) and for possible interactions with other backends and compile options/definitions. Some examples of this logic already exist in the root CMakeLists.txt. For example, the following code snippet
if( NOT LPF_INSTALL_PATH AND
(WITH_BSP1D_BACKEND OR WITH_HYBRID_BACKEND) )
message( SEND_ERROR "The BSP1D and Hybrid backends require LPF" )
message( SEND_ERROR "Hence, you should set LPF_INSTALL_PATH" )
message( FATAL_ERROR "or not enable WITH_BSP1D_BACKEND or WITH_HYBRID_BACKEND")
endif()checks that the install path of LPF is given if the bsp1d or hybrid backends are
enabled, because LPF is needed to build either of them.
Another example is finding the dependencies specific to your backend, which is
usually done via CMake's find_package command.
For example, the following logic
if( WITH_BSP1D_BACKEND OR WITH_HYBRID_BACKEND )
find_package( MPI REQUIRED )
find_package( LPF REQUIRED )
endif( )checks whether MPI and LPF are installed in the system, since they are both
required for either the bsp1d and the hybrid backend; if not, the REQUIRED
keyword instructs CMake to immediately halt and emit an error message.
In the
Official documentation
you can find a list of pre-defined modules to look for dependencies, or you can
easily write your own called Find<DependencyName>.cmake and add it to the
cmake directory in the root; you may invoke it as
if( WITH_EXAMPLE_BACKEND )
find_package( <DependencyName> REQUIRED )
endif()For an overview of importing dependencies you may refer to the official Using Dependencies Guide. For an example on how to write a custom module for a dependency, you may check the internal FindNuma module.
The file
<ALP/GraphBLAS root>/cmake/AddGRBVars.cmake
stores the relevant variables for all the main steps of the compilation, like
default names of backend targets, compilation and linking options and so on.
You should add the new ones for the new backend.
Examples of needed variables:
-
a variable storing the default name of the backend target for tests, according to the conventions explained in Naming conventions for targets, for example
set( EXAMPLE_BACKEND_DEFAULT_NAME "backend_example" )
-
a variable storing the compilation definitions to include the relevant headers, for example
set( EXAMPLE_INCLUDE_DEFS "_GRB_WITH_EXAMPLE" )
-
a variable storing the compilation definitions to select the example backend by default when compiling executables, for example
set( EXAMPLE_SELECTION_DEFS "_GRB_BACKEND=example" )
-
the variable
ALL_BACKENDSlists all possible backends (even if not enabled) to detect potential configuration errors: therefore, you should always add the new backend to this variable; on the contrary, the variableAVAILABLE_BACKENDSlists only the backends actually available in the building infrastructure, depending on the user's inputs; you may add your backend with something likeif ( WITH_EXAMPLE_BACKEND ) list( APPEND AVAILABLE_BACKENDS "example" ) endif()
-
the variable
AVAILABLE_TEST_BACKENDSlists all backends that were enabled and for which tests are built; usually it is a subset ofAVAILABLE_BACKENDS, which also contains backends pulled in as dependencies of user-chosen backends; for example, if the user enables only the hyperdags backend, the reference backend is also listed inAVAILABLE_BACKENDS, whileAVAILABLE_TEST_BACKENDSlists only hyperdags.
For more details, you may see inside
<ALP/GraphBLAS root>/cmake/AddGRBVars.cmake how
existing backends populate these variables.
The file
<ALP/GraphBLAS root>/cmake/AddGRBInstall.cmake
stores variables to generate the wrapper scripts for the usage of ALP/GraphBLAS
from external projects, explained in
ALP/GraphBLAS Wrapper Scripts.
The first variable of interest is the install location for the binary file, which may be set via a variable like
set( EXAMPLE_BACKEND_INSTALL_DIR "${BINARY_LIBRARIES_INSTALL_DIR}/example" )used in the following steps. The same binary file may implement multiple backends. For example, both the reference and the OMP backend share the same binary file, i.e., the one generated for shared memory backends.
For convenience, the macro addBackendWrapperGenOptions is provided to
automatically generate the necessary variables according to the internal naming
conventions.
You should invoke it by listing the relevant options for compilation and
linking, after the backend name; for example
if( WITH_EXAMPLE_BACKEND )
addBackendWrapperGenOptions( "example"
COMPILE_DEFINITIONS "${EXAMPLE_INCLUDE_DEFS}" "${EXAMPLE_SELECTION_DEFS}"
COMPILE_OPTIONS "-Wall" "-Wextra"
LINK_FLAGS "${EXAMPLE_BACKEND_INSTALL_DIR}/lib${BACKEND_LIBRARY_OUTPUT_NAME}.a"
)
endif()Note that some compilation definitions and options are already applied to all
backends, as listed in COMMON_WRAPPER_DEFINITIONS and COMMON_WRAPPER_OPTIONS.
For more options, you may read the documentation of the
addBackendWrapperGenOptions macro, in the same file.
Since it is practically impossible to automatically get all compilation options
and all linking dependencies from a CMake target, you should carefully add all
relevant information for the building and linking processes, pretty much as if
you were manually invoking the compiler from the command line in order to
compile an application depending on your backend.
Similarly, you should add all relevant options for the runner (e.g., the LPF
runner for distributed targets) and its environment.
For more examples, you may inspect the usages of the
addBackendWrapperGenOptions macro inside
<ALP/GraphBLAS root>/cmake/AddGRBInstall.cmake.
As a final validation step, you may check the content of the wrapper scripts
described in
ALP/GraphBLAS Wrapper Scripts
and even try it against a simple application (like a test).
Add a target listing the headers of your new backend to the
<ALP/GraphBLAS root>/include/CMakeLists.txt file,
usually enclosed in
if( WITH_EXAMPLE_BACKEND )
...
endif()This usually requires:
-
creating an
INTERFACElibrary, for exampleadd_library( backend_example_headers INTERFACE )
-
adding a dependency on
backend_headers_nodefsto add the global include path (<ALP/GraphBLAS root>/include) all source files assumetarget_link_libraries( backend_example_headers INTERFACE backend_headers_nodefs )
-
adding other dependencies (if any) as
INTERFACEtarget_link_libraries( backend_example_headers INTERFACE <cmake targets example headers depend on> )
-
adding relevant compile definitions/options (if any)
target_compile_definitions( backend_example_headers INTERFACE "KEY=VALUE" "SYMBOL_TO_BE_DEFINED" ) target_compile_options( backend_example_headers "-Wall" "-Wextra" ) # if you want very verbose warnings
please, note that these settings will propagate to all targets depending on
backend_example_headers, so you should add only what is really needed -
adding the installation options
install( DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/graphblas/example" # path with the headers DESTINATION "${GRB_INCLUDE_INSTALL_DIR}" # installation destination: you should leave the default FILES_MATCHING REGEX "${HEADERS_REGEX}" # regex to install headers only (in case "${CMAKE_CURRENT_SOURCE_DIR}/graphblas/example" contains other files) ) install( TARGETS backend_example_headers EXPORT GraphBLASTargets )
to copy the header files (first call) and to add the
backend_example_headerstarget to the configuration file during installation (so that users can consume the backends directly from their CMake infrastructure), while the second call exports the newly created target to the CMake infrastructure automatically generated inside the installation directory
To actually build the library, you should
-
create a new
CMakeLists.txtfile inside<ALP/GraphBLAS root>/src/graphblas/example, where you write the compilation instruction for the binary library of the new backend -
include it hierarchically from
<ALP/GraphBLAS root>/src/graphblas/CMakeLists.txtby adding the sub-directory at the endif( WITH_EXAMPLE_BACKEND ) add_subdirectory( example ) endif()
-
inside
<ALP/GraphBLAS root>/src/graphblas/example/CMakeLists.txt, create the library targetadd_library( backend_example_static STATIC <sources of new backend> )
note that this creates a static library, while you may want to also create a shared library with the
SHAREDkeyword -
link the new target against all needed targets, in particular the related backend headers and the
backend_flagstargettarget_link_libraries( backend_example_static PRIVATE backend_flags PUBLIC backend_example_headers PRIVATE <other dependencies, e.g. Numa::Numa> )
here, it is important to note that:
- the
backend_flagstarget, which stores the default flags for all backends and in particular the optimization flags, is linked asPRIVATEin order to keep its flags only local tobackend_example_staticand not propagate them to depending targets (which have their own flags) backend_example_headersis linked asPUBLICto expose the include paths and the related definitions also to depending targets- the other dependencies are here all linked as
PRIVATEfor the sake of example, the actual visibility though depends on the implementation of the backend and should be evaluated for each dependency
- the
-
add the relevant compile definitions to select the new backend by default when compiling depending targets (like tests), for example
target_compile_definitions( backend_example_static PUBLIC "${EXAMPLE_BACKEND_SELECTION_DEFS}" )
where
EXAMPLE_BACKEND_SELECTION_DEFSis a variable defined in step 2 inside<ALP/GraphBLAS root>/cmake/AddGRBVars.cmake, usually something like"_GRB_BACKEND=example"; here, thePUBLICkeyword causes the definitions to be used for both the backend library and the depending targets -
add the needed definitions and compile options (if any), with the usual
-
target_compile_definitions( backend_example_static ...) -
target_compile_options( backend_example_static ... ) -
similarly, you may want to set specific target properties, like the binary build path, with
set_target_properties( backend_example_static PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example_output_dir" )
-
-
add the new library to the
libstarget, which allows users to compile all backend libraries at onceadd_dependencies( libs backend_example_static ) -
add the installation options
install( TARGETS backend_example_static EXPORT GraphBLASTargets ARCHIVE DESTINATION "${EXAMPLE_BACKEND_INSTALL_DIR}" )
which exports the binary target to the generated CMake infrastructure in the installation directory and the binary library to the directory
${EXAMPLE_BACKEND_INSTALL_DIR}defined previously -
if the target name
backend_example_staticis different from the corresponding default one specified during step 2 inside<ALP/GraphBLAS root>/cmake/AddGRBVars.cmake, you should create anALIAStarget for the default backend target to existadd_library( "${EXAMPLE_BACKEND_DEFAULT_NAME}" ALIAS backend_example_static )
where
EXAMPLE_BACKEND_DEFAULT_NAMEis the variable defined in step 2 that stores the default name according to the conventionbackend_<backend name>, for exampleset( EXAMPLE_BACKEND_DEFAULT_NAME backend_example )(for more information, you may refer to the targets naming conventions); note that a separateALIAStarget also allows easily switching between static and shared libraries for the default target, e.g. if the user may want to control this from the initial configuration (e.g., by adding a hypothetical configuration option-DLINK_SHARED_LIBRARIES_BY_DEFAULT=ONto the initial cmake invocation).
Since not all tests may run against all backends (for example because of
functionalities still under development), you should add the example option to
each test you want to compile against.
Tests are listed in the CMakeLists.txt files under
<ALP/GraphBLAS root>/tests/ according to their category.
For example, to compile the existing argmax test (defined in
tests/unit/CMakeLists.txt) also against the new
backend, you may add example to its list of compatible backends as in the
following
add_grb_executables( argmax argmax.cpp
BACKENDS reference reference_omp bsp1d hybrid example
)From here, the function add_grb_executables (defined in
cmake/AddGRBTests.cmake):
- checks that a backend named
exampleis present inALL_BACKENDS(as from step 2.); if not, an error is raised - check whether it is actually enabled, i.e. it it inside
AVAILABLE_BACKENDS(as from step 2.); if not, no binary is built for this test and backend - checks that a target
backend_exampleexists; if not, no default target associated to theexamplebackend exists and an error is raised - creates an executable target linked against
backend_exampleand populates the related variables (list of per-category tests and so on); the target is namedtest_argmax_[<test mode>_]exampleaccording to the conventions - on compilation, a binary named
argmax_exampleis generated in<build directory>/tests/unit
Similarly, you may want to add an example built against your test to showcase your new backend's nitty-gritties. This is done by adding an executable target inside examples/CMakeLists.txt, for example
if( WITH_EXAMPLE_BACKEND )
add_executable( sp_example sp.cpp )
target_link_libraries( sp_example backend_example )
add_dependencies( examples sp_example )
endif()Tests are grouped in categories according to what they test:
- unit tests assess the functionalities of ALP/GraphBLAS and aim at broad code coverage by testing that the code is compliant to the specification both with the most common execution conditions and with corner cases
- performance tests have as a primary aim to check that new commits do not introduce performance bugs. Secondarily and ideally, performance tests also can be used to generate results that go into publications, to validate published results are correct (for external parties), and/or to easily repeat past experiments on new platforms.
- smoke tests aim to test typical usage of the library, and focus on the "happy path" only. In ALP/GraphBLAS, this includes aiming for more "advanced" usages such as integration with MPI, or manual launching where multiple OS processes are combined ad hoc to co-execute an ALP program. These tests could also be used to very quickly verify that changes are sane-- e.g., after making changes to a backend, run the smoke tests, and only if those are OK, push them for the CI to run the full unit test suite.
The test categories are defined in the CMake variable TEST_CATEGORIES inside
the root CMakeLists.txt: each test must belong to one or
more of the categories listed there; in case of unknown category specified with
a test (see Adding a new test), an error is raised.
Categories requiring a test to be compiled in multiple ways may define so-called modes, i.e. set of naming conventions for the test's target name and the corresponding executable and specific compilation flags. The various compilation flags and modes are defined and documented in the CompileFlags.cmake file. During configuration this file generates a report with all compilation flags for the various target types and categories/modes.
To ease building and deploying ALP/GraphBLAS, dedicated Docker images can be
built from the Dockerfiles in the ALP/ReproducibleBuild repository
https://github.com/Algebraic-Programming/ReproducibleBuilds
The images built from these files represent the standard build environment used by most ALP/GraphBLAS developers and contain all necessary dependencies to build all ALP/GraphBLAS backends and tests, including the necessary input datasets. You may refer to the README for more information about the content and how to build the images.
The same images can be used for a Continuous Integration (CI) setup, as they
provide all needed dependencies and tools.
Indeed, the file .gitlab-ci.yml describes the CI jobs
that internally test ALP/GraphBLAS via GitLab,
which is available open source.
The here described image and CI pipeline are confirmed to work with x86 runners with 24 virtual CPU cores and 32GB RAM.
The Coverage build type stores coverage information into machine- or human-readable files after running one or more test binaries. These files can be directly read by users or can be consumed by tools to display the coverage information in a user-friendly interface, possibly integrated within a CI/CD infrastructure (e.g., GitHub or GitLab).
The coverage infrastructure prescribes additional dependencies:
- gcov to instrument the binary
during compilation; it usually comes together with a GNU C/C++ compiler, in
form of a compiler-specific library (
libgcov.a) and a command-line tool (e.g.,gcov-9forgcc-9/g++-9) - gcovr, a Python3 tool that translates gcov
traces to multiple formats; it can be installed via
pipaspython3 -m pip install gcovr(on some distributions also via the package manager, e.g.,apt-get install gcovr-- though the first method is preferable as it provides a more up-to-date version) and clearly requires - Python3, available in most Linux distributions
(e.g.,
apt-get install python3) or as pre-built binary for many OSs and architectures (e.g., https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64_v4-unknown-linux-gnu-install_only.tar.gz)
Note that because of the gcov dependency only GCC is currently supported as a compiler. Also note that the coverage infrastructure is implemented only in the CMake infrastructure.
The Coverage build mode can be enabled in two ways:
- if you use the
bootstrap.shscript to generate the build infrastructure, you can add the--coverage-buildoption on invocation - if you directly use CMake, with
-DCMAKE_BUILD_MODE=Coverage
By doing so:
- all binary targets are compiled with the -fprofile-arcs and -ftest-coverage
flags to enable tracing: these cause the creation of
.gcnoand.gcdafiles in the directory each binary runs in that store exection traces - performance optimizations for all targets are much more restrictive than for
other build types, preventing any aggressive optimization (
-O1) and especially inlining in order to gather accurate coverage information - because of these restrictions, multiple modes for a test category are not useful anymore (the flags are the same, and numerical precision is not being tested), so only one mode is enabled: for example, only the ndebug mode is enabled for unit tests
- a new folder is created inside the build directory named
coverage, where coverage reports are stored
Note that, because of very limited performance optimizations, tests may run much slower than in release.
Selecting the coverage build type adds several make targets to produce coverage
information; these are:
coverage_json: generates coverage/coverage.jsoncoverage_cobertura: generates coverage/coverage.xmlcoverage_csv: generates coverage/coverage.csvcoverage_coveralls: generates coverage/coveralls.jsoncoverage_html: generates coverage/index.html
These targets correspond to the output formats gcovr can generate.
These commands will use any .gcno and .gcda files generated during
execution(s) of any program/test.
To clean a coverage report and all coverage information generated during the
execution of the binaries, you can use the make coverage_clean command; to
clean only the generated report, simply clean the content of the coverage
folder.
Hence, a typical workflow to extract coverage information is:
- configure with coverage build type, e.g.:
mkdir build cd build ../bootstrap.sh --prefix=./install --coverage-build - build and run one or more tests as usual, e.g.:
make unittests -j$(nproc) - parse coverage information to produce a report, e.g., a human-readable HTML
report
make coverage_html
- read the HTML report with, e.g., a browser:
xdg-open coverage/index.html
- clean coverage information and report:
make coverage_clean