Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
sisteczko committed Mar 29, 2019
1 parent 2ce1093 commit a9b68f0
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 6 deletions.
3 changes: 3 additions & 0 deletions docs/about.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Overview
=========

Beetroot project is a set of CMake macros that set a new, user friendly paradigm of writing CMake code. It targets medium to large CMake deployments and helps by faciliating modularization and parametrization of the project description.

The idea was started in 2017, and was initially motivated by a) the fact, that usually CMake scripts rely heavily on global variables and b) CMake has very poor function parameter handling. This led to an effort to define a set of macros that facilate checking of types of function arguments. After several iterations and complete redesigning, the current shape Beetroot was formed.
Expand Down
6 changes: 3 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
:scale: 50 %
:align: right

Welcome to The Beetroot Project's documentation!
================================================
The Beetroot Project's documentation
======================================

.. toctree::
:maxdepth: 2
:caption: Contents:

about
features
start
features


Indices and tables
Expand Down
7 changes: 4 additions & 3 deletions docs/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ All those functions are parametrized by the three categories of variables, which

If you find yourself defining a large list of `TARGET_PARAMETERS` over and over again for different targets (for instance to pass targets from a library to a consumer), you can spare yourself a time and call a function `include_target_parameters_of(TEMPLATE_NAME)` in the targets body (i.e. outside of any functions defined therein) to include all the target parameters defined in that file. The file will be processed in the context just after parsing the targets file, including all declared and non-declared variables defined in this file.

#### include_target_parameters_of()::
#### include_target_parameters_of, include_features_of and include_link_parameters_of::


include_target_parameters_of(<TEMPLATE_NAME> [ALL_EXCEPT <VAR_NAME>... | INCLUDE_ONLY <VAR_NAME>...])
<function_name>(<TEMPLATE_NAME> [NONRECURSIVE] [SOURCE LINKPARS|LINK_PARAMETERS|PARAMETERS|FEATURES] [ALL_EXCEPT <VAR_NAME>... | INCLUDE_ONLY <VAR_NAME>...])

Use any of these functions to import any kind of parameters as any other kind of parameters. The syntax of this function will change, but at the moment there is no limit on what combinations of types of parameters to convert. `NONRECURSIVE` mean, that that only the parameters declared directly in the target will get imported. `SOURCE` determines what is the source of the parameters; `LINKPARS` and `LINK_PARAMETERS` are synonims. `TARGET_PARAMETERS` from another template file. Only the variable definitions will be imported, just like if you would paste them into the own `TARGET_PARAMETERS`, not other variables and it will not set a build dependency. If the included file itself includes target parameters, they can be included too.

Use this function to import `TARGET_PARAMETERS` from another template file. Only the variable definitions will be imported, just like if you would paste them into the own `TARGET_PARAMETERS`, not other variables and it will not set a build dependency. If the included file itself includes target parameters, they can be included too.
####

### apply_dependency_to_target()

Expand Down
144 changes: 144 additions & 0 deletions docs/start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,148 @@ After we build, we should get three executables: ``hello_simple1``, ``hello_simp
$./hello_simple3
Hello Mars!

The ``targets.cmake`` defines a target _template_, that can be used to define as many targets, as there are unique combinations of target parameters. That is why the ``generate_targets()`` function requires user to use ``${TARGET_NAME}`` instead of hard-coded name, that is usual in standard CMake practice. The function will be called exactly once for each distinct ``${TARGET_NAME}`` that Beetroot found is required to satisfy the parameters.

Subprojects
^^^^^^^^^^^
Here you will learn how to combine targets together.

Suppose we have a program, that requires a function ``get_string`` from a library to run. The `hello_with_lib.cpp`::

#include <iostream>
#include "libhello.h"
#ifndef LIBPAR
#define LIBPAR 0
#endif
int main()
{
int libpar = LIBPAR;
std::cout << "Hello "<< get_string()<<"!"<< std::endl;
return 0;
}

To compile it, we need a `libhello.h` that provides the ``get_string()``::

#include<string>
std::string get_string();

The library's implementation is in the file ``libhello.cpp``::

#include "libhello.h"
#define STRINGIFY2(X) #X
#define STRINGIFY(X) STRINGIFY2(X)

#ifndef WHO
#define WHO World
#endif

std::string get_string() {
return(STRINGIFY(WHO));
}

The library depends on one macro: ``WHO`` that influences the text returned by the function.

