diff options
Diffstat (limited to 'Help/guide')
165 files changed, 7625 insertions, 0 deletions
diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst new file mode 100644 index 0000000..3a11a34 --- /dev/null +++ b/Help/guide/ide-integration/index.rst @@ -0,0 +1,161 @@ +IDE Integration Guide +********************* + +.. only:: html + + .. contents:: + +Introduction +============ + +Integrated development environments (IDEs) may want to integrate with CMake to +improve the development experience for CMake users. This document lays out the +recommended best practices for such integration. + +Bundling +======== + +Many IDE vendors will want to bundle a copy of CMake with their IDE. IDEs that +bundle CMake should present the user with the option of using an external CMake +installation instead of the bundled one, in case the bundled copy becomes +outdated and the user wants to use a newer version. + +While IDE vendors may be tempted to bundle different versions of CMake with +their application, such practice is not recommended. CMake has strong +guarantees of backwards compatibility, and there is no reason not to use a +newer version of CMake than what a project requires, or indeed, the very latest +version. Therefore, it is recommended that IDE vendors that bundle CMake with +their application always include the very latest patch version of CMake +available at the time of release. + +As a suggestion, IDEs may also ship a copy of the Ninja buildsystem alongside +CMake. Ninja is highly performant and well-supported on all platforms that +support CMake. IDEs that bundle Ninja should use Ninja 1.10 or later, which +contains features needed to support Fortran builds. + +Presets +======= + +CMake supports a file format called ``CMakePresets.json``, and its +user-specific counterpart, ``CMakeUserPresets.json``. This file contains +information on the various configure presets that a user may want. Each preset +may have a different compiler, build flags, etc. The details of this format are +explained in the :manual:`cmake(1)` manual. + +IDE vendors are encouraged to read and evaluate this file the same way CMake +does, and present the user with the presets listed in the file. Users should be +able to see (and possibly edit) the CMake cache variables, environment +variables, and command line options that are defined for a given preset. The +IDE should then construct the list of appropriate :manual:`cmake(1)` command +line arguments based on these settings, rather than using the +:option:`--preset= <cmake --preset>` option directly. The +:option:`--preset= <cmake --preset>` option is intended only as a convenient +frontend for command line users, and should not be used by the IDE. + +For example, if a preset named ``ninja`` specifies ``Ninja`` as the generator +and ``${sourceDir}/build`` as the build directory, instead of running: + +.. code-block:: console + + cmake -S /path/to/source --preset=ninja + +the IDE should instead calculate the settings of the ``ninja`` preset, and then +run: + +.. code-block:: console + + cmake -S /path/to/source -B /path/to/source/build -G Ninja + +In cases where a preset contains lots of cache variables, and passing all of +them as :option:`-D <cmake -D>` flags would cause the command line length limit +of the platform to be exceeded, the IDE should instead construct a temporary +cache script and pass it with the :option:`-C <cmake -C>` flag. + +While reading, parsing, and evaluating the contents of ``CMakePresets.json`` is +straightforward, it is not trivial. In addition to the documentation, IDE +vendors may also wish to refer to the CMake source code and test cases for a +better understanding of how to implement the format. +:download:`This file <../../manual/presets/schema.json>` provides a +machine-readable JSON schema for the ``CMakePresets.json`` format that IDE +vendors may find useful for validation and providing editing assistance. + +Configuring +=========== + +IDEs that invoke :manual:`cmake(1)` to run the configure step may wish to +receive information about the artifacts that the build will produce, as well +as the include directories, compile definitions, etc. used to build the +artifacts. Such information can be obtained by using the +:manual:`File API <cmake-file-api(7)>`. The manual page for the File API +contains more information about the API and how to invoke it. +:manual:`Server mode <cmake-server(7)>` was removed as of CMake 3.20 and +should not be used on CMake 3.14 or later. + +IDEs should avoid creating more build trees than necessary, and only create +multiple build trees if the user wishes to switch to a different compiler, +use different compile flags, etc. In particular, IDEs should NOT create +multiple single-config build trees which all have the same properties except +for a differing :variable:`CMAKE_BUILD_TYPE`, effectively creating a +multi-config environment. Instead, the :generator:`Ninja Multi-Config` +generator, in conjunction with the :manual:`File API <cmake-file-api(7)>` to +get the list of build configurations, should be used for this purpose. + +IDEs should not use the "extra generators" with Makefile or Ninja generators, +which generate IDE project files in addition to the Makefile or Ninja files. +Instead the :manual:`File API <cmake-file-api(7)>` should be used to get the +list of build artifacts. + +Building +======== + +If a Makefile or Ninja generator is used to generate the build tree, it is not +recommended to invoke ``make`` or ``ninja`` directly. Instead, it is +recommended that the IDE invoke :manual:`cmake(1)` with the +:option:`--build <cmake --build>` argument, which will in turn invoke the +appropriate build tool. + +If an IDE project generator is used, such as :generator:`Xcode` or one of the +Visual Studio generators, and the IDE understands the project format used, the +IDE should read the project file and build it the same way it would otherwise. + +The :manual:`File API <cmake-file-api(7)>` can be used to obtain a list of +build configurations from the build tree, and the IDE should present this list +to the user to select a build configuration. + +Testing +======= + +:manual:`ctest(1)` supports outputting a JSON format with information about the +available tests and test configurations. IDEs which want to run CTest should +obtain this information and use it to present the user with a list of tests. + +IDEs should not invoke the ``test`` target of the generated buildsystem. +Instead, they should invoke :manual:`ctest(1)` directly. + +IDEs with CMake integration +=========================== + +The following IDEs support CMake natively: + +* `CLion`_ +* `KDevelop`_ +* `QtCreator`_ +* `Vim`_ (via a plugin) +* `Visual Studio`_ +* `VSCode`_ (via a plugin) + +.. _CLion: https://www.jetbrains.com/clion/ +.. _KDevelop: https://kdevelop.org/ +.. _QtCreator: https://www.qt.io/product/development-tools +.. _Vim: https://www.vim.org/ +.. _Visual Studio: https://visualstudio.microsoft.com/ +.. _VSCode: https://code.visualstudio.com/ + +Additionally, CMake has builtin support for some IDEs: + +* :ref:`IDE Build Tool Generators`: + Generate IDE native build systems such as Visual Studio or Xcode. +* :ref:`Extra Generators`: + Extend :ref:`Command-Line Build Tool Generators` to generate IDE + project files that hook into the command-line build system. + Superseded by the :manual:`File API <cmake-file-api(7)>`. diff --git a/Help/guide/importing-exporting/Downstream/CMakeLists.txt b/Help/guide/importing-exporting/Downstream/CMakeLists.txt new file mode 100644 index 0000000..381c875 --- /dev/null +++ b/Help/guide/importing-exporting/Downstream/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.15) +project(Downstream) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# find MathFunctions +find_package(MathFunctions 3.4.1 EXACT) + +# create executable +add_executable(myexe main.cc) + +# use MathFunctions library +target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions) diff --git a/Help/guide/importing-exporting/Downstream/main.cc b/Help/guide/importing-exporting/Downstream/main.cc new file mode 100644 index 0000000..8574373 --- /dev/null +++ b/Help/guide/importing-exporting/Downstream/main.cc @@ -0,0 +1,23 @@ +// A simple program that outputs the square root of a number +#include <iostream> +#include <string> + +#include "MathFunctions.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + // calculate square root + const double sqrt = MathFunctions::sqrt(inputValue); + std::cout << "The square root of " << inputValue << " is " << sqrt + << std::endl; + + return 0; +} diff --git a/Help/guide/importing-exporting/DownstreamComponents/CMakeLists.txt b/Help/guide/importing-exporting/DownstreamComponents/CMakeLists.txt new file mode 100644 index 0000000..88b46c8 --- /dev/null +++ b/Help/guide/importing-exporting/DownstreamComponents/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(DownstreamComponents) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# find MathFunctions +find_package(MathFunctions 3.4 COMPONENTS Addition SquareRoot) + +# create executable +add_executable(myexe main.cc) + +# use MathFunctions library +target_link_libraries(myexe PRIVATE MathFunctions::Addition MathFunctions::SquareRoot) + +# Workaround for GCC on AIX to avoid -isystem, not needed in general. +set_property(TARGET myexe PROPERTY NO_SYSTEM_FROM_IMPORTED 1) diff --git a/Help/guide/importing-exporting/DownstreamComponents/main.cc b/Help/guide/importing-exporting/DownstreamComponents/main.cc new file mode 100644 index 0000000..f5e8fa6 --- /dev/null +++ b/Help/guide/importing-exporting/DownstreamComponents/main.cc @@ -0,0 +1,28 @@ +// A simple program that outputs the square root of a number +#include <iostream> +#include <string> + +#include "Addition.h" +#include "SquareRoot.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + // calculate square root + const double sqrt = MathFunctions::sqrt(inputValue); + std::cout << "The square root of " << inputValue << " is " << sqrt + << std::endl; + + // calculate sum + const double sum = MathFunctions::add(inputValue, inputValue); + std::cout << inputValue << " + " << inputValue << " = " << sum << std::endl; + + return 0; +} diff --git a/Help/guide/importing-exporting/Importing/CMakeLists.txt b/Help/guide/importing-exporting/Importing/CMakeLists.txt new file mode 100644 index 0000000..fe7d704 --- /dev/null +++ b/Help/guide/importing-exporting/Importing/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.15) +project(Importing) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Add executable +add_executable(myexe IMPORTED) + +# Set imported location +set_property(TARGET myexe PROPERTY + IMPORTED_LOCATION "../InstallMyExe/bin/myexe") + +# Add custom command to create source file +add_custom_command(OUTPUT main.cc COMMAND myexe) + +# Use source file +add_executable(mynewexe main.cc) diff --git a/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt b/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..d277b54 --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctions/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.15) +project(MathFunctions) + +# make cache variables for install destinations +include(GNUInstallDirs) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# create library +add_library(MathFunctions STATIC MathFunctions.cxx) + +# add include directories +target_include_directories(MathFunctions + PUBLIC + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" +) + +# install the target and create export-set +install(TARGETS MathFunctions + EXPORT MathFunctionsTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# install header file +install(FILES MathFunctions.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# generate and install export file +install(EXPORT MathFunctionsTargets + FILE MathFunctionsTargets.cmake + NAMESPACE MathFunctions:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions +) + +# include CMakePackageConfigHelpers macro +include(CMakePackageConfigHelpers) + +# set version +set(version 3.4.1) + +set_property(TARGET MathFunctions PROPERTY VERSION ${version}) +set_property(TARGET MathFunctions PROPERTY SOVERSION 3) +set_property(TARGET MathFunctions PROPERTY + INTERFACE_MathFunctions_MAJOR_VERSION 3) +set_property(TARGET MathFunctions APPEND PROPERTY + COMPATIBLE_INTERFACE_STRING MathFunctions_MAJOR_VERSION +) + +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" + VERSION "${version}" + COMPATIBILITY AnyNewerVersion +) + +# create config file +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions +) + +# install config files +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions +) + +# generate the export targets for the build tree +export(EXPORT MathFunctionsTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/MathFunctionsTargets.cmake" + NAMESPACE MathFunctions:: +) diff --git a/Help/guide/importing-exporting/MathFunctions/Config.cmake.in b/Help/guide/importing-exporting/MathFunctions/Config.cmake.in new file mode 100644 index 0000000..eba1ff6 --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctions/Config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake") + +check_required_components(MathFunctions) diff --git a/Help/guide/importing-exporting/MathFunctions/MathFunctions.cxx b/Help/guide/importing-exporting/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..e75fe74 --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctions/MathFunctions.cxx @@ -0,0 +1,10 @@ +#include "MathFunctions.h" + +#include <cmath> + +namespace MathFunctions { +double sqrt(double x) +{ + return std::sqrt(x); +} +} diff --git a/Help/guide/importing-exporting/MathFunctions/MathFunctions.h b/Help/guide/importing-exporting/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..b38596d --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace MathFunctions { +double sqrt(double x); +} diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/Addition/Addition.cxx b/Help/guide/importing-exporting/MathFunctionsComponents/Addition/Addition.cxx new file mode 100644 index 0000000..0a6b98b --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/Addition/Addition.cxx @@ -0,0 +1,8 @@ +#include "Addition.h" + +namespace MathFunctions { +double add(double x, double y) +{ + return x + y; +} +} diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/Addition/Addition.h b/Help/guide/importing-exporting/MathFunctionsComponents/Addition/Addition.h new file mode 100644 index 0000000..b061d5e --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/Addition/Addition.h @@ -0,0 +1,5 @@ +#pragma once + +namespace MathFunctions { +double add(double x, double y); +} diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/Addition/CMakeLists.txt b/Help/guide/importing-exporting/MathFunctionsComponents/Addition/CMakeLists.txt new file mode 100644 index 0000000..9de935e --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/Addition/CMakeLists.txt @@ -0,0 +1,30 @@ +# create library +add_library(Addition STATIC Addition.cxx) + +add_library(MathFunctions::Addition ALIAS Addition) + +# add include directories +target_include_directories(Addition + PUBLIC + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>" + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> +) + +# install the target and create export-set +install(TARGETS Addition + EXPORT AdditionTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# install header file +install(FILES Addition.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# generate and install export file +install(EXPORT AdditionTargets + FILE MathFunctionsAdditionTargets.cmake + NAMESPACE MathFunctions:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions +) diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/CMakeLists.txt b/Help/guide/importing-exporting/MathFunctionsComponents/CMakeLists.txt new file mode 100644 index 0000000..90ee89f --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) +project(MathFunctionsComponents) + +# make cache variables for install destinations +include(GNUInstallDirs) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_subdirectory(Addition) +add_subdirectory(SquareRoot) + +# include CMakePackageConfigHelpers macro +include(CMakePackageConfigHelpers) + +# set version +set(version 3.4.1) + +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" + VERSION "${version}" + COMPATIBILITY AnyNewerVersion +) + +# create config file +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) + +# install config files +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions +) diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/Config.cmake.in b/Help/guide/importing-exporting/MathFunctionsComponents/Config.cmake.in new file mode 100644 index 0000000..a535969 --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/Config.cmake.in @@ -0,0 +1,11 @@ +@PACKAGE_INIT@ + +set(_MathFunctions_supported_components Addition SquareRoot) + +foreach(_comp ${MathFunctions_FIND_COMPONENTS}) + if (NOT _comp IN_LIST _MathFunctions_supported_components) + set(MathFunctions_FOUND False) + set(MathFunctions_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}") + endif() + include("${CMAKE_CURRENT_LIST_DIR}/MathFunctions${_comp}Targets.cmake") +endforeach() diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/MathFunctions.cxx b/Help/guide/importing-exporting/MathFunctionsComponents/MathFunctions.cxx new file mode 100644 index 0000000..e75fe74 --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/MathFunctions.cxx @@ -0,0 +1,10 @@ +#include "MathFunctions.h" + +#include <cmath> + +namespace MathFunctions { +double sqrt(double x) +{ + return std::sqrt(x); +} +} diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/MathFunctions.h b/Help/guide/importing-exporting/MathFunctionsComponents/MathFunctions.h new file mode 100644 index 0000000..b38596d --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace MathFunctions { +double sqrt(double x); +} diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/CMakeLists.txt b/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/CMakeLists.txt new file mode 100644 index 0000000..517c5e2 --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/CMakeLists.txt @@ -0,0 +1,30 @@ +# create library +add_library(SquareRoot STATIC SquareRoot.cxx) + +add_library(MathFunctions::SquareRoot ALIAS SquareRoot) + +# add include directories +target_include_directories(SquareRoot + PUBLIC + "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>" + "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" +) + +# install the target and create export-set +install(TARGETS SquareRoot + EXPORT SquareRootTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +# install header file +install(FILES SquareRoot.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# generate and install export file +install(EXPORT SquareRootTargets + FILE MathFunctionsSquareRootTargets.cmake + NAMESPACE MathFunctions:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions +) diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/SquareRoot.cxx b/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/SquareRoot.cxx new file mode 100644 index 0000000..29c0c4a --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/SquareRoot.cxx @@ -0,0 +1,10 @@ +#include "SquareRoot.h" + +#include <cmath> + +namespace MathFunctions { +double sqrt(double x) +{ + return std::sqrt(x); +} +} diff --git a/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/SquareRoot.h b/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/SquareRoot.h new file mode 100644 index 0000000..b38596d --- /dev/null +++ b/Help/guide/importing-exporting/MathFunctionsComponents/SquareRoot/SquareRoot.h @@ -0,0 +1,5 @@ +#pragma once + +namespace MathFunctions { +double sqrt(double x); +} diff --git a/Help/guide/importing-exporting/MyExe/CMakeLists.txt b/Help/guide/importing-exporting/MyExe/CMakeLists.txt new file mode 100644 index 0000000..34e37a4 --- /dev/null +++ b/Help/guide/importing-exporting/MyExe/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) +project(MyExe) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Add executable +add_executable(myexe main.cxx) + +# install executable +install(TARGETS myexe) diff --git a/Help/guide/importing-exporting/MyExe/main.cxx b/Help/guide/importing-exporting/MyExe/main.cxx new file mode 100644 index 0000000..35ab2a7 --- /dev/null +++ b/Help/guide/importing-exporting/MyExe/main.cxx @@ -0,0 +1,16 @@ +// A simple program that outputs a file with the given name +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + std::ofstream outfile("main.cc"); + outfile << "int main(int argc, char* argv[])" << std::endl; + outfile << "{" << std::endl; + outfile << " // Your code here" << std::endl; + outfile << " return 0;" << std::endl; + outfile << "}" << std::endl; + outfile.close(); + + return 0; +} diff --git a/Help/guide/importing-exporting/index.rst b/Help/guide/importing-exporting/index.rst new file mode 100644 index 0000000..51a09c0 --- /dev/null +++ b/Help/guide/importing-exporting/index.rst @@ -0,0 +1,771 @@ +Importing and Exporting Guide +***************************** + +.. only:: html + + .. contents:: + +Introduction +============ + +In this guide, we will present the concept of :prop_tgt:`IMPORTED` targets +and demonstrate how to import existing executable or library files from disk +into a CMake project. We will then show how CMake supports exporting targets +from one CMake-based project and importing them into another. Finally, we +will demonstrate how to package a project with a configuration file to allow +for easy integration into other CMake projects. This guide and the complete +example source code can be found in the ``Help/guide/importing-exporting`` +directory of the CMake source code tree. + + +Importing Targets +================= + +:prop_tgt:`IMPORTED` targets are used to convert files outside of a CMake +project into logical targets inside of the project. :prop_tgt:`IMPORTED` +targets are created using the ``IMPORTED`` option of the +:command:`add_executable` and :command:`add_library` commands. No build +files are generated for :prop_tgt:`IMPORTED` targets. Once imported, +:prop_tgt:`IMPORTED` targets may be referenced like any other target within +the project and provide a convenient, flexible reference to outside +executables and libraries. + +By default, the :prop_tgt:`IMPORTED` target name has scope in the directory in +which it is created and below. We can use the ``GLOBAL`` option to extended +visibility so that the target is accessible globally in the build system. + +Details about the :prop_tgt:`IMPORTED` target are specified by setting +properties whose names begin in ``IMPORTED_`` and ``INTERFACE_``. For example, +:prop_tgt:`IMPORTED_LOCATION` contains the full path to the target on +disk. + +Importing Executables +--------------------- + +To start, we will walk through a simple example that creates an +:prop_tgt:`IMPORTED` executable target and then references it from the +:command:`add_custom_command` command. + +We'll need to do some setup to get started. We want to create an executable +that when run creates a basic ``main.cc`` file in the current directory. The +details of this project are not important. Navigate to +``Help/guide/importing-exporting/MyExe``, create a build directory, run +:manual:`cmake <cmake(1)>` and build and install the project. + +.. code-block:: console + + $ cd Help/guide/importing-exporting/MyExe + $ mkdir build + $ cd build + $ cmake .. + $ cmake --build . + $ cmake --install . --prefix <install location> + $ <install location>/myexe + $ ls + [...] main.cc [...] + +Now we can import this executable into another CMake project. The source code +for this section is available in ``Help/guide/importing-exporting/Importing``. +In the CMakeLists file, use the :command:`add_executable` command to create a +new target called ``myexe``. Use the ``IMPORTED`` option to tell CMake that +this target references an executable file located outside of the project. No +rules will be generated to build it and the :prop_tgt:`IMPORTED` target +property will be set to ``true``. + +.. literalinclude:: Importing/CMakeLists.txt + :language: cmake + :start-after: # Add executable + :end-before: # Set imported location + +Next, set the :prop_tgt:`IMPORTED_LOCATION` property of the target using +the :command:`set_property` command. This will tell CMake the location of the +target on disk. The location may need to be adjusted to the +``<install location>`` specified in the previous step. + +.. literalinclude:: Importing/CMakeLists.txt + :language: cmake + :start-after: # Set imported location + :end-before: # Add custom command + +We can now reference this :prop_tgt:`IMPORTED` target just like any target +built within the project. In this instance, let's imagine that we want to use +the generated source file in our project. Use the :prop_tgt:`IMPORTED` +target in the :command:`add_custom_command` command: + +.. literalinclude:: Importing/CMakeLists.txt + :language: cmake + :start-after: # Add custom command + :end-before: # Use source file + +As ``COMMAND`` specifies an executable target name, it will automatically be +replaced by the location of the executable given by the +:prop_tgt:`IMPORTED_LOCATION` property above. + +Finally, use the output from :command:`add_custom_command`: + +.. literalinclude:: Importing/CMakeLists.txt + :language: cmake + :start-after: # Use source file + +Importing Libraries +------------------- + +In a similar manner, libraries from other projects may be accessed through +:prop_tgt:`IMPORTED` targets. + +Note: The full source code for the examples in this section is not provided +and is left as an exercise for the reader. + +In the CMakeLists file, add an :prop_tgt:`IMPORTED` library and specify its +location on disk: + +.. code-block:: cmake + + add_library(foo STATIC IMPORTED) + set_property(TARGET foo PROPERTY + IMPORTED_LOCATION "/path/to/libfoo.a") + +Then use the :prop_tgt:`IMPORTED` library inside of our project: + +.. code-block:: cmake + + add_executable(myexe src1.c src2.c) + target_link_libraries(myexe PRIVATE foo) + + +On Windows, a .dll and its .lib import library may be imported together: + +.. code-block:: cmake + + add_library(bar SHARED IMPORTED) + set_property(TARGET bar PROPERTY + IMPORTED_LOCATION "c:/path/to/bar.dll") + set_property(TARGET bar PROPERTY + IMPORTED_IMPLIB "c:/path/to/bar.lib") + add_executable(myexe src1.c src2.c) + target_link_libraries(myexe PRIVATE bar) + +A library with multiple configurations may be imported with a single target: + +.. code-block:: cmake + + find_library(math_REL NAMES m) + find_library(math_DBG NAMES md) + add_library(math STATIC IMPORTED GLOBAL) + set_target_properties(math PROPERTIES + IMPORTED_LOCATION "${math_REL}" + IMPORTED_LOCATION_DEBUG "${math_DBG}" + IMPORTED_CONFIGURATIONS "RELEASE;DEBUG" + ) + add_executable(myexe src1.c src2.c) + target_link_libraries(myexe PRIVATE math) + +The generated build system will link ``myexe`` to ``m.lib`` when built in the +release configuration, and ``md.lib`` when built in the debug configuration. + +Exporting Targets +================= + +While :prop_tgt:`IMPORTED` targets on their own are useful, they still +require that the project that imports them knows the locations of the target +files on disk. The real power of :prop_tgt:`IMPORTED` targets is when the +project providing the target files also provides a CMake file to help import +them. A project can be setup to produce the necessary information so that it +can easily be used by other CMake projects be it from a build directory, a +local install or when packaged. + +In the remaining sections, we will walk through a set of example projects +step-by-step. The first project will create and install a library and +corresponding CMake configuration and package files. The second project will +use the generated package. + +Let's start by looking at the ``MathFunctions`` project in the +``Help/guide/importing-exporting/MathFunctions`` directory. Here we have a +header file ``MathFunctions.h`` that declares a ``sqrt`` function: + +.. literalinclude:: MathFunctions/MathFunctions.h + :language: c++ + +And a corresponding source file ``MathFunctions.cxx``: + +.. literalinclude:: MathFunctions/MathFunctions.cxx + :language: c++ + +Don't worry too much about the specifics of the C++ files, they are just meant +to be a simple example that will compile and run on many build systems. + +Now we can create a ``CMakeLists.txt`` file for the ``MathFunctions`` +project. Start by specifying the :command:`cmake_minimum_required` version and +:command:`project` name: + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :end-before: # create library + +The :module:`GNUInstallDirs` module is included in order to provide the +project with the flexibility to install into different platform layouts by +making the directories available as cache variables. + +Create a library called ``MathFunctions`` with the :command:`add_library` +command: + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # create library + :end-before: # add include directories + +And then use the :command:`target_include_directories` command to specify the +include directories for the target: + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # add include directories + :end-before: # install the target and create export-set + +We need to tell CMake that we want to use different include directories +depending on if we're building the library or using it from an installed +location. If we don't do this, when CMake creates the export information it +will export a path that is specific to the current build directory +and will not be valid for other projects. We can use +:manual:`generator expressions <cmake-generator-expressions(7)>` to specify +that if we're building the library include the current source directory. +Otherwise, when installed, include the ``include`` directory. See the `Creating +Relocatable Packages`_ section for more details. + +The :command:`install(TARGETS)` and :command:`install(EXPORT)` commands +work together to install both targets (a library in our case) and a CMake +file designed to make it easy to import the targets into another CMake project. + +First, in the :command:`install(TARGETS)` command we will specify the target, +the ``EXPORT`` name and the destinations that tell CMake where to install the +targets. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # install the target and create export-set + :end-before: # install header file + +Here, the ``EXPORT`` option tells CMake to create an export called +``MathFunctionsTargets``. The generated :prop_tgt:`IMPORTED` targets have +appropriate properties set to define their +:ref:`usage requirements <Target Usage Requirements>`, such as +:prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`, +:prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` and other relevant built-in +``INTERFACE_`` properties. The ``INTERFACE`` variant of user-defined +properties listed in :prop_tgt:`COMPATIBLE_INTERFACE_STRING` and other +:ref:`Compatible Interface Properties` are also propagated to the +generated :prop_tgt:`IMPORTED` targets. For example, in this case, the +:prop_tgt:`IMPORTED` target will have its +:prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` property populated with +the directory specified by the ``INCLUDES DESTINATION`` property. As a +relative path was given, it is treated as relative to the +:variable:`CMAKE_INSTALL_PREFIX`. + +Note, we have *not* asked CMake to install the export yet. + +We don't want to forget to install the ``MathFunctions.h`` header file with the +:command:`install(FILES)` command. The header file should be installed to the +``include`` directory, as specified by the +:command:`target_include_directories` command above. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # install header file + :end-before: # generate and install export file + +Now that the ``MathFunctions`` library and header file are installed, we also +need to explicitly install the ``MathFunctionsTargets`` export details. Use +the :command:`install(EXPORT)` command to export the targets in +``MathFunctionsTargets``, as defined by the :command:`install(TARGETS)` +command. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # generate and install export file + :end-before: # include CMakePackageConfigHelpers macro + +This command generates the ``MathFunctionsTargets.cmake`` file and arranges +to install it to ``lib/cmake``. The file contains code suitable for +use by downstreams to import all targets listed in the install command from +the installation tree. + +The ``NAMESPACE`` option will prepend ``MathFunctions::`` to the target names +as they are written to the export file. This convention of double-colons +gives CMake a hint that the name is an :prop_tgt:`IMPORTED` target when it +is used by downstream projects. This way, CMake can issue a diagnostic +message if the package providing it was not found. + +The generated export file contains code that creates an :prop_tgt:`IMPORTED` library. + +.. code-block:: cmake + + # Create imported target MathFunctions::MathFunctions + add_library(MathFunctions::MathFunctions STATIC IMPORTED) + + set_target_properties(MathFunctions::MathFunctions PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include" + ) + +This code is very similar to the example we created by hand in the +`Importing Libraries`_ section. Note that ``${_IMPORT_PREFIX}`` is computed +relative to the file location. + +An outside project may load this file with the :command:`include` command and +reference the ``MathFunctions`` library from the installation tree as if it +were built in its own tree. For example: + +.. code-block:: cmake + :linenos: + + include(${INSTALL_PREFIX}/lib/cmake/MathFunctionTargets.cmake) + add_executable(myexe src1.c src2.c ) + target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions) + +Line 1 loads the target CMake file. Although we only exported a single +target, this file may import any number of targets. Their locations are +computed relative to the file location so that the install tree may be +easily moved. Line 3 references the imported ``MathFunctions`` library. The +resulting build system will link to the library from its installed location. + +Executables may also be exported and imported using the same process. + +Any number of target installations may be associated with the same +export name. Export names are considered global so any directory may +contribute a target installation. The :command:`install(EXPORT)` command only +needs to be called once to install a file that references all targets. Below +is an example of how multiple exports may be combined into a single +export file, even if they are in different subdirectories of the project. + +.. code-block:: cmake + + # A/CMakeLists.txt + add_executable(myexe src1.c) + install(TARGETS myexe DESTINATION lib/myproj + EXPORT myproj-targets) + + # B/CMakeLists.txt + add_library(foo STATIC foo1.c) + install(TARGETS foo DESTINATION lib EXPORTS myproj-targets) + + # Top CMakeLists.txt + add_subdirectory (A) + add_subdirectory (B) + install(EXPORT myproj-targets DESTINATION lib/myproj) + +Creating Packages +----------------- + +At this point, the ``MathFunctions`` project is exporting the target +information required to be used by other projects. We can make this project +even easier for other projects to use by generating a configuration file so +that the CMake :command:`find_package` command can find our project. + +To start, we will need to make a few additions to the ``CMakeLists.txt`` +file. First, include the :module:`CMakePackageConfigHelpers` module to get +access to some helper functions for creating config files. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # include CMakePackageConfigHelpers macro + :end-before: # set version + +Then we will create a package configuration file and a package version file. + +Creating a Package Configuration File +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use the :command:`configure_package_config_file` command provided by the +:module:`CMakePackageConfigHelpers` to generate the package configuration +file. Note that this command should be used instead of the plain +:command:`configure_file` command. It helps to ensure that the resulting +package is relocatable by avoiding hardcoded paths in the installed +configuration file. The path given to ``INSTALL_DESTINATION`` must be the +destination where the ``MathFunctionsConfig.cmake`` file will be installed. +We will examine the contents of the package configuration file in the next +section. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # create config file + :end-before: # install config files + +Install the generated configuration files with the :command:`INSTALL(files)` +command. Both ``MathFunctionsConfigVersion.cmake`` and +``MathFunctionsConfig.cmake`` are installed to the same location, completing +the package. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # install config files + :end-before: # generate the export targets for the build tree + +Now we need to create the package configuration file itself. In this case, the +``Config.cmake.in`` file is very simple but sufficient to allow downstreams +to use the :prop_tgt:`IMPORTED` targets. + +.. literalinclude:: MathFunctions/Config.cmake.in + +The first line of the file contains only the string ``@PACKAGE_INIT@``. This +expands when the file is configured and allows the use of relocatable paths +prefixed with ``PACKAGE_``. It also provides the ``set_and_check()`` and +``check_required_components()`` macros. + +The ``check_required_components`` helper macro ensures that all requested, +non-optional components have been found by checking the +``<Package>_<Component>_FOUND`` variables for all required components. This +macro should be called at the end of the package configuration file even if the +package does not have any components. This way, CMake can make sure that the +downstream project hasn't specified any non-existent components. If +``check_required_components`` fails, the ``<Package>_FOUND`` variable is set to +FALSE, and the package is considered to be not found. + +The ``set_and_check()`` macro should be used in configuration files instead +of the normal ``set()`` command for setting directories and file locations. +If a referenced file or directory does not exist, the macro will fail. + +If any macros should be provided by the ``MathFunctions`` package, they should +be in a separate file which is installed to the same location as the +``MathFunctionsConfig.cmake`` file, and included from there. + +**All required dependencies of a package must also be found in the package +configuration file.** Let's imagine that we require the ``Stats`` library in +our project. In the CMakeLists file, we would add: + +.. code-block:: cmake + + find_package(Stats 2.6.4 REQUIRED) + target_link_libraries(MathFunctions PUBLIC Stats::Types) + +As the ``Stats::Types`` target is a ``PUBLIC`` dependency of ``MathFunctions``, +downstreams must also find the ``Stats`` package and link to the +``Stats::Types`` library. The ``Stats`` package should be found in the +configuration file to ensure this. + +.. code-block:: cmake + + include(CMakeFindDependencyMacro) + find_dependency(Stats 2.6.4) + +The ``find_dependency`` macro from the :module:`CMakeFindDependencyMacro` +module helps by propagating whether the package is ``REQUIRED``, or +``QUIET``, etc. The ``find_dependency`` macro also sets +``MathFunctions_FOUND`` to ``False`` if the dependency is not found, along +with a diagnostic that the ``MathFunctions`` package cannot be used without +the ``Stats`` package. + +**Exercise:** Add a required library to the ``MathFunctions`` project. + +Creating a Package Version File +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The :module:`CMakePackageConfigHelpers` module provides the +:command:`write_basic_package_version_file` command for creating a simple +package version file. This file is read by CMake when :command:`find_package` +is called to determine the compatibility with the requested version, and to set +some version-specific variables such as ``<PackageName>_VERSION``, +``<PackageName>_VERSION_MAJOR``, ``<PackageName>_VERSION_MINOR``, etc. See +:manual:`cmake-packages <cmake-packages(7)>` documentation for more details. + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # set version + :end-before: # create config file + +In our example, ``MathFunctions_MAJOR_VERSION`` is defined as a +:prop_tgt:`COMPATIBLE_INTERFACE_STRING` which means that it must be +compatible among the dependencies of any depender. By setting this +custom defined user property in this version and in the next version of +``MathFunctions``, :manual:`cmake <cmake(1)>` will issue a diagnostic if +there is an attempt to use version 3 together with version 4. Packages can +choose to employ such a pattern if different major versions of the package +are designed to be incompatible. + + +Exporting Targets from the Build Tree +------------------------------------- + +Typically, projects are built and installed before being used by an outside +project. However, in some cases, it is desirable to export targets directly +from a build tree. The targets may then be used by an outside project that +references the build tree with no installation involved. The :command:`export` +command is used to generate a file exporting targets from a project build tree. + +If we want our example project to also be used from a build directory we only +have to add the following to ``CMakeLists.txt``: + +.. literalinclude:: MathFunctions/CMakeLists.txt + :language: cmake + :start-after: # generate the export targets for the build tree + +Here we use the :command:`export` command to generate the export targets for +the build tree. In this case, we'll create a file called +``MathFunctionsTargets.cmake`` in the ``cmake`` subdirectory of the build +directory. The generated file contains the required code to import the target +and may be loaded by an outside project that is aware of the project build +tree. This file is specific to the build-tree, and **is not relocatable**. + +It is possible to create a suitable package configuration file and package +version file to define a package for the build tree which may be used without +installation. Consumers of the build tree can simply ensure that the +:variable:`CMAKE_PREFIX_PATH` contains the build directory, or set the +``MathFunctions_DIR`` to ``<build_dir>/MathFunctions`` in the cache. + +An example application of this feature is for building an executable on a host +platform when cross-compiling. The project containing the executable may be +built on the host platform and then the project that is being cross-compiled +for another platform may load it. + +Building and Installing a Package +--------------------------------- + +At this point, we have generated a relocatable CMake configuration for our +project that can be used after the project has been installed. Let's try to +build the ``MathFunctions`` project: + +.. code-block:: console + + mkdir MathFunctions_build + cd MathFunctions_build + cmake ../MathFunctions + cmake --build . + +In the build directory, notice that the file ``MathFunctionsTargets.cmake`` +has been created in the ``cmake`` subdirectory. + +Now install the project: + +.. code-block:: console + + $ cmake --install . --prefix "/home/myuser/installdir" + +Creating Relocatable Packages +============================= + +Packages created by :command:`install(EXPORT)` are designed to be relocatable, +using paths relative to the location of the package itself. They must not +reference absolute paths of files on the machine where the package is built +that will not exist on the machines where the package may be installed. + +When defining the interface of a target for ``EXPORT``, keep in mind that the +include directories should be specified as relative paths to the +:variable:`CMAKE_INSTALL_PREFIX` but should not explicitly include the +:variable:`CMAKE_INSTALL_PREFIX`: + +.. code-block:: cmake + + target_include_directories(tgt INTERFACE + # Wrong, not relocatable: + $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/TgtName> + ) + + target_include_directories(tgt INTERFACE + # Ok, relocatable: + $<INSTALL_INTERFACE:include/TgtName> + ) + +The :genex:`$<INSTALL_PREFIX>` generator expression may be used as +a placeholder for the install prefix without resulting in a non-relocatable +package. This is necessary if complex generator expressions are used: + +.. code-block:: cmake + + target_include_directories(tgt INTERFACE + # Ok, relocatable: + $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/TgtName> + ) + +This also applies to paths referencing external dependencies. +It is not advisable to populate any properties which may contain +paths, such as :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` or +:prop_tgt:`INTERFACE_LINK_LIBRARIES`, with paths relevant to dependencies. +For example, this code may not work well for a relocatable package: + +.. code-block:: cmake + + target_link_libraries(MathFunctions INTERFACE + ${Foo_LIBRARIES} ${Bar_LIBRARIES} + ) + target_include_directories(MathFunctions INTERFACE + "$<INSTALL_INTERFACE:${Foo_INCLUDE_DIRS};${Bar_INCLUDE_DIRS}>" + ) + +The referenced variables may contain the absolute paths to libraries +and include directories **as found on the machine the package was made on**. +This would create a package with hard-coded paths to dependencies not +suitable for relocation. + +Ideally such dependencies should be used through their own +:ref:`IMPORTED targets <Imported Targets>` that have their own +:prop_tgt:`IMPORTED_LOCATION` and usage requirement properties +such as :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` populated +appropriately. Those imported targets may then be used with +the :command:`target_link_libraries` command for ``MathFunctions``: + +.. code-block:: cmake + + target_link_libraries(MathFunctions INTERFACE Foo::Foo Bar::Bar) + +With this approach the package references its external dependencies +only through the names of :ref:`IMPORTED targets <Imported Targets>`. +When a consumer uses the installed package, the consumer will run the +appropriate :command:`find_package` commands (via the ``find_dependency`` +macro described above) to find the dependencies and populate the +imported targets with appropriate paths on their own machine. + +Using the Package Configuration File +==================================== + +Now we're ready to create a project to use the installed ``MathFunctions`` +library. In this section we will be using source code from +``Help\guide\importing-exporting\Downstream``. In this directory, there is a +source file called ``main.cc`` that uses the ``MathFunctions`` library to +calculate the square root of a given number and then prints the results: + +.. literalinclude:: Downstream/main.cc + :language: c++ + +As before, we'll start with the :command:`cmake_minimum_required` and +:command:`project` commands in the ``CMakeLists.txt`` file. For this project, +we'll also specify the C++ standard. + +.. literalinclude:: Downstream/CMakeLists.txt + :language: cmake + :end-before: # find MathFunctions + +We can use the :command:`find_package` command: + +.. literalinclude:: Downstream/CMakeLists.txt + :language: cmake + :start-after: # find MathFunctions + :end-before: # create executable + +Create an executable: + +.. literalinclude:: Downstream/CMakeLists.txt + :language: cmake + :start-after: # create executable + :end-before: # use MathFunctions library + +And link to the ``MathFunctions`` library: + +.. literalinclude:: Downstream/CMakeLists.txt + :language: cmake + :start-after: # use MathFunctions library + +That's it! Now let's try to build the ``Downstream`` project. + +.. code-block:: console + + mkdir Downstream_build + cd Downstream_build + cmake ../Downstream + cmake --build . + +A warning may have appeared during CMake configuration: + +.. code-block:: console + + CMake Warning at CMakeLists.txt:4 (find_package): + By not providing "FindMathFunctions.cmake" in CMAKE_MODULE_PATH this + project has asked CMake to find a package configuration file provided by + "MathFunctions", but CMake did not find one. + + Could not find a package configuration file provided by "MathFunctions" + with any of the following names: + + MathFunctionsConfig.cmake + mathfunctions-config.cmake + + Add the installation prefix of "MathFunctions" to CMAKE_PREFIX_PATH or set + "MathFunctions_DIR" to a directory containing one of the above files. If + "MathFunctions" provides a separate development package or SDK, be sure it + has been installed. + +Set the ``CMAKE_PREFIX_PATH`` to where MathFunctions was installed previously +and try again. Ensure that the newly created executable runs as expected. + +Adding Components +================= + +Let's edit the ``MathFunctions`` project to use components. The source code for +this section can be found in +``Help\guide\importing-exporting\MathFunctionsComponents``. The CMakeLists file +for this project adds two subdirectories: ``Addition`` and ``SquareRoot``. + +.. literalinclude:: MathFunctionsComponents/CMakeLists.txt + :language: cmake + :end-before: # include CMakePackageConfigHelpers macro + +Generate and install the package configuration and package version files: + +.. literalinclude:: MathFunctionsComponents/CMakeLists.txt + :language: cmake + :start-after: # include CMakePackageConfigHelpers macro + +If ``COMPONENTS`` are specified when the downstream uses +:command:`find_package`, they are listed in the +``<PackageName>_FIND_COMPONENTS`` variable. We can use this variable to verify +that all necessary component targets are included in ``Config.cmake.in``. At +the same time, this function will act as a custom ``check_required_components`` +macro to ensure that the downstream only attempts to use supported components. + +.. literalinclude:: MathFunctionsComponents/Config.cmake.in + +Here, the ``MathFunctions_NOT_FOUND_MESSAGE`` is set to a diagnosis that the +package could not be found because an invalid component was specified. This +message variable can be set for any case where the ``_FOUND`` variable is set +to ``False``, and will be displayed to the user. + +The ``Addition`` and ``SquareRoot`` directories are similar. Let's look at one +of the CMakeLists files: + +.. literalinclude:: MathFunctionsComponents/SquareRoot/CMakeLists.txt + :language: cmake + +Now we can build the project as described in earlier sections. To test using +this package, we can use the project in +``Help\guide\importing-exporting\DownstreamComponents``. There's two +differences from the previous ``Downstream`` project. First, we need to find +the package components. Change the ``find_package`` line from: + +.. literalinclude:: Downstream/CMakeLists.txt + :language: cmake + :start-after: # find MathFunctions + :end-before: # create executable + +To: + +.. literalinclude:: DownstreamComponents/CMakeLists.txt + :language: cmake + :start-after: # find MathFunctions + :end-before: # create executable + +and the ``target_link_libraries`` line from: + +.. literalinclude:: Downstream/CMakeLists.txt + :language: cmake + :start-after: # use MathFunctions library + +To: + +.. literalinclude:: DownstreamComponents/CMakeLists.txt + :language: cmake + :start-after: # use MathFunctions library + :end-before: # Workaround for GCC on AIX to avoid -isystem + +In ``main.cc``, replace ``#include MathFunctions.h`` with: + +.. literalinclude:: DownstreamComponents/main.cc + :language: c + :start-after: #include <string> + :end-before: int main + +Finally, use the ``Addition`` library: + +.. literalinclude:: DownstreamComponents/main.cc + :language: c + :start-after: // calculate sum + :end-before: return 0; + +Build the ``Downstream`` project and confirm that it can find and use the +package components. diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst new file mode 100644 index 0000000..37b0668 --- /dev/null +++ b/Help/guide/tutorial/A Basic Starting Point.rst @@ -0,0 +1,460 @@ +Step 1: A Basic Starting Point +============================== + +Where do I start with CMake? This step will provide an introduction to some of +CMake's basic syntax, commands, and variables. As these concepts are +introduced, we will work through three exercises and create a simple CMake +project. + +Each exercise in this step will start with some background information. Then, a +goal and list of helpful resources are provided. Each file in the +``Files to Edit`` section is in the ``Step1`` directory and contains one or +more ``TODO`` comments. Each ``TODO`` represents a line or two of code to +change or add. The ``TODO`` s are intended to be completed in numerical order, +first complete ``TODO 1`` then ``TODO 2``, etc. The ``Getting Started`` +section will give some helpful hints and guide you through the exercise. Then +the ``Build and Run`` section will walk step-by-step through how to build and +test the exercise. Finally, at the end of each exercise the intended solution +is discussed. + +Also note that each step in the tutorial builds on the next. So, for example, +the starting code for ``Step2`` is the complete solution to ``Step1``. + +Exercise 1 - Building a Basic Project +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most basic CMake project is an executable built from a single source code +file. For simple projects like this, a ``CMakeLists.txt`` file with three +commands is all that is required. + +**Note:** Although upper, lower and mixed case commands are supported by CMake, +lower case commands are preferred and will be used throughout the tutorial. + +Any project's top most CMakeLists.txt must start by specifying a minimum CMake +version using the :command:`cmake_minimum_required` command. This establishes +policy settings and ensures that the following CMake functions are run with a +compatible version of CMake. + +To start a project, we use the :command:`project` command to set the project +name. This call is required with every project and should be called soon after +:command:`cmake_minimum_required`. As we will see later, this command can +also be used to specify other project level information such as the language +or version number. + +Finally, the :command:`add_executable` command tells CMake to create an +executable using the specified source code files. + +Goal +---- + +Understand how to create a simple CMake project. + +Helpful Resources +----------------- + +* :command:`add_executable` +* :command:`cmake_minimum_required` +* :command:`project` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +---------------- + +The source code for ``tutorial.cxx`` is provided in the +``Help/guide/tutorial/Step1`` directory and can be used to compute the square +root of a number. This file does not need to be edited in this step. + +In the same directory is a ``CMakeLists.txt`` file which you will complete. +Start with ``TODO 1`` and work through ``TODO 3``. + +Build and Run +------------- + +Once ``TODO 1`` through ``TODO 3`` have been completed, we are ready to build +and run our project! First, run the :manual:`cmake <cmake(1)>` executable or the +:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it +with your chosen build tool. + +For example, from the command line we could navigate to the +``Help/guide/tutorial`` directory of the CMake source code tree and create a +build directory: + +.. code-block:: console + + mkdir Step1_build + +Next, navigate to that build directory and run +:manual:`cmake <cmake(1)>` to configure the project and generate a native build +system: + +.. code-block:: console + + cd Step1_build + cmake ../Step1 + +Then call that build system to actually compile/link the project: + +.. code-block:: console + + cmake --build . + +Finally, try to use the newly built ``Tutorial`` with these commands: + +.. code-block:: console + + Tutorial 4294967296 + Tutorial 10 + Tutorial + +Solution +-------- + +As mentioned above, a three line ``CMakeLists.txt`` is all that we need to get +up and running. The first line is to use :command:`cmake_minimum_required` to +set the CMake version as follows: + +.. raw:: html + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step2/CMakeLists.txt + :caption: TODO 1: CMakeLists.txt + :name: CMakeLists.txt-cmake_minimum_required + :language: cmake + :end-before: # set the project name and version + +.. raw:: html + + </details> + +The next step to make a basic project is to use the :command:`project` +command as follows to set the project name: + +.. raw:: html + + <details><summary>TODO 2: Click to show/hide answer</summary> + +.. code-block:: cmake + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-project + + project(Tutorial) + +.. raw:: html + + </details> + +The last command to call for a basic project is +:command:`add_executable`. We call it as follows: + +.. raw:: html + + <details><summary>TODO 3: Click to show/hide answer</summary> + +.. literalinclude:: Step2/CMakeLists.txt + :caption: TODO 3: CMakeLists.txt + :name: CMakeLists.txt-add_executable + :language: cmake + :start-after: # add the executable + :end-before: # TODO 3: + +.. raw:: html + + </details> + +Exercise 2 - Specifying the C++ Standard +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +CMake has some special variables that are either created behind the scenes or +have meaning to CMake when set by project code. Many of these variables start +with ``CMAKE_``. Avoid this naming convention when creating variables for your +projects. Two of these special user settable variables are +:variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`. +These may be used together to specify the C++ standard needed to build the +project. + +Goal +---- + +Add a feature that requires C++11. + +Helpful Resources +----------------- + +* :variable:`CMAKE_CXX_STANDARD` +* :variable:`CMAKE_CXX_STANDARD_REQUIRED` +* :command:`set` + +Files to Edit +------------- + +* ``CMakeLists.txt`` +* ``tutorial.cxx`` + +Getting Started +--------------- + +Continue editing files in the ``Step1`` directory. Start with ``TODO 4`` and +complete through ``TODO 6``. + +First, edit ``tutorial.cxx`` by adding a feature that requires C++11. Then +update ``CMakeLists.txt`` to require C++11. + +Build and Run +------------- + +Let's build our project again. Since we already created a build directory and +ran CMake for Exercise 1, we can skip to the build step: + +.. code-block:: console + + cd Step1_build + cmake --build . + +Now we can try to use the newly built ``Tutorial`` with same commands as +before: + +.. code-block:: console + + Tutorial 4294967296 + Tutorial 10 + Tutorial + +Solution +-------- + +We start by adding some C++11 features to our project by replacing +``atof`` with ``std::stod`` in ``tutorial.cxx``. This looks like +the following: + +.. raw:: html + + <details><summary>TODO 4: Click to show/hide answer</summary> + +.. literalinclude:: Step2/tutorial.cxx + :caption: TODO 4: tutorial.cxx + :name: tutorial.cxx-cxx11 + :language: c++ + :start-after: // convert input to double + :end-before: // TODO 6: + +.. raw:: html + + </details> + +To complete ``TODO 5``, simply remove ``#include <cstdlib>``. + +We will need to explicitly state in the CMake code that it should use the +correct flags. One way to enable support for a specific C++ standard in CMake +is by using the :variable:`CMAKE_CXX_STANDARD` variable. For this tutorial, set +the :variable:`CMAKE_CXX_STANDARD` variable in the ``CMakeLists.txt`` file to +``11`` and :variable:`CMAKE_CXX_STANDARD_REQUIRED` to ``True``. Make sure to +add the :variable:`CMAKE_CXX_STANDARD` declarations above the call to +:command:`add_executable`. + +.. raw:: html + + <details><summary>TODO 6: Click to show/hide answer</summary> + +.. literalinclude:: Step2/CMakeLists.txt + :caption: TODO 6: CMakeLists.txt + :name: CMakeLists.txt-CXX_STANDARD + :language: cmake + :start-after: # specify the C++ standard + :end-before: # configure a header file + +.. raw:: html + + </details> + +Exercise 3 - Adding a Version Number and Configured Header File +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes it may be useful to have a variable that is defined in your +``CMakelists.txt`` file also be available in your source code. In this case, we +would like to print the project version. + +One way to accomplish this is by using a configured header file. We create an +input file with one or more variables to replace. These variables have special +syntax which looks like ``@VAR@``. +Then, we use the :command:`configure_file` command to copy the input file to a +given output file and replace these variables with the current value of ``VAR`` +in the ``CMakelists.txt`` file. + +While we could edit the version directly in the source code, using this +feature is preferred since it creates a single source of truth and avoids +duplication. + +Goal +---- + +Define and report the project's version number. + +Helpful Resources +----------------- + +* :variable:`<PROJECT-NAME>_VERSION_MAJOR` +* :variable:`<PROJECT-NAME>_VERSION_MINOR` +* :command:`configure_file` +* :command:`target_include_directories` + +Files to Edit +------------- + +* ``CMakeLists.txt`` +* ``tutorial.cxx`` + +Getting Started +--------------- + +Continue to edit files from ``Step1``. Start on ``TODO 7`` and complete through +``TODO 12``. In this exercise, we start by adding a project version number in +``CMakeLists.txt``. In that same file, use :command:`configure_file` to copy a +given input file to an output file and substitute some variable values in the +input file content. + +Next, create an input header file ``TutorialConfig.h.in`` defining version +numbers which will accept variables passed from :command:`configure_file`. + +Finally, update ``tutorial.cxx`` to print out its version number. + +Build and Run +------------- + +Let's build our project again. As before, we already created a build directory +and ran CMake so we can skip to the build step: + +.. code-block:: console + + cd Step1_build + cmake --build . + +Verify that the version number is now reported when running the executable +without any arguments. + +Solution +-------- + +In this exercise, we improve our executable by printing a version number. +While we could do this exclusively in the source code, using ``CMakeLists.txt`` +lets us maintain a single source of data for the version number. + +First, we modify the ``CMakeLists.txt`` file to use the +:command:`project` command to set both the project name and version number. +When the :command:`project` command is called, CMake defines +``Tutorial_VERSION_MAJOR`` and ``Tutorial_VERSION_MINOR`` behind the scenes. + +.. raw:: html + + <details><summary>TODO 7: Click to show/hide answer</summary> + +.. literalinclude:: Step2/CMakeLists.txt + :caption: TODO 7: CMakeLists.txt + :name: CMakeLists.txt-project-VERSION + :language: cmake + :start-after: # set the project name and version + :end-before: # specify the C++ standard + +.. raw:: html + + </details> + +Then we used :command:`configure_file` to copy the input file with the +specified CMake variables replaced: + +.. raw:: html + + <details><summary>TODO 8: Click to show/hide answer</summary> + +.. literalinclude:: Step2/CMakeLists.txt + :caption: TODO 8: CMakeLists.txt + :name: CMakeLists.txt-configure_file + :language: cmake + :start-after: # to the source code + :end-before: # TODO 2: + +.. raw:: html + + </details> + +Since the configured file will be written into the project binary +directory, we must add that directory to the list of paths to search for +include files. + +**Note:** Throughout this tutorial, we will refer to the project build and +the project binary directory interchangeably. These are the same and are not +meant to refer to a `bin/` directory. + +We used :command:`target_include_directories` to specify +where the executable target should look for include files. + +.. raw:: html + + <details><summary>TODO 9: Click to show/hide answer</summary> + +.. literalinclude:: Step2/CMakeLists.txt + :caption: TODO 9: CMakeLists.txt + :name: CMakeLists.txt-target_include_directories + :language: cmake + :start-after: # so that we will find TutorialConfig.h + +.. raw:: html + + </details> + +``TutorialConfig.h.in`` is the input header file to be configured. +When :command:`configure_file` is called from our ``CMakeLists.txt``, the +values for ``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will +be replaced with the corresponding version numbers from the project in +``TutorialConfig.h``. + +.. raw:: html + + <details><summary>TODO 10: Click to show/hide answer</summary> + +.. literalinclude:: Step2/TutorialConfig.h.in + :caption: TODO 10: TutorialConfig.h.in + :name: TutorialConfig.h.in + :language: c++ + +.. raw:: html + + </details> + +Next, we need to modify ``tutorial.cxx`` to include the configured header file, +``TutorialConfig.h``. + +.. raw:: html + + <details><summary>TODO 11: Click to show/hide answer</summary> + +.. code-block:: c++ + :caption: TODO 11: tutorial.cxx + + #include "TutorialConfig.h" + +.. raw:: html + + </details> + +Finally, we print out the executable name and version number by updating +``tutorial.cxx`` as follows: + +.. raw:: html + + <details><summary>TODO 12: Click to show/hide answer</summary> + +.. literalinclude:: Step2/tutorial.cxx + :caption: TODO 12 : tutorial.cxx + :name: tutorial.cxx-print-version + :language: c++ + :start-after: { + :end-before: // convert input to double + +.. raw:: html + + </details> diff --git a/Help/guide/tutorial/Adding Export Configuration.rst b/Help/guide/tutorial/Adding Export Configuration.rst new file mode 100644 index 0000000..6c83276 --- /dev/null +++ b/Help/guide/tutorial/Adding Export Configuration.rst @@ -0,0 +1,140 @@ +Step 11: Adding Export Configuration +==================================== + +During :guide:`tutorial/Installing and Testing` of the tutorial we added the +ability for CMake to install the library and headers of the project. During +:guide:`tutorial/Packaging an Installer` we added the ability to package up +this information so it could be distributed to other people. + +The next step is to add the necessary information so that other CMake projects +can use our project, be it from a build directory, a local install or when +packaged. + +The first step is to update our :command:`install(TARGETS)` commands to not +only specify a ``DESTINATION`` but also an ``EXPORT``. The ``EXPORT`` keyword +generates a CMake file containing code to import all targets listed in the +install command from the installation tree. So let's go ahead and explicitly +``EXPORT`` the ``MathFunctions`` library by updating the ``install`` command +in ``MathFunctions/CMakeLists.txt`` to look like: + +.. literalinclude:: Complete/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-install-TARGETS-EXPORT + :language: cmake + :start-after: # install libs + +Now that we have ``MathFunctions`` being exported, we also need to explicitly +install the generated ``MathFunctionsTargets.cmake`` file. This is done by +adding the following to the bottom of the top-level ``CMakeLists.txt``: + +.. literalinclude:: Complete/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-install-EXPORT + :language: cmake + :start-after: # install the configuration targets + :end-before: include(CMakePackageConfigHelpers) + +At this point you should try and run CMake. If everything is setup properly +you will see that CMake will generate an error that looks like: + +.. code-block:: console + + Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains + path: + + "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions" + + which is prefixed in the source directory. + +What CMake is trying to say is that during generating the export information +it will export a path that is intrinsically tied to the current machine and +will not be valid on other machines. The solution to this is to update the +``MathFunctions`` :command:`target_include_directories` to understand that it +needs different ``INTERFACE`` locations when being used from within the build +directory and from an install / package. This means converting the +:command:`target_include_directories` call for ``MathFunctions`` to look like: + +.. literalinclude:: Step12/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-target_include_directories + :language: cmake + :start-after: # to find MathFunctions.h, while we don't. + :end-before: # should we use our own math functions + +Once this has been updated, we can re-run CMake and verify that it doesn't +warn anymore. + +At this point, we have CMake properly packaging the target information that is +required but we will still need to generate a ``MathFunctionsConfig.cmake`` so +that the CMake :command:`find_package` command can find our project. So let's go +ahead and add a new file to the top-level of the project called +``Config.cmake.in`` with the following contents: + +.. literalinclude:: Step12/Config.cmake.in + :caption: Config.cmake.in + :name: Config.cmake.in + +Then, to properly configure and install that file, add the following to the +bottom of the top-level ``CMakeLists.txt``: + +.. literalinclude:: Step12/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-install-Config.cmake + :language: cmake + :start-after: # install the configuration targets + :end-before: # generate the config file + + +Next, we execute the :command:`configure_package_config_file`. This command +will configure a provided file but with a few specific differences from the +standard :command:`configure_file` way. +To properly utilize this function, the input file should have a single line +with the text ``@PACKAGE_INIT@`` in addition to the content that is desired. +That variable will be replaced with a block of code which turns set values into +relative paths. These values which are new can be referenced by the same name +but prepended with a ``PACKAGE_`` prefix. + +.. literalinclude:: Step12/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-configure-package-config.cmake + :language: cmake + :start-after: # install the configuration targets + :end-before: # generate the version file + +The :command:`write_basic_package_version_file` is next. This command writes +a file which is used by :command:`find_package`, documenting the version and +compatibility of the desired package. Here, we use the ``Tutorial_VERSION_*`` +variables and say that it is compatible with ``AnyNewerVersion``, which +denotes that this version or any higher one are compatible with the requested +version. + +.. literalinclude:: Step12/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-basic-version-file.cmake + :language: cmake + :start-after: # generate the version file + :end-before: # install the generated configuration files + +Finally, set both generated files to be installed: + +.. literalinclude:: Step12/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-install-configured-files.cmake + :language: cmake + :start-after: # install the generated configuration files + :end-before: # generate the export + +At this point, we have generated a relocatable CMake Configuration for our +project that can be used after the project has been installed or packaged. If +we want our project to also be used from a build directory we only have to add +the following to the bottom of the top level ``CMakeLists.txt``: + +.. literalinclude:: Step12/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-export + :language: cmake + :start-after: # needs to be after the install(TARGETS) command + +With this export call we now generate a ``MathFunctionsTargets.cmake``, allowing the +configured ``MathFunctionsConfig.cmake`` in the build directory to be used by +other projects, without needing it to be installed. diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst new file mode 100644 index 0000000..08f3eea --- /dev/null +++ b/Help/guide/tutorial/Adding Generator Expressions.rst @@ -0,0 +1,306 @@ +Step 4: Adding Generator Expressions +===================================== + +:manual:`Generator expressions <cmake-generator-expressions(7)>` are evaluated +during build system generation to produce information specific to each build +configuration. + +:manual:`Generator expressions <cmake-generator-expressions(7)>` are allowed in +the context of many target properties, such as :prop_tgt:`LINK_LIBRARIES`, +:prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_DEFINITIONS` and others. +They may also be used when using commands to populate those properties, such as +:command:`target_link_libraries`, :command:`target_include_directories`, +:command:`target_compile_definitions` and others. + +:manual:`Generator expressions <cmake-generator-expressions(7)>` may be used +to enable conditional linking, conditional definitions used when compiling, +conditional include directories and more. The conditions may be based on the +build configuration, target properties, platform information or any other +queryable information. + +There are different types of +:manual:`generator expressions <cmake-generator-expressions(7)>` including +Logical, Informational, and Output expressions. + +Logical expressions are used to create conditional output. The basic +expressions are the ``0`` and ``1`` expressions. A ``$<0:...>`` results in the +empty string, and ``<1:...>`` results in the content of ``...``. They can also +be nested. + +Exercise 1 - Setting the C++ Standard with Interface Libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before we use :manual:`generator expressions <cmake-generator-expressions(7)>` +let's refactor our existing code to use an ``INTERFACE`` library. We will +use that library in the next step to demonstrate a common use for +:manual:`generator expressions <cmake-generator-expressions(7)>`. + +Goal +---- + +Add an ``INTERFACE`` library target to specify the required C++ standard. + +Helpful Resources +----------------- + +* :command:`add_library` +* :command:`target_compile_features` +* :command:`target_link_libraries` + +Files to Edit +------------- + +* ``CMakeLists.txt`` +* ``MathFunctions/CMakeLists.txt`` + +Getting Started +--------------- + +In this exercise, we will refactor our code to use an ``INTERFACE`` library to +specify the C++ standard. + +The starting source code is provided in the ``Step4`` directory. In this +exercise, complete ``TODO 1`` through ``TODO 3``. + +Start by editing the top level ``CMakeLists.txt`` file. Construct an +``INTERFACE`` library target called ``tutorial_compiler_flags`` and +specify ``cxx_std_11`` as a target compiler feature. + +Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all +targets have a :command:`target_link_libraries` call to +``tutorial_compiler_flags``. + +Build and Run +------------- + +Make a new directory called ``Step4_build``, run the :manual:`cmake <cmake(1)>` +executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project +and then build it with your chosen build tool or by using ``cmake --build .`` +from the build directory. + +Here's a refresher of what that looks like from the command line: + +.. code-block:: console + + mkdir Step4_build + cd Step4_build + cmake ../Step4 + cmake --build . + +Next, use the newly built ``Tutorial`` and verify that it is working as +expected. + +Solution +-------- + +Let's update our code from the previous step to use interface libraries +to set our C++ requirements. + +To start, we need to remove the two :command:`set` calls on the variables +:variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`. +The specific lines to remove are as follows: + +.. literalinclude:: Step4/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-CXX_STANDARD-variable-remove + :language: cmake + :start-after: # specify the C++ standard + :end-before: # TODO 5: Create helper variables + +Next, we need to create an interface library, ``tutorial_compiler_flags``. And +then use :command:`target_compile_features` to add the compiler feature +``cxx_std_11``. + + +.. raw:: html + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step5/CMakeLists.txt + :caption: TODO 1: CMakeLists.txt + :name: CMakeLists.txt-cxx_std-feature + :language: cmake + :start-after: # specify the C++ standard + :end-before: # add compiler warning flags just + +.. raw:: html + + </details> + +Finally, with our interface library set up, we need to link our +executable ``Target`` and our ``MathFunctions`` library to our new +``tutorial_compiler_flags`` library. Respectively, the code will look like +this: + +.. raw:: html + + <details><summary>TODO 2: Click to show/hide answer</summary> + +.. literalinclude:: Step5/CMakeLists.txt + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-target_link_libraries-step4 + :language: cmake + :start-after: add_executable(Tutorial tutorial.cxx) + :end-before: # add the binary tree to the search path for include file + +.. raw:: html + + </details> + +and this: + +.. raw:: html + + <details><summary>TODO 3: Click to show/hide answer</summary> + +.. literalinclude:: Step5/MathFunctions/CMakeLists.txt + :caption: TODO 3: MathFunctions/CMakeLists.txt + :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4 + :language: cmake + :start-after: # link our compiler flags interface library + :end-before: # TODO 1 + +.. raw:: html + + </details> + +With this, all of our code still requires C++ 11 to build. Notice +though that with this method, it gives us the ability to be specific about +which targets get specific requirements. In addition, we create a single +source of truth in our interface library. + +Exercise 2 - Adding Compiler Warning Flags with Generator Expressions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A common usage of +:manual:`generator expressions <cmake-generator-expressions(7)>` is to +conditionally add compiler flags, such as those for language levels or +warnings. A nice pattern is to associate this information to an ``INTERFACE`` +target allowing this information to propagate. + +Goal +---- + +Add compiler warning flags when building but not for installed versions. + +Helpful Resources +----------------- + +* :manual:`cmake-generator-expressions(7)` +* :command:`cmake_minimum_required` +* :command:`set` +* :command:`target_compile_options` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +Start with the resulting files from Exercise 1. Complete ``TODO 4`` through +``TODO 7``. + +First, in the top level ``CMakeLists.txt`` file, we need to set the +:command:`cmake_minimum_required` to ``3.15``. In this exercise we are going +to use a generator expression which was introduced in CMake 3.15. + +Next we add the desired compiler warning flags that we want for our project. +As warning flags vary based on the compiler, we use the +``COMPILE_LANG_AND_ID`` generator expression to control which flags to apply +given a language and a set of compiler ids. + +Build and Run +------------- + +Since we have our build directory already configured from Exercise 1, simply +rebuild our code by calling the following: + +.. code-block:: console + + cd Step4_build + cmake --build . + +Solution +-------- + +Update the :command:`cmake_minimum_required` to require at least CMake +version ``3.15``: + +.. raw:: html + + <details><summary>TODO 4: Click to show/hide answer</summary> + +.. literalinclude:: Step5/CMakeLists.txt + :caption: TODO 4: CMakeLists.txt + :name: MathFunctions-CMakeLists.txt-minimum-required-step4 + :language: cmake + :end-before: # set the project name and version + +.. raw:: html + + </details> + +Next we determine which compiler our system is currently using to build +since warning flags vary based on the compiler we use. This is done with +the ``COMPILE_LANG_AND_ID`` generator expression. We set the result in the +variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows: + +.. raw:: html + + <details><summary>TODO 5: Click to show/hide answer</summary> + +.. literalinclude:: Step5/CMakeLists.txt + :caption: TODO 5: CMakeLists.txt + :name: CMakeLists.txt-compile_lang_and_id + :language: cmake + :start-after: # the BUILD_INTERFACE genex + :end-before: target_compile_options(tutorial_compiler_flags INTERFACE + +.. raw:: html + + </details> + +Next we add the desired compiler warning flags that we want for our project. +Using our variables ``gcc_like_cxx`` and ``msvc_cxx``, we can use another +generator expression to apply the respective flags only when the variables are +true. We use :command:`target_compile_options` to apply these flags to our +interface library. + +.. raw:: html + + <details><summary>TODO 6: Click to show/hide answer</summary> + +.. code-block:: cmake + :caption: TODO 6: CMakeLists.txt + :name: CMakeLists.txt-compile_flags + + target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>" + "$<${msvc_cxx}:-W3>" + ) + +.. raw:: html + + </details> + +Lastly, we only want these warning flags to be used during builds. Consumers +of our installed project should not inherit our warning flags. To specify +this, we wrap our flags in a generator expression using the ``BUILD_INTERFACE`` +condition. The resulting full code looks like the following: + +.. raw:: html + + <details><summary>TODO 7: Click to show/hide answer</summary> + +.. literalinclude:: Step5/CMakeLists.txt + :caption: TODO 7: CMakeLists.txt + :name: CMakeLists.txt-target_compile_options-genex + :language: cmake + :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") + :end-before: # configure a header file to pass some of the CMake settings + +.. raw:: html + + </details> diff --git a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst new file mode 100644 index 0000000..787e777 --- /dev/null +++ b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst @@ -0,0 +1,110 @@ +Step 6: Adding Support for a Testing Dashboard +============================================== + +Adding support for submitting our test results to a dashboard is simple. We +already defined a number of tests for our project in +:ref:`Testing Support <Tutorial Testing Support>`. Now we just have to run +those tests and submit them to CDash. + + +Exercise 1 - Send Results to a Testing Dashboard +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Goal +---- + +Display our CTest results with CDash. + +Helpful Resources +----------------- + +* :manual:`ctest(1)` +* :command:`include` +* :module:`CTest` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +For this exercise, complete ``TODO 1`` in the top-level ``CMakeLists.txt`` by +including the :module:`CTest` module. This will enable testing with CTest as +well as dashboard submissions to CDash, so we can safely remove the call to +:command:`enable_testing`. + +We will also need to acquire a ``CTestConfig.cmake`` file to be placed in the +top-level directory. When run, the :manual:`ctest <ctest(1)>` executable will +read this file to gather information about the testing dashboard. It contains: + +* The project name + +* The project "Nightly" start time + + * The time when a 24 hour "day" starts for this project. + +* The URL of the CDash instance where the submission's generated documents + will be sent + +For this tutorial, a public dashboard server is used and its corresponding +``CTestConfig.cmake`` file is provided for you in this step's root directory. +In practice, this file would be downloaded from a project's ``Settings`` page +on the CDash instance intended to host the test results. Once downloaded from +CDash, the file should not be modified locally. + +.. literalinclude:: Step7/CTestConfig.cmake + :caption: CTestConfig.cmake + :name: CTestConfig.cmake + :language: cmake + + +Build and Run +------------- + +Note that as part of the CDash submission some information about your +development system (e.g. site name or full pathnames) may displayed publicly. + +To create a simple test dashboard, run the :manual:`cmake <cmake(1)>` +executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project +but do not build it yet. Instead, navigate to the build directory and run: + +.. code-block:: console + + ctest [-VV] -D Experimental + +Remember, for multi-config generators (e.g. Visual Studio), the configuration +type must be specified: + +.. code-block:: console + + ctest [-VV] -C Debug -D Experimental + +Or, from an IDE, build the ``Experimental`` target. + +The :manual:`ctest <ctest(1)>` executable will build the project, run any +tests, and submit the results to Kitware's public dashboard: +https://my.cdash.org/index.php?project=CMakeTutorial. + +Solution +-------- + +The only CMake code changed needed in this step was to enable dashboard +submissions to CDash by including the :module:`CTest` module in our top-level +``CMakeLists.txt``: + +.. raw:: html + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step7/CMakeLists.txt + :caption: TODO 1: CMakeLists.txt + :name: CMakeLists.txt-include-CTest + :language: cmake + :start-after: # enable testing + :end-before: # does the application run + +.. raw:: html + + </details> diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst new file mode 100644 index 0000000..b314773 --- /dev/null +++ b/Help/guide/tutorial/Adding System Introspection.rst @@ -0,0 +1,163 @@ +Step 7: Adding System Introspection +=================================== + +Let us consider adding some code to our project that depends on features the +target platform may not have. For this example, we will add some code that +depends on whether or not the target platform has the ``log`` and ``exp`` +functions. Of course almost every platform has these functions but for this +tutorial assume that they are not common. + +Exercise 1 - Assessing Dependency Availability +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Goal +---- + +Change implementation based on available system dependencies. + +Helpful Resources +----------------- + +* :module:`CheckCXXSourceCompiles` +* :command:`target_compile_definitions` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/mysqrt.cxx`` + +Getting Started +--------------- + +The starting source code is provided in the ``Step7`` directory. In this +exercise, complete ``TODO 1`` through ``TODO 5``. + +Start by editing ``MathFunctions/CMakeLists.txt``. Include the +:module:`CheckCXXSourceCompiles` module. Then, use +``check_cxx_source_compiles`` to determine whether ``log`` and ``exp`` are +available from ``cmath``. If they are available, use +:command:`target_compile_definitions` to specify ``HAVE_LOG`` and ``HAVE_EXP`` +as compile definitions. + +In the ``MathFunctions/mysqrt.cxx``, include ``cmath``. Then, if the system has +``log`` and ``exp``, use them to compute the square root. + +Build and Run +------------- + +Make a new directory called ``Step7_build``. Run the +:manual:`cmake <cmake(1)>` executable or the +:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it +with your chosen build tool and run the ``Tutorial`` executable. + +This can look like the following: + +.. code-block:: console + + mkdir Step7_build + cd Step7_build + cmake ../Step7 + cmake --build . + +Which function gives better results now, ``sqrt`` or ``mysqrt``? + +Solution +-------- + +In this exercise we will use functions from the +:module:`CheckCXXSourceCompiles` module so first we must include it in +``MathFunctions/CMakeLists.txt``. + +.. raw:: html + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt + :caption: TODO 1: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-include-check_cxx_source_compiles + :language: cmake + :start-after: # does this system provide the log and exp functions? + :end-before: check_cxx_source_compiles + +.. raw:: html + + </details> + +Then test for the availability of +``log`` and ``exp`` using ``check_cxx_compiles_source``. This function +lets us try compiling simple code with the required dependency prior to +the true source code compilation. The resulting variables ``HAVE_LOG`` +and ``HAVE_EXP`` represent whether those dependencies are available. + +.. raw:: html + + <details><summary>TODO 2: Click to show/hide answer</summary> + +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt + :caption: TODO 2: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-check_cxx_source_compiles + :language: cmake + :start-after: include(CheckCXXSourceCompiles) + :end-before: # add compile definitions + +.. raw:: html + + </details> + +Next, we need to pass these CMake variables to our source code. This way, +our source code can tell what resources are available. If both ``log`` and +``exp`` are available, use :command:`target_compile_definitions` to specify +``HAVE_LOG`` and ``HAVE_EXP`` as ``PRIVATE`` compile definitions. + +.. raw:: html + + <details><summary>TODO 3: Click to show/hide answer</summary> + +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt + :caption: TODO 3: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-target_compile_definitions + :language: cmake + :start-after: # add compile definitions + :end-before: # state + +.. raw:: html + + </details> + +Since we may be using ``log`` and ``exp``, we need to modify +``mysqrt.cxx`` to include ``cmath``. + +.. raw:: html + + <details><summary>TODO 4: Click to show/hide answer</summary> + +.. literalinclude:: Step8/MathFunctions/mysqrt.cxx + :caption: TODO 4: MathFunctions/mysqrt.cxx + :name: MathFunctions/mysqrt.cxx-include-cmath + :language: c++ + :start-after: #include "mysqrt.h" + :end-before: include <iostream> + +.. raw:: html + + </details> + +If ``log`` and ``exp`` are available on the system, then use them to +compute the square root in the ``mysqrt`` function. The ``mysqrt`` function in +``MathFunctions/mysqrt.cxx`` will look as follows: + +.. raw:: html + + <details><summary>TODO 5: Click to show/hide answer</summary> + +.. literalinclude:: Step8/MathFunctions/mysqrt.cxx + :caption: TODO 5: MathFunctions/mysqrt.cxx + :name: MathFunctions/mysqrt.cxx-ifdef + :language: c++ + :start-after: // if we have both log and exp then use them + :end-before: // do ten iterations + +.. raw:: html + + </details> diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst new file mode 100644 index 0000000..74b7496 --- /dev/null +++ b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst @@ -0,0 +1,147 @@ +Step 3: Adding Usage Requirements for a Library +=============================================== + +Exercise 1 - Adding Usage Requirements for a Library +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:ref:`Usage requirements <Target Usage Requirements>` of a target parameters +allow for far better control over a library or executable's link and include +line while also giving more control over the transitive property of targets +inside CMake. The primary commands that +leverage usage requirements are: + +* :command:`target_compile_definitions` +* :command:`target_compile_options` +* :command:`target_include_directories` +* :command:`target_link_directories` +* :command:`target_link_options` +* :command:`target_precompile_headers` +* :command:`target_sources` + + +Goal +---- + +Add usage requirements for a library. + +Helpful Materials +----------------- + +* :variable:`CMAKE_CURRENT_SOURCE_DIR` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``CMakeLists.txt`` + +Getting Started +--------------- + +In this exercise, we will refactor our code from +:guide:`tutorial/Adding a Library` to use the modern CMake approach. We will +let our library define its own usage requirements so they are passed +transitively to other targets as necessary. In this case, ``MathFunctions`` +will specify any needed include directories itself. Then, the consuming target +``Tutorial`` simply needs to link to ``MathFunctions`` and not worry about +any additional include directories. + +The starting source code is provided in the ``Step3`` directory. In this +exercise, complete ``TODO 1`` through ``TODO 3``. + +First, add a call to :command:`target_include_directories` in +``MathFunctions/CMakeLists``. Remember that +:variable:`CMAKE_CURRENT_SOURCE_DIR` is the path to the source directory +currently being processed. + +Then, update (and simplify!) the call to +:command:`target_include_directories` in the top-level ``CMakeLists.txt``. + +Build and Run +------------- + +Make a new directory called ``Step3_build``, run the :manual:`cmake +<cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to +configure the project and then build it with your chosen build tool or by +using :option:`cmake --build . <cmake --build>` from the build directory. +Here's a refresher of what that looks like from the command line: + +.. code-block:: console + + mkdir Step3_build + cd Step3_build + cmake ../Step3 + cmake --build . + +Next, use the newly built ``Tutorial`` and verify that it is working as +expected. + +Solution +-------- + +Let's update the code from the previous step to use the modern CMake +approach of usage requirements. + +We want to state that anybody linking to ``MathFunctions`` needs to include +the current source directory, while ``MathFunctions`` itself doesn't. This +can be expressed with an ``INTERFACE`` usage requirement. Remember +``INTERFACE`` means things that consumers require but the producer doesn't. + +At the end of ``MathFunctions/CMakeLists.txt``, use +:command:`target_include_directories` with the ``INTERFACE`` keyword, as +follows: + +.. raw:: html + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step4/MathFunctions/CMakeLists.txt + :caption: TODO 1: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE + :language: cmake + :start-after: # to find MathFunctions.h + :end-before: option + +.. raw:: html + + </details> + +Now that we've specified usage requirements for ``MathFunctions`` we can +safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level +``CMakeLists.txt``, here: + +.. raw:: html + + <details><summary>TODO 2: Click to show/hide answer</summary> + +.. literalinclude:: Step4/CMakeLists.txt + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-remove-EXTRA_INCLUDES + :language: cmake + :start-after: # add the MathFunctions library + :end-before: # TODO 2: Link to tutorial_compiler_flags + +.. raw:: html + + </details> + +And here: + +.. raw:: html + + <details><summary>TODO 3: Click to show/hide answer</summary> + +.. literalinclude:: Step4/CMakeLists.txt + :caption: TODO 3: CMakeLists.txt + :name: CMakeLists.txt-target_include_directories-remove-EXTRA_INCLUDES + :language: cmake + :start-after: # so that we will find TutorialConfig.h + +.. raw:: html + + </details> + +Notice that with this technique, the only thing our executable target does to +use our library is call :command:`target_link_libraries` with the name +of the library target. In larger projects, the classic method of specifying +library dependencies manually becomes very complicated very quickly. diff --git a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst new file mode 100644 index 0000000..9e6311e --- /dev/null +++ b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst @@ -0,0 +1,107 @@ +Step 8: Adding a Custom Command and Generated File +================================================== + +Suppose, for the purpose of this tutorial, we decide that we never want to use +the platform ``log`` and ``exp`` functions and instead would like to +generate a table of precomputed values to use in the ``mysqrt`` function. +In this section, we will create the table as part of the build process, +and then compile that table into our application. + +First, let's remove the check for the ``log`` and ``exp`` functions in +``MathFunctions/CMakeLists.txt``. Then remove the check for ``HAVE_LOG`` and +``HAVE_EXP`` from ``mysqrt.cxx``. At the same time, we can remove +:code:`#include <cmath>`. + +In the ``MathFunctions`` subdirectory, a new source file named +``MakeTable.cxx`` has been provided to generate the table. + +After reviewing the file, we can see that the table is produced as valid C++ +code and that the output filename is passed in as an argument. + +The next step is to add the appropriate commands to the +``MathFunctions/CMakeLists.txt`` file to build the MakeTable executable and +then run it as part of the build process. A few commands are needed to +accomplish this. + +First, in the ``USE_MYMATH`` section of ``MathFunctions/CMakeLists.txt``, +we add an executable for ``MakeTable``. + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add_executable-MakeTable + :language: cmake + :start-after: # first we add the executable that generates the table + :end-before: target_link_libraries + +After creating the executable, we add the ``tutorial_compiler_flags`` to our +executable using :command:`target_link_libraries`. + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-link-tutorial-compiler-flags + :language: cmake + :start-after: add_executable + :end-before: # add the command to generate + +Then we add a custom command that specifies how to produce ``Table.h`` +by running MakeTable. + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add_custom_command-Table.h + :language: cmake + :start-after: # add the command to generate the source code + :end-before: # library that just does sqrt + +Next we have to let CMake know that ``mysqrt.cxx`` depends on the generated +file ``Table.h``. This is done by adding the generated ``Table.h`` to the list +of sources for the library MathFunctions. + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add_library-Table.h + :language: cmake + :start-after: # library that just does sqrt + :end-before: # state that we depend on + +We also have to add the current binary directory to the list of include +directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``. + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-target_include_directories-Table.h + :language: cmake + :start-after: # state that we depend on our bin + :end-before: target_link_libraries + +As the last thing in our ``USE_MYMATH`` section, we need to link the our +flags onto ``SqrtLibrary`` and then link ``SqrtLibrary`` onto +``MathFunctions``. This makes the resulting ``USE_MYMATH`` section look like +the following: + +.. literalinclude:: Step9/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-full_USE_MYMATH-section + :language: cmake + :start-after: if (USE_MYMATH) + :end-before: endif() + +Now let's use the generated table. First, modify ``mysqrt.cxx`` to include +``Table.h``. Next, we can rewrite the ``mysqrt`` function to use the table: + +.. literalinclude:: Step9/MathFunctions/mysqrt.cxx + :caption: MathFunctions/mysqrt.cxx + :name: MathFunctions/mysqrt.cxx + :language: c++ + :start-after: // a hack square root calculation using simple operations + +Run the :manual:`cmake <cmake(1)>` executable or the +:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it +with your chosen build tool. + +When this project is built it will first build the ``MakeTable`` executable. +It will then run ``MakeTable`` to produce ``Table.h``. Finally, it will +compile ``mysqrt.cxx`` which includes ``Table.h`` to produce the +``MathFunctions`` library. + +Run the Tutorial executable and verify that it is using the table. diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst new file mode 100644 index 0000000..2dd731f --- /dev/null +++ b/Help/guide/tutorial/Adding a Library.rst @@ -0,0 +1,386 @@ +Step 2: Adding a Library +======================== + +At this point, we have seen how to create a basic project using CMake. In this +step, we will learn how to create and use a library in our project. We will +also see how to make the use of our library optional. + +Exercise 1 - Creating a Library +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To add a library in CMake, use the :command:`add_library` command and specify +which source files should make up the library. + +Rather than placing all of the source files in one directory, we can organize +our project with one or more subdirectories. In this case, we will create a +subdirectory specifically for our library. Here, we can add a new +``CMakeLists.txt`` file and one or more source files. In the top level +``CMakeLists.txt`` file, we will use the :command:`add_subdirectory` command +to add the subdirectory to the build. + +Once the library is created, it is connected to our executable target with +:command:`target_include_directories` and :command:`target_link_libraries`. + +Goal +---- + +Add and use a library. + +Helpful Resources +----------------- + +* :command:`add_library` +* :command:`add_subdirectory` +* :command:`target_include_directories` +* :command:`target_link_libraries` +* :variable:`PROJECT_SOURCE_DIR` + +Files to Edit +------------- + +* ``CMakeLists.txt`` +* ``tutorial.cxx`` +* ``MathFunctions/CMakeLists.txt`` + +Getting Started +--------------- + +In this exercise, we will add a library to our project that contains our own +implementation for computing the square root of a number. The executable can +then use this library instead of the standard square root function provided by +the compiler. + +For this tutorial we will put the library into a subdirectory called +``MathFunctions``. This directory already contains the header files +``MathFunctions.h`` and ``mysqrt.h``. Their respective source files +``MathFunctions.cxx`` and ``mysqrt.cxx`` are also provided. We will not need +to modify any of these files. ``mysqrt.cxx`` has one function called +``mysqrt`` that provides similar functionality to the compiler's ``sqrt`` +function. ``MathFunctions.cxx`` contains one function ``sqrt`` which serves +to hide the implementation details of ``sqrt``. + +From the ``Help/guide/tutorial/Step2`` directory, start with ``TODO 1`` and +complete through ``TODO 6``. + +First, fill in the one line ``CMakeLists.txt`` in the ``MathFunctions`` +subdirectory. + +Next, edit the top level ``CMakeLists.txt``. + +Finally, use the newly created ``MathFunctions`` library in ``tutorial.cxx`` + +Build and Run +------------- + +Run the :manual:`cmake <cmake(1)>` executable or the +:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it +with your chosen build tool. + +Below is a refresher of what that looks like from the command line: + +.. code-block:: console + + mkdir Step2_build + cd Step2_build + cmake ../Step2 + cmake --build . + +Try to use the newly built ``Tutorial`` and ensure that it is still +producing accurate square root values. + +Solution +-------- + +In the ``CMakeLists.txt`` file in the ``MathFunctions`` directory, we create +a library target called ``MathFunctions`` with :command:`add_library`. The +source files for the library are passed as an argument to +:command:`add_library`. This looks like the following line: + +.. raw:: html/ + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step3/MathFunctions/CMakeLists.txt + :caption: TODO 1: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add_library + :language: cmake + :end-before: # TODO 1 + +.. raw:: html + + </details> + +To make use of the new library we will add an :command:`add_subdirectory` +call in the top-level ``CMakeLists.txt`` file so that the library will get +built. + +.. raw:: html + + <details><summary>TODO 2: Click to show/hide answer</summary> + +.. code-block:: cmake + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-add_subdirectory + + add_subdirectory(MathFunctions) + +.. raw:: html + + </details> + +Next, the new library target is linked to the executable target using +:command:`target_link_libraries`. + +.. raw:: html + + <details><summary>TODO 3: Click to show/hide answer</summary> + +.. code-block:: cmake + :caption: TODO 3: CMakeLists.txt + :name: CMakeLists.txt-target_link_libraries + + target_link_libraries(Tutorial PUBLIC MathFunctions) + +.. raw:: html + + </details> + +Finally we need to specify the library's header file location. Modify +:command:`target_include_directories` to add the ``MathFunctions`` subdirectory +as an include directory so that the ``MathFunctions.h`` header file can be +found. + +.. raw:: html + + <details><summary>TODO 4: Click to show/hide answer</summary> + +.. code-block:: cmake + :caption: TODO 4: CMakeLists.txt + :name: CMakeLists.txt-target_include_directories-step2 + + target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/MathFunctions" + ) + +.. raw:: html + + </details> + +Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``: + +.. raw:: html + + <details><summary>TODO 5: Click to show/hide answer</summary> + +.. literalinclude:: Step3/tutorial.cxx + :caption: TODO 5: tutorial.cxx + :name: CMakeLists.txt-include-MathFunctions.h + :language: cmake + :start-after: #include <string> + :end-before: #include "TutorialConfig.h" + +.. raw:: html + + </details> + +Lastly, replace ``sqrt`` with our library function ``mathfunctions::mysqrt``. + +.. raw:: html + + <details><summary>TODO 6: Click to show/hide answer</summary> + +.. literalinclude:: Step3/tutorial.cxx + :caption: TODO 7: tutorial.cxx + :name: CMakeLists.txt-option + :language: cmake + :start-after: const double inputValue = std::stod(argv[1]); + :end-before: std::cout + +.. raw:: html + + </details> + +Exercise 2 - Adding an Option +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Now let us add an option in the MathFunctions library to allow developers to +select either the custom square root implementation or the built in standard +implementation. While for the tutorial +there really isn't any need to do so, for larger projects this is a common +occurrence. + +CMake can do this using the :command:`option` command. This gives users a +variable which they can change when configuring their cmake build. This +setting will be stored in the cache so that the user does not need to set +the value each time they run CMake on a build directory. + +Goal +---- + +Add the option to build without ``MathFunctions``. + + +Helpful Resources +----------------- + +* :command:`if` +* :command:`option` +* :command:`target_compile_definitions` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.cxx`` + +Getting Started +--------------- + +Start with the resulting files from Exercise 1. Complete ``TODO 7`` through +``TODO 9``. + +First create a variable ``USE_MYMATH`` using the :command:`option` command +in ``MathFunctions/CMakeLists.txt``. In that same file, use that option +to pass a compile definition to the ``MathFunctions`` library. + +Then, update ``MathFunctions.cxx`` to redirect compilation based on +``USE_MYMATH``. + +Build and Run +------------- + +Since we have our build directory already configured from Exercise 1, we can +rebuild by simply calling the following: + +.. code-block:: console + + cd ../Step2_build + cmake --build . + +Next, run the ``Tutorial`` executable on a few numbers to verify that it's +still correct. + +Now let's update the value of ``USE_MYMATH`` to ``OFF``. The easiest way is to +use the :manual:`cmake-gui <cmake-gui(1)>` or :manual:`ccmake <ccmake(1)>` +if you're in the terminal. Or, alternatively, if you want to change the +option from the command-line, try: + +.. code-block:: console + + cmake ../Step2 -DUSE_MYMATH=OFF + +Now, rebuild the code with the following: + +.. code-block:: console + + cmake --build . + +Then, run the executable again to ensure that it still works with +``USE_MYMATH`` set to ``OFF``. Which function gives better results, ``sqrt`` +or ``mysqrt``? + +Solution +-------- + +The first step is to add an option to ``MathFunctions/CMakeLists.txt``. +This option will be displayed in the :manual:`cmake-gui <cmake-gui(1)>` and +:manual:`ccmake <ccmake(1)>` with a default value of ``ON`` that can be +changed by the user. + +.. raw:: html + + <details><summary>TODO 7: Click to show/hide answer</summary> + +.. literalinclude:: Step3/MathFunctions/CMakeLists.txt + :caption: TODO 7: MathFunctions/CMakeLists.txt + :name: CMakeLists.txt-option-library-level + :language: cmake + :start-after: # should we use our own math functions + :end-before: if (USE_MYMATH) + +.. raw:: html + + </details> + +Next, make building and linking our library with ``mysqrt`` function +conditional using this new option. + +Create an :command:`if` statement which checks the value of +``USE_MYMATH``. Inside the :command:`if` block, put the +:command:`target_compile_definitions` command with the compile +definition ``USE_MYMATH``. + +.. raw:: html + + <details><summary>TODO 8: Click to show/hide answer</summary> + +.. literalinclude:: Step3/MathFunctions/CMakeLists.txt + :caption: TODO 8: MathFunctions/CMakeLists.txt + :name: CMakeLists.txt-USE_MYMATH + :language: cmake + :start-after: USE_MYMATH "Use tutorial provided math implementation" ON) + +.. raw:: html + + </details> + +The corresponding changes to the source code are fairly straightforward. +In ``MathFunctions.cxx``, we make ``USE_MYMATH`` control which square root +function is used: + +.. raw:: html + + <details><summary>TODO 9: Click to show/hide answer</summary> + +.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx + :caption: TODO 9: MathFunctions/MathFunctions.cxx + :name: MathFunctions-USE_MYMATH-if + :language: c++ + :start-after: which square root function should we use? + :end-before: } + +.. raw:: html + + </details> + +Next, we need to include ``mysqrt.h`` if ``USE_MYMATH`` is defined. + +.. raw:: html + + <details><summary>TODO 10: Click to show/hide answer</summary> + +.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx + :caption: TODO 10: MathFunctions/MathFunctions.cxx + :name: MathFunctions-USE_MYMATH-if-include + :language: c++ + :start-after: include <cmath> + :end-before: namespace mathfunctions + +.. raw:: html + + </details> + +Finally, we need to include ``cmath`` now that we are using ``std::sqrt``. + +.. raw:: html + + <details><summary>TODO 11: Click to show/hide answer</summary> + +.. code-block:: c++ + :caption: TODO 11 : MathFunctions/MathFunctions.cxx + :name: tutorial.cxx-include_cmath + + #include <cmath> + +.. raw:: html + + </details> + +When ``USE_MYMATH`` is ``ON``, the compile definition ``USE_MYMATH`` will +be set. We can then use this compile definition to enable or disable +sections of our source code. With this strategy, we allow users to +toggle ``USE_MYMATH`` to manipulate what library is used in the build. + +With these changes, the ``mysqrt`` function is now completely optional to +whoever is building and using the ``MathFunctions`` library. diff --git a/Help/guide/tutorial/Complete/CMakeLists.txt b/Help/guide/tutorial/Complete/CMakeLists.txt new file mode 100644 index 0000000..3cdaaae --- /dev/null +++ b/Help/guide/tutorial/Complete/CMakeLists.txt @@ -0,0 +1,126 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +set(CMAKE_DEBUG_POSTFIX d) + +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# control where the static and shared libraries are built so that on windows +# we don't need to tinker with the path to run the executable +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +if(APPLE) + set(CMAKE_INSTALL_RPATH "@executable_path/../lib") +elseif(UNIX) + set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") +endif() + +# configure a header file to pass the version number only +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) +set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +enable_testing() + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") + +# setup installer +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_SOURCE_GENERATOR "TGZ") +include(CPack) + +# install the configuration targets +install(EXPORT MathFunctionsTargets + FILE MathFunctionsTargets.cmake + DESTINATION lib/cmake/MathFunctions +) + +include(CMakePackageConfigHelpers) +# generate the config file that is includes the exports +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" + INSTALL_DESTINATION "lib/cmake/example" + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" + VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}" + COMPATIBILITY AnyNewerVersion +) + +# install the configuration file +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake + DESTINATION lib/cmake/MathFunctions + ) + +# generate the export targets for the build tree +# needs to be after the install(TARGETS ) command +export(EXPORT MathFunctionsTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake" +) diff --git a/Help/guide/tutorial/Complete/CTestConfig.cmake b/Help/guide/tutorial/Complete/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Complete/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Complete/Config.cmake.in b/Help/guide/tutorial/Complete/Config.cmake.in new file mode 100644 index 0000000..17cbabd --- /dev/null +++ b/Help/guide/tutorial/Complete/Config.cmake.in @@ -0,0 +1,4 @@ + +@PACKAGE_INIT@ + +include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" ) diff --git a/Help/guide/tutorial/Complete/License.txt b/Help/guide/tutorial/Complete/License.txt new file mode 100644 index 0000000..85760e5 --- /dev/null +++ b/Help/guide/tutorial/Complete/License.txt @@ -0,0 +1,2 @@ +This is the open source License.txt file introduced in +CMake/Tutorial/Step9... diff --git a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..d256db2 --- /dev/null +++ b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt @@ -0,0 +1,68 @@ +# add the library that runs +add_library(MathFunctions MathFunctions.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> + $<INSTALL_INTERFACE:include> + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if(USE_MYMATH) + + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + # state that SqrtLibrary need PIC when the default is shared libraries + set_target_properties(SqrtLibrary PROPERTIES + POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# define the symbol stating we are using the declspec(dllexport) when +# building on windows +target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH") + +# setup the version numbering +set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0") +set_property(TARGET MathFunctions PROPERTY SOVERSION "1") + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() +install(TARGETS ${installable_libs} + EXPORT MathFunctionsTargets + DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx new file mode 100644 index 0000000..ee58556 --- /dev/null +++ b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include <cmath> +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + // make sure we have enough arguments + if (argc < 2) { + return 1; + } + + std::ofstream fout(argv[1], std::ios_base::out); + const bool fileOpen = fout.is_open(); + if (fileOpen) { + fout << "double sqrtTable[] = {" << std::endl; + for (int i = 0; i < 10; ++i) { + fout << sqrt(static_cast<double>(i)) << "," << std::endl; + } + // close the table with a zero + fout << "0};" << std::endl; + fout.close(); + } + return fileOpen ? 0 : 1; // return 0 if wrote the file +} diff --git a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..c0991b9 --- /dev/null +++ b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx @@ -0,0 +1,20 @@ + +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..3fb547b --- /dev/null +++ b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h @@ -0,0 +1,14 @@ + +#if defined(_WIN32) +# if defined(EXPORTING_MYMATH) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(dllimport) +# endif +#else // non windows +# define DECLSPEC +#endif + +namespace mathfunctions { +double DECLSPEC sqrt(double x); +} diff --git a/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..8153f18 --- /dev/null +++ b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx @@ -0,0 +1,37 @@ +#include <iostream> + +#include "MathFunctions.h" + +// include the generated table +#include "Table.h" + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // use the table to help find an initial value + double result = x; + if (x >= 1 && x < 10) { + std::cout << "Use the table to help find an initial value " << std::endl; + result = sqrtTable[static_cast<int>(x)]; + } + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + + return result; +} +} +} diff --git a/Help/guide/tutorial/Complete/MathFunctions/mysqrt.h b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.h new file mode 100644 index 0000000..e1c42ef --- /dev/null +++ b/Help/guide/tutorial/Complete/MathFunctions/mysqrt.h @@ -0,0 +1,6 @@ + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Complete/MultiCPackConfig.cmake b/Help/guide/tutorial/Complete/MultiCPackConfig.cmake new file mode 100644 index 0000000..c2583df --- /dev/null +++ b/Help/guide/tutorial/Complete/MultiCPackConfig.cmake @@ -0,0 +1,6 @@ +include("release/CPackConfig.cmake") + +set(CPACK_INSTALL_CMAKE_PROJECTS + "debug;Tutorial;ALL;/" + "release;Tutorial;ALL;/" + ) diff --git a/Help/guide/tutorial/Complete/TutorialConfig.h.in b/Help/guide/tutorial/Complete/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Complete/tutorial.cxx b/Help/guide/tutorial/Complete/tutorial.cxx new file mode 100644 index 0000000..a4f44d5 --- /dev/null +++ b/Help/guide/tutorial/Complete/tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Installing and Testing.rst b/Help/guide/tutorial/Installing and Testing.rst new file mode 100644 index 0000000..7a59fcb --- /dev/null +++ b/Help/guide/tutorial/Installing and Testing.rst @@ -0,0 +1,311 @@ +Step 5: Installing and Testing +============================== + +Exercise 1 - Install Rules +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Often, it is not enough to only build an executable, it should also be +installable. With CMake, we can specify install rules using the +:command:`install` command. Supporting local installations for your builds in +CMake is often as simple as specifying an install location and the targets and +files to be installed. + +Goal +---- + +Install the ``Tutorial`` executable and the ``MathFunctions`` library. + +Helpful Materials +----------------- + +* :command:`install` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``CMakeLists.txt`` + +Getting Started +--------------- + +The starting code is provided in the ``Step5`` directory. In this +exercise, complete ``TODO 1`` through ``TODO 4``. + +First, update ``MathFunctions/CMakeLists.txt`` to install the +``MathFunctions`` and ``tutorial_compiler_flags`` libraries to the ``lib`` +directory. In that same file, specify the install rules needed to install +``MathFunctions.h`` to the ``include`` directory. + +Then, update the top level ``CMakeLists.txt`` to install +the ``Tutorial`` executable to the ``bin`` directory. Lastly, any header files +should be installed to the ``include`` directory. Remember that +``TutorialConfig.h`` is in the :variable:`PROJECT_BINARY_DIR`. + +Build and Run +------------- + +Make a new directory called ``Step5_build``. Run the +:manual:`cmake <cmake(1)>` executable or the +:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it +with your chosen build tool. + +Then, run the install step by using the :option:`--install <cmake --install>` +option of the :manual:`cmake <cmake(1)>` command (introduced in 3.15, older +versions of CMake must use ``make install``) from the command line. This step +will install the appropriate header files, libraries, and executables. +For example: + +.. code-block:: console + + cmake --install . + +For multi-configuration tools, don't forget to use the +:option:`--config <cmake--build --config>` argument to specify the configuration. + +.. code-block:: console + + cmake --install . --config Release + +If using an IDE, simply build the ``INSTALL`` target. You can build the same +install target from the command line like the following: + +.. code-block:: console + + cmake --build . --target install --config Debug + +The CMake variable :variable:`CMAKE_INSTALL_PREFIX` is used to determine the +root of where the files will be installed. If using the :option:`cmake --install` +command, the installation prefix can be overridden via the +:option:`--prefix <cmake--install --prefix>` argument. For example: + +.. code-block:: console + + cmake --install . --prefix "/home/myuser/installdir" + +Navigate to the install directory and verify that the installed ``Tutorial`` +runs. + +Solution +-------- + +The install rules for our project are fairly simple: + +* For ``MathFunctions``, we want to install the libraries and header file to + the ``lib`` and ``include`` directories respectively. + +* For the ``Tutorial`` executable, we want to install the executable and + configured header file to the ``bin`` and ``include`` directories + respectively. + +So to the end of ``MathFunctions/CMakeLists.txt`` we add: + +.. raw:: html + + <details><summary>TODO 1: Click to show/hide answer</summary> + +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :caption: TODO 1: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-install-TARGETS + :language: cmake + :start-after: # install libs + :end-before: # install include headers + +.. raw:: html + + </details> + +and + +.. raw:: html + + <details><summary>TODO 2: Click to show/hide answer</summary> + +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :caption: TODO 2: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-install-headers + :language: cmake + :start-after: # install include headers + +.. raw:: html + + </details> + +The install rules for the ``Tutorial`` executable and configured header file +are similar. To the end of the top-level ``CMakeLists.txt`` we add: + +.. raw:: html + + <details><summary>TODO 3,4: Click to show/hide answer</summary> + +.. literalinclude:: Step6/CMakeLists.txt + :caption: CMakeLists.txt + :name: TODO 3,4: CMakeLists.txt-install-TARGETS + :language: cmake + :start-after: # add the install targets + :end-before: # TODO 1: Replace enable_testing() with include(CTest) + +.. raw:: html + + </details> + +That is all that is needed to create a basic local +install of the tutorial. + +.. _`Tutorial Testing Support`: + +Exercise 2 - Testing Support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +CTest offers a way to easily manage tests for your project. Tests can be +added through the :command:`add_test` command. Although it is not +explicitly covered in this tutorial, there is a lot of compatibility +between CTest and other testing frameworks such as :module:`GoogleTest`. + +Goal +---- + +Create unit tests for our executable using CTest. + +Helpful Materials +----------------- + +* :command:`enable_testing` +* :command:`add_test` +* :command:`function` +* :command:`set_tests_properties` +* :manual:`ctest <ctest(1)>` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +The starting source code is provided in the ``Step5`` directory. In this +exercise, complete ``TODO 5`` through ``TODO 9``. + +First, we need to enable testing. Next, begin adding tests to our project +using :command:`add_test`. We will work through adding 3 simple tests and +then you can add additional testing as you see fit. + +Build and Run +------------- + +Navigate to the build directory and rebuild the application. Then, run the +:program:`ctest` executable: :option:`ctest -N` and :option:`ctest -VV`. For +multi-config generators (e.g. Visual Studio), the configuration type must be +specified with the :option:`-C \<mode\> <ctest -C>` flag. For example, to run tests in Debug +mode use ``ctest -C Debug -VV`` from the build directory +(not the Debug subdirectory!). Release mode would be executed from the same +location but with a ``-C Release``. Alternatively, build the ``RUN_TESTS`` +target from the IDE. + +Solution +-------- + +Let's test our application. At the end of the top-level ``CMakeLists.txt`` +file we first need to enable testing with the +:command:`enable_testing` command. + +.. raw:: html + + <details><summary>TODO 5: Click to show/hide answer</summary> + +.. literalinclude:: Step6/CMakeLists.txt + :caption: TODO 5: CMakeLists.txt + :name: CMakeLists.txt-enable_testing + :language: cmake + :start-after: # enable testing + :end-before: # does the application run + +.. raw:: html + + </details> + +With testing enabled, we will add a number of basic tests to verify +that the application is working correctly. First, we create a test using +:command:`add_test` which runs the ``Tutorial`` executable with the +parameter 25 passed in. For this test, we are not going to check the +executable's computed answer. This test will verify that +application runs, does not segfault or otherwise crash, and has a zero +return value. This is the basic form of a CTest test. + +.. raw:: html + + <details><summary>TODO 6: Click to show/hide answer</summary> + +.. literalinclude:: Step6/CMakeLists.txt + :caption: TODO 6: CMakeLists.txt + :name: CMakeLists.txt-test-runs + :language: cmake + :start-after: # does the application run + :end-before: # does the usage message work + +.. raw:: html + + </details> + +Next, let's use the :prop_test:`PASS_REGULAR_EXPRESSION` test property to +verify that the output of the test contains certain strings. In this case, +verifying that the usage message is printed when an incorrect number of +arguments are provided. + +.. raw:: html + + <details><summary>TODO 7: Click to show/hide answer</summary> + +.. literalinclude:: Step6/CMakeLists.txt + :caption: TODO 7: CMakeLists.txt + :name: CMakeLists.txt-test-usage + :language: cmake + :start-after: # does the usage message work? + :end-before: # define a function to simplify adding tests + +.. raw:: html + + </details> + +The next test we will add verifies the computed value is truly the +square root. + +.. raw:: html + + <details><summary>TODO 8: Click to show/hide answer</summary> + +.. code-block:: cmake + :caption: TODO 8: CMakeLists.txt + :name: CMakeLists.txt-test-standard + + add_test(NAME StandardUse COMMAND Tutorial 4) + set_tests_properties(StandardUse + PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2" + ) + +.. raw:: html + + </details> + +This one test is not enough to give us confidence that it will +work for all values passed in. We should add more tests to verify this. +To easily add more tests, we make a function called ``do_test`` that runs the +application and verifies that the computed square root is correct for +given input. For each invocation of ``do_test``, another test is added to +the project with a name, input, and expected results based on the passed +arguments. + +.. raw:: html + + <details><summary>TODO 9: Click to show/hide answer</summary> + +.. literalinclude:: Step6/CMakeLists.txt + :caption: TODO 9: CMakeLists.txt + :name: CMakeLists.txt-generalized-tests + :language: cmake + :start-after: # define a function to simplify adding tests + +.. raw:: html + + </details> diff --git a/Help/guide/tutorial/Packaging Debug and Release.rst b/Help/guide/tutorial/Packaging Debug and Release.rst new file mode 100644 index 0000000..8c660a3 --- /dev/null +++ b/Help/guide/tutorial/Packaging Debug and Release.rst @@ -0,0 +1,86 @@ +Step 12: Packaging Debug and Release +==================================== + +**Note:** This example is valid for single-configuration generators and will +not work for multi-configuration generators (e.g. Visual Studio). + +By default, CMake's model is that a build directory only contains a single +configuration, be it Debug, Release, MinSizeRel, or RelWithDebInfo. It is +possible, however, to setup CPack to bundle multiple build directories and +construct a package that contains multiple configurations of the same project. + +First, we want to ensure that the debug and release builds use different names +for the libraries that will be installed. Let's use `d` as the +postfix for the debug libraries. + +Set :variable:`CMAKE_DEBUG_POSTFIX` near the beginning of the top-level +``CMakeLists.txt`` file: + +.. literalinclude:: Complete/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-CMAKE_DEBUG_POSTFIX-variable + :language: cmake + :start-after: project(Tutorial VERSION 1.0) + :end-before: target_compile_features(tutorial_compiler_flags + +And the :prop_tgt:`DEBUG_POSTFIX` property on the tutorial executable: + +.. literalinclude:: Complete/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-DEBUG_POSTFIX-property + :language: cmake + :start-after: # add the executable + :end-before: # add the binary tree to the search path for include files + +Let's also add version numbering to the ``MathFunctions`` library. In +``MathFunctions/CMakeLists.txt``, set the :prop_tgt:`VERSION` and +:prop_tgt:`SOVERSION` properties: + +.. literalinclude:: Complete/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-VERSION-properties + :language: cmake + :start-after: # setup the version numbering + :end-before: # install libs + +From the ``Step12`` directory, create ``debug`` and ``release`` +subdirectories. The layout will look like: + +.. code-block:: none + + - Step12 + - debug + - release + +Now we need to setup debug and release builds. We can use +:variable:`CMAKE_BUILD_TYPE` to set the configuration type: + +.. code-block:: console + + cd debug + cmake -DCMAKE_BUILD_TYPE=Debug .. + cmake --build . + cd ../release + cmake -DCMAKE_BUILD_TYPE=Release .. + cmake --build . + +Now that both the debug and release builds are complete, we can use a custom +configuration file to package both builds into a single release. In the +``Step12`` directory, create a file called ``MultiCPackConfig.cmake``. In this +file, first include the default configuration file that was created by the +:manual:`cmake <cmake(1)>` executable. + +Next, use the ``CPACK_INSTALL_CMAKE_PROJECTS`` variable to specify which +projects to install. In this case, we want to install both debug and release. + +.. literalinclude:: Complete/MultiCPackConfig.cmake + :caption: MultiCPackConfig.cmake + :name: MultiCPackConfig.cmake + :language: cmake + +From the ``Step12`` directory, run :manual:`cpack <cpack(1)>` specifying our +custom configuration file with the ``config`` option: + +.. code-block:: console + + cpack --config MultiCPackConfig.cmake diff --git a/Help/guide/tutorial/Packaging an Installer.rst b/Help/guide/tutorial/Packaging an Installer.rst new file mode 100644 index 0000000..11a1952 --- /dev/null +++ b/Help/guide/tutorial/Packaging an Installer.rst @@ -0,0 +1,62 @@ +Step 9: Packaging an Installer +============================== + +Next suppose that we want to distribute our project to other people so that +they can use it. We want to provide both binary and source distributions on a +variety of platforms. This is a little different from the install we did +previously in :guide:`tutorial/Installing and Testing`, where we were +installing the binaries that we had built from the source code. In this +example we will be building installation packages that support binary +installations and package management features. To accomplish this we will use +CPack to create platform specific installers. Specifically we need to add a +few lines to the bottom of our top-level ``CMakeLists.txt`` file. + +.. literalinclude:: Step10/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-include-CPack + :language: cmake + :start-after: # setup installer + +That is all there is to it. We start by including +:module:`InstallRequiredSystemLibraries`. This module will include any runtime +libraries that are needed by the project for the current platform. Next we set +some CPack variables to where we have stored the license and version +information for this project. The version information was set earlier in this +tutorial and the ``License.txt`` has been included in the top-level source +directory for this step. The :variable:`CPACK_SOURCE_GENERATOR` variable +selects a file format for the source package. + +Finally we include the :module:`CPack module <CPack>` which will use these +variables and some other properties of the current system to setup an +installer. + +The next step is to build the project in the usual manner and then run the +:manual:`cpack <cpack(1)>` executable. To build a binary distribution, from the +binary directory run: + +.. code-block:: console + + cpack + +To specify the generator, use the :option:`-G <cpack -G>` option. For multi-config builds, +use :option:`-C <cpack -C>` to specify the configuration. For example: + +.. code-block:: console + + cpack -G ZIP -C Debug + +For a list of available generators, see :manual:`cpack-generators(7)` or call +:option:`cpack --help`. An :cpack_gen:`archive generator <CPack Archive Generator>` +like ZIP creates a compressed archive of all *installed* files. + +To create an archive of the *full* source tree you would type: + +.. code-block:: console + + cpack --config CPackSourceConfig.cmake + +Alternatively, run ``make package`` or right click the ``Package`` target and +``Build Project`` from an IDE. + +Run the installer found in the binary directory. Then run the installed +executable and verify that it works. diff --git a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst new file mode 100644 index 0000000..504e42f --- /dev/null +++ b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst @@ -0,0 +1,50 @@ +Step 10: Selecting Static or Shared Libraries +============================================= + +In this section we will show how the :variable:`BUILD_SHARED_LIBS` variable can +be used to control the default behavior of :command:`add_library`, +and allow control over how libraries without an explicit type (``STATIC``, +``SHARED``, ``MODULE`` or ``OBJECT``) are built. + +To accomplish this we need to add :variable:`BUILD_SHARED_LIBS` to the +top-level ``CMakeLists.txt``. We use the :command:`option` command as it allows +users to optionally select if the value should be ``ON`` or ``OFF``. + +.. literalinclude:: Step11/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-option-BUILD_SHARED_LIBS + :language: cmake + :start-after: set(CMAKE_RUNTIME_OUTPUT_DIRECTORY + :end-before: # configure a header file to pass the version number only + +Next, we need to specify output directories for our static and shared +libraries. + +.. literalinclude:: Step11/CMakeLists.txt + :caption: CMakeLists.txt + :name: CMakeLists.txt-cmake-output-directories + :language: cmake + :start-after: # we don't need to tinker with the path to run the executable + :end-before: # configure a header file to pass the version number only + +Finally, update ``MathFunctions/MathFunctions.h`` to use dll export defines: + +.. literalinclude:: Step11/MathFunctions/MathFunctions.h + :caption: MathFunctions/MathFunctions.h + :name: MathFunctions/MathFunctions.h + :language: c++ + +At this point, if you build everything, you may notice that linking fails +as we are combining a static library without position independent code with a +library that has position independent code. The solution to this is to +explicitly set the :prop_tgt:`POSITION_INDEPENDENT_CODE` target property of +SqrtLibrary to be ``True`` when building shared libraries. + +.. literalinclude:: Step11/MathFunctions/CMakeLists.txt + :caption: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-POSITION_INDEPENDENT_CODE + :language: cmake + :lines: 37-42 + +**Exercise**: We modified ``MathFunctions.h`` to use dll export defines. +Using CMake documentation can you find a helper module to simplify this? diff --git a/Help/guide/tutorial/Step1/CMakeLists.txt b/Help/guide/tutorial/Step1/CMakeLists.txt new file mode 100644 index 0000000..6fcce90 --- /dev/null +++ b/Help/guide/tutorial/Step1/CMakeLists.txt @@ -0,0 +1,16 @@ +# TODO 1: Set the minimum required version of CMake to be 3.10 + +# TODO 2: Create a project named Tutorial + +# TODO 7: Set the project version number as 1.0 in the above project command + +# TODO 6: Set the variable CMAKE_CXX_STANDARD to 11 +# and the variable CMAKE_CXX_STANDARD_REQUIRED to True + +# TODO 8: Use configure_file to configure and copy TutorialConfig.h.in to +# TutorialConfig.h + +# TODO 3: Add an executable called Tutorial to the project +# Hint: Be sure to specify the source file as tutorial.cxx + +# TODO 9: Use target_include_directories to include ${PROJECT_BINARY_DIR} diff --git a/Help/guide/tutorial/Step1/TutorialConfig.h.in b/Help/guide/tutorial/Step1/TutorialConfig.h.in new file mode 100644 index 0000000..990bfbd --- /dev/null +++ b/Help/guide/tutorial/Step1/TutorialConfig.h.in @@ -0,0 +1,2 @@ +// the configured options and settings for Tutorial +// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR diff --git a/Help/guide/tutorial/Step1/tutorial.cxx b/Help/guide/tutorial/Step1/tutorial.cxx new file mode 100644 index 0000000..64d0916 --- /dev/null +++ b/Help/guide/tutorial/Step1/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <cstdlib> // TODO 5: Remove this line +#include <iostream> +#include <string> + +// TODO 11: Include TutorialConfig.h + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // TODO 12: Create a print statement using Tutorial_VERSION_MAJOR + // and Tutorial_VERSION_MINOR + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + // TODO 4: Replace atof(argv[1]) with std::stod(argv[1]) + const double inputValue = atof(argv[1]); + + // calculate square root + const double outputValue = sqrt(inputValue); + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt new file mode 100644 index 0000000..2dd6db5 --- /dev/null +++ b/Help/guide/tutorial/Step10/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# configure a header file to pass the version number only +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +include(CTest) + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") + +# setup installer +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_SOURCE_GENERATOR "TGZ") +include(CPack) diff --git a/Help/guide/tutorial/Step10/CTestConfig.cmake b/Help/guide/tutorial/Step10/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step10/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step10/License.txt b/Help/guide/tutorial/Step10/License.txt new file mode 100644 index 0000000..85760e5 --- /dev/null +++ b/Help/guide/tutorial/Step10/License.txt @@ -0,0 +1,2 @@ +This is the open source License.txt file introduced in +CMake/Tutorial/Step9... diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..6f6c430 --- /dev/null +++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt @@ -0,0 +1,51 @@ +# add the library that runs +add_library(MathFunctions MathFunctions.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if(USE_MYMATH) + + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() +install(TARGETS ${installable_libs} DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx new file mode 100644 index 0000000..ee58556 --- /dev/null +++ b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include <cmath> +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + // make sure we have enough arguments + if (argc < 2) { + return 1; + } + + std::ofstream fout(argv[1], std::ios_base::out); + const bool fileOpen = fout.is_open(); + if (fileOpen) { + fout << "double sqrtTable[] = {" << std::endl; + for (int i = 0; i < 10; ++i) { + fout << sqrt(static_cast<double>(i)) << "," << std::endl; + } + // close the table with a zero + fout << "0};" << std::endl; + fout.close(); + } + return fileOpen ? 0 : 1; // return 0 if wrote the file +} diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..c0991b9 --- /dev/null +++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx @@ -0,0 +1,20 @@ + +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..1e916e1 --- /dev/null +++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h @@ -0,0 +1,3 @@ +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..8153f18 --- /dev/null +++ b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx @@ -0,0 +1,37 @@ +#include <iostream> + +#include "MathFunctions.h" + +// include the generated table +#include "Table.h" + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // use the table to help find an initial value + double result = x; + if (x >= 1 && x < 10) { + std::cout << "Use the table to help find an initial value " << std::endl; + result = sqrtTable[static_cast<int>(x)]; + } + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + + return result; +} +} +} diff --git a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.h new file mode 100644 index 0000000..e1c42ef --- /dev/null +++ b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.h @@ -0,0 +1,6 @@ + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step10/TutorialConfig.h.in b/Help/guide/tutorial/Step10/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step10/tutorial.cxx b/Help/guide/tutorial/Step10/tutorial.cxx new file mode 100644 index 0000000..37a0333 --- /dev/null +++ b/Help/guide/tutorial/Step10/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <iostream> +#include <sstream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step11/CMakeLists.txt b/Help/guide/tutorial/Step11/CMakeLists.txt new file mode 100644 index 0000000..046bfc9 --- /dev/null +++ b/Help/guide/tutorial/Step11/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# control where the static and shared libraries are built so that on windows +# we don't need to tinker with the path to run the executable +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +# configure a header file to pass the version number only +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +include(CTest) + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") + +# setup installer +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +set(CPACK_SOURCE_GENERATOR "TGZ") +include(CPack) diff --git a/Help/guide/tutorial/Step11/CTestConfig.cmake b/Help/guide/tutorial/Step11/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step11/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step11/License.txt b/Help/guide/tutorial/Step11/License.txt new file mode 100644 index 0000000..85760e5 --- /dev/null +++ b/Help/guide/tutorial/Step11/License.txt @@ -0,0 +1,2 @@ +This is the open source License.txt file introduced in +CMake/Tutorial/Step9... diff --git a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..a60fb63 --- /dev/null +++ b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt @@ -0,0 +1,60 @@ +# add the library that runs +add_library(MathFunctions MathFunctions.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if(USE_MYMATH) + + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + # state that SqrtLibrary need PIC when the default is shared libraries + set_target_properties(SqrtLibrary PROPERTIES + POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# define the symbol stating we are using the declspec(dllexport) when +# building on windows +target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH") + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() +install(TARGETS ${installable_libs} DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx new file mode 100644 index 0000000..ee58556 --- /dev/null +++ b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include <cmath> +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + // make sure we have enough arguments + if (argc < 2) { + return 1; + } + + std::ofstream fout(argv[1], std::ios_base::out); + const bool fileOpen = fout.is_open(); + if (fileOpen) { + fout << "double sqrtTable[] = {" << std::endl; + for (int i = 0; i < 10; ++i) { + fout << sqrt(static_cast<double>(i)) << "," << std::endl; + } + // close the table with a zero + fout << "0};" << std::endl; + fout.close(); + } + return fileOpen ? 0 : 1; // return 0 if wrote the file +} diff --git a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..c0991b9 --- /dev/null +++ b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx @@ -0,0 +1,20 @@ + +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..3fb547b --- /dev/null +++ b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h @@ -0,0 +1,14 @@ + +#if defined(_WIN32) +# if defined(EXPORTING_MYMATH) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(dllimport) +# endif +#else // non windows +# define DECLSPEC +#endif + +namespace mathfunctions { +double DECLSPEC sqrt(double x); +} diff --git a/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..8153f18 --- /dev/null +++ b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx @@ -0,0 +1,37 @@ +#include <iostream> + +#include "MathFunctions.h" + +// include the generated table +#include "Table.h" + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // use the table to help find an initial value + double result = x; + if (x >= 1 && x < 10) { + std::cout << "Use the table to help find an initial value " << std::endl; + result = sqrtTable[static_cast<int>(x)]; + } + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + + return result; +} +} +} diff --git a/Help/guide/tutorial/Step11/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.h new file mode 100644 index 0000000..e1c42ef --- /dev/null +++ b/Help/guide/tutorial/Step11/MathFunctions/mysqrt.h @@ -0,0 +1,6 @@ + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step11/TutorialConfig.h.in b/Help/guide/tutorial/Step11/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step11/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step11/tutorial.cxx b/Help/guide/tutorial/Step11/tutorial.cxx new file mode 100644 index 0000000..37a0333 --- /dev/null +++ b/Help/guide/tutorial/Step11/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <iostream> +#include <sstream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step12/CMakeLists.txt b/Help/guide/tutorial/Step12/CMakeLists.txt new file mode 100644 index 0000000..1d8b5a6 --- /dev/null +++ b/Help/guide/tutorial/Step12/CMakeLists.txt @@ -0,0 +1,122 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# control where the static and shared libraries are built so that on windows +# we don't need to tinker with the path to run the executable +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +if(APPLE) + set(CMAKE_INSTALL_RPATH "@executable_path/../lib") +elseif(UNIX) + set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") +endif() + +# configure a header file to pass the version number only +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +enable_testing() + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") + +# setup installer +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}") +include(CPack) + +# install the configuration targets +install(EXPORT MathFunctionsTargets + FILE MathFunctionsTargets.cmake + DESTINATION lib/cmake/MathFunctions +) + +include(CMakePackageConfigHelpers) +# generate the config file that includes the exports +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" + INSTALL_DESTINATION "lib/cmake/example" + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) +# generate the version file for the config file +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake" + VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}" + COMPATIBILITY AnyNewerVersion +) + +# install the generated configuration files +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake + DESTINATION lib/cmake/MathFunctions + ) + +# generate the export targets for the build tree +# needs to be after the install(TARGETS) command +export(EXPORT MathFunctionsTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake" +) diff --git a/Help/guide/tutorial/Step12/CTestConfig.cmake b/Help/guide/tutorial/Step12/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step12/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step12/Config.cmake.in b/Help/guide/tutorial/Step12/Config.cmake.in new file mode 100644 index 0000000..17cbabd --- /dev/null +++ b/Help/guide/tutorial/Step12/Config.cmake.in @@ -0,0 +1,4 @@ + +@PACKAGE_INIT@ + +include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" ) diff --git a/Help/guide/tutorial/Step12/License.txt b/Help/guide/tutorial/Step12/License.txt new file mode 100644 index 0000000..85760e5 --- /dev/null +++ b/Help/guide/tutorial/Step12/License.txt @@ -0,0 +1,2 @@ +This is the open source License.txt file introduced in +CMake/Tutorial/Step9... diff --git a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..a85f3cb --- /dev/null +++ b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt @@ -0,0 +1,64 @@ +# add the library that runs +add_library(MathFunctions MathFunctions.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> + $<INSTALL_INTERFACE:include> + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if(USE_MYMATH) + + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + # state that SqrtLibrary need PIC when the default is shared libraries + set_target_properties(SqrtLibrary PROPERTIES + POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# define the symbol stating we are using the declspec(dllexport) when +# building on windows +target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH") + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() +install(TARGETS ${installable_libs} + EXPORT MathFunctionsTargets + DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx new file mode 100644 index 0000000..ee58556 --- /dev/null +++ b/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include <cmath> +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + // make sure we have enough arguments + if (argc < 2) { + return 1; + } + + std::ofstream fout(argv[1], std::ios_base::out); + const bool fileOpen = fout.is_open(); + if (fileOpen) { + fout << "double sqrtTable[] = {" << std::endl; + for (int i = 0; i < 10; ++i) { + fout << sqrt(static_cast<double>(i)) << "," << std::endl; + } + // close the table with a zero + fout << "0};" << std::endl; + fout.close(); + } + return fileOpen ? 0 : 1; // return 0 if wrote the file +} diff --git a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..c0991b9 --- /dev/null +++ b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx @@ -0,0 +1,20 @@ + +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..3fb547b --- /dev/null +++ b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.h @@ -0,0 +1,14 @@ + +#if defined(_WIN32) +# if defined(EXPORTING_MYMATH) +# define DECLSPEC __declspec(dllexport) +# else +# define DECLSPEC __declspec(dllimport) +# endif +#else // non windows +# define DECLSPEC +#endif + +namespace mathfunctions { +double DECLSPEC sqrt(double x); +} diff --git a/Help/guide/tutorial/Step12/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step12/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..8153f18 --- /dev/null +++ b/Help/guide/tutorial/Step12/MathFunctions/mysqrt.cxx @@ -0,0 +1,37 @@ +#include <iostream> + +#include "MathFunctions.h" + +// include the generated table +#include "Table.h" + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // use the table to help find an initial value + double result = x; + if (x >= 1 && x < 10) { + std::cout << "Use the table to help find an initial value " << std::endl; + result = sqrtTable[static_cast<int>(x)]; + } + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + + return result; +} +} +} diff --git a/Help/guide/tutorial/Step12/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step12/MathFunctions/mysqrt.h new file mode 100644 index 0000000..e1c42ef --- /dev/null +++ b/Help/guide/tutorial/Step12/MathFunctions/mysqrt.h @@ -0,0 +1,6 @@ + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step12/TutorialConfig.h.in b/Help/guide/tutorial/Step12/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step12/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step12/tutorial.cxx b/Help/guide/tutorial/Step12/tutorial.cxx new file mode 100644 index 0000000..a4f44d5 --- /dev/null +++ b/Help/guide/tutorial/Step12/tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step2/CMakeLists.txt b/Help/guide/tutorial/Step2/CMakeLists.txt new file mode 100644 index 0000000..0a06ed7 --- /dev/null +++ b/Help/guide/tutorial/Step2/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# TODO 2: Use add_subdirectory() to add MathFunctions to this project + +# add the executable +add_executable(Tutorial tutorial.cxx) + +# TODO 3: Use target_link_libraries to link the library to our executable + +# TODO 4: Add MathFunctions to Tutorial's target_include_directories() +# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder! + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) diff --git a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..c468b0e --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt @@ -0,0 +1,7 @@ +# TODO 1: Add a library called MathFunctions +# Hint: You will need the add_library command + +# TODO 7: Create a variable USE_MYMATH using option and set default to ON + +# TODO 8: If USE_MYMATH is ON, use target_compile_definitions to pass +# USE_MYMATH as a precompiled definition to our source files diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..781d0ec --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx @@ -0,0 +1,15 @@ +#include "MathFunctions.h" + +// TODO 11: include cmath + +// TODO 10: Wrap the mysqrt include in a precompiled ifdef based on USE_MYMATH +#include "mysqrt.h" + +namespace mathfunctions { +double sqrt(double x) +{ + // TODO 9: If USE_MYMATH is defined, use detail::mysqrt. + // Otherwise, use std::sqrt. + return detail::mysqrt(x); +} +} diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..ba0ac64 --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx @@ -0,0 +1,28 @@ +#include "mysqrt.h" + +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + return result; +} +} +} diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step2/TutorialConfig.h.in b/Help/guide/tutorial/Step2/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step2/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step2/tutorial.cxx b/Help/guide/tutorial/Step2/tutorial.cxx new file mode 100644 index 0000000..7a2a595 --- /dev/null +++ b/Help/guide/tutorial/Step2/tutorial.cxx @@ -0,0 +1,29 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +// TODO 5: Include MathFunctions.h +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + // TODO 6: Replace sqrt with mathfunctions::sqrt + + // calculate square root + const double outputValue = sqrt(inputValue); + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step3/CMakeLists.txt b/Help/guide/tutorial/Step3/CMakeLists.txt new file mode 100644 index 0000000..f051826 --- /dev/null +++ b/Help/guide/tutorial/Step3/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# TODO 2: Remove EXTRA_INCLUDES list + +# add the MathFunctions library +add_subdirectory(MathFunctions) +list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions") + +# add the executable +add_executable(Tutorial tutorial.cxx) + +target_link_libraries(Tutorial PUBLIC MathFunctions) + +# TODO 3: Remove use of EXTRA_INCLUDES + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ${EXTRA_INCLUDES} + ) diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..90d6c24 --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# TODO 1: State that anybody linking to MathFunctions needs to include the +# current source directory, while MathFunctions itself doesn't. +# Hint: Use target_include_directories with the INTERFACE keyword + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..ba0ac64 --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx @@ -0,0 +1,28 @@ +#include "mysqrt.h" + +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + return result; +} +} +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step3/TutorialConfig.h.in b/Help/guide/tutorial/Step3/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step3/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step3/tutorial.cxx b/Help/guide/tutorial/Step3/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step3/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step4/CMakeLists.txt b/Help/guide/tutorial/Step4/CMakeLists.txt new file mode 100644 index 0000000..dcda135 --- /dev/null +++ b/Help/guide/tutorial/Step4/CMakeLists.txt @@ -0,0 +1,52 @@ +# TODO 4: Update the minimum required version to 3.15 + +cmake_minimum_required(VERSION 3.10) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# TODO 1: Replace the following code by: +# * Creating an interface library called tutorial_compiler_flags +# Hint: use add_library() with the INTERFACE signature +# * Add compiler feature cxx_std_11 to tutorial_compiler_flags +# Hint: Use target_compile_features() + +# specify the C++ standard +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# TODO 5: Create helper variables to determine which compiler we are using: +# * Create a new variable gcc_like_cxx that is true if we are using CXX and +# any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC +# * Create a new variable msvc_cxx that is true if we are using CXX and MSVC +# Hint: Use set() and COMPILE_LANG_AND_ID + +# TODO 6: Add warning flag compile options to the interface library +# tutorial_compiler_flags. +# * For gcc_like_cxx, add flags -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused +# * For msvc_cxx, add flags -W3 +# Hint: Use target_compile_options() + +# TODO 7: With nested generator expressions, only use the flags for the +# build-tree +# Hint: Use BUILD_INTERFACE + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) + +# TODO 2: Link to tutorial_compiler_flags + +target_link_libraries(Tutorial PUBLIC MathFunctions) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..cc71d86 --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() + +# TODO 3: Link to tutorial_compiler_flags diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..ba0ac64 --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx @@ -0,0 +1,28 @@ +#include "mysqrt.h" + +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + return result; +} +} +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step4/TutorialConfig.h.in b/Help/guide/tutorial/Step4/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step4/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step4/tutorial.cxx b/Help/guide/tutorial/Step4/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step4/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step5/CMakeLists.txt b/Help/guide/tutorial/Step5/CMakeLists.txt new file mode 100644 index 0000000..ad814f6 --- /dev/null +++ b/Help/guide/tutorial/Step5/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# TODO 3: Install Tutorial in the bin directory +# Hint: Use the TARGETS and DESTINATION parameters + +# TODO 4: Install TutorialConfig.h to the include directory +# Hint: Use the FILES and DESTINATION parameters + +# TODO 5: Enable testing + +# TODO 6: Add a test called Runs which runs the following command: +# $ Tutorial 25 + +# TODO 7: Add a test called Usage which runs the following command: +# $ Tutorial +# Make sure the expected output is displayed. +# Hint: Use the PASS_REGULAR_EXPRESSION property with "Usage.*number" + +# TODO 8: Add a test which runs the following command: +# $ Tutorial 4 +# Make sure the result is correct. +# Hint: Use the PASS_REGULAR_EXPRESSION property with "4 is 2" + +# TODO 9: Add more tests. Create a function called do_test to avoid copy + +# paste. Test the following values: 4, 9, 5, 7, 25, -25 and 0.0001. diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..000a786 --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt @@ -0,0 +1,24 @@ +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() + +# link our compiler flags interface library +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# TODO 1: Create a variable called installable_libs that is a list of all +# libraries we want to install (e.g. MathFunctions and tutorial_compiler_flags) +# Then install the installable libraries to the lib folder. +# Hint: Use the TARGETS and DESTINATION parameters + +# TODO 2: Install the library headers to the include folder. +# Hint: Use the FILES and DESTINATION parameters diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..ba0ac64 --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx @@ -0,0 +1,28 @@ +#include "mysqrt.h" + +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + return result; +} +} +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step5/TutorialConfig.h.in b/Help/guide/tutorial/Step5/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step5/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step5/tutorial.cxx b/Help/guide/tutorial/Step5/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step5/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt new file mode 100644 index 0000000..a86d60a --- /dev/null +++ b/Help/guide/tutorial/Step6/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# TODO 1: Replace enable_testing() with include(CTest) +# enable testing +enable_testing() + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") diff --git a/Help/guide/tutorial/Step6/CTestConfig.cmake b/Help/guide/tutorial/Step6/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step6/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..623cb74 --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt @@ -0,0 +1,22 @@ +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") +endif() + +# link our compiler flags interface library +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +install(TARGETS ${installable_libs} DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..ba0ac64 --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx @@ -0,0 +1,28 @@ +#include "mysqrt.h" + +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + return result; +} +} +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step6/TutorialConfig.h.in b/Help/guide/tutorial/Step6/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step6/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step6/tutorial.cxx b/Help/guide/tutorial/Step6/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step6/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt new file mode 100644 index 0000000..97ec6aa --- /dev/null +++ b/Help/guide/tutorial/Step7/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +include(CTest) + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") diff --git a/Help/guide/tutorial/Step7/CTestConfig.cmake b/Help/guide/tutorial/Step7/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step7/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..c0d1e72 --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt @@ -0,0 +1,42 @@ +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # TODO 1: Include CheckCXXSourceCompiles + + # TODO 2: Use check_cxx_source_compiles with simple C++ code to verify + # availability of: + # * std::log + # * std::exp + # Store the results in HAVE_LOG and HAVE_EXP respectively. + + # Hint: Sample C++ code which uses log: + # #include <cmath> + # int main() { + # std::log(1.0); + # return 0; + # } + + # TODO 3: Conditionally on HAVE_LOG and HAVE_EXP, add private compile + # definitions "HAVE_LOG" and "HAVE_EXP" to the MathFunctions target. + + # Hint: Use target_compile_definitions() +endif() + +# link our compiler flags interface library +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +install(TARGETS ${installable_libs} DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..9963cff --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx @@ -0,0 +1,37 @@ +#include "mysqrt.h" + +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // TODO 5: If both HAVE_LOG and HAVE_EXP are defined, use the following: + //// double result = std::exp(std::log(x) * 0.5); + //// std::cout << "Computing sqrt of " << x << " to be " << result + //// << " using log and exp" << std::endl; + // else, use the existing logic. + + // Hint: Don't forget the #endif before returning the result! + + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + + return result; +} +} +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step7/TutorialConfig.h.in b/Help/guide/tutorial/Step7/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step7/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step7/tutorial.cxx b/Help/guide/tutorial/Step7/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step7/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt new file mode 100644 index 0000000..97ec6aa --- /dev/null +++ b/Help/guide/tutorial/Step8/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +include(CTest) + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") diff --git a/Help/guide/tutorial/Step8/CTestConfig.cmake b/Help/guide/tutorial/Step8/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step8/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..861014d --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt @@ -0,0 +1,46 @@ +add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # does this system provide the log and exp functions? + include(CheckCXXSourceCompiles) + check_cxx_source_compiles(" + #include <cmath> + int main() { + std::log(1.0); + return 0; + } + " HAVE_LOG) + check_cxx_source_compiles(" + #include <cmath> + int main() { + std::exp(1.0); + return 0; + } + " HAVE_EXP) + + # add compile definitions + if(HAVE_LOG AND HAVE_EXP) + target_compile_definitions(MathFunctions + PRIVATE "HAVE_LOG" "HAVE_EXP" + ) + endif() +endif() + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# link our compiler flags interface library +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +install(TARGETS ${installable_libs} DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx new file mode 100644 index 0000000..ee58556 --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include <cmath> +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + // make sure we have enough arguments + if (argc < 2) { + return 1; + } + + std::ofstream fout(argv[1], std::ios_base::out); + const bool fileOpen = fout.is_open(); + if (fileOpen) { + fout << "double sqrtTable[] = {" << std::endl; + for (int i = 0; i < 10; ++i) { + fout << sqrt(static_cast<double>(i)) << "," << std::endl; + } + // close the table with a zero + fout << "0};" << std::endl; + fout.close(); + } + return fileOpen ? 0 : 1; // return 0 if wrote the file +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..28ab042 --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx @@ -0,0 +1,36 @@ +#include "mysqrt.h" + +#include <cmath> +#include <iostream> + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // if we have both log and exp then use them +#if defined(HAVE_LOG) && defined(HAVE_EXP) + double result = std::exp(std::log(x) * 0.5); + std::cout << "Computing sqrt of " << x << " to be " << result + << " using log and exp" << std::endl; +#else + double result = x; + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } +#endif + return result; +} +} +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step8/TutorialConfig.h.in b/Help/guide/tutorial/Step8/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step8/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step8/tutorial.cxx b/Help/guide/tutorial/Step8/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step8/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt new file mode 100644 index 0000000..97ec6aa --- /dev/null +++ b/Help/guide/tutorial/Step9/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.15) + +# set the project name and version +project(Tutorial VERSION 1.0) + +# specify the C++ standard +add_library(tutorial_compiler_flags INTERFACE) +target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) + +# add compiler warning flags just when building this project via +# the BUILD_INTERFACE genex +set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>") +set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>") +target_compile_options(tutorial_compiler_flags INTERFACE + "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>" + "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>" +) + +# configure a header file to pass some of the CMake settings +# to the source code +configure_file(TutorialConfig.h.in TutorialConfig.h) + +# add the MathFunctions library +add_subdirectory(MathFunctions) + +# add the executable +add_executable(Tutorial tutorial.cxx) + +target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags) + +# add the binary tree to the search path for include files +# so that we will find TutorialConfig.h +target_include_directories(Tutorial PUBLIC + "${PROJECT_BINARY_DIR}" + ) + +# add the install targets +install(TARGETS Tutorial DESTINATION bin) +install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" + DESTINATION include + ) + +# enable testing +include(CTest) + +# does the application run +add_test(NAME Runs COMMAND Tutorial 25) + +# does the usage message work? +add_test(NAME Usage COMMAND Tutorial) +set_tests_properties(Usage + PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" + ) + +# define a function to simplify adding tests +function(do_test target arg result) + add_test(NAME Comp${arg} COMMAND ${target} ${arg}) + set_tests_properties(Comp${arg} + PROPERTIES PASS_REGULAR_EXPRESSION ${result} + ) +endfunction() + +# do a bunch of result based tests +do_test(Tutorial 4 "4 is 2") +do_test(Tutorial 9 "9 is 3") +do_test(Tutorial 5 "5 is 2.236") +do_test(Tutorial 7 "7 is 2.645") +do_test(Tutorial 25 "25 is 5") +do_test(Tutorial -25 "-25 is (-nan|nan|0)") +do_test(Tutorial 0.0001 "0.0001 is 0.01") diff --git a/Help/guide/tutorial/Step9/CTestConfig.cmake b/Help/guide/tutorial/Step9/CTestConfig.cmake new file mode 100644 index 0000000..73efdb1 --- /dev/null +++ b/Help/guide/tutorial/Step9/CTestConfig.cmake @@ -0,0 +1,7 @@ +set(CTEST_PROJECT_NAME "CMakeTutorial") +set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Help/guide/tutorial/Step9/License.txt b/Help/guide/tutorial/Step9/License.txt new file mode 100644 index 0000000..85760e5 --- /dev/null +++ b/Help/guide/tutorial/Step9/License.txt @@ -0,0 +1,2 @@ +This is the open source License.txt file introduced in +CMake/Tutorial/Step9... diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..05c8616 --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt @@ -0,0 +1,49 @@ +add_library(MathFunctions MathFunctions.cxx) + +# state that anybody linking to us needs to include the current source dir +# to find MathFunctions.h, while we don't. +target_include_directories(MathFunctions + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} + ) + +# should we use our own math functions +option(USE_MYMATH "Use tutorial provided math implementation" ON) +if (USE_MYMATH) + target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") + + # first we add the executable that generates the table + add_executable(MakeTable MakeTable.cxx) + target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags) + + # add the command to generate the source code + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h + COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h + DEPENDS MakeTable + ) + + # library that just does sqrt + add_library(SqrtLibrary STATIC + mysqrt.cxx + ${CMAKE_CURRENT_BINARY_DIR}/Table.h + ) + + # state that we depend on our binary dir to find Table.h + target_include_directories(SqrtLibrary PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) + + target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +endif() + +target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) + +# install libs +set(installable_libs MathFunctions tutorial_compiler_flags) +if(TARGET SqrtLibrary) + list(APPEND installable_libs SqrtLibrary) +endif() +install(TARGETS ${installable_libs} DESTINATION lib) +# install include headers +install(FILES MathFunctions.h DESTINATION include) diff --git a/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx new file mode 100644 index 0000000..ee58556 --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include <cmath> +#include <fstream> +#include <iostream> + +int main(int argc, char* argv[]) +{ + // make sure we have enough arguments + if (argc < 2) { + return 1; + } + + std::ofstream fout(argv[1], std::ios_base::out); + const bool fileOpen = fout.is_open(); + if (fileOpen) { + fout << "double sqrtTable[] = {" << std::endl; + for (int i = 0; i < 10; ++i) { + fout << sqrt(static_cast<double>(i)) << "," << std::endl; + } + // close the table with a zero + fout << "0};" << std::endl; + fout.close(); + } + return fileOpen ? 0 : 1; // return 0 if wrote the file +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..dc28b4b --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx @@ -0,0 +1,19 @@ +#include "MathFunctions.h" + +#include <cmath> + +#ifdef USE_MYMATH +# include "mysqrt.h" +#endif + +namespace mathfunctions { +double sqrt(double x) +{ +// which square root function should we use? +#ifdef USE_MYMATH + return detail::mysqrt(x); +#else + return std::sqrt(x); +#endif +} +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx new file mode 100644 index 0000000..477d715 --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx @@ -0,0 +1,37 @@ +#include "mysqrt.h" + +#include <iostream> + +// include the generated table +#include "Table.h" + +namespace mathfunctions { +namespace detail { +// a hack square root calculation using simple operations +double mysqrt(double x) +{ + if (x <= 0) { + return 0; + } + + // use the table to help find an initial value + double result = x; + if (x >= 1 && x < 10) { + std::cout << "Use the table to help find an initial value " << std::endl; + result = sqrtTable[static_cast<int>(x)]; + } + + // do ten iterations + for (int i = 0; i < 10; ++i) { + if (result <= 0) { + result = 0.1; + } + double delta = x - (result * result); + result = result + 0.5 * delta / result; + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + + return result; +} +} +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h new file mode 100644 index 0000000..593d41e --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h @@ -0,0 +1,7 @@ +#pragma once + +namespace mathfunctions { +namespace detail { +double mysqrt(double x); +} +} diff --git a/Help/guide/tutorial/Step9/TutorialConfig.h.in b/Help/guide/tutorial/Step9/TutorialConfig.h.in new file mode 100644 index 0000000..7e4d7fa --- /dev/null +++ b/Help/guide/tutorial/Step9/TutorialConfig.h.in @@ -0,0 +1,3 @@ +// the configured options and settings for Tutorial +#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ +#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ diff --git a/Help/guide/tutorial/Step9/tutorial.cxx b/Help/guide/tutorial/Step9/tutorial.cxx new file mode 100644 index 0000000..a3a2bdc --- /dev/null +++ b/Help/guide/tutorial/Step9/tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include <cmath> +#include <iostream> +#include <string> + +#include "MathFunctions.h" +#include "TutorialConfig.h" + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // report version + std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." + << Tutorial_VERSION_MINOR << std::endl; + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + const double inputValue = std::stod(argv[1]); + + const double outputValue = mathfunctions::sqrt(inputValue); + + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; + return 0; +} diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst new file mode 100644 index 0000000..1ab0009 --- /dev/null +++ b/Help/guide/tutorial/index.rst @@ -0,0 +1,44 @@ +CMake Tutorial +************** + +Introduction +============ + +The CMake tutorial provides a step-by-step guide that covers common build +system issues that CMake helps address. Seeing how various topics all +work together in an example project can be very helpful. + +Steps +===== + +.. include:: source.txt + +|tutorial_source| +Each step has its own subdirectory containing code that may be used as a +starting point. The tutorial examples are progressive so that each step +provides the complete solution for the previous step. + +.. toctree:: + :maxdepth: 2 + + A Basic Starting Point + Adding a Library + Adding Usage Requirements for a Library + Adding Generator Expressions + Installing and Testing + Adding Support for a Testing Dashboard + Adding System Introspection + Adding a Custom Command and Generated File + Packaging an Installer + Selecting Static or Shared Libraries + Adding Export Configuration + Packaging Debug and Release + +.. + Whenever a step above is renamed or removed, leave forwarding text in + its original document file, and list it below to preserve old links + to cmake.org/cmake/help/latest/ URLs. + +.. toctree:: + :maxdepth: 1 + :hidden: diff --git a/Help/guide/tutorial/source.txt b/Help/guide/tutorial/source.txt new file mode 100644 index 0000000..bb45e86 --- /dev/null +++ b/Help/guide/tutorial/source.txt @@ -0,0 +1,3 @@ +.. |tutorial_source| replace:: + The tutorial documentation and source code examples can be found in + the ``Help/guide/tutorial`` directory of the CMake source code tree. diff --git a/Help/guide/user-interaction/GUI-Add-Entry.png b/Help/guide/user-interaction/GUI-Add-Entry.png Binary files differnew file mode 100644 index 0000000..1e9be7e --- /dev/null +++ b/Help/guide/user-interaction/GUI-Add-Entry.png diff --git a/Help/guide/user-interaction/GUI-Choose-Generator.png b/Help/guide/user-interaction/GUI-Choose-Generator.png Binary files differnew file mode 100644 index 0000000..19ad2c0 --- /dev/null +++ b/Help/guide/user-interaction/GUI-Choose-Generator.png diff --git a/Help/guide/user-interaction/GUI-Configure-Dialog.png b/Help/guide/user-interaction/GUI-Configure-Dialog.png Binary files differnew file mode 100644 index 0000000..9839cac --- /dev/null +++ b/Help/guide/user-interaction/GUI-Configure-Dialog.png diff --git a/Help/guide/user-interaction/GUI-Source-Binary.png b/Help/guide/user-interaction/GUI-Source-Binary.png Binary files differnew file mode 100644 index 0000000..e338354 --- /dev/null +++ b/Help/guide/user-interaction/GUI-Source-Binary.png diff --git a/Help/guide/user-interaction/VS-Choose-Arch.png b/Help/guide/user-interaction/VS-Choose-Arch.png Binary files differnew file mode 100644 index 0000000..816b0f4 --- /dev/null +++ b/Help/guide/user-interaction/VS-Choose-Arch.png diff --git a/Help/guide/user-interaction/index.rst b/Help/guide/user-interaction/index.rst new file mode 100644 index 0000000..3355992 --- /dev/null +++ b/Help/guide/user-interaction/index.rst @@ -0,0 +1,772 @@ +User Interaction Guide +********************** + +.. only:: html + + .. contents:: + +Introduction +============ + +Where a software package supplies a CMake-based buildsystem +with the source of their software, the consumer of the +software is required to run a CMake user interaction tool +in order to build it. + +Well-behaved CMake-based buildsystems do not create any +output in the source directory, so typically, the user +performs an out-of-source build and performs the build +there. First, CMake must be instructed to generate a +suitable buildsystem, then the user invokes a build tool +to process that generated buildsystem. The generated +buildsystem is specific to the machine used to generate +it and is not redistributable. Each consumer of a provided +source software package is required to use CMake to +generate a buildsystem specific to their system. + +Generated buildsystems should generally be treated as +read-only. The CMake files as a primary artifact should +completely specify the buildsystem and there should be no +reason to populate properties manually in an IDE for +example after generating the buildsystem. CMake will +periodically rewrite the generated buildsystem, so +modifications by users will be overwritten. + +The features and user interfaces described in this manual +are available for all CMake-based build systems by virtue +of providing CMake files. + +The CMake tooling may report errors to the user when +processing provided CMake files, such as reporting that +the compiler is not supported, or the compiler does not +support a required compile option, or a dependency can +not be found. These errors must be resolved by the user +by choosing a different compiler, +:guide:`installing dependencies <Using Dependencies Guide>`, +or instructing CMake where to find them, etc. + +Command Line cmake tool +----------------------- + +A simple but typical use of :manual:`cmake(1)` with a fresh +copy of software source code is to create a build directory +and invoke cmake there: + +.. code-block:: console + + $ cd some_software-1.4.2 + $ mkdir build + $ cd build + $ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/the/prefix + $ cmake --build . + $ cmake --build . --target install + +It is recommended to build in a separate directory to the +source because that keeps the source directory pristine, +allows for building a single source with multiple +toolchains, and allows easy clearing of build artifacts by +simply deleting the build directory. + +The CMake tooling may report warnings which are intended +for the provider of the software, not intended for the +consumer of the software. Such warnings end with "This +warning is for project developers". Users may disable +such warnings by passing the :option:`-Wno-dev <cmake -Wno-dev>` +flag to :manual:`cmake(1)`. + +cmake-gui tool +-------------- + +Users more accustomed to GUI interfaces may use the +:manual:`cmake-gui(1)` tool to invoke CMake and generate +a buildsystem. + +The source and binary directories must first be +populated. It is always advised to use different +directories for the source and the build. + +.. image:: GUI-Source-Binary.png + :alt: Choosing source and binary directories + +Generating a Buildsystem +======================== + +There are several user interface tools which may be used +to generate a buildsystem from CMake files. The +:manual:`ccmake(1)` and :manual:`cmake-gui(1)` tools guide +the user through setting the various necessary options. +The :manual:`cmake(1)` tool can be invoked to specify +options on the command line. This manual describes options +which may be set using any of the user interface tools, +though the mode of setting an option is different for each +tool. + +Command line environment +------------------------ + +When invoking :manual:`cmake(1)` with a command line +buildsystem such as ``Makefiles`` or ``Ninja``, it is +necessary to use the correct build environment to +ensure that build tools are available. CMake must be +able to find the appropriate +:variable:`build tool <CMAKE_MAKE_PROGRAM>`, +compiler, linker and other tools as needed. + +On Linux systems, the appropriate tools are often +provided in system-wide locations and may be readily +installed through the system package manager. Other +toolchains provided by the user or installed in +non-default locations can also be used. + +When cross-compiling, some platforms may require +environment variables to be set or may provide +scripts to set the environment. + +Visual Studio ships multiple command prompts and +``vcvarsall.bat`` scripts for setting up the +correct environments for command line buildsystems. While +not strictly necessary to use a corresponding +command line environment when using a Visual Studio +generator, doing so has no disadvantages. + +When using Xcode, there can be more than one Xcode +version installed. Which one to use can be selected +in a number of different ways, but the most common +methods are: + +* Setting the default version in the preferences + of the Xcode IDE. +* Setting the default version via the ``xcode-select`` + command line tool. +* Overriding the default version by setting the + ``DEVELOPER_DIR`` environment variable when running + CMake and the build tool. + +For convenience, :manual:`cmake-gui(1)` provides an +environment variable editor. + +Command line ``-G`` option +-------------------------- + +CMake chooses a generator by default based on the +platform. Usually, the default generator is sufficient +to allow the user to proceed to build the software. + +The user may override the default generator with +the :option:`-G <cmake -G>` option: + +.. code-block:: console + + $ cmake .. -G Ninja + +The output of :option:`cmake --help` includes a list of +:manual:`generators <cmake-generators(7)>` available +for the user to choose from. Note that generator +names are case sensitive. + +On Unix-like systems (including Mac OS X), the +:generator:`Unix Makefiles` generator is used by +default. A variant of that generator can also be used +on Windows in various environments, such as the +:generator:`NMake Makefiles` and +:generator:`MinGW Makefiles` generator. These generators +generate a ``Makefile`` variant which can be executed +with ``make``, ``gmake``, ``nmake`` or similar tools. +See the individual generator documentation for more +information on targeted environments and tools. + +The :generator:`Ninja` generator is available on all +major platforms. ``ninja`` is a build tool similar +in use-cases to ``make``, but with a focus on +performance and efficiency. + +On Windows, :manual:`cmake(1)` can be used to generate +solutions for the Visual Studio IDE. Visual Studio +versions may be specified by the product name of the +IDE, which includes a four-digit year. Aliases are +provided for other means by which Visual Studio +versions are sometimes referred to, such as two +digits which correspond to the product version of the +VisualC++ compiler, or a combination of the two: + +.. code-block:: console + + $ cmake .. -G "Visual Studio 2019" + $ cmake .. -G "Visual Studio 16" + $ cmake .. -G "Visual Studio 16 2019" + +Visual Studio generators can target different architectures. +One can specify the target architecture using the +:option:`-A <cmake -A>` option: + +.. code-block:: console + + cmake .. -G "Visual Studio 2019" -A x64 + cmake .. -G "Visual Studio 16" -A ARM + cmake .. -G "Visual Studio 16 2019" -A ARM64 + +On Apple, the :generator:`Xcode` generator may be used to +generate project files for the Xcode IDE. + +Some IDEs such as KDevelop4, QtCreator and CLion have +native support for CMake-based buildsystems. Those IDEs +provide user interface for selecting an underlying +generator to use, typically a choice between a ``Makefile`` +or a ``Ninja`` based generator. + +Note that it is not possible to change the generator +with :option:`-G <cmake -G>` after the first invocation of CMake. +To change the generator, the build directory must be +deleted and the build must be started from scratch. + +When generating Visual Studio project and solutions +files several other options are available to use when +initially running :manual:`cmake(1)`. + +The Visual Studio toolset can be specified with the +:option:`cmake -T` option: + +.. code-block:: console + + $ # Build with the clang-cl toolset + $ cmake.exe .. -G "Visual Studio 16 2019" -A x64 -T ClangCL + $ # Build targeting Windows XP + $ cmake.exe .. -G "Visual Studio 16 2019" -A x64 -T v120_xp + +Whereas the :option:`-A <cmake -A>` option specifies the _target_ +architecture, the :option:`-T <cmake -T>` option can be used to specify +details of the toolchain used. For example, ``-Thost=x64`` +can be given to select the 64-bit version of the host +tools. The following demonstrates how to use 64-bit +tools and also build for a 64-bit target architecture: + +.. code-block:: console + + $ cmake .. -G "Visual Studio 16 2019" -A x64 -Thost=x64 + +Choosing a generator in cmake-gui +--------------------------------- + +The "Configure" button triggers a new dialog to +select the CMake generator to use. + +.. image:: GUI-Configure-Dialog.png + :alt: Configuring a generator + +All generators available on the command line are also +available in :manual:`cmake-gui(1)`. + +.. image:: GUI-Choose-Generator.png + :alt: Choosing a generator + +When choosing a Visual Studio generator, further options +are available to set an architecture to generate for. + +.. image:: VS-Choose-Arch.png + :alt: Choosing an architecture for Visual Studio generators + +.. _`Setting Build Variables`: + +Setting Build Variables +======================= + +Software projects often require variables to be +set on the command line when invoking CMake. Some of +the most commonly used CMake variables are listed in +the table below: + +========================================== ============================================================ + Variable Meaning +========================================== ============================================================ + :variable:`CMAKE_PREFIX_PATH` Path to search for + :guide:`dependent packages <Using Dependencies Guide>` + :variable:`CMAKE_MODULE_PATH` Path to search for additional CMake modules + :variable:`CMAKE_BUILD_TYPE` Build configuration, such as + ``Debug`` or ``Release``, determining + debug/optimization flags. This is only + relevant for single-configuration buildsystems such + as ``Makefile`` and ``Ninja``. Multi-configuration + buildsystems such as those for Visual Studio and Xcode + ignore this setting. + :variable:`CMAKE_INSTALL_PREFIX` Location to install the + software to with the + ``install`` build target + :variable:`CMAKE_TOOLCHAIN_FILE` File containing cross-compiling + data such as + :manual:`toolchains and sysroots <cmake-toolchains(7)>`. + :variable:`BUILD_SHARED_LIBS` Whether to build shared + instead of static libraries + for :command:`add_library` + commands used without a type + :variable:`CMAKE_EXPORT_COMPILE_COMMANDS` Generate a ``compile_commands.json`` + file for use with clang-based tools +========================================== ============================================================ + +Other project-specific variables may be available +to control builds, such as enabling or disabling +components of the project. + +There is no convention provided by CMake for how +such variables are named between different +provided buildsystems, except that variables with +the prefix ``CMAKE_`` usually refer to options +provided by CMake itself and should not be used +in third-party options, which should use +their own prefix instead. The +:manual:`cmake-gui(1)` tool can display options +in groups defined by their prefix, so it makes +sense for third parties to ensure that they use a +self-consistent prefix. + +Setting variables on the command line +------------------------------------- + +CMake variables can be set on the command line either +when creating the initial build: + +.. code-block:: console + + $ mkdir build + $ cd build + $ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug + +or later on a subsequent invocation of +:manual:`cmake(1)`: + +.. code-block:: console + + $ cd build + $ cmake . -DCMAKE_BUILD_TYPE=Debug + +The :option:`-U <cmake -U>` flag may be used to unset variables +on the :manual:`cmake(1)` command line: + +.. code-block:: console + + $ cd build + $ cmake . -UMyPackage_DIR + +A CMake buildsystem which was initially created +on the command line can be modified using the +:manual:`cmake-gui(1)` and vice-versa. + +The :manual:`cmake(1)` tool allows specifying a +file to use to populate the initial cache using +the :option:`-C <cmake -C>` option. This can be useful to simplify +commands and scripts which repeatedly require the +same cache entries. + +Setting variables with cmake-gui +-------------------------------- + +Variables may be set in the cmake-gui using the "Add Entry" +button. This triggers a new dialog to set the value of +the variable. + +.. image:: GUI-Add-Entry.png + :alt: Editing a cache entry + +The main view of the :manual:`cmake-gui(1)` user interface +can be used to edit existing variables. + +The CMake Cache +--------------- + +When CMake is executed, it needs to find the locations of +compilers, tools and dependencies. It also needs to be +able to consistently re-generate a buildsystem to use the +same compile/link flags and paths to dependencies. Such +parameters are also required to be configurable by the +user because they are paths and options specific to the +users system. + +When it is first executed, CMake generates a +``CMakeCache.txt`` file in the build directory containing +key-value pairs for such artifacts. The cache file can be +viewed or edited by the user by running the +:manual:`cmake-gui(1)` or :manual:`ccmake(1)` tool. The +tools provide an interactive interface for re-configuring +the provided software and re-generating the buildsystem, +as is needed after editing cached values. Each cache +entry may have an associated short help text which is +displayed in the user interface tools. + +The cache entries may also have a type to signify how it +should be presented in the user interface. For example, +a cache entry of type ``BOOL`` can be edited by a +checkbox in a user interface, a ``STRING`` can be edited +in a text field, and a ``FILEPATH`` while similar to a +``STRING`` should also provide a way to locate filesystem +paths using a file dialog. An entry of type ``STRING`` +may provide a restricted list of allowed values which are +then provided in a drop-down menu in the +:manual:`cmake-gui(1)` user interface (see the +:prop_cache:`STRINGS` cache property). + +The CMake files shipped with a software package may also +define boolean toggle options using the :command:`option` +command. The command creates a cache entry which has a +help text and a default value. Such cache entries are +typically specific to the provided software and affect +the configuration of the build, such as whether tests +and examples are built, whether to build with exceptions +enabled etc. + +Presets +======= + +CMake understands a file, ``CMakePresets.json``, and its +user-specific counterpart, ``CMakeUserPresets.json``, for +saving presets for commonly-used configure settings. These +presets can set the build directory, generator, cache +variables, environment variables, and other command-line +options. All of these options can be overridden by the +user. The full details of the ``CMakePresets.json`` format +are listed in the :manual:`cmake-presets(7)` manual. + +Using presets on the command-line +--------------------------------- + +When using the :manual:`cmake(1)` command line tool, a +preset can be invoked by using the :option:`--preset <cmake --preset>` +option. If :option:`--preset <cmake --preset>` is specified, +the generator and build directory are not required, but can be +specified to override them. For example, if you have the following +``CMakePresets.json`` file: + +.. code-block:: json + + { + "version": 1, + "configurePresets": [ + { + "name": "ninja-release", + "binaryDir": "${sourceDir}/build/${presetName}", + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ] + } + +and you run the following: + +.. code-block:: console + + cmake -S /path/to/source --preset=ninja-release + +This will generate a build directory in +``/path/to/source/build/ninja-release`` with the +:generator:`Ninja` generator, and with +:variable:`CMAKE_BUILD_TYPE` set to ``Release``. + +If you want to see the list of available presets, you can +run: + +.. code-block:: console + + cmake -S /path/to/source --list-presets + +This will list the presets available in +``/path/to/source/CMakePresets.json`` and +``/path/to/source/CMakeUsersPresets.json`` without +generating a build tree. + +Using presets in cmake-gui +-------------------------- + +If a project has presets available, either through +``CMakePresets.json`` or ``CMakeUserPresets.json``, the +list of presets will appear in a drop-down menu in +:manual:`cmake-gui(1)` between the source directory and +the binary directory. Choosing a preset sets the binary +directory, generator, environment variables, and cache +variables, but all of these options can be overridden after +a preset is selected. + +Invoking the Buildsystem +======================== + +After generating the buildsystem, the software can be +built by invoking the particular build tool. In the +case of the IDE generators, this can involve loading +the generated project file into the IDE to invoke the +build. + +CMake is aware of the specific build tool needed to invoke +a build so in general, to build a buildsystem or project +from the command line after generating, the following +command may be invoked in the build directory: + +.. code-block:: console + + $ cmake --build . + +The :option:`--build <cmake --build>` flag enables a +particular mode of operation for the :manual:`cmake(1)` +tool. It invokes the :variable:`CMAKE_MAKE_PROGRAM` +command associated with the +:manual:`generator <cmake-generators(7)>`, or +the build tool configured by the user. + +The :option:`--build <cmake --build>` mode also accepts +the parameter :option:`--target <cmake--build --target>` to +specify a particular target to build, for example a +particular library, executable or custom target, or a +particular special target like ``install``: + +.. code-block:: console + + $ cmake --build . --target myexe + +The :option:`--build <cmake --build>` mode also accepts a +:option:`--config <cmake--build --config>` parameter +in the case of multi-config generators to specify which +particular configuration to build: + +.. code-block:: console + + $ cmake --build . --target myexe --config Release + +The :option:`--config <cmake--build --config>` option has no +effect if the generator generates a buildsystem specific +to a configuration which is chosen when invoking cmake +with the :variable:`CMAKE_BUILD_TYPE` variable. + +Some buildsystems omit details of command lines invoked +during the build. The :option:`--verbose <cmake--build --verbose>` +flag can be used to cause those command lines to be shown: + +.. code-block:: console + + $ cmake --build . --target myexe --verbose + +The :option:`--build <cmake --build>` mode can also pass +particular command line options to the underlying build +tool by listing them after ``--``. This can be useful +to specify options to the build tool, such as to continue the +build after a failed job, where CMake does not +provide a high-level user interface. + +For all generators, it is possible to run the underlying +build tool after invoking CMake. For example, ``make`` +may be executed after generating with the +:generator:`Unix Makefiles` generator to invoke the build, +or ``ninja`` after generating with the :generator:`Ninja` +generator etc. The IDE buildsystems usually provide +command line tooling for building a project which can +also be invoked. + +Selecting a Target +------------------ + +Each executable and library described in the CMake files +is a build target, and the buildsystem may describe +custom targets, either for internal use, or for user +consumption, for example to create documentation. + +CMake provides some built-in targets for all buildsystems +providing CMake files. + +``all`` + The default target used by ``Makefile`` and ``Ninja`` + generators. Builds all targets in the buildsystem, + except those which are excluded by their + :prop_tgt:`EXCLUDE_FROM_ALL` target property or + :prop_dir:`EXCLUDE_FROM_ALL` directory property. The + name ``ALL_BUILD`` is used for this purpose for the + Xcode and Visual Studio generators. +``help`` + Lists the targets available for build. This target is + available when using the :generator:`Unix Makefiles` or + :generator:`Ninja` generator, and the exact output is + tool-specific. +``clean`` + Delete built object files and other output files. The + ``Makefile`` based generators create a ``clean`` target + per directory, so that an individual directory can be + cleaned. The ``Ninja`` tool provides its own granular + ``-t clean`` system. +``test`` + Runs tests. This target is only automatically available + if the CMake files provide CTest-based tests. See also + `Running Tests`_. +``install`` + Installs the software. This target is only automatically + available if the software defines install rules with the + :command:`install` command. See also + `Software Installation`_. +``package`` + Creates a binary package. This target is only + automatically available if the CMake files provide + CPack-based packages. +``package_source`` + Creates a source package. This target is only + automatically available if the CMake files provide + CPack-based packages. + +For ``Makefile`` based systems, ``/fast`` variants of binary +build targets are provided. The ``/fast`` variants are used +to build the specified target without regard for its +dependencies. The dependencies are not checked and +are not rebuilt if out of date. The :generator:`Ninja` +generator is sufficiently fast at dependency checking that +such targets are not provided for that generator. + +``Makefile`` based systems also provide build-targets to +preprocess, assemble and compile individual files in a +particular directory. + +.. code-block:: console + + $ make foo.cpp.i + $ make foo.cpp.s + $ make foo.cpp.o + +The file extension is built into the name of the target +because another file with the same name but a different +extension may exist. However, build-targets without the +file extension are also provided. + +.. code-block:: console + + $ make foo.i + $ make foo.s + $ make foo.o + +In buildsystems which contain ``foo.c`` and ``foo.cpp``, +building the ``foo.i`` target will preprocess both files. + +Specifying a Build Program +-------------------------- + +The program invoked by the :option:`--build <cmake --build>` +mode is determined by the :variable:`CMAKE_MAKE_PROGRAM` variable. +For most generators, the particular program does not need to be +configured. + +===================== =========================== =========================== + Generator Default make program Alternatives +===================== =========================== =========================== + XCode ``xcodebuild`` + Unix Makefiles ``make`` + NMake Makefiles ``nmake`` ``jom`` + NMake Makefiles JOM ``jom`` ``nmake`` + MinGW Makefiles ``mingw32-make`` + MSYS Makefiles ``make`` + Ninja ``ninja`` + Visual Studio ``msbuild`` + Watcom WMake ``wmake`` +===================== =========================== =========================== + +The ``jom`` tool is capable of reading makefiles of the +``NMake`` flavor and building in parallel, while the +``nmake`` tool always builds serially. After generating +with the :generator:`NMake Makefiles` generator a user +can run ``jom`` instead of ``nmake``. The +:option:`--build <cmake --build>` +mode would also use ``jom`` if the +:variable:`CMAKE_MAKE_PROGRAM` was set to ``jom`` while +using the :generator:`NMake Makefiles` generator, and +as a convenience, the :generator:`NMake Makefiles JOM` +generator is provided to find ``jom`` in the normal way +and use it as the :variable:`CMAKE_MAKE_PROGRAM`. For +completeness, ``nmake`` is an alternative tool which +can process the output of the +:generator:`NMake Makefiles JOM` generator, but doing +so would be a pessimization. + +Software Installation +===================== + +The :variable:`CMAKE_INSTALL_PREFIX` variable can be +set in the CMake cache to specify where to install the +provided software. If the provided software has install +rules, specified using the :command:`install` command, +they will install artifacts into that prefix. On Windows, +the default installation location corresponds to the +``ProgramFiles`` system directory which may be +architecture specific. On Unix hosts, ``/usr/local`` is +the default installation location. + +The :variable:`CMAKE_INSTALL_PREFIX` variable always +refers to the installation prefix on the target +filesystem. + +In cross-compiling or packaging scenarios where the +sysroot is read-only or where the sysroot should otherwise +remain pristine, the :variable:`CMAKE_STAGING_PREFIX` +variable can be set to a location to actually install +the files. + +The commands: + +.. code-block:: console + + $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_SYSROOT=$HOME/root \ + -DCMAKE_STAGING_PREFIX=/tmp/package + $ cmake --build . + $ cmake --build . --target install + +result in files being installed to paths such +as ``/tmp/package/lib/libfoo.so`` on the host machine. +The ``/usr/local`` location on the host machine is +not affected. + +Some provided software may specify ``uninstall`` rules, +but CMake does not generate such rules by default itself. + +Running Tests +============= + +The :manual:`ctest(1)` tool is shipped with the CMake +distribution to execute provided tests and report +results. The ``test`` build-target is provided to run +all available tests, but the :manual:`ctest(1)` tool +allows granular control over which tests to run, how to +run them, and how to report results. Executing +:manual:`ctest(1)` in the build directory is equivalent +to running the ``test`` target: + +.. code-block:: console + + $ ctest + +A regular expression can be passed to run only tests +which match the expression. To run only tests with +``Qt`` in their name: + +.. code-block:: console + + $ ctest -R Qt + +Tests can be excluded by regular expression too. To +run only tests without ``Qt`` in their name: + +.. code-block:: console + + $ ctest -E Qt + +Tests can be run in parallel by passing :option:`-j <ctest -j>` +arguments to :manual:`ctest(1)`: + +.. code-block:: console + + $ ctest -R Qt -j8 + +The environment variable :envvar:`CTEST_PARALLEL_LEVEL` +can alternatively be set to avoid the need to pass +:option:`-j <ctest -j>`. + +By default :manual:`ctest(1)` does not print the output +from the tests. The command line argument :option:`-V <ctest -V>` +(or ``--verbose``) enables verbose mode to print the +output from all tests. +The :option:`--output-on-failure <ctest --output-on-failure>` +option prints the test output for failing tests only. +The environment variable :envvar:`CTEST_OUTPUT_ON_FAILURE` +can be set to ``1`` as an alternative to passing the +:option:`--output-on-failure <ctest --output-on-failure>` +option to :manual:`ctest(1)`. diff --git a/Help/guide/using-dependencies/index.rst b/Help/guide/using-dependencies/index.rst new file mode 100644 index 0000000..8b270aa --- /dev/null +++ b/Help/guide/using-dependencies/index.rst @@ -0,0 +1,419 @@ +Using Dependencies Guide +************************ + +.. only:: html + + .. contents:: + +Introduction +============ + +Projects will frequently depend on other projects, assets, and artifacts. +CMake provides a number of ways to incorporate such things into the build. +Projects and users have the flexibility to choose between methods that +best suit their needs. + +The primary methods of bringing dependencies into the build are the +:command:`find_package` command and the :module:`FetchContent` module. +The :module:`FindPkgConfig` module is also sometimes used, although it +lacks some of the integration of the other two and is not discussed any +further in this guide. + +Dependencies can also be made available by a custom +:ref:`dependency provider <dependency_providers>`. +This might be a third party package manager, or it might be custom code +implemented by the developer. Dependency providers co-operate with the +primary methods mentioned above to extend their flexibility. + +.. _prebuilt_find_package: + +Using Pre-built Packages With ``find_package()`` +================================================ + +A package needed by the project may already be built and available at some +location on the user's system. That package might have also been built by +CMake, or it could have used a different build system entirely. It might +even just be a collection of files that didn't need to be built at all. +CMake provides the :command:`find_package` command for these scenarios. +It searches well-known locations, along with additional hints and paths +provided by the project or user. It also supports package components and +packages being optional. Result variables are provided to allow the project +to customize its own behavior according to whether the package or specific +components were found. + +In most cases, projects should generally use the :ref:`basic signature`. +Most of the time, this will involve just the package name, maybe a version +constraint, and the ``REQUIRED`` keyword if the dependency is not optional. +A set of package components may also be specified. + +.. code-block:: cmake + :caption: Examples of ``find_package()`` basic signature + + find_package(Catch2) + find_package(GTest REQUIRED) + find_package(Boost 1.79 COMPONENTS date_time) + +The :command:`find_package` command supports two main methods for carrying +out the search: + +**Config mode** + With this method, the command looks for files that are typically provided + by the package itself. This is the more reliable method of the two, since + the package details should always be in sync with the package. + +**Module mode** + Not all packages are CMake-aware. Many don't provide the files needed to + support config mode. For such cases, a Find module file can be provided + separately, either by the project or by CMake. A Find module is typically + a heuristic implementation which knows what the package normally provides + and how to present that package to the project. Since Find modules are + usually distributed separately from the package, they are not as reliable. + They are typically maintained separately, and they are likely to follow + different release schedules, so they can easily become out-of-date. + +Depending on the arguments used, :command:`find_package` may use one or both +of the above methods. By restricting the options to just the basic signature, +both config mode and module mode can be used to satisfy the dependency. +The presence of other options may restrict the call to using only one of the +two methods, potentially reducing the command's ability to find the dependency. +See the :command:`find_package` documentation for full details about this +complex topic. + +For both search methods, the user can also set cache variables on the +:manual:`cmake(1)` command line or in the :manual:`ccmake(1)` or +:manual:`cmake-gui(1)` UI tools to influence and override where to find +packages. See the :ref:`User Interaction Guide <Setting Build Variables>` +for more on how to set cache variables. + +.. _Libraries providing Config-file packages: + +Config-file packages +-------------------- + +The preferred way for a third party to provide executables, libraries, +headers, and other files for use with CMake is to provide +:ref:`config files <Config File Packages>`. These are text files shipped +with the package, which define CMake targets, variables, commands, and so on. +The config file is an ordinary CMake script, which is read in by the +:command:`find_package` command. + +The config files can usually be found in a directory whose name matches the +pattern ``lib/cmake/<PackageName>``, although they may be in other locations +instead (see :ref:`search procedure`). The ``<PackageName>`` is usually the +first argument to the :command:`find_package` command, and it may even be the +only argument. Alternative names can also be specified with the ``NAMES`` +option: + +.. code-block:: cmake + :caption: Providing alternative names when finding a package + + find_package(SomeThing + NAMES + SameThingOtherName # Another name for the package + SomeThing # Also still look for its canonical name + ) + +The config file must be named either ``<PackageName>Config.cmake`` or +``<LowercasePackageName>-config.cmake`` (the former is used for the remainder +of this guide, but both are supported). This file is the entry point +to the package for CMake. A separate optional file named +``<PackageName>ConfigVersion.cmake`` or +``<LowercasePackageName>-config-version.cmake`` may also exist in the same +directory. This file is used by CMake to determine whether the version of +the package satisfies any version constraint included in the call to +:command:`find_package`. It is optional to specify a version when calling +:command:`find_package`, even if a ``<PackageName>ConfigVersion.cmake`` +file is present. + +If the ``<PackageName>Config.cmake`` file is found and any version constraint +is satisfied, the :command:`find_package` command considers the package to be +found, and the entire package is assumed to be complete as designed. + +There may be additional files providing CMake commands or +:ref:`imported targets` for you to use. CMake does not enforce any naming +convention for these files. They are related to the primary +``<PackageName>Config.cmake`` file by use of the CMake :command:`include` +command. The ``<PackageName>Config.cmake`` file would typically include +these for you, so they won't usually require any additional step other than +the call to :command:`find_package`. + +If the location of the package is in a +:ref:`directory known to CMake <search procedure>`, the +:command:`find_package` call should succeed. The directories known to CMake +are platform-specific. For example, packages installed on Linux with a +standard system package manager will be found in the ``/usr`` prefix +automatically. Packages installed in ``Program Files`` on Windows will +similarly be found automatically. + +Packages will not be found automatically without help if they are in +locations not known to CMake, such as ``/opt/mylib`` or ``$HOME/dev/prefix``. +This is a normal situation, and CMake provides several ways for users to +specify where to find such libraries. + +The :variable:`CMAKE_PREFIX_PATH` variable may be +:ref:`set when invoking CMake <Setting Build Variables>`. +It is treated as a list of base paths in which to search for +:ref:`config files <Config File Packages>`. A package installed in +``/opt/somepackage`` will typically install config files such as +``/opt/somepackage/lib/cmake/somePackage/SomePackageConfig.cmake``. +In that case, ``/opt/somepackage`` should be added to +:variable:`CMAKE_PREFIX_PATH`. + +The environment variable ``CMAKE_PREFIX_PATH`` may also be populated with +prefixes to search for packages. Like the ``PATH`` environment variable, +this is a list, but it needs to use the platform-specific environment variable +list item separator (``:`` on Unix and ``;`` on Windows). + +The :variable:`CMAKE_PREFIX_PATH` variable provides convenience in cases +where multiple prefixes need to be specified, or when multiple packages +are available under the same prefix. Paths to packages may also be +specified by setting variables matching ``<PackageName>_DIR``, such as +``SomePackage_DIR``. Note that this is not a prefix, but should be a full +path to a directory containing a config-style package file, such as +``/opt/somepackage/lib/cmake/SomePackage`` in the above example. +See the :command:`find_package` documentation for other CMake variables and +environment variables that can affect the search. + +.. _Libraries not Providing Config-file Packages: + +Find Module Files +----------------- + +Packages which do not provide config files can still be found with the +:command:`find_package` command, if a ``FindSomePackage.cmake`` file is +available. These Find module files are different to config files in that: + +#. Find module files should not be provided by the package itself. +#. The availability of a ``Find<PackageName>.cmake`` file does not indicate + the availability of the package, or any particular part of the package. +#. CMake does not search the locations specified in the + :variable:`CMAKE_PREFIX_PATH` variable for ``Find<PackageName>.cmake`` + files. Instead, CMake searches for such files in the locations given + by the :variable:`CMAKE_MODULE_PATH` variable. It is common for users to + set the :variable:`CMAKE_MODULE_PATH` when running CMake, and it is common + for CMake projects to append to :variable:`CMAKE_MODULE_PATH` to allow use + of local Find module files. +#. CMake ships ``Find<PackageName>.cmake`` files for some + :manual:`third party packages <cmake-modules(7)>`. These files are a + maintenance burden for CMake, and it is not unusual for these to fall + behind the latest releases of the packages they are associated with. + In general, new Find modules are not added to CMake any more. Projects + should encourage the upstream packages to provide a config file where + possible. If that is unsuccessful, the project should provide its own + Find module for the package. + +See :ref:`Find Modules` for a detailed discussion of how to write a +Find module file. + +.. _Imported Targets from Packages: + +Imported Targets +---------------- + +Both config files and Find module files can define :ref:`Imported targets`. +These will typically have names of the form ``SomePrefix::ThingName``. +Where these are available, the project should prefer to use them instead of +any CMake variables that may also be provided. Such targets typically carry +usage requirements and apply things like header search paths, compiler +definitions, etc. automatically to other targets that link to them (e.g. using +:command:`target_link_libraries`). This is both more robust and more +convenient than trying to apply the same things manually using variables. +Check the documentation for the package or Find module to see what imported +targets it defines, if any. + +Imported targets should also encapsulate any configuration-specific paths. +This includes the location of binaries (libraries, executables), compiler +flags, and any other configuration-dependent quantities. Find modules may +be less reliable in providing these details than config files. + +A complete example which finds a third party package and uses a library +from it might look like the following: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.10) + project(MyExeProject VERSION 1.0.0) + + # Make project-provided Find modules available + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + + find_package(SomePackage REQUIRED) + add_executable(MyExe main.cpp) + target_link_libraries(MyExe PRIVATE SomePrefix::LibName) + +Note that the above call to :command:`find_package` could be resolved by +a config file or a Find module. It uses only the basic arguments supported +by the :ref:`basic signature`. A ``FindSomePackage.cmake`` file in the +``${CMAKE_CURRENT_SOURCE_DIR}/cmake`` directory would allow the +:command:`find_package` command to succeed using module mode, for example. +If no such module file is present, the system would be searched for a config +file. + + +Downloading And Building From Source With ``FetchContent`` +========================================================== + +Dependencies do not necessarily have to be pre-built in order to use them +with CMake. They can be built from sources as part of the main project. +The :module:`FetchContent` module provides functionality to download +content (typically sources, but can be anything) and add it to the main +project if the dependency also uses CMake. The dependency's sources will +be built along with the rest of the project, just as though the sources were +part of the project's own sources. + +The general pattern is that the project should first declare all the +dependencies it wants to use, then ask for them to be made available. +The following demonstrates the principle (see :ref:`fetch-content-examples` +for more): + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0 + ) + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG 605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1 + ) + FetchContent_MakeAvailable(googletest Catch2) + +Various download methods are supported, including downloading and extracting +archives from a URL (a range of archive formats are supported), and a number +of repository formats including Git, Subversion, and Mercurial. +Custom download, update, and patch commands can also be used to support +arbitrary use cases. + +When a dependency is added to the project with :module:`FetchContent`, the +project links to the dependency's targets just like any other target from the +project. If the dependency provides namespaced targets of the form +``SomePrefix::ThingName``, the project should link to those rather than to +any non-namespaced targets. See the next section for why this is recommended. + +Not all dependencies can be brought into the project this way. Some +dependencies define targets whose names clash with other targets from the +project or other dependencies. Concrete executable and library targets +created by :command:`add_executable` and :command:`add_library` are global, +so each one must be unique across the whole build. If a dependency would +add a clashing target name, it cannot be brought directly into the build +with this method. + +``FetchContent`` And ``find_package()`` Integration +=================================================== + +.. versionadded:: 3.24 + +Some dependencies support being added by either :command:`find_package` or +:module:`FetchContent`. Such dependencies must ensure they define the same +namespaced targets in both installed and built-from-source scenarios. +A consuming project then links to those namespaced targets and can handle +both scenarios transparently, as long as the project does not use anything +else that isn't provided by both methods. + +The project can indicate it is happy to accept a dependency by either method +using the ``FIND_PACKAGE_ARGS`` option to :command:`FetchContent_Declare`. +This allows :command:`FetchContent_MakeAvailable` to try satisfying the +dependency with a call to :command:`find_package` first, using the arguments +after the ``FIND_PACKAGE_ARGS`` keyword, if any. If that doesn't find the +dependency, it is built from source as described previously instead. + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0 + FIND_PACKAGE_ARGS NAMES GTest + ) + FetchContent_MakeAvailable(googletest) + + add_executable(ThingUnitTest thing_ut.cpp) + target_link_libraries(ThingUnitTest GTest::gtest_main) + +The above example calls +:command:`find_package(googletest NAMES GTest) <find_package>` first. +CMake provides a :module:`FindGTest` module, so if that finds a GTest package +installed somewhere, it will make it available, and the dependency will not be +built from source. If no GTest package is found, it *will* be built from +source. In either case, the ``GTest::gtest_main`` target is expected to be +defined, so we link our unit test executable to that target. + +High-level control is also available through the +:variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` variable. This can be set to +``NEVER`` to disable all redirection to :command:`find_package`. It can be +set to ``ALWAYS`` to try :command:`find_package` even if ``FIND_PACKAGE_ARGS`` +was not specified (this should be used with caution). + +The project might also decide that a particular dependency must be built from +source. This might be needed if a patched or unreleased version of the +dependency is required, or to satisfy some policy that requires all +dependencies to be built from source. The project can enforce this by adding +the ``OVERRIDE_FIND_PACKAGE`` keyword to :command:`FetchContent_Declare`. +A call to :command:`find_package` for that dependency will then be redirected +to :command:`FetchContent_MakeAvailable` instead. + +.. code-block:: cmake + + include(FetchContent) + FetchContent_Declare( + Catch2 + URL https://intranet.mycomp.com/vendored/Catch2_2.13.4_patched.tgz + URL_HASH MD5=abc123... + OVERRIDE_FIND_PACKAGE + ) + + # The following is automatically redirected to FetchContent_MakeAvailable(Catch2) + find_package(Catch2) + +For more advanced use cases, see the +:variable:`CMAKE_FIND_PACKAGE_REDIRECTS_DIR` variable. + +.. _dependency_providers_overview: + +Dependency Providers +==================== + +.. versionadded:: 3.24 + +The preceding section discussed techniques that projects can use to specify +their dependencies. Ideally, the project shouldn't really care where a +dependency comes from, as long as it provides the things it expects (often +just some imported targets). The project says what it needs and may also +specify where to get it from, in the absence of any other details, so that it +can still be built out-of-the-box. + +The developer, on the other hand, may be much more interested in controlling +*how* a dependency is provided to the project. You might want to use a +particular version of a package that you built yourself. You might want +to use a third party package manager. You might want to redirect some +requests to a different URL on a system you control for security or +performance reasons. CMake supports these sort of scenarios through +:ref:`dependency_providers`. + +A dependency provider can be set to intercept :command:`find_package` and +:command:`FetchContent_MakeAvailable` calls. The provider is given an +opportunity to satisfy such requests before falling back to the built-in +implementation if the provider doesn't fulfill it. + +Only one dependency provider can be set, and it can only be set at a very +specific point early in the CMake run. +The :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable lists CMake files +that will be read while processing the first :command:`project()` call (and +only that call). This is the only time a dependency provider may be set. +At most, one single provider is expected to be used throughout the whole +project. + +For some scenarios, the user wouldn't need to know the details of how the +dependency provider is set. A third party may provide a file that can be +added to :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES`, which will set up +the dependency provider on the user's behalf. This is the recommended +approach for package managers. The developer can use such a file like so:: + + cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=/path/to/package_manager/setup.cmake ... + +For details on how to implement your own custom dependency provider, see the +:command:`cmake_language(SET_DEPENDENCY_PROVIDER)` command. |
