summaryrefslogtreecommitdiffstats
path: root/Help/guide/tutorial/Step11/TutorialProject
diff options
context:
space:
mode:
authorVito Gamberini <vito.gamberini@kitware.com>2025-08-20 16:42:42 (GMT)
committerBrad King <brad.king@kitware.com>2025-09-17 15:57:23 (GMT)
commitb2e3e3e30e5601192577d15e043b33db96fe1661 (patch)
tree914f9ccecb2ca54a86791cb41300a3cffaca54fb /Help/guide/tutorial/Step11/TutorialProject
parent9e89400d13199b17abc40f8837a846d563465189 (diff)
downloadCMake-b2e3e3e30e5601192577d15e043b33db96fe1661.zip
CMake-b2e3e3e30e5601192577d15e043b33db96fe1661.tar.gz
CMake-b2e3e3e30e5601192577d15e043b33db96fe1661.tar.bz2
Tutorial: Rewrite using conventions enabled by CMake 3.23
This is a full re-write of the CMake Tutorial for CMake 3.23, both the functionality it provides, as well as the modern workflows that developers use when interfacing with CMake. Issue: #22663, #23086, #23799, #26053, #26105, #26153, #26914
Diffstat (limited to 'Help/guide/tutorial/Step11/TutorialProject')
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/CMakeLists.txt59
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/CMakePresets.json16
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/CMakeLists.txt55
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt28
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx25
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt3
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx6
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h5
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx6
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h5
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx6
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h5
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.cxx101
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.h9
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt6
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathFormatting.h27
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathLogger.h22
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathOutput.h11
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/Tests/CMakeLists.txt16
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/Tests/TestMathFunctions.cxx22
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/Tutorial/CMakeLists.txt39
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/Tutorial/Tutorial.cxx27
-rw-r--r--Help/guide/tutorial/Step11/TutorialProject/cmake/TutorialConfig.cmake1
26 files changed, 533 insertions, 0 deletions
diff --git a/Help/guide/tutorial/Step11/TutorialProject/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/CMakeLists.txt
new file mode 100644
index 0000000..9a4ecbd
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/CMakeLists.txt
@@ -0,0 +1,59 @@
+cmake_minimum_required(VERSION 3.23)
+
+project(Tutorial
+ VERSION 1.0.0
+)
+
+option(TUTORIAL_BUILD_UTILITIES "Build the Tutorial executable" ON)
+option(TUTORIAL_USE_STD_SQRT "Use std::sqrt" OFF)
+option(TUTORIAL_ENABLE_IPO "Check for and use IPO support" ON)
+option(BUILD_TESTING "Enable testing and build tests" ON)
+
+if(TUTORIAL_ENABLE_IPO)
+ include(CheckIPOSupported)
+ check_ipo_supported(RESULT result OUTPUT output)
+ if(result)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
+ else()
+ message(WARNING "IPO is not supported ${message}")
+ endif()
+endif()
+
+if(TUTORIAL_BUILD_UTILITIES)
+ add_subdirectory(Tutorial)
+endif()
+
+if(BUILD_TESTING)
+ enable_testing()
+ add_subdirectory(Tests)
+endif()
+
+add_subdirectory(MathFunctions)
+
+include(GNUInstallDirs)
+
+install(
+ TARGETS MathFunctions OpAdd OpMul OpSub MathLogger SqrtTable
+ EXPORT TutorialTargets
+ FILE_SET HEADERS
+)
+
+install(
+ EXPORT TutorialTargets
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial
+ NAMESPACE Tutorial::
+)
+
+include(CMakePackageConfigHelpers)
+
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/TutorialConfigVersion.cmake
+ COMPATIBILITY ExactVersion
+)
+
+install(
+ FILES
+ cmake/TutorialConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/TutorialConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial
+)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/CMakePresets.json b/Help/guide/tutorial/Step11/TutorialProject/CMakePresets.json
new file mode 100644
index 0000000..fee177b
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/CMakePresets.json
@@ -0,0 +1,16 @@
+{
+ "version": 4,
+ "configurePresets": [
+ {
+ "name": "tutorial",
+ "displayName": "Tutorial Preset",
+ "description": "Preset to use with the tutorial",
+ "binaryDir": "${sourceDir}/build",
+ "cacheVariables": {
+ "CMAKE_PREFIX_PATH": "${sourceParentDir}/install",
+ "TUTORIAL_USE_STD_SQRT": "OFF",
+ "TUTORIAL_ENABLE_IPO": "OFF"
+ }
+ }
+ ]
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/CMakeLists.txt
new file mode 100644
index 0000000..c3ea012
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/CMakeLists.txt
@@ -0,0 +1,55 @@
+add_library(MathFunctions)
+# TODO1: Add an alias for the MathFunctions library to match the exported target
+
+target_sources(MathFunctions
+ PRIVATE
+ MathFunctions.cxx
+
+ PUBLIC
+ FILE_SET HEADERS
+ FILES
+ MathFunctions.h
+)
+
+target_link_libraries(MathFunctions
+ PRIVATE
+ MathLogger
+ SqrtTable
+
+ PUBLIC
+ OpAdd
+ OpMul
+ OpSub
+)
+
+target_compile_features(MathFunctions PRIVATE cxx_std_20)
+
+if(TUTORIAL_USE_STD_SQRT)
+ target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_STD_SQRT)
+endif()
+
+include(CheckIncludeFiles)
+check_include_files(emmintrin.h HAS_EMMINTRIN LANGUAGE CXX)
+
+if(HAS_EMMINTRIN)
+ target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_SSE2)
+endif()
+
+include(CheckSourceCompiles)
+check_source_compiles(CXX
+ [=[
+ typedef double v2df __attribute__((vector_size(16)));
+ int main() {
+ __builtin_ia32_sqrtsd(v2df{});
+ }
+ ]=]
+ HAS_GNU_BUILTIN
+)
+
+if(HAS_GNU_BUILTIN)
+ target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_GNU_BUILTIN)
+endif()
+
+add_subdirectory(MathLogger)
+add_subdirectory(MathExtensions)
+add_subdirectory(MakeTable)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt
new file mode 100644
index 0000000..6aa2a32
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt
@@ -0,0 +1,28 @@
+add_executable(MakeTable)
+
+target_sources(MakeTable
+ PRIVATE
+ MakeTable.cxx
+)
+
+add_custom_command(
+ OUTPUT SqrtTable.h
+ COMMAND MakeTable SqrtTable.h
+ DEPENDS MakeTable
+ VERBATIM
+)
+
+add_custom_target(RunMakeTable DEPENDS SqrtTable.h)
+
+add_library(SqrtTable INTERFACE)
+
+target_sources(SqrtTable
+ INTERFACE
+ FILE_SET HEADERS
+ BASE_DIRS
+ ${CMAKE_CURRENT_BINARY_DIR}
+ FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/SqrtTable.h
+)
+
+add_dependencies(SqrtTable RunMakeTable)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx
new file mode 100644
index 0000000..f85b278
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/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);
+ bool const 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/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt
new file mode 100644
index 0000000..b113786
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(OpAdd)
+add_subdirectory(OpMul)
+add_subdirectory(OpSub)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt
new file mode 100644
index 0000000..f35da81
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(OpAdd OBJECT)
+
+target_sources(OpAdd
+ PRIVATE
+ OpAdd.cxx
+
+ INTERFACE
+ FILE_SET HEADERS
+ FILES
+ OpAdd.h
+)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx
new file mode 100644
index 0000000..ea11496
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx
@@ -0,0 +1,6 @@
+namespace mathfunctions {
+double OpAdd(double a, double b)
+{
+ return a + b;
+}
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h
new file mode 100644
index 0000000..9c9efc3
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h
@@ -0,0 +1,5 @@
+#pragma once
+
+namespace mathfunctions {
+double OpAdd(double a, double b);
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt
new file mode 100644
index 0000000..f494fc6
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(OpMul OBJECT)
+
+target_sources(OpMul
+ PRIVATE
+ OpMul.cxx
+
+ INTERFACE
+ FILE_SET HEADERS
+ FILES
+ OpMul.h
+)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx
new file mode 100644
index 0000000..c8eb016
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx
@@ -0,0 +1,6 @@
+namespace mathfunctions {
+double OpMul(double a, double b)
+{
+ return a * b;
+}
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h
new file mode 100644
index 0000000..52b467b
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h
@@ -0,0 +1,5 @@
+#pragma once
+
+namespace mathfunctions {
+double OpMul(double a, double b);
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt
new file mode 100644
index 0000000..1a108fd
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(OpSub OBJECT)
+
+target_sources(OpSub
+ PRIVATE
+ OpSub.cxx
+
+ INTERFACE
+ FILE_SET HEADERS
+ FILES
+ OpSub.h
+)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx
new file mode 100644
index 0000000..b7b35da
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx
@@ -0,0 +1,6 @@
+namespace mathfunctions {
+double OpSub(double a, double b)
+{
+ return a - b;
+}
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h
new file mode 100644
index 0000000..1406733
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h
@@ -0,0 +1,5 @@
+#pragma once
+
+namespace mathfunctions {
+double OpSub(double a, double b);
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000..4bf8051
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,101 @@
+#include <cmath>
+#include <format>
+
+#include <MathLogger.h>
+
+#ifdef TUTORIAL_USE_SSE2
+# include <emmintrin.h>
+#endif
+
+namespace {
+
+mathlogger::Logger Logger;
+
+#if defined(TUTORIAL_USE_GNU_BUILTIN)
+typedef double v2df __attribute__((vector_size(16)));
+
+double gnu_mysqrt(double x)
+{
+ v2df root = __builtin_ia32_sqrtsd(v2df{ x, 0.0 });
+ double result = root[0];
+ Logger.Log(std::format("Computed sqrt of {} to be {} with GNU-builtins\n", x,
+ result));
+ return result;
+}
+#elif defined(TUTORIAL_USE_SSE2)
+double sse2_mysqrt(double x)
+{
+ __m128d root = _mm_sqrt_sd(_mm_setzero_pd(), _mm_set_sd(x));
+ double result = _mm_cvtsd_f64(root);
+ Logger.Log(
+ std::format("Computed sqrt of {} to be {} with SSE2\n", x, result));
+ return result;
+}
+#endif
+
+// a hack square root calculation using simple operations
+double fallback_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;
+
+ Logger.Log(std::format("Computing sqrt of {} to be {}\n", x, result));
+ }
+ return result;
+}
+
+#include <SqrtTable.h>
+
+double table_sqrt(double x)
+{
+ double 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;
+ }
+ Logger.Log(
+ std::format("Computed sqrt of {} to be {} with TableSqrt\n", x, result));
+ return result;
+}
+
+double mysqrt(double x)
+{
+ if (x >= 1 && x < 10) {
+ return table_sqrt(x);
+ }
+
+#if defined(TUTORIAL_USE_GNU_BUILTIN)
+ return gnu_mysqrt(x);
+#elif defined(TUTORIAL_USE_SSE2)
+ return sse2_mysqrt(x);
+#else
+ return fallback_mysqrt(x);
+#endif
+}
+}
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef TUTORIAL_USE_STD_SQRT
+ return std::sqrt(x);
+#else
+ return mysqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.h
new file mode 100644
index 0000000..91cb176
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <OpAdd.h>
+#include <OpMul.h>
+#include <OpSub.h>
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt
new file mode 100644
index 0000000..b20151f
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_library(MathLogger INTERFACE)
+
+target_sources(MathLogger
+ INTERFACE
+ FILE_SET HEADERS
+)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathFormatting.h
new file mode 100644
index 0000000..3b6d61c
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathFormatting.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <string>
+
+namespace mathlogger {
+
+enum LogLevel
+{
+ INFO,
+ WARN,
+ ERROR,
+};
+
+inline std::string FormatLog(LogLevel level, std::string const& message)
+{
+ switch (level) {
+ case INFO:
+ return "INFO: " + message;
+ case WARN:
+ return "WARN: " + message;
+ case ERROR:
+ return "ERROR: " + message;
+ }
+ return "UNKNOWN: " + message;
+}
+
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathLogger.h
new file mode 100644
index 0000000..ef7b31a
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathLogger.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <string>
+
+#include "MathFormatting.h"
+#include "MathOutput.h"
+
+namespace mathlogger {
+
+struct Logger
+{
+ LogLevel level = INFO;
+
+ void SetLevel(LogLevel new_level) { level = new_level; }
+ void Log(std::string const& message)
+ {
+ std::string formatted = FormatLog(level, message);
+ WriteLog(formatted);
+ }
+};
+
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathOutput.h
new file mode 100644
index 0000000..63f0aeb
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathOutput.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <iostream>
+#include <string>
+
+namespace mathlogger {
+inline void WriteLog(std::string const& msg)
+{
+ std::cout << msg;
+}
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/Tests/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/Tests/CMakeLists.txt
new file mode 100644
index 0000000..9b5bcd1
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/Tests/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_executable(TestMathFunctions)
+
+target_sources(TestMathFunctions
+ PRIVATE
+ TestMathFunctions.cxx
+)
+
+find_package(SimpleTest REQUIRED)
+
+target_link_libraries(TestMathFunctions
+ PRIVATE
+ MathFunctions
+ SimpleTest::SimpleTest
+)
+
+simpletest_discover_tests(TestMathFunctions)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/Tests/TestMathFunctions.cxx b/Help/guide/tutorial/Step11/TutorialProject/Tests/TestMathFunctions.cxx
new file mode 100644
index 0000000..166fd5d
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/Tests/TestMathFunctions.cxx
@@ -0,0 +1,22 @@
+#include <MathFunctions.h>
+#include <SimpleTest.h>
+
+TEST("add")
+{
+ REQUIRE(mathfunctions::OpAdd(2.0, 2.0) == 4.0);
+}
+
+TEST("sub")
+{
+ REQUIRE(mathfunctions::OpSub(4.0, 2.0) == 2.0);
+}
+
+TEST("mul")
+{
+ REQUIRE(mathfunctions::OpMul(5.0, 5.0) == 25.0);
+}
+
+TEST("sqrt")
+{
+ REQUIRE(mathfunctions::sqrt(25.0) == 5.0);
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step11/TutorialProject/Tutorial/CMakeLists.txt
new file mode 100644
index 0000000..79b232b
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/Tutorial/CMakeLists.txt
@@ -0,0 +1,39 @@
+add_executable(Tutorial)
+
+target_sources(Tutorial
+ PRIVATE
+ Tutorial.cxx
+)
+
+target_link_libraries(Tutorial
+ PRIVATE
+ MathFunctions
+)
+
+target_compile_features(Tutorial PRIVATE cxx_std_20)
+
+if(
+ (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR
+ (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
+)
+
+ target_compile_options(Tutorial PRIVATE /W3)
+
+elseif(
+ (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR
+ (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+)
+
+ target_compile_options(Tutorial PRIVATE -Wall)
+
+endif()
+
+find_path(UnpackagedIncludeFolder Unpackaged.h REQUIRED
+ PATH_SUFFIXES
+ Unpackaged
+)
+
+target_include_directories(Tutorial
+ PRIVATE
+ ${UnpackagedIncludeFolder}
+)
diff --git a/Help/guide/tutorial/Step11/TutorialProject/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step11/TutorialProject/Tutorial/Tutorial.cxx
new file mode 100644
index 0000000..ac133d4
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/Tutorial/Tutorial.cxx
@@ -0,0 +1,27 @@
+// A simple program that computes the square root of a number
+#include <format>
+#include <iostream>
+#include <string>
+
+#include <MathFunctions.h>
+#include <Unpackaged.h>
+
+int main(int argc, char* argv[])
+{
+ if (argc < 2) {
+ std::cout << std::format("Usage: {} number\n", argv[0]);
+ return 1;
+ }
+
+ // convert input to double
+ double const inputValue = std::stod(argv[1]);
+
+ // calculate square root
+ double const outputValue = mathfunctions::sqrt(inputValue);
+ std::cout << std::format("The square root of {} is {}\n", inputValue,
+ outputValue);
+
+ double const checkValue = mathfunctions::OpMul(outputValue, outputValue);
+ std::cout << std::format("The square of {} is {}\n", outputValue,
+ checkValue);
+}
diff --git a/Help/guide/tutorial/Step11/TutorialProject/cmake/TutorialConfig.cmake b/Help/guide/tutorial/Step11/TutorialProject/cmake/TutorialConfig.cmake
new file mode 100644
index 0000000..d13caa4
--- /dev/null
+++ b/Help/guide/tutorial/Step11/TutorialProject/cmake/TutorialConfig.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/TutorialTargets.cmake)