We would like to have the ``hello_with_lib.cpp`` compiled and linked with the ``libhello``. Although there is nothing wrong with putting the additional CMake commands in the old ``targets.cmake`` file, it is not the best practice. Instead we will create two separate targets, so it will be easy to re-use the ``libhello`` by simply importing it.

Another thing to notice is that the Beetroot be default does not care about the location of the target definitions. Instead it scans recursively all the superproject files in search for files ``targets.cmake`` and subfolder structure ``cmake/targets/*.cmake``. Then it loads each fond file and learns the name of the targets/templates exposed there to build a mapping target/template name -> path of the target definition file, so user does not need to care about the paths anymore. On the other hand it requires that each each target/template name is unique across the whole superproject.

Let's create the following directory structure::


| superproject
| ├── cmake
| │ ├── beetroot (beetroot clone)
| │ │ └── ...
| │ └── root.cmake
| ├── hello_with_lib
| │ ├── hello_with_lib.cpp
| │ ├── CMakeLists.txt
| │ └── targets.cmake
| └── libhello
| │ ├── include
| │ │ └── libhello.h
| │ ├── source
| │ │ └── libhello.cpp
| │ └── targets.cmake
| └── CMakeLists.txt
|
|
This is the definition of the ``libhello/targets.cmake``::

set(ENUM_TEMPLATES LIBHELLO)
set(TARGET_PARAMETERS
WHO SCALAR STRING "Jupiter"
)
function(generate_targets TEMPLATE_NAME)
add_library(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/source/libhello.cpp")
add_source(${TARGET_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/libhello.h") #For better IDE integration
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_compile_definitions(${TARGET_NAME} PRIVATE "WHO=${WHO}")
endfunction()

Nothing new, except we use ``add_library`` instead of ``add_executable``. Adding ``libhello.h`` to sources is not strictly necessary, but is a good CMake practice, that helps various IDE generators generate better projects.

This is the definition of the ``hello_with_lib/targets.cmake``::

set(ENUM_TEMPLATES HELLO_WITH_LIB)
function(declare_dependencies TEMPLATE_NAME)
build_target(LIBHELLO WHO "Saturn")
endfunction()
function(generate_targets TEMPLATE_NAME)
add_executable(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/hello_with_lib.cpp")
endfunction()

The new element, the ``declare_dependencies()`` function, is used to declare dependencies. It is a function, so user can build complex logic that turns certain dependencies on and off depending on the Target Parameters and Features. To declare a certain target/template a dependency we call a function ``build_target(<TEMPLATE_OR_TARGET_NAME> [<PARAMETERS>...])``. The API and behaviour is exactly the same, as in ``CMakeLists.txt``.

Let's step up our example and require that the ``HELLO_WITH_LIB`` is also parametrized by the parameter ``WHO``. There are two ways to do it. The most obvious one is simply to add the ``set(TARGET_PARAMETERS WHO SCALAR STRING "Jupiter")`` to the ``hello_with_lib/targets.cmake`` but that will not scale, if there are many parameters in question and they change. The better solution is to import the parameters using the special function designed for this purpose.

This is the definition of the ``hello_with_lib/targets.cmake``::

set(ENUM_TEMPLATES HELLO_WITH_LIB)
include_target_parameters_of(LIBHELLO)
function(declare_dependencies TEMPLATE_NAME)
build_target(LIBHELLO WHO "Saturn")
endfunction()
function(generate_targets TEMPLATE_NAME)
add_executable(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/hello_with_lib.cpp")
endfunction()



Now let's complicate matters a bit and add two things. First imagine, that the ``hello_with_lib`` is also responsible for setting a macro variable in the client's code. Let's predend that this variable modifies behaviour of the header-only part of this library. Consequently will not change the library code. We only need to make sure, that clients linking to our library receive a new preprocessor macro::

set(ENUM_TEMPLATES LIBHELLO)
set(TARGET_PARAMETERS
WHO SCALAR STRING "Jupiter"
)
set(LINK_PARAMETERS
LIBPAR INTEGER
)
function(generate_targets TEMPLATE_NAME)
add_library(${TARGET_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/source/libhello.cpp")
add_source(${TARGET_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/libhello.h") #For better IDE integration
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_compile_definitions(${TARGET_NAME} PRIVATE "WHO=${WHO}")
endfunction()

function(apply_dependency_to_target DEPENDEE_TARGET_NAME OUR_TARGET_NAME)
target_compile_definitions(${DEPENDEE_TARGET_NAME} PRIVATE "LIBPAR=${LIBPAR}")
endfunction()


0 comments on commit a9b68f0

Please sign in to comment.