commit 0a5348b40173a198da6503ac26b27f07ab65bdb7 Author: Matteo Settenvini Date: Sat Jul 27 18:28:47 2019 +0200 Initial import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..da7c29b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,62 @@ +project(presentation LANGUAGES NONE) +cmake_minimum_required(VERSION 3.5) + +# ------------------------------------ + +set(jobname "cmake-workshop-20190729") +set(output "${jobname}.pdf") +set(mainsource "main.tex") +set(sources + ${mainsource} + cmake-for-scripting.tex + cross-compilation-toolchains.tex + feature-platform-check.tex + outro.tex + preamble.tex + project-handling.tex + rpath-build-install-tree.tex + starting-from-today.tex + troubleshooting.tex + usage-of-targets.tex) + +file(GLOB images "images/*") +list(APPEND sources ${images}) + +# ------------------------------------ + +find_program(XETEX NAMES xetex) +if(NOT XETEX) + message(FATAL_ERROR "Please install xetex") +endif() + +find_program(RUBBER NAMES rubber) +if(NOT RUBBER) + message(FATAL_ERROR "Please install rubber") +endif() + +find_program(PYGMENTS NAMES pygmentize) +if(NOT PYGMENTS) + message(FATAL_ERROR "Please install python3-pygments") +endif() + + +add_custom_command(OUTPUT ${output} + COMMAND ${RUBBER} --unsafe --pdf + --module xelatex + --module beamer + --module shell_escape + --into ${CMAKE_CURRENT_BINARY_DIR} + --jobname "${jobname}" + "${CMAKE_CURRENT_SOURCE_DIR}/${mainsource}" + MAIN_DEPENDENCY ${mainsource} + DEPENDS ${sources} + BYPRODUCTS ${jobname}.aux + ${jobname}.nav + ${jobname}.toc + ${jobname}.log + ${jobname}.out + ${jobname}.snm + COMMENT "Generating pdf file for the presentation" + VERBATIM) + +add_custom_target(presentation ALL DEPENDS ${output}) diff --git a/cmake-for-scripting.tex b/cmake-for-scripting.tex new file mode 100644 index 0000000..7b75bec --- /dev/null +++ b/cmake-for-scripting.tex @@ -0,0 +1,215 @@ + +\begin{frame} + \frametitle{CMake as a language} + + \begin{center} + \huge CMake as a language: variables, iterating, and functions + \end{center} +\end{frame} + +% -------------------------------------------------------------------- + +\subsection{Variables} + +\begin{frame}[containsverbatim] + \frametitle{Setting variables} + +\begin{codebox}{CMake}{} +set(local_variable "a string") +set(local_list "item1" "item2" "item3") +set(another_local_list "item1;item2;item3") + +set_property(GLOBAL PROPERTY + A_GLOBAL_PROPERTY "${local_variable}") + +# Not overridden if already in cache: +set(CACHE_VARIABLE ${local_list} + CACHE BOOL "Variable description") + +# Always override, for our internal use: +set(INTERNAL_CACHE_VARIABLE ${local_list} + CACHE INTERNAL "Variable description") +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Scoping of variables} + +\begin{codebox}{CMake}{} +# in folder A +set(local_variable "A") +add_subdirectory("B") +message(STATUS ${local_variable}) # -- prints A + +# in folder B +set(local_variable "B") +message(STATUS ${local_variable}) # -- prints B +\end{codebox} + + \texttt{add\_subdirectory} introduces a new scope. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Scoping of variables -- propagating} + +\begin{codebox}{CMake}{} +# in folder A +set(local_variable "A") +add_subdirectory("B") +message(STATUS ${local_variable}) # -- prints B + +# in folder B +set(local_variable "B" PARENT_SCOPE) +message(STATUS ${local_variable}) # -- prints B +\end{codebox} + + \texttt{add\_subdirectory} introduces a new scope. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Scoping of variables -- caching} + +\begin{codebox}{CMake}{} +message(STATUS ${VARIABLE}) # 1st run: nothing, 2nd run: B +add_subdirectory("C") + +# in folder C +set(VARIABLE "A") +message(STATUS ${VARIABLE}) # 1st run: A, 2nd run: A +set(VARIABLE "B" CACHE STRING "A looney var") +message(STATUS ${VARIABLE}) # 1st run: B, 2nd run: A +\end{codebox} + +Note how, the second time, setting a variable in the cache +is effectively a no-op, since it is already present there. + +\end{frame} + +% -------------------------------------------------------------------- + +\subsection{Conditionals and iterating} + +\begin{frame}[containsverbatim] + \frametitle{Conditionals} + + \texttt{if} has a lot of comparison operators. Read the docs! + + \vspace{.5em} + +\begin{codebox}{CMake}{} +# for historical reasons, unquoted variable +# names are always expanded: +if(var1 STREQUAL var2) + # if true... + +elseif("var1" STREQUAL "var2") # ... but not if quoted! + # (never enters here) + +else() + # otherwise +endif() +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Iterating on lists} + +\begin{codebox}{CMake}{} +set(a_list "a" "b" "c") +foreach(var IN LISTS a_list) + message(STATUS ${var}) # prints on separate lines: a, b, c +endforeach() + +foreach(var IN ITEMS "one" "two" "three" ${a_list}) + message(STATUS ${var}) # prints on separate lines: + # one, two, three, a, b, c +endforeach() + +# Note that quoted lists do not expand twice here: +foreach(var IN ITEMS "${a_list}") + message(STATUS ${var}) # prints on ONE line a;b;c +endforeach() +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\subsection{Functions} + +\begin{frame}[containsverbatim] + \frametitle{Defining functions} + +\begin{codebox}{CMake}{} +function(f a b c) + # here you can use a, b, and c. + # ARGN is a magic list that contains all elements + # provided after the third ("c"). +endfunction(f) + +# Invocation: +f("${var1}" "${var2}" "${var3}" "${var4}") + +# This could lead to unexpected results with empty vars: +f(${var1} ${var2} ${var3} ${var4}) +\end{codebox} + + {\footnotesize Macros are like functions, but do not create a new + scope. Plus assigning values to parameters is strange. Short + rule: do \emph{not} use macros unless you know what you are + doing.} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Returning values from functions} + + There is no ``\texttt{return value}'' construct. + + \vspace{.5em} + +\begin{codebox}{CMake}{} +function(identity VAR a) + # We use VAR as the name of the return variable in the + # parent scope. + set("${VAR}" "${a}" PARENT_SCOPE) +endfunction(identity) + +# Invocation: +set(a "value") +identity(a "${a}") +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\subsection{Invoking as a script} + +\begin{frame}[containsverbatim] + \frametitle{Invoking a file as a script} + + Why using CMake for scripting? + + Well, it is more portable than bash across systems! + + \vspace{.5em} + +\begin{codebox}{Bash}{} +$ cmake -DVAR1="value1" ... -P script-name.cmake +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- diff --git a/cmake_install.cmake b/cmake_install.cmake new file mode 100644 index 0000000..a1468d7 --- /dev/null +++ b/cmake_install.cmake @@ -0,0 +1,49 @@ +# Install script for directory: /home/matteo/cmake-presentation + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +if(CMAKE_INSTALL_COMPONENT) + set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") +else() + set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +file(WRITE "/home/matteo/cmake-presentation/${CMAKE_INSTALL_MANIFEST}" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") diff --git a/cross-compilation-toolchains.tex b/cross-compilation-toolchains.tex new file mode 100644 index 0000000..d485077 --- /dev/null +++ b/cross-compilation-toolchains.tex @@ -0,0 +1,104 @@ +\subsection{Cross-Compilation, Toolchain files} + +\begin{frame} + \frametitle{Cross-compilation} + + \begin{center} + \huge Dealing with different platforms + \end{center} +\end{frame} + +%-------------------------------------------------------------------------------------------------- + +\begin{frame} + \frametitle{Cross-Compilation, Concepts} + Building binaries for a platform other than the one on which the + compiler runs. + + \vspace{1em} + + \textbf {Host Platform} \\ + {\info {Platform/System \textbf {ON} which the binaries are built, compiler(cross-compiler) runs.}} + + \vspace{1em} + + \textbf{Target Platform} \\ + {\info {Platform \textbf {FOR} which the binaries are built.}} +\end{frame} + +%-------------------------------------------------------------------------------------------------- + +\begin{frame} + \frametitle{Cross-Compilation, Concepts} + \textbf {Toolchain}\\ + {\info {Set of tools and libraries to built binaries for a target platform, such as:} + \begin{itemize} + \item Binutils + \item Cross Compiler + \item C Library + \item Debugger + \end{itemize} + } +\end{frame} + + +%-------------------------------------------------------------------------------------------------- + +\begin{frame} + \frametitle{CMake \& Cross-Compilation} + CMake needs help to recognize Cross-Compilation, e.g.\\ + \begin{itemize} + \item Target System Name + \item Cross Compiler Location + \item Library Search Paths + \item etc. + \end{itemize} +\end{frame} + +%-------------------------------------------------------------------------------------------------- + +\begin{frame} + \frametitle{CMake \& Cross-Compilation} + + \textbf {Toolchain Files} - CMake Script enclosing toolchain info. + + E.g.: + + \begin{itemize} + \item Target System Name -- {\code CMAKE\_SYSTEM\_NAME} + \item Cross-Compiler to be used -- {\code CMAKE\_C\_COMPILER} + \item Library and Package search root -- {\code {CMAKE\_FIND\_ROOT\_PATH}} + \item Default compiler and linker flags specific to the target platform + \end{itemize} + + {\info {\code {CMAKE\_TOOLCHAIN\_FILE}} used to provide toolchain file location} +\end{frame} + +%-------------------------------------------------------------------------------------------------- + +\begin{frame} + \frametitle{CMake \& Cross-Compilation} + + System introspection is still possible while cross-compiling: + + \begin{itemize} + \item {\code {try\_compile}} - works as expected + \item {\code {try\_run}} - limited functionality, run phase is usually skipped + \end{itemize} +\end{frame} + +%-------------------------------------------------------------------------------------------------- + +\note{ + What is cross-compilation + HOST, TARGET, compilers + Toolchain files + - meaning and content + Cross compilation with cmake + - CMAKE Variables + - Searching \& finding packages + - toolchain files + - system introspection (try-compile, try-run) + - HOST-TOOLS, why might be required + - Using Executables in the build created during the build +} diff --git a/exercises/CMakeLists.txt b/exercises/CMakeLists.txt new file mode 100644 index 0000000..08e68ac --- /dev/null +++ b/exercises/CMakeLists.txt @@ -0,0 +1,145 @@ +# cmake_minimum_required(VERSION 3.11) +cmake_minimum_required(VERSION 3.11) + +## Declare a project named "cmake-exercise" +project(cmake-exercise VERSION 0.90.0) + +if(CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR) + message(FATAL_ERROR "Don't build in the source directory, pretty please.") +endif() + +## Includes +include(GNUInstallDirs) +include(GenerateExportHeader) +include(CMakePackageConfigHelpers) + +## Find necessary packages +find_package(OpenMP) +find_package(Threads REQUIRED) + +## Artifacts to create +add_library(obj OBJECT) +add_library(static STATIC) +add_library(shared SHARED) +add_library(interface INTERFACE) +add_executable(tests) + +## Generate other files +generate_export_header(shared + BASE_NAME DSO + EXPORT_FILE_NAME include/cmake-exercise/config.hh) + +## Specify source files +target_sources(obj + PUBLIC $ + $ + PRIVATE src/a.cc) +target_sources(shared + PUBLIC $ + $ + $ + $ + PRIVATE src/b.cc + $ + $) +target_sources(interface + INTERFACE $ + $) +target_sources(tests + PRIVATE test/test.cc) + + +## Add include folders +target_include_directories(interface + INTERFACE $ + $) +target_include_directories(obj + PUBLIC $ + $) +target_include_directories(shared + PUBLIC $ + $ + $) + + +## Set target properties for visibility and PIC +set_property(TARGET obj PROPERTY POSITION_INDEPENDENT_CODE TRUE) +set_property(TARGET obj shared PROPERTY CXX_VISIBILITY_PRESET hidden) +set_property(TARGET obj shared PROPERTY VISIBILITY_INLINES_HIDDEN TRUE) + + +## Specify C++ version to use +target_compile_features(interface + INTERFACE cxx_std_11) +target_compile_features(obj + INTERFACE cxx_std_11 + PRIVATE cxx_std_14) + + +## Link to other libraries, and inherit their settings +target_link_libraries(obj + PUBLIC Threads::Threads) +target_link_libraries(static + PRIVATE interface + PUBLIC obj) +target_link_libraries(shared + PRIVATE interface obj) +target_link_libraries(tests + PRIVATE static) + +if(TARGET OpenMP::OpenMP_CXX) + target_compile_definitions(tests PRIVATE HAVE_OPENMP) + target_link_libraries(tests PRIVATE OpenMP::OpenMP_CXX) +endif() + + +## Install all needed files for packaging +install(FILES $ + $ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT devel) +install(TARGETS shared + EXPORT cmake-exercise + DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(TARGETS obj interface + EXPORT cmake-exercise + COMPONENT devel + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(TARGETS tests + DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake-exercise/test + COMPONENT test) + +## Add a test +enable_testing() +add_test(NAME not-really-a-test + COMMAND tests --ignored-param) + + +## Generate the export sets to be able to import this project +## in other projects via find_package (the config file is not complete though) +set(CMAKE_INSTALL_CMAKEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/cmake-exercise) +export(EXPORT cmake-exercise + NAMESPACE cmake-exercise:: + FILE cmake-exercise-targets.cmake) +install(EXPORT cmake-exercise + NAMESPACE cmake-exercise:: + DESTINATION ${CMAKE_INSTALL_CMAKEDIR} + FILE cmake-exercise-targets.cmake + COMPONENT devel) +configure_package_config_file(cmake-exercise-config.cmake.in + cmake-exercise-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_CMAKEDIR}) +write_basic_package_version_file(cmake-exercise-version.cmake + COMPATIBILITY SameMajorVersion) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake-exercise-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/cmake-exercise-version.cmake + DESTINATION ${CMAKE_INSTALL_CMAKEDIR} + COMPONENT devel) + + +## Trigger packaging +set(CPACK_PACKAGE_FILE_NAME package) +set(CPACK_GENERATOR TXZ) +set(CPACK_ARCHIVE_COMPONENT_INSTALL TRUE) +include(CPack) diff --git a/exercises/README b/exercises/README new file mode 100644 index 0000000..97e537b --- /dev/null +++ b/exercises/README @@ -0,0 +1,6 @@ +This is a simple exercise to get familiar a bit with CMake. + +See the deployment diagram in doc/ to know what is the final result +should look like. + +Other than that, follow the comments in CMakeLists.txt diff --git a/exercises/cmake-exercise-config.cmake.in b/exercises/cmake-exercise-config.cmake.in new file mode 100644 index 0000000..c8ca631 --- /dev/null +++ b/exercises/cmake-exercise-config.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/cmake-exercise-targets.cmake) diff --git a/exercises/doc/deployment-diagram.png b/exercises/doc/deployment-diagram.png new file mode 100644 index 0000000..ddd1b27 Binary files /dev/null and b/exercises/doc/deployment-diagram.png differ diff --git a/exercises/doc/deployment-diagram.puml b/exercises/doc/deployment-diagram.puml new file mode 100644 index 0000000..b61b979 --- /dev/null +++ b/exercises/doc/deployment-diagram.puml @@ -0,0 +1,119 @@ +@startuml +left to right direction + +hide <> stereotype +skinparam rectangle<> { + borderColor Transparent + backgroundColor Transparent + fontColor Transparent + shadowing false +} + +folder "source directory" as srcdir { + folder "include/cmake-exercise" as include_srcdir { + file a.hh + file b.hh + file c.hh + } + + folder "src" { + file a.cc + file b.cc + } + + folder "test" as test_srcdir { + file test.cc + } + + file CMakeLists.txt +} + +folder "build directory" as builddir { + rectangle folders_layout <> { + folder "src" as src_builddir { + file a.o + file b.o + } + + folder "test" as test_builddir { + file test.o + } + + folder "include/cmake-exercise" as include_builddir { + file config.hh + } + } + + rectangle artifacts_layout <> { + artifact "<>\nlibstatic.a" as static + artifact "<>\nlibshared.so" as shared + artifact "<>\ntest-binary" as test + + static -[hidden]r- shared + shared -[hidden]r- test + } +} + +srcdir -[hidden]r- builddir + +package package.tar.xz { + folder "/include/cmake-exercise" { + file a.hh as installed_a.hh + file b.hh as installed_b.hh + file c.hh as installed_c.hh + file config.hh as installed_config.hh + } + + folder "/share/cmake-exercise/test" { + artifact "<>\ntest" as installed_test + } + + folder "/lib" { + artifact "<>\nlibshared.so" as installed_shared + } +} + +actor CMake + +a.hh --> installed_a.hh : <> +b.hh --> installed_b.hh : <> +c.hh --> installed_c.hh : <> +config.hh --> installed_config.hh : <> +test --> installed_test : <> +shared --> installed_shared : <> + +file "<>\na.cc" as a.cc +file "<>\nb.cc" as b.cc +file "<>\na.hh" as a.hh +file "<>\nb.hh" as b.hh +file "<>\nc.hh" as c.hh +file "<>\nconfig.hh" as config.hh + +file "<>\na.o" as a.o +file "<>\nb.o" as b.o +file "<>\ntest.o" as test.o + +file "<>\ntest.cc" as test.cc + +CMake -u-> config.hh : <> + +a.cc --> a.o : <> +b.cc --> b.o : <> +test.cc --> test.o : <> + +test ..> test.o : <> +test ..> static : <> +static ..> a.o : <> +shared ..> a.o : <> +shared ..> b.o : <> + +b.hh ~r~> a.hh : <> +a.hh ~~> c.hh : <> +b.hh ~~> c.hh : <> +a.hh ~~> config.hh : <> +b.hh ~~> config.hh : <> +a.cc ~~> a.hh : <> +b.cc ~~> b.hh : <> +test.cc ~~> a.hh : <> + +@enduml diff --git a/exercises/include/cmake-exercise/a.hh b/exercises/include/cmake-exercise/a.hh new file mode 100644 index 0000000..eb4d416 --- /dev/null +++ b/exercises/include/cmake-exercise/a.hh @@ -0,0 +1,16 @@ +#pragma once + +#include + +class A +{ +public: + A (); + A (A&&) = default; + ~A (); + + A& operator= (A&&) = default; + +private: + std::thread _t; +}; diff --git a/exercises/include/cmake-exercise/b.hh b/exercises/include/cmake-exercise/b.hh new file mode 100644 index 0000000..59940e1 --- /dev/null +++ b/exercises/include/cmake-exercise/b.hh @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include + +#include + +class B +{ +public: + DSO_EXPORT B (); + +private: + std::vector _a; +}; diff --git a/exercises/include/cmake-exercise/c.hh b/exercises/include/cmake-exercise/c.hh new file mode 100644 index 0000000..0742b40 --- /dev/null +++ b/exercises/include/cmake-exercise/c.hh @@ -0,0 +1,4 @@ +#pragma once + +template +void unused_function (); // nothing to see here diff --git a/exercises/src/a.cc b/exercises/src/a.cc new file mode 100644 index 0000000..15eb104 --- /dev/null +++ b/exercises/src/a.cc @@ -0,0 +1,31 @@ +#include + +#include +#include +#include +#include + +A::A () + : _t ([] () +{ + { + std::ostringstream oss; + oss << "Starting thread " << std::this_thread::get_id () << std::endl; + std::cout << oss.str() << std::flush; + } + + std::this_thread::sleep_for (std::chrono::milliseconds(std::rand() % 100 * 10)); + + { + std::ostringstream oss; + oss << "Thread " << std::this_thread::get_id () << " finished" << std::endl; + std::cout << oss.str() << std::flush; + } +}) +{ +} + +A::~A () +{ + _t.join (); +} diff --git a/exercises/src/b.cc b/exercises/src/b.cc new file mode 100644 index 0000000..71e5492 --- /dev/null +++ b/exercises/src/b.cc @@ -0,0 +1,7 @@ +#include +#include + +B::B () + : _a (10) +{ +} diff --git a/exercises/test/test.cc b/exercises/test/test.cc new file mode 100644 index 0000000..bf955a8 --- /dev/null +++ b/exercises/test/test.cc @@ -0,0 +1,19 @@ +#include + +#include + +int +main() +{ + // not a real test, heh. +#ifdef HAVE_OPENMP +# pragma omp parallel for + for (int n = 0; n < 20; ++n) + { + A a; + } + +#else + std::vector a (20); +#endif +} diff --git a/feature-platform-check.tex b/feature-platform-check.tex new file mode 100644 index 0000000..683804b --- /dev/null +++ b/feature-platform-check.tex @@ -0,0 +1,142 @@ +\subsection{Feature vs. Platform Checks} + +\begin{frame} + \frametitle{Feature checks} + + \begin{center} + \huge Probing the outside system\\for capabilities + \end{center} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame} + \frametitle{What's a ``Feature''} + Distinctive attribute which may have influence on the final system being built.\\ + \\ + Can be classified in 3 domains: + \begin{itemize} + \item Platform features\\ + {\info {library, symbols, functions, type size}} + \item Compiler features\\ + {\info {variadic templates, constexpr, final, override}} + \item System dictated features\\ + {\info {no-exceptions, no-rtti, no logging}} + \end{itemize} +\end{frame} + + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle {Platform specific feature check} + + \textbf{Bad} +\begin{codebox}{CMake}{} +if(IOS OR ANDROID OR UNIX) + target_compile_definitions(lua PRIVATE HAVE_POSIX_SPAWN) +endif() +\end{codebox} + +\textbf{Good} +\begin{codebox}{CMake}{} +check_symbol_exists("posix_spawn" "spawn.h" has_posix_spawn) +if(has_posix_spawn) + target_compile_definitions(lua PRIVATE HAVE_POSIX_SPAWN) +endif() +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle {Compiler specific feature checks} + + \textbf{Bad} +\begin{codebox}[minted options={fontsize=\tiny}]{CMake}{} +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + target_compile_definitions(my-library + PRIVATE -DBOOST_THREAD_DONT_USE_CHRONO) +endif() +\end{codebox} + + \textbf{Good} + \begin{codebox}[minted options={fontsize=\tiny}]{CMake}{} +# (incomplete example, checking for exception support is slightly harder) +check_cxx_source_compile(" + int main() { + #if !defined(__EXCEPTIONS) + #error Exceptions not supported. + #endif + }" exceptions_supported) + +if(NOT exceptions_supported) + target_compile_definitions(my-library + PRIVATE -DBOOST_THREAD_DONT_USE_CHRONO) +endif() +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle {System dictated feature checks} + + \textbf{Bad} +\begin{codebox}[minted options={fontsize=\scriptsize}]{CMake}{} +find_package(Boost) +if(Boost_FOUND) + add_definition(ENABLE_FEATURE_X) +endif() +\end{codebox} + + \textbf {Good} +\begin{codebox}[minted options={fontsize=\scriptsize}]{CMake}{} +# In product config file +set(ENABLE_FEATURE_X TRUE) + +# In project configuration +if(ENABLE_FEATURE_X) + find_package(Boost REQUIRED) + add_definition(FEATURE_X_ENABLED) +endif() +\end{codebox} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame} + \frametitle{Functions for configure checks} + + CMake provides an API to check: + + \begin{itemize} + \item Header existence: {\code{check\_include\_file()}} + \item Library existence: {\code{check\_library\_exists()}} + \item Symbol existence: {\code{check\_symbol\_exists()}} + \item Supported type sizes: {\code{check\_type\_size()}} + \end{itemize} + + CMake provides an API to write custom checks via: + + \begin{itemize} + \item {\code{try\_compile()}} + \item {\code{try\_run()}} + \end{itemize} + + With {\code configure\_file} you can generate a header file + enabling required code switches based on the checks. +\end{frame} + +% -------------------------------------------------------------------- + +\note{ + What is Platform check + What is feature check + CMAKE system introspection interface + -What type of checks can be done with CMAKE + https://cmake.org/Wiki/CMake:How\_To\_Write\_Platform\_Checks + Pros and Cons +} diff --git a/images/cmake-logo.png b/images/cmake-logo.png new file mode 100644 index 0000000..17fde16 Binary files /dev/null and b/images/cmake-logo.png differ diff --git a/images/cmake-simple-flowchart.png b/images/cmake-simple-flowchart.png new file mode 100644 index 0000000..720420c Binary files /dev/null and b/images/cmake-simple-flowchart.png differ diff --git a/images/dylib_rpath.png b/images/dylib_rpath.png new file mode 100644 index 0000000..2e04afd Binary files /dev/null and b/images/dylib_rpath.png differ diff --git a/images/eye-of-god.png b/images/eye-of-god.png new file mode 100644 index 0000000..847123c Binary files /dev/null and b/images/eye-of-god.png differ diff --git a/images/so_rpath.png b/images/so_rpath.png new file mode 100644 index 0000000..825ab1f Binary files /dev/null and b/images/so_rpath.png differ diff --git a/main.tex b/main.tex new file mode 100644 index 0000000..7c786f7 --- /dev/null +++ b/main.tex @@ -0,0 +1,82 @@ +%\documentclass[notes,usenames,dvipsnames]{beamer} +\documentclass[usenames,dvipsnames]{beamer} +%\documentclass[usenames,dvipsnames,handout]{beamer} + +\input{preamble} + +% for the handout +\usepackage{pgfpages} +\setbeamertemplate{headline}{} +\addtobeamertemplate{navigation symbols}{}{% + \usebeamerfont{footline}% + \usebeamercolor[fg]{footline}% + \hspace{1em}% + \insertframenumber/\inserttotalframenumber +} + +\begin{document} + +\hypersetup{ + colorlinks=true, + urlcolor=cyan +} + +\title{Introduction to CMake} +\author[M. Settenvini] { + Matteo Settenvini \\ + $\langle$\href{mailto:matteo@member.fsf.org}{matteo@member.fsf.org}$\rangle$ +} + +\date{\today} + +% -------------------------------------------------- + +\begin{frame} + \begin{columns}[T] + \begin{column}{.5\textwidth} + \titlepage + \end{column} + + \begin{column}{.5\textwidth} + \begin{center} + \begin{tikzpicture} + \node (logo) {\includegraphics[width=.6\textwidth]{images/cmake-logo.png}}; + \node (eye) at ([shift={(logo)}] -90:.55) {\includegraphics[width=.3\textwidth]{images/eye-of-god.png}}; + \end{tikzpicture} + + \vspace{1em} + + \end{center} + \end{column} + \end{columns} +\end{frame} + +% -------------------------------------------------- + +\begin{frame} + \frametitle{Table of Contents} + + \begin{multicols}{2} + \footnotesize + \tableofcontents + \end{multicols} +\end{frame} + +% -------------------------------------------------- + +\section{CMake as a language} +\input{cmake-for-scripting} + +\section{Setting up a CMake project} +\input{usage-of-targets} +\input{project-handling} + +\section{Starting from today} +\input{starting-from-today} + +\section{Outro} +\input{outro} + +% -------------------------------------------------- + +\end{document} diff --git a/outro.tex b/outro.tex new file mode 100644 index 0000000..2ebeea1 --- /dev/null +++ b/outro.tex @@ -0,0 +1,13 @@ +% -------------------------------------------------- + +\subsection{Q\&A} + +\begin{frame} + \frametitle{Q\&A} + + \begin{center} + \huge Questions? + \end{center} +\end{frame} + +% -------------------------------------------------- diff --git a/preamble.tex b/preamble.tex new file mode 100644 index 0000000..61b36ea --- /dev/null +++ b/preamble.tex @@ -0,0 +1,145 @@ +\usepackage{fontspec} +\usepackage{polyglossia} +\usepackage[justification=centering]{caption} +\usepackage{graphics} +\usepackage[listings,skins,minted]{tcolorbox} +\usepackage{etoolbox,xpatch} +\usepackage{multicol} + +\setdefaultlanguage{english} + +\newfontfamily\emotifont{Noto Emoji} + +% --------------------------------------------------------------------- + +% new commands +\newcommand\hint[1]{\tiny \color{Black} {#1}\\} +\newcommand\todo[1]{\tiny \color{Red} {TODO: #1}\\} +\newcommand\code[1]{\scriptsize \ttfamily \color{blue} {#1}} +\newcommand\info[1]{\scriptsize \slshape {#1}} + +% --------------------------------------------------------------------- + +% style thingies +\usetheme{Malmoe} + +\tikzset{ + every overlay node/.style={ + rounded corners,anchor=north west, + }, +} +% Usage: +% \tikzoverlay at (-1cm,-5cm) {content}; +% or +% \tikzoverlay[text width=5cm] at (-1cm,-5cm) {content}; +\def\tikzoverlay{% + \tikz[baseline,overlay]\node[every overlay node] +}% + + +\addtobeamertemplate{headline}{}{% + \tikzoverlay at (-.97\textwidth,.18\textheight) {% + \includegraphics[height=.15\textheight,keepaspectratio]{images/cmake-logo.png}% + }; +} + +\definecolor{herenavy}{RGB}{15,22,33} +\definecolor{hereblue}{RGB}{72,218,208} + +\setbeamercolor{structure}{fg=herenavy} + +\setbeamercolor{palette primary}{fg=white,bg=herenavy!70} +\setbeamercolor{palette secondary}{fg=white,bg=herenavy!80} +\setbeamercolor{palette tertiary}{fg=white,bg=herenavy!90} +\setbeamercolor{palette quaternary}{fg=white,bg=herenavy} + +\setbeamercolor{titlelike}{parent=palette quaternary} + +\setbeamercolor{subsection in head/foot}{bg=herenavy!90} + +\setbeamercolor{block title}{fg=white,bg=herenavy} +\setbeamercolor{block title alerted}{use=alerted text,fg=white,bg=alerted text.fg!75!bg} +\setbeamercolor{block title example}{use=example text,fg=white,bg=example text.fg!75!bg} + +\setbeamercolor{block body}{parent=normal text,use=block title,bg=block title.bg!25!bg} +\setbeamercolor{block body alerted}{parent=normal text,use=block title alerted,bg=block title alerted.bg!25!bg} +\setbeamercolor{block body example}{parent=normal text,use=block title example,bg=block title example.bg!25!bg} + +\setbeamercolor{sidebar}{bg=hereblue!70} + +\setbeamercolor{palette sidebar primary}{fg=herenavy} +\setbeamercolor{palette sidebar secondary}{fg=herenavy!75} +\setbeamercolor{palette sidebar tertiary}{fg=herenavy!75} +\setbeamercolor{palette sidebar quaternary}{fg=herenavy} + +\setbeamercolor*{separation line}{} +\setbeamercolor*{fine separation line}{} + +% --------------------------------------------------------------------- + +\newcommand\fullscreenimage[2]{ + \begin{figure} + \centering + \includegraphics[width=\textwidth,height=.7\textheight,keepaspectratio]{#1} + \caption*{\tiny #2} + \end{figure} +} + +% --------------------------------------------------------------------- + +% Listings stuff + +\newcommand{\mynewminted}[3]{% + \newminted[#1]{#2}{#3}% + \tcbset{myminted/#1/.style={minted language=#2,minted options={#3}}}} + +\mynewminted{CMake}{cmake}{tabsize=2,fontsize=\footnotesize} +\mynewminted{C++}{cpp}{tabsize=4,fontsize=\footnotesize} +\mynewminted{Bash}{bash}{tabsize=2,fontsize=\footnotesize} +\mynewminted{Shell}{shell-session}{tabsize=2,fontsize=\footnotesize,breaklines} + + +\newtcblisting{codebox}[3][]{% + listing only,% + title={#3},% + enhanced,% + colback=structure.bg!20,% + colframe=black!60,% + drop fuzzy shadow,% + myminted/#2,% + #1} + +\newtcblisting{goodcodebox}[2][]{% + listing only,% + title={Good},% + enhanced,% + top=.3em,% + left=.3em,% + fonttitle=\tiny\bfseries,% + colback=structure.bg!20,% + colframe=green!80!black!90,% + drop fuzzy shadow,% + myminted/#2,% + minted options={fontsize=\tiny},% + #1} + +\newtcblisting{badcodebox}[2][]{% + listing only,% + title={Bad},% + enhanced,% + top=.3em,% + left=.3em,% + fonttitle=\tiny\bfseries,% + colback=structure.bg!20,% + colframe=red!90!black!90,% + drop fuzzy shadow,% + myminted/#2,% + minted options={fontsize=\tiny},% + #1} + +% We ignore parsing errors as part of this presentation +\makeatletter +\AtBeginEnvironment{minted}{\dontdofcolorbox} +\def\dontdofcolorbox{\renewcommand\fcolorbox[4][]{##4}} +\xpatchcmd{\inputminted}{\minted@fvset}{\minted@fvset\dontdofcolorbox}{}{} +\makeatother diff --git a/project-handling.tex b/project-handling.tex new file mode 100644 index 0000000..bc2a92d --- /dev/null +++ b/project-handling.tex @@ -0,0 +1,6 @@ +\input{rpath-build-install-tree} +\input{cross-compilation-toolchains} +\input{feature-platform-check} +\input{troubleshooting} + +% -------------------------------------------------------------------- diff --git a/rpath-build-install-tree.tex b/rpath-build-install-tree.tex new file mode 100644 index 0000000..c9c22d1 --- /dev/null +++ b/rpath-build-install-tree.tex @@ -0,0 +1,135 @@ + +\begin{frame} + \frametitle{Relocatable binaries and resources} + + \begin{center} + \huge Making packages relocatable + \end{center} +\end{frame} + + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{rpath} + + Runtime search path - embedded in the binaries + + \vspace{1em} + + \includegraphics[width=.95\textwidth,keepaspectratio]{images/so_rpath.png} + + \vspace{1em} + + \includegraphics[width=.95\textwidth,keepaspectratio]{images/dylib_rpath.png} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{rpath} + + Used by the Dynamic Linker (DL) on UNIX systems + + \begin{itemize} + \item rpath - almost the first place to look for the libraries, + \newline nowadays {\code{DT\_RUNPATH}} is more common than + {\code{DT\_RPATH}} as a tag + \item Search path sequence for DL - + {\code{RPATH, LD\_LIBRARY\_PATH, RUNPATH, /etc/ld.so.conf, /lib/, + /usr/lib}, …} + \end{itemize} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{rpath} + + \begin{itemize} + \item Can be overwritten - {\code {chrpath, patchelf}} + \item Can be extracted - {\code {objdump, readelf, otool, etc.}} + \end{itemize} + +\begin{codebox}{Shell}{} +$ objdump -x | grep RPATH +$ readelf -d | grep RPATH +$ otool -L +\end{codebox} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame} + \frametitle{rpath} + + \begin{tabular}{ | p{5cm} | p{5cm} |} + \hline + {\textbf {In Build Tree}} & {\textbf {In Install Tree}} \\ + \hline + {Points to directories in the build folder + {\begin{itemize} + \item Usually full paths + \item Updated while installing + \end{itemize}}} & + {Points to directories specified during build for installation + {\begin{itemize} + \item Can be full path + \item Can be relative path + \item Can be empty + \end{itemize}}} \\ + \hline + \end{tabular} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame} + \frametitle {Relocatable packages} + Relative or empty rpaths when installed + \begin{itemize} + \item {\code \$ORIGIN} - *nix OSs + \item {\code @rpath} (+ {\code @loader\_path}, {\code @executable\_path}) - Mac OSX \& IOS + \item Always Empty - Android\\ + {\info {Usually libraries are packed with {\code APK} and loaded using {\code + dlopen} interface\\}} + \item Always Empty - for Windows\\ + {\info {Only user account and system env paths are + searched for libraries, plus the current folder\\}} + \end{itemize} + +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame} + \frametitle{Making loading resources relocatable} + + Resources used by package should be easily locatable\\ + + \textbf{Bad:} when resource paths are hardcoded in the source code\\ + + \textbf{Good:} when resource paths are dynamically determined at runtime\\ + +\end{frame} + +\begin{frame} + \frametitle{Making loading resources relocatable} + + You need to write a function able to determine the relative path from its + containing binary to the installation root at runtime.\\ + + \begin{itemize} + \item No need to hardcode resouce location in the sources + \item Path to resources should be located based on their types, such as:\\ + {\code LIBRARY}, {\code BINARY}, {\code DATA}, {\code CONFIG}, + etc. + \item Aware of the install tree directory structure + \item Just make sure that install tree structure is not violated + \end{itemize} + +\end{frame} + +% -------------------------------------------------------------------- diff --git a/rules.ninja b/rules.ninja new file mode 100644 index 0000000..65627fb --- /dev/null +++ b/rules.ninja @@ -0,0 +1,45 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Ninja" Generator, CMake Version 3.13 + +# This file contains all the rules used to get the outputs files +# built from the input files. +# It is included in the main 'build.ninja'. + +# ============================================================================= +# Project: presentation +# Configuration: +# ============================================================================= +# ============================================================================= + +############################################# +# Rule for running custom commands. + +rule CUSTOM_COMMAND + command = $COMMAND + description = $DESC + + +############################################# +# Rule for re-running cmake. + +rule RERUN_CMAKE + command = /usr/bin/cmake -S/home/matteo/cmake-presentation -B/home/matteo/cmake-presentation + description = Re-running CMake... + generator = 1 + + +############################################# +# Rule for cleaning all built files. + +rule CLEAN + command = /usr/bin/ninja -t clean + description = Cleaning all built files... + + +############################################# +# Rule for printing all primary targets available. + +rule HELP + command = /usr/bin/ninja -t targets + description = All primary targets available: + diff --git a/starting-from-today.tex b/starting-from-today.tex new file mode 100644 index 0000000..3ecc964 --- /dev/null +++ b/starting-from-today.tex @@ -0,0 +1,234 @@ + +\begin{frame} + \frametitle{Starting from today} + + \begin{center} + \huge Things you can start doing\newline Right Now™ + \end{center} +\end{frame} + +% -------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Use targets in place of variables} + +\begin{codebox}[minted options={fontsize=\scriptsize}]{CMake}{} +find_package(GTest REQUIRED) +find_package(Boost REQUIRED COMPONENTS system thread) +find_package(EXPAT REQUIRED) # only vars + +add_library(alexandria …) +\end{codebox} + + \begin{columns} + \begin{column}{.5\textwidth} +\begin{badcodebox}[equal height group=targets1]{CMake} +target_link_libraries(alexandria + PRIVATE + ${BOOST_LIBRARIES} + ${EXPAT_LIBRARIES} + ${GTEST_MAIN_LIBRARIES}) +\end{badcodebox} + \end{column} + + \begin{column}{.5\textwidth} +\begin{goodcodebox}[equal height group=targets1]{CMake} +target_link_libraries(alexandria + PRIVATE + Boost::system + Boost::thread + GTest::Main + ${EXPAT_LIBRARIES}) # legacy +\end{goodcodebox} + \end{column} + \end{columns} + +\end{frame} + + +% -------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Use target-specific commands\footnote{An exception is of + course in toolchain files.}} + + Some commands propagate to all subfolders and included files. This + can lead to potential problems where some targets get extra + unintended flags. + + \vspace{.3em} + + \begin{columns} + \begin{column}{.5\textwidth} +\begin{badcodebox}[equal height group=tgtspecific1]{CMake} +add_definitions(-DDEFINE_SOMETHING=1) +add_compile_options(-fvisibility=hidden) +set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fno-exceptions") +\end{badcodebox} + \end{column} + + \begin{column}{.5\textwidth} +\begin{goodcodebox}[equal height group=tgtspecific1]{CMake} +target_compile_definitions(tgt PRIVATE + DEFINE_SOMETHING=1) +target_compile_options(tgt PRIVATE + "-fno-tree-vrp") +\end{goodcodebox} + \end{column} + \end{columns} + +\end{frame} + +% -------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Avoid \texttt{target\_include\_directories}} + +\begin{codebox}[minted options={fontsize=\scriptsize}]{CMake}{} +find_package(EXPAT REQUIRED) # external, has only vars +find_package(Boost REQUIRED) # external, has targets +add_library(alexandria egypt.cc) +target_link_libraries(alexandria + PUBLIC calliope clio Boost::filesystem + PRIVATE euterpe erato ${EXPAT_LIBRARIES}) +\end{codebox} + +\vspace{.2em} + + \begin{columns} + \begin{column}{.5\textwidth} +\begin{badcodebox}[equal height group=tgtincdir1]{CMake} +target_include_directories(alexandria + PUBLIC ${PROJECT_SOURCE_DIR}/include + $ + PRIVATE ${EUTERPE_INCLUDE_DIRS} # ... + ${CMAKE_SOURCE_DIR}/calliope/inc + ${BOOST_INCLUDE_DIRS} + ${EXPAT_INCLUDE_DIRS}) +\end{badcodebox} + \end{column} + + \begin{column}{.5\textwidth} +\begin{goodcodebox}[equal height group=tgtincdir1]{CMake} +target_include_directories(alexandria + PUBLIC $ + $ + PRIVATE ${EXPAT_INCLUDE_DIRS}) +\end{goodcodebox} + \end{column} + \end{columns} + +\end{frame} + +% -------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Use PRIVATE and PUBLIC} + + \begin{columns} + \begin{column}{.5\textwidth} +\begin{badcodebox}[equal height group=privpub1]{CMake} +target_link_libraries(tgt + libA + libB + libC) + +target_compile_definitions(tgt + HAVE_THINGIE) +\end{badcodebox} + \end{column} + + \begin{column}{.5\textwidth} +\begin{goodcodebox}[equal height group=privpub1]{CMake} +target_link_libraries(tgt + PRIVATE libA + libB + PUBLIC libC) + +target_compile_definitions(tgt + PRIVATE HAVE_THINGIE) +\end{goodcodebox} + \end{column} + \end{columns} + +\end{frame} + +% -------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Prefer feature checks} + + \begin{columns} + \begin{column}{.5\textwidth} +\begin{badcodebox}[equal height group=feat1]{CMake} +if(APPLE OR IOS OR (UNIX AND NOT ANDROID)) + target_compile_definitions(lua PRIVATE + HAVE_POSIX_SPAWN) +endif() +\end{badcodebox} + \end{column} + + \begin{column}{.5\textwidth} +\begin{goodcodebox}[equal height group=feat1]{CMake} +check_symbol_exists("posix_spawn" "spawn.h" + has_posix_spawn) +if(has_posix_spawn) + target_compile_definitions(lua PRIVATE + HAVE_POSIX_SPAWN) +endif() +\end{goodcodebox} + \end{column} + \end{columns} + +\end{frame} + +% -------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Make your package relocatable} + + \begin{columns} + \begin{column}{.5\textwidth} +\begin{badcodebox}[equal height group=relocatable1]{CMake} +install(FILES "example.conf" + DESTINATION some/strange/folder) +\end{badcodebox} + +\begin{badcodebox}[equal height group=relocatable2]{C++} +char buf[2048]; +getcwd(buf, 2048); + +using boost::filesystem::path; +path rez = path(buf) / + HARDCODED_ABSOLUTE_PREFIX_PATH + / "some" / "strange" / "folder" / + "example.conf"; +\end{badcodebox} + \end{column} + + \begin{column}{.5\textwidth} +\begin{goodcodebox}[equal height group=relocatable1]{CMake} +include(GNUInstallDirs) +install(RESOURCES "example.conf" + DESTINATION + ${CMAKE_INSTALL_SYSCONFDIR}/appname) +\end{goodcodebox} + +\begin{goodcodebox}[equal height group=relocatable2]{C++} +using your_ns::some_enum::ResourceType; + +using boost::filesystem::path; +path = your_ns::get_resource_path( + ResourceType::CONFIG, + { "appname", "example.conf" }); +\end{goodcodebox} + \end{column} + \end{columns} + +\end{frame} + +% -------------------------------------------------- diff --git a/troubleshooting.tex b/troubleshooting.tex new file mode 100644 index 0000000..2ba9e00 --- /dev/null +++ b/troubleshooting.tex @@ -0,0 +1,20 @@ +\subsection{Troubleshooting} + +% -------------------------------------------------------------------- + +\begin{frame} + \frametitle{Troubleshooting} + When problems arise while hacking CMake code, do \textbf{not} panic! + + \vspace{1em} + + Run in the build directory:\\ + \texttt{\$ cmake --trace-expand . 2> trace\_expand.txt}. + + \vspace{1em} + + This will give more insight about what happens behind the scenes. +\end{frame} + +% -------------------------------------------------------------------- + diff --git a/usage-of-targets.tex b/usage-of-targets.tex new file mode 100644 index 0000000..8d2782c --- /dev/null +++ b/usage-of-targets.tex @@ -0,0 +1,288 @@ +\subsection{The CMake processing steps} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Projects and targets} + + \begin{center} + \huge Defining what to build + \end{center} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{The CMake Process} + + \begin{columns} + \begin{column}{0.425\textwidth} + CMake has two main steps: + \begin{itemize} + \item Configure step. + \item Generate step. + \end{itemize} + \end{column} + + \begin{column}{0.575\textwidth} + \begin{figure} + \centering + \includegraphics[width=.8\textwidth,keepaspectratio]{images/cmake-simple-flowchart.png} + \caption*{\tiny 2017 © Jeff Preshing \url{http://preshing.com/20170511/how-to-build-a-cmake-based-project/}} + \end{figure} + \end{column} + \end{columns} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{$1^{st}$ step - Configuration} + + In the \emph{configure} step CMake processes all the input given to + it, via \texttt{CMakeLists.txt}, and creates an internal + representation of the build to be performed. \texttt{CMakeCache.txt} + is generated at this step. + + \vspace{1em} + + The end of this step is expressed by \texttt{"Configuring done"} + message from CMake. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{$2^{nd}$ step - Generation} + + In the \emph{generate} step CMake will produce native build tool + files e.g. Visual Studio solution files, XCode project files, Ninja + build files etc. + + \vspace{1em} + + The end of this step is expressed by \texttt{"Generating done"} + message from CMake. +\end{frame} + +% -------------------------------------------------------------------- + +\subsection{About targets} + +\begin{frame} + \frametitle{Targets and dependency propagation} + + \begin{center} + \huge Targets and their dependencies + \end{center} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Targets} + + A CMake-based buildsystem is organized as a set of high-level + logical \emph{targets}. + + \vspace{1em} + + Each target corresponds to an executable or library, or is a custom + target containing custom commands. + + \vspace{1em} + +\begin{codebox}{CMake}{} +add_library(archive archive.cpp zip.cpp lzma.cpp) +add_executable(zipapp zipapp.cpp) +target_link_libraries(zipapp archive) +\end{codebox} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Transitive Usage Requirements} + + The usage requirements of a target can transitively propagate to + dependents. + + \vspace{1em} + + The {\code{target\_link\_libraries()}} command has + \texttt{PRIVATE}, \texttt{INTERFACE}, and \texttt{PUBLIC} keywords + to control the propagation. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Transitive Usage Requirements} + + A dependency should be specified in a use of + {\code{target\_link\_libraries()}} with the: + + \begin{itemize} + \item \texttt{PRIVATE} keyword, if it is used \emph{only} by the + implementation of a library, and not in its public header files. + \item \texttt{PUBLIC} keyword, if a dependency is additionally + used in the header files of a library (e.g. any header + inclusion). + \item \texttt{INTERFACE} keyword, if is \emph{not} used by the + implementation of a library, but only by its public header + files. + \end{itemize} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Transitive Usage Requirements – Example} + +\begin{codebox}{CMake}{} +add_library(liba a.cpp) +target_compile_definitions(liba INTERFACE USING_LIB_A) + +add_library(libb b.cpp) +target_compile_definitions(libb INTERFACE USING_LIB_B) + +add_library(libc c.cpp) +target_link_libraries(libc PUBLIC liba PRIVATE libb) +# libc is compiled with -DUSING_LIB_A and -DUSING_LIB_B + +add_executable(app main.cpp) +target_link_libraries(app libc) +# app is compiled with -DUSING_LIB_A +\end{codebox} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Targets – Modifier functions} + + You should use the following functions to control your targets: + + \begin{itemize} + \item {\code{target\_include\_directories()}} – adds directories to + the include search path. + \item {\code{target\_compile\_definitions()}} – adds pre-processor + definitions. + \item {\code{target\_compile\_options()}} – adds compiler options + (\texttt{-Wall, /bigobj}). + \item {\code{target\_compile\_features()}} – adds necessary compiler + features (\texttt{cxx\_constexpr, cxx\_variadic\_templates}). + \item {\code{target\_sources()}} – adds more source files to the + target. + \item {\code{target\_link\_libraries()}} – add library dependency. + \end{itemize} +\end{frame} + +% -------------------------------------------------------------------- + +\subsection{Preparing for packaging} + +\begin{frame} + \frametitle{Locating packages} + + \begin{center} + \huge Preparing the project for packaging + \end{center} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{CMake packages} + Packages provide dependency information to CMake. + + \vspace{1em} + + Packages are found with the {\code{find\_package()}} command. + + \vspace{1em} + + The result of using {\code{find\_package()}} is either a set of + \texttt{IMPORTED} targets, or a set of variables + e.g. \texttt{\_FOUND}, \texttt{\_INCLUDE\_DIRS}, + \texttt{\_LIBRARIES}. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Packages – Example} + CMake provides direct support for two forms of packages: + \begin{itemize} + \item \texttt{CONFIG}-file packages (the new way). + \item Find-\texttt{MODULE} packages (prioritized for backwards-compat). + \end{itemize} + +\begin{codebox}{CMake}{} +# CMake provides a Boost find-module +find_package(Boost 1.60.0 MODULE REQUIRED) + +# Qt Project provides a Qt5 package config file. +find_package(Qt5Core 5.9.0 CONFIG REQUIRED + COMPONENTS Widgets) + +# Use pkg-config via the LibXml2 find-module +find_package(LibXml2 MODULE REQUIRED) +\end{codebox} +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Packages – Config-file} + + A config-file package the recommended CMake package type. + + \vspace{1em} + + The config-file package consists of a set of files provided by + upstreams for downstreams to use. + + \vspace{1em} + + The Qt Project doesn't use CMake as their own build system, but + they do provide a config-file package for CMake users. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Packages -- Find-module} + + A find-module is needed when the upstream is not built with CMake, + or is not CMake-aware enough to otherwise provide a package + configuration file. + + \vspace{1em} + + The find-module is a file with a set of rules for finding the + required pieces of a dependency, primarily header files and + libraries e.g. \texttt{FindBoost.cmake}. +\end{frame} + +% -------------------------------------------------------------------- + +\begin{frame}[containsverbatim] + \frametitle{Packages – Config-file continued} + + A config-file package can be used in two ways: + \begin{itemize} + \item to find a configured project that is already part of the + build. + \item to import an installed package. + \end{itemize} + + \vspace{1em} + + The process of creating a config-file package using CMake commands + ({\code{configure\_package\_config\_file()}}, + {\code{write\_basic\_package\_version\_file()}}, {\code{install()}}, + {\code{export()}}) is quite verbose, and error prone. +\end{frame} + +% --------------------------------------------------------------------