mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-22 16:07:49 +08:00
806 lines
26 KiB
ReStructuredText
806 lines
26 KiB
ReStructuredText
Step 1: Getting Started with CMake
|
|
==================================
|
|
|
|
This first step in the CMake tutorial is intended as a quick-start into writing
|
|
useful builds for small projects with CMake. By the end, you will be able to
|
|
describe executables, libraries, source and header files, and the linkage
|
|
relationships between them using CMake.
|
|
|
|
Each exercise in this step will start with a discussion of the concepts and
|
|
commands needed for the exercise. 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 ``TODOs`` are intended to be
|
|
completed in numerical order, first complete ``TODO 1`` then ``TODO 2``, etc.
|
|
|
|
.. note::
|
|
Each step in the tutorial builds on the previous, but the steps are not
|
|
strictly contiguous. Code not relevant to learning CMake, such as C++
|
|
function implementations or CMake code outside the scope of the tutorial,
|
|
will sometimes be added between steps.
|
|
|
|
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 reviewed.
|
|
|
|
Background
|
|
^^^^^^^^^^
|
|
|
|
Typical usage of CMake revolves around one or more files named
|
|
``CMakeLists.txt``. This file is sometimes referred to as a "lists file" or
|
|
"CML". Within a given software project, a ``CMakeLists.txt`` will exist within
|
|
any directory where we want to provide instructions to CMake on how to handle
|
|
files and operations local to that directory or subdirectories. Each consists of
|
|
a set of commands which describe some information or actions relevant to
|
|
building the software project.
|
|
|
|
Not every directory in a software project needs a CML, but it's strongly
|
|
recommended that the project root contains one. This will serve as the entry
|
|
point for CMake for its initial setup during configuration. This *root* CML
|
|
should always contain the same two commands at or near the top the file.
|
|
|
|
.. code-block:: cmake
|
|
|
|
cmake_minimum_required(VERSION 3.23)
|
|
|
|
project(MyProjectName)
|
|
|
|
The :command:`cmake_minimum_required` is a compatibility guarantee provided by
|
|
CMake to the project developer. When called, it ensures that CMake will adopt
|
|
the behavior of the listed version. If a later version of CMake is invoked on a
|
|
CML containing the above code, it will act exactly as if it were CMake 3.23.
|
|
|
|
The :command:`project` command is a conceptually simple command which provides a
|
|
complex function. It informs CMake that what follows is the description of a
|
|
distinct software project of a given name (as opposed to a shell-like script).
|
|
When CMake sees the :command:`project` command it performs various checks to
|
|
ensure the environment is suitable for building software; such as checking for
|
|
compilers and other build tooling, and discovering properties like the
|
|
endianness of the host and target machines.
|
|
|
|
.. note::
|
|
While links to complete documentation are provided for every command, it is
|
|
not intended the reader understand the full semantics of each CMake command
|
|
they use. Effectively learning CMake, like any piece of software, is an
|
|
incremental process.
|
|
|
|
The rest of this tutorial step will be chiefly concerned with the usage of four
|
|
more commands. The :command:`add_executable` and :command:`add_library` commands
|
|
for describing output artifacts the software project wants to produce, the
|
|
:command:`target_sources` command for associating input files with their
|
|
respective output artifacts, and the :command:`target_link_libraries` command
|
|
for associating output artifacts with one another.
|
|
|
|
These four commands are the backbone of most CMake usage. As we'll learn, they
|
|
are sufficient for describing the majority of a typical project's requirements.
|
|
|
|
Exercise 1 - Building an Executable
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
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 only
|
|
four commands is needed.
|
|
|
|
.. note::
|
|
Although upper, lower and mixed case commands are supported by CMake,
|
|
lower case commands are preferred and will be used throughout the tutorial.
|
|
|
|
The first two commands we have already introduced, :command:`cmake_minimum_required`
|
|
and :command:`project`. There is no usage of CMake where the first command in a
|
|
root CML will be anything other than :command:`cmake_minimum_required`. There
|
|
are some advanced usages where :command:`project` might not be the second
|
|
command in a CML, but for our purposes it always will be.
|
|
|
|
The next command we need is :command:`add_executable`.
|
|
This command creates a *target*. In CMake lingo, a target is a name the
|
|
developer gives to a collection of properties.
|
|
|
|
Some examples of properties a target might want to keep track of are:
|
|
- The artifact kind (executable, library, header collection, etc)
|
|
- Source files
|
|
- Include directories
|
|
- Output name of an executable or library
|
|
- Dependencies
|
|
- Compiler and linker flags
|
|
|
|
The mechanisms of CMake are often best understood as describing and manipulating
|
|
targets and their properties. There are many more properties than those listed
|
|
here. Documentation of CMake commands will often discuss their function in terms
|
|
of the target properties they operate on.
|
|
|
|
Targets themselves are simply names, a handle to this collection of properties.
|
|
Using the :command:`add_executable` command is as easy as specifying the name
|
|
we want to use for the target.
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_executable(MyProgram)
|
|
|
|
Now that we have a name for our target, we can start associating properties
|
|
with it like source files we want to build and link. The primary command for
|
|
this is :command:`target_sources`, which takes as arguments a target name
|
|
followed by one or more collections of files.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_sources(MyProgram
|
|
PRIVATE
|
|
main.cxx
|
|
)
|
|
|
|
.. note::
|
|
Paths in CMake are generally either absolute, or relative to the
|
|
:variable:`CMAKE_CURRENT_SOURCE_DIR`. We haven't talked about variables like
|
|
that yet, so you can read this as "relative to the location of the current
|
|
CML".
|
|
|
|
Each collection of files is prefixed by a :ref:`scope keyword <Target Command Scope>`.
|
|
We'll discuss the complete semantics of these keywords when we talk about
|
|
linking targets together, but the quick explanation is these describe how a
|
|
property should be inherited by dependents of our target.
|
|
|
|
Typically, nothing depends on an executable. Other programs and libraries don't
|
|
need to link to an executable, or inherit headers, or anything of that nature.
|
|
So the appropriate scope to use here is ``PRIVATE``, which informs CMake that
|
|
this property only belongs to ``MyProgram`` and is not inheritable.
|
|
|
|
.. note::
|
|
This rule is true almost everywhere. Outside advanced and esoteric usages,
|
|
the scope keyword for executables should *always* be ``PRIVATE``. The same
|
|
holds for implementation files generally, regardless of whether the target
|
|
is an executable or a library. The only target which needs to "see" the
|
|
``.cxx`` files is the target building them.
|
|
|
|
Goal
|
|
----
|
|
|
|
Understand how to create a simple CMake project with a single executable.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`project`
|
|
* :command:`cmake_minimum_required`
|
|
* :command:`add_executable`
|
|
* :command:`target_sources`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``CMakeLists.txt``
|
|
|
|
Getting Started
|
|
----------------
|
|
|
|
The source code for ``Tutorial.cxx`` is provided in the
|
|
``Help/guide/tutorial/Step1/Tutorial`` directory and can be used to compute the
|
|
square root of a number. This file does not need to be edited in this exercise.
|
|
|
|
In the parent directory, ``Help/guide/tutorial/Step1``, is a ``CMakeLists.txt``
|
|
file which you will complete. Start with ``TODO 1`` and work through ``TODO 4``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
Once ``TODO 1`` through ``TODO 4`` 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/Step1`` directory and invoke CMake for configuration
|
|
as follows:
|
|
|
|
.. code-block:: console
|
|
|
|
cmake -B build
|
|
|
|
The :option:`-B <cmake -B>` flag tells CMake to use the given relative
|
|
path as the location to generate files and store artifacts during the build
|
|
process. If it is omitted, the current working directory is used. It is
|
|
generally considered bad practice to do "in-source" builds, placing these
|
|
generated files in the source tree itself.
|
|
|
|
Next, tell CMake to build the project with
|
|
:option:`cmake --build <cmake --build>`, passing it the same relative path
|
|
we did with the :option:`-B <cmake -B>` flag.
|
|
|
|
.. code-block:: console
|
|
|
|
cmake --build build
|
|
|
|
The ``Tutorial`` executable will be built into the ``build`` directory. For
|
|
multi-config generators (e.g. Visual Studio), it might be placed in a
|
|
subdirectory such as ``build/Debug``.
|
|
|
|
Finally, try to use the newly built ``Tutorial``:
|
|
|
|
.. code-block:: console
|
|
|
|
Tutorial 4294967296
|
|
Tutorial 10
|
|
Tutorial
|
|
|
|
.. note::
|
|
Depending on the shell, the correct syntax may be ``Tutorial``,
|
|
``./Tutorial``, ``.\Tutorial``, or even ``.\Tutorial.exe``. For simplicity,
|
|
the exercises will use ``Tutorial`` throughout.
|
|
|
|
Solution
|
|
--------
|
|
|
|
As mentioned above, a four command ``CMakeLists.txt`` is all that we need to get
|
|
up and running. The first line should be :command:`cmake_minimum_required`, to
|
|
set the CMake version as follows:
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 1: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/CMakeLists.txt
|
|
:caption: TODO 1: CMakeLists.txt
|
|
:name: CMakeLists.txt-cmake_minimum_required
|
|
:language: cmake
|
|
:start-at: cmake_minimum_required
|
|
:end-at: cmake_minimum_required
|
|
|
|
.. 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 and inform CMake we intend to build
|
|
software with this ``CMakeLists.txt``.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 2: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/CMakeLists.txt
|
|
:caption: TODO 2: CMakeLists.txt
|
|
:name: CMakeLists.txt-project
|
|
:language: cmake
|
|
:start-at: project
|
|
:end-at: project
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Now we can setup our executable target for the Tutorial with :command:`add_executable`.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 3: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/Tutorial/CMakeLists.txt
|
|
:caption: TODO 3: CMakeLists.txt
|
|
:name: CMakeLists.txt-add_executable
|
|
:language: cmake
|
|
:start-at: add_executable
|
|
:end-at: add_executable
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Finally, we can associate our source file with the Tutorial executable target
|
|
using :command:`target_sources`.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 4: Click to show/hide answer</summary>
|
|
|
|
.. code-block:: cmake
|
|
:caption: TODO 4: CMakeLists.txt
|
|
:name: CMakeLists.txt-target_sources
|
|
|
|
target_sources(Tutorial
|
|
PRIVATE
|
|
Tutorial/Tutorial.cxx
|
|
)
|
|
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Exercise 2 - Building a Library
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
We only need to introduce one more command to build a library,
|
|
:command:`add_library`. This works exactly like :command:`add_executable`, but
|
|
for libraries.
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_library(MyLibrary)
|
|
|
|
However, now is a good time to introduce header files. Header files are not
|
|
directly built as translation units, which is to say they are not a *build*
|
|
requirement. They are a *usage* requirement. We need to know about header files
|
|
in order to build other parts of a given target.
|
|
|
|
As such, header files are described slightly differently than implementation
|
|
files like ``tutorial.cxx``. They're also going to need different
|
|
:ref:`scope keywords <Target Command Scope>` than the ``PRIVATE`` keyword we
|
|
have used so far.
|
|
|
|
To describe a collection of header files, we're going to use what's known as a
|
|
``FILE_SET``.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_sources(MyLibrary
|
|
PRIVATE
|
|
library_implementation.cxx
|
|
|
|
PUBLIC
|
|
FILE_SET myHeaders
|
|
TYPE HEADERS
|
|
BASE_DIRS
|
|
include
|
|
FILES
|
|
include/library_header.h
|
|
)
|
|
|
|
This is a lot of complexity, but we'll go through it point by point. First,
|
|
note that we have our implementation file as a ``PRIVATE`` source, same as
|
|
with the executable previously. However, we now use ``PUBLIC`` for our
|
|
header file. This allows consumers of our library to "see" the library's
|
|
header files.
|
|
|
|
.. note::
|
|
We're not quite ready to discuss the full semantics of scope keywords. We'll
|
|
cover them more completely in Exercise 3.
|
|
|
|
Following the scope keyword is a ``FILE_SET``, a collection of files to be
|
|
described as a single unit. A ``FILE_SET`` consists of the following parts:
|
|
|
|
* ``FILE_SET <name>`` is the name of the ``FILE_SET``. This is a handle which
|
|
we can use to describe the collection in other contexts.
|
|
|
|
* ``TYPE <type>`` is the kind of files we are describing. Most commonly this
|
|
will be headers, but newer versions of CMake support other types like C++20
|
|
modules.
|
|
|
|
* ``BASE_DIRS`` is the "base" locations for the files. This can be most easily
|
|
understood as the locations that will be described to compilers for header
|
|
discovery via ``-I`` flags.
|
|
|
|
* ``FILES`` is the list of files, same as with the implementation sources list
|
|
earlier.
|
|
|
|
This is a lot of information to describe, so there are some useful shortcuts
|
|
we can take. Notably, if the ``FILE_SET`` name is the same as the type, we
|
|
don't need to provide the ``TYPE`` field.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_sources(MyLibrary
|
|
PRIVATE
|
|
library_implementation.cxx
|
|
|
|
PUBLIC
|
|
FILE_SET HEADERS
|
|
BASE_DIRS
|
|
include
|
|
FILES
|
|
include/library_header.h
|
|
)
|
|
|
|
There are other shortcuts we can take, but we'll discuss those more in later
|
|
steps.
|
|
|
|
Goal
|
|
----
|
|
|
|
Build a library.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`add_library`
|
|
* :command:`target_sources`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``CMakeLists.txt``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
Continue editing files in the ``Step1`` directory. Start with ``TODO 5`` and
|
|
complete through ``TODO 6``.
|
|
|
|
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
|
|
|
|
cmake --build build
|
|
|
|
We should be able to see our library created alongside the Tutorial executable.
|
|
|
|
Solution
|
|
--------
|
|
|
|
We start by adding the library target in the same manner as the the Tutorial
|
|
executable.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 5: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 5: CMakeLists.txt
|
|
:name: CMakeLists.txt-add_library
|
|
:language: cmake
|
|
:start-at: add_library
|
|
:end-at: add_library
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Next we need to describe the source files. For the implementation file,
|
|
``MathFunctions.cxx``, this is straight-forward; for the header file
|
|
``MathFunctions.h`` we will need to use a ``FILE_SET``.
|
|
|
|
We can either give this ``FILE_SET`` its own name, or use the shortcut of naming
|
|
it ``HEADERS``. For this tutorial, we'll be using the shortcut, but either
|
|
solution is valid.
|
|
|
|
For ``BASE_DIRS`` we need to determine the directory which will allow for the
|
|
desired ``#include <MathFunctions.h>`` directive. To achieve this, the
|
|
``MathFunctions`` folder itself will be a base directory. We would make a
|
|
different choice if the desired include directive were
|
|
``#include <MathFunctions/MathFunctions.h>`` or similar.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 6: Click to show/hide answer</summary>
|
|
|
|
.. code-block:: cmake
|
|
:caption: TODO 6: CMakeLists.txt
|
|
:name: CMakeLists.txt-library_sources
|
|
|
|
target_sources(MathFunctions
|
|
PRIVATE
|
|
MathFunctions/MathFunctions.cxx
|
|
|
|
PUBLIC
|
|
FILE_SET HEADERS
|
|
BASE_DIRS
|
|
MathFunctions
|
|
FILES
|
|
MathFunctions/MathFunctions.h
|
|
)
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Exercise 3 - Linking Together Libraries and Executables
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
We're ready to combine our library with our executable, for this we must
|
|
introduce a new command, :command:`target_link_libraries`. The name of this
|
|
command can be somewhat misleading, as it does a great deal more than just
|
|
invoke linkers. It describes relationships between targets generally.
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_link_libraries(MyProgram
|
|
PRIVATE
|
|
MyLibrary
|
|
)
|
|
|
|
We're finally ready to discuss the :ref:`scope keywords <Target Command Scope>`.
|
|
There are three of them, ``PRIVATE``, ``INTERFACE``, and ``PUBLIC``. These
|
|
describe how properties are made available to targets.
|
|
|
|
* A ``PRIVATE`` property (also called a "non-interface" property) is only
|
|
available to the target which owns it, for example ``PRIVATE`` headers will
|
|
only be visible to the target they're attached to.
|
|
|
|
* An ``INTERFACE`` property is only available to targets *which link* the
|
|
owning target. The owning target does not have access to these properties. A
|
|
header-only library is an example of a collection of ``INTERFACE`` properties,
|
|
as header-only libraries do not build anything themselves and do not need to
|
|
access their own files.
|
|
|
|
* ``PUBLIC`` is not a distinct kind of property, but rather is the union of the
|
|
``PRIVATE`` and ``INTERFACE`` properties. Thus requirements described with
|
|
``PUBLIC`` are available to both the owning target and consuming targets.
|
|
|
|
Consider the following concrete example:
|
|
|
|
.. code-block:: cmake
|
|
|
|
target_sources(MyLibrary
|
|
PRIVATE
|
|
FILE_SET internalOnlyHeaders
|
|
TYPE HEADERS
|
|
FILES
|
|
InternalOnlyHeader.h
|
|
|
|
INTERFACE
|
|
FILE_SET consumerOnlyHeaders
|
|
TYPE HEADERS
|
|
FILES
|
|
ConsumerOnlyHeader.h
|
|
|
|
PUBLIC
|
|
FILE_SET publicHeaders
|
|
TYPE HEADERS
|
|
FILES
|
|
PublicHeader.h
|
|
)
|
|
|
|
.. note::
|
|
We excluded ``BASE_DIRS`` for each file set here, that's another shortcut.
|
|
When excluded, ``BASE_DIRS`` defaults to the current source directory.
|
|
|
|
The ``MyLibrary`` target has several properties which will be modified by this
|
|
call to :command:`target_sources`. Until now we've used the term "properties"
|
|
generically, but properties are themselves named values we can reason about.
|
|
Two specific properties which will be modified here are :prop_tgt:`HEADER_SETS`
|
|
and :prop_tgt:`INTERFACE_HEADER_SETS`, which both contain lists of header file
|
|
sets added via :command:`target_sources`.
|
|
|
|
The value ``internalOnlyHeaders`` will be added to :prop_tgt:`HEADER_SETS`,
|
|
``consumerOnlyHeaders`` to :prop_tgt:`INTERFACE_HEADER_SETS`, and
|
|
``publicHeaders`` will be added to both.
|
|
|
|
When a given target is being built, it will use its own *non-interface*
|
|
properties (eg, :prop_tgt:`HEADER_SETS`), combined with the *interface*
|
|
properties of any targets it links to (eg, :prop_tgt:`INTERFACE_HEADER_SETS`).
|
|
|
|
.. note::
|
|
**It is not necessary to reason about CMake properties at this level of
|
|
detail.** The above is described for completeness. Most of the time you don't
|
|
need to be concerned with the specific properties a command is modifying.
|
|
|
|
Scope keywords have a simple intuition associated with them, when considering
|
|
a command from the point of view of the target it is being applied to:
|
|
**PRIVATE** is for me, **INTERFACE** is for others, **PUBLIC** is for all of
|
|
us.
|
|
|
|
Goal
|
|
----
|
|
|
|
In the Tutorial executable, use the ``sqrt()`` function provided by the
|
|
``MathFunctions`` library.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`target_link_libraries`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``CMakeLists.txt``
|
|
* ``Tutorial/Tutorial.cxx``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
Continue to edit files from ``Step1``. Start on ``TODO 7`` and complete through
|
|
``TODO 9``. In this exercise, we need to add the ``MathFunctions`` target to
|
|
the ``Tutorial`` target's linked libraries using :command:`target_link_libraries`.
|
|
|
|
After modifying the CML, update ``tutorial.cxx`` to use the
|
|
``mathfunctions::sqrt()`` function instead of ``std::sqrt``.
|
|
|
|
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
|
|
|
|
cmake --build build
|
|
|
|
Verify that the output matches what you would expect from the ``MathFunctions``
|
|
library.
|
|
|
|
Solution
|
|
--------
|
|
|
|
In this exercise, we are describing the ``Tutorial`` executable as a consumer
|
|
of the ``MathFunctions`` target by adding ``MathFunctions`` to the linked
|
|
libraries of the ``Tutorial``.
|
|
|
|
To achieve this, we modify ``CMakeLists.txt`` file to use the
|
|
:command:`target_link_libraries` command, using ``Tutorial`` as the target to
|
|
be modified and ``MathFunctions`` as the library we want to add.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 7: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/Tutorial/CMakeLists.txt
|
|
:caption: TODO 7: CMakeLists.txt
|
|
:name: CMakeLists.txt-target_link_libraries
|
|
:language: cmake
|
|
:start-at: target_link_libraries(Tutorial
|
|
:end-at: )
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
.. note::
|
|
The order here is only loosely relevant. That we call
|
|
:command:`target_link_libraries` prior to defining ``MathFunctions`` with
|
|
:command:`add_library` doesn't matter to CMake. We are recording that
|
|
``Tutorial`` has a dependency on something named ``MathFunctions``, but what
|
|
``MathFunctions`` means isn't resolved at this stage.
|
|
|
|
The only target which needs to be defined when calling a CMake command like
|
|
:command:`target_sources` or :command:`target_link_libraries` is the target
|
|
being modified.
|
|
|
|
Finally, all that's left to do is modify ``Tutorial.cxx`` to use the newly
|
|
provided ``mathfunctions::sqrt`` function. That means adding the appropriate
|
|
header file and modifying our ``sqrt()`` call.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 8-9: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/Tutorial/Tutorial.cxx
|
|
:caption: TODO 8: Tutorial/Tutorial.cxx
|
|
:name: Tutorial/Tutorial.cxx-MathFunctions-headers
|
|
:language: c++
|
|
:start-at: iostream
|
|
:end-at: MathFunctions.h
|
|
|
|
.. literalinclude:: Step3/Tutorial/Tutorial.cxx
|
|
:caption: TODO 9: Tutorial/Tutorial.cxx
|
|
:name: Tutorial/Tutorial.cxx-MathFunctions-code
|
|
:language: c++
|
|
:start-at: calculate square root
|
|
:end-at: mathfunctions::sqrt
|
|
:dedent: 2
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Exercise 4 - Subdirectories
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
As we move through the tutorial, we will be adding more commands to manipulate
|
|
the ``Tutorial`` executable and the ``MathFunctions`` library. We want to make
|
|
sure we keep commands local to the files they are dealing with. While not a
|
|
major concern for a small project like this, it can be very useful for large
|
|
projects with many targets and thousands of files.
|
|
|
|
The :command:`add_subdirectory` command allows us to incorporate CMLs located
|
|
in subdirectories of the project.
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_subdirectory(SubdirectoryName)
|
|
|
|
When a ``CMakeLists.txt`` in a subdirectory is being processed by CMake all
|
|
relative paths described in the subdirectory CML are relative to that
|
|
subdirectory, not the top-level CML.
|
|
|
|
Goal
|
|
----
|
|
|
|
Use :command:`add_subdirectory` to organize the project.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`add_subdirectory`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``CMakeLists.txt``
|
|
* ``Tutorial/CMakeLists.txt``
|
|
* ``MathFunctions/CMakeLists.txt``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
The ``TODOs`` for this step are spread across three ``CMakeLists.txt`` files.
|
|
Be sure to pay attention to the path changes necessary when moving the
|
|
:command:`target_sources` commands into subdirectories.
|
|
|
|
.. note::
|
|
Previously we said that ``BASE_DIRS`` defaults to the current source
|
|
directory. As the desired include directory for ``MathFunctions`` will now be
|
|
the same directory as the CML calling :command:`target_sources`, we should
|
|
remove the ``BASE_DIRS`` keyword and argument entirely.
|
|
|
|
Complete ``TODO 10`` through ``TODO 13``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
Because of the reorganization, we'll need to clean the original build
|
|
directory prior to rebuilding (otherwise our new ``Target`` build folder would
|
|
conflict with our previously created ``Target`` executable). We can achieve
|
|
this with the :option:`--clean-first <cmake--build --clean-first>` flag.
|
|
|
|
There's no need for a reconfiguration. CMake will automatically
|
|
re-configure itself due to the changes in the CMLs.
|
|
|
|
.. code-block:: console
|
|
|
|
cmake --build build --clean-first
|
|
|
|
.. note::
|
|
Our executable and library will be output to a new location in the build tree.
|
|
A subdirectory which mirrors where :command:`add_executable` and
|
|
:command:`add_library` were called in the source tree. You will need to
|
|
navigate to this subdirectory in the build tree to run the tutorial
|
|
executable in future steps.
|
|
|
|
You can verify this behavior by deleting the old ``Tutorial`` executable,
|
|
and observing that the new one is produced at ``Tutorial/Tutorial``.
|
|
|
|
Solution
|
|
--------
|
|
|
|
We need to move all the commands concerning the ``Tutorial`` executable into
|
|
``Tutorial/CMakeLists.txt``, and replace them with an
|
|
:command:`add_subdirectory` command. We also need to update the path for
|
|
``Tutorial.cxx``.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 10-11: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/Tutorial/CMakeLists.txt
|
|
:caption: TODO 10: Tutorial/CMakeLists.txt
|
|
:name: Tutorial/CMakeLists.txt-moved
|
|
:language: cmake
|
|
|
|
.. code-block:: cmake
|
|
:caption: TODO 11: CMakeLists.txt
|
|
:name: CMakeLists.txt-add_subdirectory-Tutorial
|
|
|
|
add_subdirectory(Tutorial)
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
We need to do the same with the commands for ``MathFunctions``, changing the
|
|
relative paths as appropriate and removing ``BASE_DIRS`` as it is no longer
|
|
necessary, the default value will work.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 12-13: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 12: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions/CMakeLists.txt-moved
|
|
:language: cmake
|
|
|
|
.. literalinclude:: Step3/CMakeLists.txt
|
|
:caption: TODO 13: CMakeLists.txt
|
|
:name: CMakeLists.txt-add_subdirectory-MathFunctions
|
|
:language: cmake
|
|
:start-at: add_subdirectory(MathFunctions
|
|
:end-at: add_subdirectory(MathFunctions
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|