Tutorial: Add Step 1 background info and update style

This commit is contained in:
Markus Ferrell
2022-07-21 13:43:58 -04:00
parent 1493ed10a1
commit c59e164155
4 changed files with 316 additions and 107 deletions

View File

@@ -1,45 +1,81 @@
Step 1: A Basic Starting Point
==============================
The most basic project is an executable built from source code files.
For simple projects, a three line ``CMakeLists.txt`` file is all that is
required. This will be the starting point for our tutorial. Create a
``CMakeLists.txt`` file in the ``Step1`` directory that looks like:
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.
.. code-block:: cmake
:caption: CMakeLists.txt
:name: CMakeLists.txt-start
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.
cmake_minimum_required(VERSION 3.10)
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``.
# set the project name
project(Tutorial)
Exercise 1 - Building a Basic Project
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# add the executable
add_executable(Tutorial tutorial.cxx)
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 :command:`cmake_minimum_required`. This ensures
that the later CMake functions are run with a compatible version of CMake.
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 :command:`project` to set the project name. This
call is required with every project and should be called soon after
:command:`cmake_minimum_required`.
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.
Lastly, we use :command:`add_executable` to specify we want an executable
named Tutorial generated using ``tutorial.cxx`` as the source.
Finally, the :command:`add_executable` command tells CMake to create an
executable using the specified source code files.
Note that this example uses lower case commands in the ``CMakeLists.txt``
file. Upper, lower, and mixed case commands are supported by CMake. The source
code for ``tutorial.cxx`` is provided in the ``Step1`` directory and can be
used to compute the square root of a number.
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
-------------
That's all that is needed - we can build and run our project now! First, run
the :manual:`cmake <cmake(1)>` executable or the
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.
@@ -51,8 +87,9 @@ build directory:
mkdir Step1_build
Next, navigate to the build directory and run CMake to configure the project
and generate a native build system:
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
@@ -73,109 +110,90 @@ Finally, try to use the newly built ``Tutorial`` with these commands:
Tutorial 10
Tutorial
Solution
--------
Adding a Version Number and Configured Header File
--------------------------------------------------
The first feature we will add is to provide our executable and project with a
version number. While we could do this exclusively in the source code, using
``CMakeLists.txt`` provides more flexibility.
First, modify the ``CMakeLists.txt`` file to use the :command:`project` command
to set the project name and version number.
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:
.. literalinclude:: Step2/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-project-VERSION
:caption: TODO 1: CMakeLists.txt
:name: CMakeLists.txt-cmake_minimum_required
:language: cmake
:end-before: # specify the C++ standard
:end-before: # set the project name and version
Then use :command:`configure_file` to pass the version number to the source
code:
The next step to make a basic project is to use the :command:`project`
command as follows to set the project name:
.. code-block:: cmake
:caption: TODO 2: CMakeLists.txt
:name: CMakeLists.txt-project
project(Tutorial)
The last command to call for a basic project is
:command:`add_executable`. We call it as follows:
.. literalinclude:: Step2/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-configure_file
:caption: TODO 3: CMakeLists.txt
:name: CMakeLists.txt-add_executable
:language: cmake
:start-after: # to the source code
:end-before: # add the executable
:start-after: # add the executable
:end-before: # add the binary tree to the search path for include files
Since the configured file will be written into the binary tree, we
must add that directory to the list of paths to search for include
files. Use :command:`target_include_directories` to add the following lines to
the end of the ``CMakeLists.txt`` file:
Exercise 2 - Specifying the C++ Standard
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. literalinclude:: Step2/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-target_include_directories
:language: cmake
:start-after: # so that we will find TutorialConfig.h
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.
Using your favorite editor, create ``TutorialConfig.h.in`` in the source
directory with the following contents:
Goal
----
.. literalinclude:: Step2/TutorialConfig.h.in
:caption: TutorialConfig.h.in
:name: TutorialConfig.h.in
:language: c++
Add a feature that requires C++11.
When CMake configures this header file, the values for
``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will be
replaced with the corresponding version numbers from the project.
Helpful Resources
-----------------
Next modify ``tutorial.cxx`` to include the configured header file,
``TutorialConfig.h``.
* :variable:`CMAKE_CXX_STANDARD`
* :variable:`CMAKE_CXX_STANDARD_REQUIRED`
* :command:`set`
Finally, let's print out the executable name and version number by updating
``tutorial.cxx`` as follows:
Files to Edit
-------------
.. literalinclude:: Step2/tutorial.cxx
:caption: tutorial.cxx
:name: tutorial.cxx-print-version
:language: c++
:start-after: {
:end-before: // convert input to double
* ``CMakeLists.txt``
* ``tutorial.cxx``
Specify the C++ Standard
-------------------------
Getting Started
---------------
Next let's add some C++11 features to our project by replacing ``atof`` with
``std::stod`` in ``tutorial.cxx``. At the same time, remove
``#include <cstdlib>``.
Continue editing files in the ``Step1`` directory. Start with ``TODO 4`` and
complete through ``TODO 6``.
.. literalinclude:: Step2/tutorial.cxx
:caption: tutorial.cxx
:name: tutorial.cxx-cxx11
:language: c++
:start-after: // convert input to double
:end-before: // calculate square root
First, edit ``tutorial.cxx`` by adding a feature that requires C++11. Then
update ``CMakeLists.txt`` to require C++11.
We will need to explicitly state in the CMake code that it should use the
correct flags. The easiest way to enable support for a specific C++ standard
in CMake is by using the :variable:`CMAKE_CXX_STANDARD` variable. For this
tutorial, :command:`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 ``CMAKE_CXX_STANDARD`` declarations above the
call to ``add_executable``.
Build and Run
-------------
.. literalinclude:: Step2/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-CXX_STANDARD
:language: cmake
:end-before: # configure a header file to pass some of the CMake settings
Rebuild
-------
Let's build our project again. We already created a build directory and ran
CMake, so we can skip to the build step:
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:
Now we can try to use the newly built ``Tutorial`` with same commands as
before:
.. code-block:: console
@@ -183,5 +201,173 @@ Now we can try to use the newly built ``Tutorial`` with same commands as before:
Tutorial 10
Tutorial
Check that the version number is now reported when running the executable without
any arguments.
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:
.. literalinclude:: Step2/tutorial.cxx
:caption: TODO 4: tutorial.cxx
:name: tutorial.cxx-cxx11
:language: c++
:start-after: // convert input to double
:end-before: // calculate square root
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`.
.. 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 to pass some of the CMake settings
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.
.. 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
Then we used :command:`configure_file` to copy the input file with the
specified CMake variables replaced:
.. literalinclude:: Step2/CMakeLists.txt
:caption: TODO 8: CMakeLists.txt
:name: CMakeLists.txt-configure_file
:language: cmake
:start-after: # to the source code
:end-before: # add the executable
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.
.. 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
``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``.
.. literalinclude:: Step2/TutorialConfig.h.in
:caption: TODO 10: TutorialConfig.h.in
:name: TutorialConfig.h.in
:language: c++
Next, we need to modify ``tutorial.cxx`` to include the configured header file,
``TutorialConfig.h``.
.. code-block:: c++
:caption: TODO 11: tutorial.cxx
#include "TutorialConfig.h"
Finally, we print out the executable name and version number by updating
``tutorial.cxx`` as follows:
.. literalinclude:: Step2/tutorial.cxx
:caption: TODO 12 : tutorial.cxx
:name: tutorial.cxx-print-version
:language: c++
:start-after: {
:end-before: // convert input to double

View File

@@ -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_REQUIRED_STANDARD 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}

View File

@@ -0,0 +1,2 @@
// the configured options and settings for Tutorial
// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR

View File

@@ -1,17 +1,22 @@
// A simple program that computes the square root of a number
#include <cmath>
#include <cstdlib>
#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