From b2e3e3e30e5601192577d15e043b33db96fe1661 Mon Sep 17 00:00:00 2001 From: Vito Gamberini Date: Wed, 20 Aug 2025 12:42:42 -0400 Subject: 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 --- Help/guide/tutorial/A Basic Starting Point.rst | 473 ------------ .../guide/tutorial/Adding Export Configuration.rst | 140 ---- .../tutorial/Adding Generator Expressions.rst | 168 ----- .../Adding Support for a Testing Dashboard.rst | 108 --- .../guide/tutorial/Adding System Introspection.rst | 163 ----- .../Adding Usage Requirements for a Library.rst | 304 -------- .../Adding a Custom Command and Generated File.rst | 103 --- Help/guide/tutorial/Adding a Library.rst | 455 ------------ .../guide/tutorial/CMake Language Fundamentals.rst | 583 +++++++++++++++ Help/guide/tutorial/Complete/CMakeLists.txt | 127 ---- Help/guide/tutorial/Complete/CTestConfig.cmake | 3 - Help/guide/tutorial/Complete/Config.cmake.in | 4 - Help/guide/tutorial/Complete/License.txt | 2 - .../tutorial/Complete/MathFunctions/CMakeLists.txt | 62 -- .../Complete/MathFunctions/MakeTable.cmake | 10 - .../tutorial/Complete/MathFunctions/MakeTable.cxx | 25 - .../Complete/MathFunctions/MathFunctions.cxx | 20 - .../Complete/MathFunctions/MathFunctions.h | 14 - .../tutorial/Complete/MathFunctions/mysqrt.cxx | 37 - .../guide/tutorial/Complete/MathFunctions/mysqrt.h | 6 - .../guide/tutorial/Complete/MultiCPackConfig.cmake | 6 - .../tutorial/Complete/SimpleTest/CMakeLists.txt | 53 ++ .../tutorial/Complete/SimpleTest/CMakePresets.json | 16 + .../tutorial/Complete/SimpleTest/SimpleTest.h | 155 ++++ .../SimpleTest/cmake/SimpleTestConfig.cmake | 5 + .../cmake/simpletest_discover_impl.cmake | 32 + .../cmake/simpletest_discover_tests.cmake | 27 + Help/guide/tutorial/Complete/TutorialConfig.h.in | 3 - .../Complete/TutorialProject/CMakeLists.txt | 59 ++ .../Complete/TutorialProject/CMakePresets.json | 16 + .../TutorialProject/MathFunctions/CMakeLists.txt | 55 ++ .../MathFunctions/MakeTable/CMakeLists.txt | 28 + .../MathFunctions/MakeTable/MakeTable.cxx | 25 + .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../MathFunctions/MathFunctions.cxx | 101 +++ .../TutorialProject/MathFunctions/MathFunctions.h | 9 + .../MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../MathFunctions/MathLogger/MathLogger.h | 22 + .../MathFunctions/MathLogger/MathOutput.h | 11 + .../Complete/TutorialProject/Tests/CMakeLists.txt | 16 + .../TutorialProject/Tests/TestMathFunctions.cxx | 22 + .../TutorialProject/Tutorial/CMakeLists.txt | 39 + .../Complete/TutorialProject/Tutorial/Tutorial.cxx | 27 + .../TutorialProject/cmake/TutorialConfig.cmake | 1 + .../install/include/Unpackaged/Unpackaged.h | 3 + .../cmake/TransitiveDep/TransitiveDepConfig.cmake | 50 ++ Help/guide/tutorial/Complete/tutorial.cxx | 26 - .../tutorial/Configuration and Cache Variables.rst | 606 ++++++++++++++++ .../Custom Commands and Generated Files.rst | 279 +++++++ Help/guide/tutorial/Finding Dependencies.rst | 529 ++++++++++++++ Help/guide/tutorial/Getting Started with CMake.rst | 805 +++++++++++++++++++++ .../tutorial/In-Depth CMake Library Concepts.rst | 446 ++++++++++++ .../tutorial/In-Depth CMake Target Commands.rst | 532 ++++++++++++++ .../tutorial/In-Depth System Introspection.rst | 418 +++++++++++ .../Installation Commands and Concepts.rst | 596 +++++++++++++++ Help/guide/tutorial/Installing and Testing.rst | 311 -------- Help/guide/tutorial/Miscellaneous Features.rst | 188 +++++ .../guide/tutorial/Packaging Debug and Release.rst | 86 --- Help/guide/tutorial/Packaging an Installer.rst | 64 -- .../Selecting Static or Shared Libraries.rst | 61 -- Help/guide/tutorial/Step1/CMakeLists.txt | 25 +- .../tutorial/Step1/MathFunctions/CMakeLists.txt | 2 + .../tutorial/Step1/MathFunctions/MathFunctions.cxx | 31 + .../tutorial/Step1/MathFunctions/MathFunctions.h | 5 + Help/guide/tutorial/Step1/Tutorial/CMakeLists.txt | 2 + Help/guide/tutorial/Step1/Tutorial/Tutorial.cxx | 23 + Help/guide/tutorial/Step1/TutorialConfig.h.in | 2 - Help/guide/tutorial/Step1/tutorial.cxx | 27 - Help/guide/tutorial/Step10/CMakeLists.txt | 77 -- Help/guide/tutorial/Step10/CTestConfig.cmake | 3 - Help/guide/tutorial/Step10/License.txt | 2 - .../tutorial/Step10/MathFunctions/CMakeLists.txt | 45 -- .../tutorial/Step10/MathFunctions/MakeTable.cmake | 10 - .../tutorial/Step10/MathFunctions/MakeTable.cxx | 25 - .../Step10/MathFunctions/MathFunctions.cxx | 20 - .../tutorial/Step10/MathFunctions/MathFunctions.h | 3 - .../guide/tutorial/Step10/MathFunctions/mysqrt.cxx | 37 - Help/guide/tutorial/Step10/MathFunctions/mysqrt.h | 6 - .../tutorial/Step10/SimpleTest/CMakeLists.txt | 52 ++ .../tutorial/Step10/SimpleTest/CMakePresets.json | 16 + Help/guide/tutorial/Step10/SimpleTest/SimpleTest.h | 151 ++++ .../Step10/SimpleTest/cmake/SimpleTestConfig.cmake | 6 + .../cmake/simpletest_discover_impl.cmake | 32 + .../cmake/simpletest_discover_tests.cmake | 27 + Help/guide/tutorial/Step10/TutorialConfig.h.in | 3 - .../tutorial/Step10/TutorialProject/CMakeLists.txt | 63 ++ .../Step10/TutorialProject/CMakePresets.json | 16 + .../TutorialProject/MathFunctions/CMakeLists.txt | 54 ++ .../MathFunctions/MakeTable/CMakeLists.txt | 28 + .../MathFunctions/MakeTable/MakeTable.cxx | 25 + .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../MathFunctions/MathFunctions.cxx | 101 +++ .../TutorialProject/MathFunctions/MathFunctions.h | 9 + .../MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../MathFunctions/MathLogger/MathLogger.h | 22 + .../MathFunctions/MathLogger/MathOutput.h | 11 + .../Step10/TutorialProject/Tests/CMakeLists.txt | 31 + .../TutorialProject/Tests/TestMathFunctions.cxx | 28 + .../Step10/TutorialProject/Tutorial/CMakeLists.txt | 36 + .../Step10/TutorialProject/Tutorial/Tutorial.cxx | 28 + .../TutorialProject/cmake/TutorialConfig.cmake | 1 + .../Step10/install/include/Unpackaged/Unpackaged.h | 3 + .../cmake/TransitiveDep/TransitiveDepConfig.cmake | 50 ++ Help/guide/tutorial/Step10/tutorial.cxx | 27 - Help/guide/tutorial/Step11/CMakeLists.txt | 85 --- Help/guide/tutorial/Step11/CTestConfig.cmake | 3 - Help/guide/tutorial/Step11/License.txt | 2 - .../tutorial/Step11/MathFunctions/CMakeLists.txt | 54 -- .../tutorial/Step11/MathFunctions/MakeTable.cmake | 10 - .../tutorial/Step11/MathFunctions/MakeTable.cxx | 25 - .../Step11/MathFunctions/MathFunctions.cxx | 20 - .../tutorial/Step11/MathFunctions/MathFunctions.h | 14 - .../guide/tutorial/Step11/MathFunctions/mysqrt.cxx | 37 - Help/guide/tutorial/Step11/MathFunctions/mysqrt.h | 6 - .../tutorial/Step11/SimpleTest/CMakeLists.txt | 54 ++ .../tutorial/Step11/SimpleTest/CMakePresets.json | 16 + Help/guide/tutorial/Step11/SimpleTest/SimpleTest.h | 155 ++++ .../Step11/SimpleTest/cmake/SimpleTestConfig.cmake | 5 + .../cmake/simpletest_discover_impl.cmake | 32 + .../cmake/simpletest_discover_tests.cmake | 27 + Help/guide/tutorial/Step11/TutorialConfig.h.in | 3 - .../tutorial/Step11/TutorialProject/CMakeLists.txt | 59 ++ .../Step11/TutorialProject/CMakePresets.json | 16 + .../TutorialProject/MathFunctions/CMakeLists.txt | 55 ++ .../MathFunctions/MakeTable/CMakeLists.txt | 28 + .../MathFunctions/MakeTable/MakeTable.cxx | 25 + .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../MathFunctions/MathFunctions.cxx | 101 +++ .../TutorialProject/MathFunctions/MathFunctions.h | 9 + .../MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../MathFunctions/MathLogger/MathLogger.h | 22 + .../MathFunctions/MathLogger/MathOutput.h | 11 + .../Step11/TutorialProject/Tests/CMakeLists.txt | 16 + .../TutorialProject/Tests/TestMathFunctions.cxx | 22 + .../Step11/TutorialProject/Tutorial/CMakeLists.txt | 39 + .../Step11/TutorialProject/Tutorial/Tutorial.cxx | 27 + .../TutorialProject/cmake/TutorialConfig.cmake | 1 + .../Step11/install/include/Unpackaged/Unpackaged.h | 3 + .../cmake/TransitiveDep/TransitiveDepConfig.cmake | 50 ++ Help/guide/tutorial/Step11/tutorial.cxx | 27 - Help/guide/tutorial/Step12/CMakeLists.txt | 124 ---- Help/guide/tutorial/Step12/CTestConfig.cmake | 3 - Help/guide/tutorial/Step12/Config.cmake.in | 4 - Help/guide/tutorial/Step12/License.txt | 2 - .../tutorial/Step12/MathFunctions/CMakeLists.txt | 58 -- .../tutorial/Step12/MathFunctions/MakeTable.cmake | 10 - .../tutorial/Step12/MathFunctions/MakeTable.cxx | 25 - .../Step12/MathFunctions/MathFunctions.cxx | 20 - .../tutorial/Step12/MathFunctions/MathFunctions.h | 14 - .../guide/tutorial/Step12/MathFunctions/mysqrt.cxx | 37 - Help/guide/tutorial/Step12/MathFunctions/mysqrt.h | 6 - Help/guide/tutorial/Step12/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step12/tutorial.cxx | 26 - Help/guide/tutorial/Step2/CMakeLists.txt | 28 - Help/guide/tutorial/Step2/Exercise1.cmake | 72 ++ Help/guide/tutorial/Step2/Exercise2.cmake | 51 ++ Help/guide/tutorial/Step2/Exercise3.cmake | 30 + .../tutorial/Step2/MathFunctions/CMakeLists.txt | 15 - .../tutorial/Step2/MathFunctions/MathFunctions.cxx | 15 - .../tutorial/Step2/MathFunctions/MathFunctions.h | 5 - Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx | 28 - Help/guide/tutorial/Step2/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step2/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step2/tutorial.cxx | 29 - Help/guide/tutorial/Step3/CMakeLists.txt | 44 +- Help/guide/tutorial/Step3/CMakePresets.json | 14 + .../tutorial/Step3/MathFunctions/CMakeLists.txt | 29 +- .../tutorial/Step3/MathFunctions/MathFunctions.cxx | 38 +- Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx | 28 - Help/guide/tutorial/Step3/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step3/Tutorial/CMakeLists.txt | 11 + Help/guide/tutorial/Step3/Tutorial/Tutorial.cxx | 26 + Help/guide/tutorial/Step3/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step3/tutorial.cxx | 27 - Help/guide/tutorial/Step4/CMakeLists.txt | 47 +- Help/guide/tutorial/Step4/CMakePresets.json | 16 + .../tutorial/Step4/MathFunctions/CMakeLists.txt | 35 +- .../tutorial/Step4/MathFunctions/MathFunctions.cxx | 41 +- Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx | 28 - Help/guide/tutorial/Step4/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step4/Tutorial/CMakeLists.txt | 33 + Help/guide/tutorial/Step4/Tutorial/Tutorial.cxx | 36 + Help/guide/tutorial/Step4/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step4/Vendor/CMakeLists.txt | 8 + Help/guide/tutorial/Step4/Vendor/include/Vendor.h | 3 + Help/guide/tutorial/Step4/Vendor/lib/Vendor.cxx | 8 + Help/guide/tutorial/Step4/tutorial.cxx | 27 - Help/guide/tutorial/Step5/CMakeLists.txt | 61 +- Help/guide/tutorial/Step5/CMakePresets.json | 14 + .../tutorial/Step5/MathFunctions/CMakeLists.txt | 47 +- .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../tutorial/Step5/MathFunctions/MathFunctions.cxx | 44 +- .../tutorial/Step5/MathFunctions/MathFunctions.h | 2 + .../Step5/MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../Step5/MathFunctions/MathLogger/MathLogger.h | 22 + .../Step5/MathFunctions/MathLogger/MathOutput.h | 11 + Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx | 28 - Help/guide/tutorial/Step5/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step5/Tutorial/CMakeLists.txt | 29 + Help/guide/tutorial/Step5/Tutorial/Tutorial.cxx | 26 + Help/guide/tutorial/Step5/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step5/tutorial.cxx | 27 - Help/guide/tutorial/Step6/CMakeLists.txt | 81 +-- Help/guide/tutorial/Step6/CMakePresets.json | 14 + Help/guide/tutorial/Step6/CTestConfig.cmake | 3 - .../tutorial/Step6/MathFunctions/CMakeLists.txt | 68 +- .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../tutorial/Step6/MathFunctions/MathFunctions.cxx | 76 +- .../tutorial/Step6/MathFunctions/MathFunctions.h | 4 + .../Step6/MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../Step6/MathFunctions/MathLogger/MathLogger.h | 22 + .../Step6/MathFunctions/MathLogger/MathOutput.h | 11 + Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx | 28 - Help/guide/tutorial/Step6/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step6/Tutorial/CMakeLists.txt | 29 + Help/guide/tutorial/Step6/Tutorial/Tutorial.cxx | 26 + Help/guide/tutorial/Step6/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step6/tutorial.cxx | 27 - Help/guide/tutorial/Step7/CMakeLists.txt | 82 +-- Help/guide/tutorial/Step7/CMakePresets.json | 15 + Help/guide/tutorial/Step7/CTestConfig.cmake | 3 - .../tutorial/Step7/MathFunctions/CMakeLists.txt | 87 ++- .../Step7/MathFunctions/MakeTable/CMakeLists.txt | 15 + .../Step7/MathFunctions/MakeTable/MakeTable.cxx | 25 + .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../tutorial/Step7/MathFunctions/MathFunctions.cxx | 99 ++- .../tutorial/Step7/MathFunctions/MathFunctions.h | 4 + .../Step7/MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../Step7/MathFunctions/MathLogger/MathLogger.h | 22 + .../Step7/MathFunctions/MathLogger/MathOutput.h | 11 + Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx | 38 - Help/guide/tutorial/Step7/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step7/Tutorial/CMakeLists.txt | 29 + Help/guide/tutorial/Step7/Tutorial/Tutorial.cxx | 26 + Help/guide/tutorial/Step7/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step7/tutorial.cxx | 27 - Help/guide/tutorial/Step8/CMakeLists.txt | 85 +-- Help/guide/tutorial/Step8/CMakePresets.json | 15 + Help/guide/tutorial/Step8/CTestConfig.cmake | 3 - .../tutorial/Step8/MathFunctions/CMakeLists.txt | 91 ++- .../tutorial/Step8/MathFunctions/MakeTable.cxx | 25 - .../Step8/MathFunctions/MakeTable/CMakeLists.txt | 28 + .../Step8/MathFunctions/MakeTable/MakeTable.cxx | 25 + .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../tutorial/Step8/MathFunctions/MathFunctions.cxx | 98 ++- .../tutorial/Step8/MathFunctions/MathFunctions.h | 4 + .../Step8/MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../Step8/MathFunctions/MathLogger/MathLogger.h | 22 + .../Step8/MathFunctions/MathLogger/MathOutput.h | 11 + Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx | 36 - Help/guide/tutorial/Step8/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step8/Tests/CMakeLists.txt | 13 + .../tutorial/Step8/Tests/TestMathFunctions.cxx | 24 + Help/guide/tutorial/Step8/Tutorial/CMakeLists.txt | 29 + Help/guide/tutorial/Step8/Tutorial/Tutorial.cxx | 26 + Help/guide/tutorial/Step8/TutorialConfig.h.in | 3 - Help/guide/tutorial/Step8/tutorial.cxx | 27 - Help/guide/tutorial/Step9/CMakeLists.txt | 107 ++- Help/guide/tutorial/Step9/CMakePresets.json | 15 + Help/guide/tutorial/Step9/CTestConfig.cmake | 3 - Help/guide/tutorial/Step9/License.txt | 2 - .../tutorial/Step9/MathFunctions/CMakeLists.txt | 78 +- .../tutorial/Step9/MathFunctions/MakeTable.cmake | 10 - .../tutorial/Step9/MathFunctions/MakeTable.cxx | 25 - .../Step9/MathFunctions/MakeTable/CMakeLists.txt | 28 + .../Step9/MathFunctions/MakeTable/MakeTable.cxx | 25 + .../MathFunctions/MathExtensions/CMakeLists.txt | 3 + .../MathExtensions/OpAdd/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.cxx | 6 + .../MathFunctions/MathExtensions/OpAdd/OpAdd.h | 5 + .../MathExtensions/OpMul/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpMul/OpMul.cxx | 6 + .../MathFunctions/MathExtensions/OpMul/OpMul.h | 5 + .../MathExtensions/OpSub/CMakeLists.txt | 11 + .../MathFunctions/MathExtensions/OpSub/OpSub.cxx | 6 + .../MathFunctions/MathExtensions/OpSub/OpSub.h | 5 + .../tutorial/Step9/MathFunctions/MathFunctions.cxx | 98 ++- .../tutorial/Step9/MathFunctions/MathFunctions.h | 4 + .../Step9/MathFunctions/MathLogger/CMakeLists.txt | 6 + .../MathFunctions/MathLogger/MathFormatting.h | 27 + .../Step9/MathFunctions/MathLogger/MathLogger.h | 22 + .../Step9/MathFunctions/MathLogger/MathOutput.h | 11 + Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx | 37 - Help/guide/tutorial/Step9/MathFunctions/mysqrt.h | 7 - Help/guide/tutorial/Step9/Tests/CMakeLists.txt | 23 + .../tutorial/Step9/Tests/TestMathFunctions.cxx | 24 + Help/guide/tutorial/Step9/Tutorial/CMakeLists.txt | 29 + Help/guide/tutorial/Step9/Tutorial/Tutorial.cxx | 26 + Help/guide/tutorial/Step9/TutorialConfig.h.in | 3 - .../tutorial/Step9/cmake/TutorialConfig.cmake | 2 + Help/guide/tutorial/Step9/tutorial.cxx | 27 - Help/guide/tutorial/Testing and CTest.rst | 232 ++++++ Help/guide/tutorial/index.rst | 23 +- Tests/RunCMake/CMakeLists.txt | 5 +- Tests/RunCMake/Tutorial/CMakeLists.txt | 4 + Tests/RunCMake/Tutorial/Inspect.cmake | 8 + Tests/RunCMake/Tutorial/RunCMakeTest.cmake | 80 +- Tests/RunCMake/Tutorial/Step-MyMath-run-stdout.txt | 2 - Tests/RunCMake/Tutorial/Step-NoMath-run-stdout.txt | 1 - .../RunCMake/Tutorial/Step8-MyMath-run-stdout.txt | 2 - Tests/RunCMake/Tutorial/inspect.cpp | 9 + 371 files changed, 10385 insertions(+), 5081 deletions(-) delete mode 100644 Help/guide/tutorial/A Basic Starting Point.rst delete mode 100644 Help/guide/tutorial/Adding Export Configuration.rst delete mode 100644 Help/guide/tutorial/Adding Generator Expressions.rst delete mode 100644 Help/guide/tutorial/Adding Support for a Testing Dashboard.rst delete mode 100644 Help/guide/tutorial/Adding System Introspection.rst delete mode 100644 Help/guide/tutorial/Adding Usage Requirements for a Library.rst delete mode 100644 Help/guide/tutorial/Adding a Custom Command and Generated File.rst delete mode 100644 Help/guide/tutorial/Adding a Library.rst create mode 100644 Help/guide/tutorial/CMake Language Fundamentals.rst delete mode 100644 Help/guide/tutorial/Complete/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Complete/CTestConfig.cmake delete mode 100644 Help/guide/tutorial/Complete/Config.cmake.in delete mode 100644 Help/guide/tutorial/Complete/License.txt delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Complete/MathFunctions/mysqrt.h delete mode 100644 Help/guide/tutorial/Complete/MultiCPackConfig.cmake create mode 100644 Help/guide/tutorial/Complete/SimpleTest/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/SimpleTest/CMakePresets.json create mode 100644 Help/guide/tutorial/Complete/SimpleTest/SimpleTest.h create mode 100644 Help/guide/tutorial/Complete/SimpleTest/cmake/SimpleTestConfig.cmake create mode 100644 Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_impl.cmake create mode 100644 Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_tests.cmake delete mode 100644 Help/guide/tutorial/Complete/TutorialConfig.h.in create mode 100644 Help/guide/tutorial/Complete/TutorialProject/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/CMakePresets.json create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathFunctions.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathFunctions.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathOutput.h create mode 100644 Help/guide/tutorial/Complete/TutorialProject/Tests/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/Tests/TestMathFunctions.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Complete/TutorialProject/Tutorial/Tutorial.cxx create mode 100644 Help/guide/tutorial/Complete/TutorialProject/cmake/TutorialConfig.cmake create mode 100644 Help/guide/tutorial/Complete/install/include/Unpackaged/Unpackaged.h create mode 100644 Help/guide/tutorial/Complete/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake delete mode 100644 Help/guide/tutorial/Complete/tutorial.cxx create mode 100644 Help/guide/tutorial/Configuration and Cache Variables.rst create mode 100644 Help/guide/tutorial/Custom Commands and Generated Files.rst create mode 100644 Help/guide/tutorial/Finding Dependencies.rst create mode 100644 Help/guide/tutorial/Getting Started with CMake.rst create mode 100644 Help/guide/tutorial/In-Depth CMake Library Concepts.rst create mode 100644 Help/guide/tutorial/In-Depth CMake Target Commands.rst create mode 100644 Help/guide/tutorial/In-Depth System Introspection.rst create mode 100644 Help/guide/tutorial/Installation Commands and Concepts.rst delete mode 100644 Help/guide/tutorial/Installing and Testing.rst create mode 100644 Help/guide/tutorial/Miscellaneous Features.rst delete mode 100644 Help/guide/tutorial/Packaging Debug and Release.rst delete mode 100644 Help/guide/tutorial/Packaging an Installer.rst delete mode 100644 Help/guide/tutorial/Selecting Static or Shared Libraries.rst create mode 100644 Help/guide/tutorial/Step1/MathFunctions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step1/MathFunctions/MathFunctions.cxx create mode 100644 Help/guide/tutorial/Step1/MathFunctions/MathFunctions.h create mode 100644 Help/guide/tutorial/Step1/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step1/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step1/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step1/tutorial.cxx delete mode 100644 Help/guide/tutorial/Step10/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step10/CTestConfig.cmake delete mode 100644 Help/guide/tutorial/Step10/License.txt delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step10/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step10/SimpleTest/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/SimpleTest/CMakePresets.json create mode 100644 Help/guide/tutorial/Step10/SimpleTest/SimpleTest.h create mode 100644 Help/guide/tutorial/Step10/SimpleTest/cmake/SimpleTestConfig.cmake create mode 100644 Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_impl.cmake create mode 100644 Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_tests.cmake delete mode 100644 Help/guide/tutorial/Step10/TutorialConfig.h.in create mode 100644 Help/guide/tutorial/Step10/TutorialProject/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/CMakePresets.json create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathFunctions.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathFunctions.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathOutput.h create mode 100644 Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/Tests/TestMathFunctions.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step10/TutorialProject/Tutorial/Tutorial.cxx create mode 100644 Help/guide/tutorial/Step10/TutorialProject/cmake/TutorialConfig.cmake create mode 100644 Help/guide/tutorial/Step10/install/include/Unpackaged/Unpackaged.h create mode 100644 Help/guide/tutorial/Step10/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake delete mode 100644 Help/guide/tutorial/Step10/tutorial.cxx delete mode 100644 Help/guide/tutorial/Step11/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step11/CTestConfig.cmake delete mode 100644 Help/guide/tutorial/Step11/License.txt delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step11/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step11/SimpleTest/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/SimpleTest/CMakePresets.json create mode 100644 Help/guide/tutorial/Step11/SimpleTest/SimpleTest.h create mode 100644 Help/guide/tutorial/Step11/SimpleTest/cmake/SimpleTestConfig.cmake create mode 100644 Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_impl.cmake create mode 100644 Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_tests.cmake delete mode 100644 Help/guide/tutorial/Step11/TutorialConfig.h.in create mode 100644 Help/guide/tutorial/Step11/TutorialProject/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/CMakePresets.json create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathFunctions.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/MathFunctions/MathLogger/MathOutput.h create mode 100644 Help/guide/tutorial/Step11/TutorialProject/Tests/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/Tests/TestMathFunctions.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step11/TutorialProject/Tutorial/Tutorial.cxx create mode 100644 Help/guide/tutorial/Step11/TutorialProject/cmake/TutorialConfig.cmake create mode 100644 Help/guide/tutorial/Step11/install/include/Unpackaged/Unpackaged.h create mode 100644 Help/guide/tutorial/Step11/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake delete mode 100644 Help/guide/tutorial/Step11/tutorial.cxx delete mode 100644 Help/guide/tutorial/Step12/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step12/CTestConfig.cmake delete mode 100644 Help/guide/tutorial/Step12/Config.cmake.in delete mode 100644 Help/guide/tutorial/Step12/License.txt delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/MathFunctions.h delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step12/MathFunctions/mysqrt.h delete mode 100644 Help/guide/tutorial/Step12/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step12/tutorial.cxx delete mode 100644 Help/guide/tutorial/Step2/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step2/Exercise1.cmake create mode 100644 Help/guide/tutorial/Step2/Exercise2.cmake create mode 100644 Help/guide/tutorial/Step2/Exercise3.cmake delete mode 100644 Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt delete mode 100644 Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx delete mode 100644 Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h delete mode 100644 Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step2/MathFunctions/mysqrt.h delete mode 100644 Help/guide/tutorial/Step2/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step2/tutorial.cxx create mode 100644 Help/guide/tutorial/Step3/CMakePresets.json delete mode 100644 Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step3/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step3/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step3/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step3/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step3/tutorial.cxx create mode 100644 Help/guide/tutorial/Step4/CMakePresets.json delete mode 100644 Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step4/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step4/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step4/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step4/TutorialConfig.h.in create mode 100644 Help/guide/tutorial/Step4/Vendor/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step4/Vendor/include/Vendor.h create mode 100644 Help/guide/tutorial/Step4/Vendor/lib/Vendor.cxx delete mode 100644 Help/guide/tutorial/Step4/tutorial.cxx create mode 100644 Help/guide/tutorial/Step5/CMakePresets.json create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathOutput.h delete mode 100644 Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step5/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step5/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step5/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step5/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step5/tutorial.cxx create mode 100644 Help/guide/tutorial/Step6/CMakePresets.json delete mode 100644 Help/guide/tutorial/Step6/CTestConfig.cmake create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathOutput.h delete mode 100644 Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step6/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step6/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step6/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step6/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step6/tutorial.cxx create mode 100644 Help/guide/tutorial/Step7/CMakePresets.json delete mode 100644 Help/guide/tutorial/Step7/CTestConfig.cmake create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MakeTable/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MakeTable/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathOutput.h delete mode 100644 Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step7/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step7/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step7/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step7/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step7/tutorial.cxx create mode 100644 Help/guide/tutorial/Step8/CMakePresets.json delete mode 100644 Help/guide/tutorial/Step8/CTestConfig.cmake delete mode 100644 Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MakeTable/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MakeTable/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathOutput.h delete mode 100644 Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step8/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step8/Tests/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/Tests/TestMathFunctions.cxx create mode 100644 Help/guide/tutorial/Step8/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step8/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step8/TutorialConfig.h.in delete mode 100644 Help/guide/tutorial/Step8/tutorial.cxx create mode 100644 Help/guide/tutorial/Step9/CMakePresets.json delete mode 100644 Help/guide/tutorial/Step9/CTestConfig.cmake delete mode 100644 Help/guide/tutorial/Step9/License.txt delete mode 100644 Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake delete mode 100644 Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MakeTable/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MakeTable/MakeTable.cxx create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/OpAdd.h create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpMul/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpMul/OpMul.cxx create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpMul/OpMul.h create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpSub/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpSub/OpSub.cxx create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpSub/OpSub.h create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathLogger/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathFormatting.h create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathLogger.h create mode 100644 Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathOutput.h delete mode 100644 Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx delete mode 100644 Help/guide/tutorial/Step9/MathFunctions/mysqrt.h create mode 100644 Help/guide/tutorial/Step9/Tests/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/Tests/TestMathFunctions.cxx create mode 100644 Help/guide/tutorial/Step9/Tutorial/CMakeLists.txt create mode 100644 Help/guide/tutorial/Step9/Tutorial/Tutorial.cxx delete mode 100644 Help/guide/tutorial/Step9/TutorialConfig.h.in create mode 100644 Help/guide/tutorial/Step9/cmake/TutorialConfig.cmake delete mode 100644 Help/guide/tutorial/Step9/tutorial.cxx create mode 100644 Help/guide/tutorial/Testing and CTest.rst create mode 100644 Tests/RunCMake/Tutorial/CMakeLists.txt create mode 100644 Tests/RunCMake/Tutorial/Inspect.cmake delete mode 100644 Tests/RunCMake/Tutorial/Step-MyMath-run-stdout.txt delete mode 100644 Tests/RunCMake/Tutorial/Step-NoMath-run-stdout.txt delete mode 100644 Tests/RunCMake/Tutorial/Step8-MyMath-run-stdout.txt create mode 100644 Tests/RunCMake/Tutorial/inspect.cpp diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst deleted file mode 100644 index b36e2e7..0000000 --- a/Help/guide/tutorial/A Basic Starting Point.rst +++ /dev/null @@ -1,473 +0,0 @@ -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 previous. 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 ` executable or the -:manual:`cmake-gui ` 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 ` 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 . - -For multi-config generators (e.g. Visual Studio), first navigate to the -appropriate subdirectory, for example: - -.. code-block:: console - - cd 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`` or ``.\Tutorial``. For simplicity, the exercises will use -``Tutorial`` throughout. - -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 - -
TODO 1: Click to show/hide answer - -.. 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 - -
- -The next step to make a basic project is to use the :command:`project` -command as follows to set the project name: - -.. raw:: html - -
TODO 2: Click to show/hide answer - -.. code-block:: cmake - :caption: TODO 2: CMakeLists.txt - :name: CMakeLists.txt-project - - project(Tutorial) - -.. raw:: html - -
- -The last command to call for a basic project is -:command:`add_executable`. We call it as follows: - -.. raw:: html - -
TODO 3: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 4: Click to show/hide answer - -.. 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 - -
- -To complete ``TODO 5``, simply remove ``#include ``. - -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 - -
TODO 6: Click to show/hide answer - -.. 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 - -
- -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:`_VERSION_MAJOR` -* :variable:`_VERSION_MINOR` -* :command:`configure_file` -* :command:`target_include_directories` - -Files to Edit -------------- - -* ``CMakeLists.txt`` -* ``tutorial.cxx`` -* ``TutorialConfig.h.in`` - -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 - -
TODO 7: Click to show/hide answer - -.. 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 - -
- -Then we used :command:`configure_file` to copy the input file with the -specified CMake variables replaced: - -.. raw:: html - -
TODO 8: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 9: Click to show/hide answer - -.. 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 - -
- -``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 - -
TODO 10: Click to show/hide answer - -.. literalinclude:: Step2/TutorialConfig.h.in - :caption: TODO 10: TutorialConfig.h.in - :name: TutorialConfig.h.in - :language: c++ - -.. raw:: html - -
- -Next, we need to modify ``tutorial.cxx`` to include the configured header file, -``TutorialConfig.h``. - -.. raw:: html - -
TODO 11: Click to show/hide answer - -.. code-block:: c++ - :caption: TODO 11: tutorial.cxx - - #include "TutorialConfig.h" - -.. raw:: html - -
- -Finally, we print out the executable name and version number by updating -``tutorial.cxx`` as follows: - -.. raw:: html - -
TODO 12: Click to show/hide answer - -.. 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 - -
diff --git a/Help/guide/tutorial/Adding Export Configuration.rst b/Help/guide/tutorial/Adding Export Configuration.rst deleted file mode 100644 index c4ab476..0000000 --- a/Help/guide/tutorial/Adding Export Configuration.rst +++ /dev/null @@ -1,140 +0,0 @@ -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. - -CMake is telling you that during the generation of 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 deleted file mode 100644 index d2dddf7..0000000 --- a/Help/guide/tutorial/Adding Generator Expressions.rst +++ /dev/null @@ -1,168 +0,0 @@ -Step 4: Adding Generator Expressions -===================================== - -:manual:`Generator expressions ` are evaluated -during build system generation to produce information specific to each build -configuration. - -:manual:`Generator expressions ` 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 ` 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 ` 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 - Adding Compiler Warning Flags with Generator Expressions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A common usage of -:manual:`generator expressions ` 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 ---------------- - -Open the file ``Step4/CMakeLists.txt`` and complete ``TODO 1`` through -``TODO 4``. - -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 -------------- - -Make a new directory called ``Step4_build``, run the :manual:`cmake ` -executable or the :manual:`cmake-gui ` to configure the project -and then build it with your chosen build tool or by using ``cmake --build .`` -from the build directory. - -.. code-block:: console - - mkdir Step4_build - cd Step4_build - cmake ../Step4 - cmake --build . - -Solution --------- - -Update the :command:`cmake_minimum_required` to require at least CMake -version ``3.15``: - -.. raw:: html - -
TODO 1: Click to show/hide answer - -.. literalinclude:: Step5/CMakeLists.txt - :caption: TODO 1: CMakeLists.txt - :name: MathFunctions-CMakeLists.txt-minimum-required-step4 - :language: cmake - :end-before: # set the project name and version - -.. raw:: html - -
- -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 - -
TODO 2: Click to show/hide answer - -.. literalinclude:: Step5/CMakeLists.txt - :caption: TODO 2: 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 - -
- -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 - -
TODO 3: Click to show/hide answer - -.. code-block:: cmake - :caption: TODO 3: 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 - -
- -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 from TODO 3 in a generator expression using the -``BUILD_INTERFACE`` condition. The resulting full code looks like the following: - -.. raw:: html - -
TODO 4: Click to show/hide answer - -.. literalinclude:: Step5/CMakeLists.txt - :caption: TODO 4: CMakeLists.txt - :name: CMakeLists.txt-target_compile_options-genex - :language: cmake - :start-after: set(msvc_cxx "$") - :end-before: # configure a header file to pass some of the CMake settings - -.. raw:: html - -
diff --git a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst deleted file mode 100644 index e91aa6a..0000000 --- a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst +++ /dev/null @@ -1,108 +0,0 @@ -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 `. 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 ` executable will -read this file to gather information about the testing dashboard. It contains: - -* 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 ` -executable or the :manual:`cmake-gui ` 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 ` 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 - -
TODO 1: Click to show/hide answer - -.. 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 - -
diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst deleted file mode 100644 index 87070ed..0000000 --- a/Help/guide/tutorial/Adding System Introspection.rst +++ /dev/null @@ -1,163 +0,0 @@ -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 ` executable or the -:manual:`cmake-gui ` 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 - -
TODO 1: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 2: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 3: Click to show/hide answer - -.. 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 - -
- -Since we may be using ``log`` and ``exp``, we need to modify -``mysqrt.cxx`` to include ``cmath``. - -.. raw:: html - -
TODO 4: Click to show/hide answer - -.. 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 - -.. raw:: html - -
- -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 - -
TODO 5: Click to show/hide answer - -.. 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: return result; - -.. raw:: html - -
diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst deleted file mode 100644 index e7aff9c..0000000 --- a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst +++ /dev/null @@ -1,304 +0,0 @@ -Step 3: Adding Usage Requirements for a Library -=============================================== - -Exercise 1 - Adding Usage Requirements for a Library -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:ref:`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 -` executable or the :manual:`cmake-gui ` to -configure the project and then build it with your chosen build tool or by -using :option:`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 - -
TODO 1: Click to show/hide answer - -.. 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: # should we use our own - -.. raw:: html - -
- -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``. - -Remove this line: - -.. raw:: html - -
TODO 2: Click to show/hide answer - -.. literalinclude:: Step3/CMakeLists.txt - :caption: TODO 2: CMakeLists.txt - :name: CMakeLists.txt-remove-EXTRA_INCLUDES - :language: cmake - :start-after: add_subdirectory(MathFunctions) - :end-before: # add the executable - -.. raw:: html - -
- -And remove ``EXTRA_INCLUDES`` from ``target_include_directories``: - -.. raw:: html - -
TODO 3: Click to show/hide answer - -.. 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 - -
- -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. - -Exercise 2 - Setting the C++ Standard with Interface Libraries -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Now that we have switched our code to a more modern approach, let's demonstrate -a modern technique to set properties to multiple targets. - -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 `. - -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. - -Start this exercise from what we left at the end of Step3 exercise 1. You will -have to complete ``TODO 4`` through ``TODO 7``. - -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 -------------- - -Since we have our build directory already configured from Exercise 1, simply -rebuild our code by calling the following: - -.. code-block:: console - - cd Step3_build - 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:: Step3/CMakeLists.txt - :caption: CMakeLists.txt - :name: CMakeLists.txt-CXX_STANDARD-variable-remove - :language: cmake - :start-after: # specify the C++ standard - :end-before: # configure a header file - -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 - -
TODO 4: Click to show/hide answer - -.. literalinclude:: Step4/CMakeLists.txt - :caption: TODO 4: CMakeLists.txt - :name: CMakeLists.txt-cxx_std-feature - :language: cmake - :start-after: # specify the C++ standard - :end-before: # TODO 2: Create helper - -.. raw:: html - -
- -Finally, with our interface library set up, we need to link our -executable ``Tutorial``, our ``SqrtLibrary`` library and our ``MathFunctions`` -library to our new ``tutorial_compiler_flags`` library. Respectively, the code -will look like this: - -.. raw:: html - -
TODO 5: Click to show/hide answer - -.. literalinclude:: Step4/CMakeLists.txt - :caption: TODO 5: 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 - -
- -this: - -.. raw:: html - -
TODO 6: Click to show/hide answer - -.. literalinclude:: Step4/MathFunctions/CMakeLists.txt - :caption: TODO 6: MathFunctions/CMakeLists.txt - :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4 - :language: cmake - :start-after: # link SqrtLibrary to tutorial_compiler_flags - :end-before: target_link_libraries(MathFunctions - -.. raw:: html - -
- -and this: - -.. raw:: html - -
TODO 7: Click to show/hide answer - -.. literalinclude:: Step4/MathFunctions/CMakeLists.txt - :caption: TODO 7: MathFunctions/CMakeLists.txt - :name: MathFunctions-SqrtLibrary-target_link_libraries-step4 - :language: cmake - :start-after: # link MathFunctions to tutorial_compiler_flags - -.. raw:: html - -
- - -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. 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 deleted file mode 100644 index c71a889..0000000 --- a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst +++ /dev/null @@ -1,103 +0,0 @@ -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 `. - -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 create ``MathFunctions/MakeTable.cmake``. Then, add the -appropriate commands to the 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, we add an executable for ``MakeTable``. - -.. literalinclude:: Step9/MathFunctions/MakeTable.cmake - :caption: MathFunctions/MakeTable.cmake - :name: MathFunctions/MakeTable.cmake-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/MakeTable.cmake - :caption: MathFunctions/MakeTable.cmake - :name: MathFunctions/MakeTable.cmake-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/MakeTable.cmake - :caption: MathFunctions/MakeTable.cmake - :name: MathFunctions/MakeTable.cmake-add_custom_command-Table.h - :language: cmake - :start-after: # add the command to generate the source code - -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 ``SqrtLibrary``. - -.. 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 step, we need to include -``MakeTable.cmake`` at the top of the ``MathFunctions/CMakeLists.txt``. - -.. literalinclude:: Step9/MathFunctions/CMakeLists.txt - :caption: MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-include-MakeTable.cmake - :language: cmake - :start-after: # generate Table.h - :end-before: # library that just does sqrt - -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 ` executable or the -:manual:`cmake-gui ` 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 deleted file mode 100644 index 8f39da3..0000000 --- a/Help/guide/tutorial/Adding a Library.rst +++ /dev/null @@ -1,455 +0,0 @@ -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 ` executable or the -:manual:`cmake-gui ` 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 - -
TODO 1: Click to show/hide answer - -.. code-block:: cmake - :caption: TODO 1: MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-add_library - - add_library(MathFunctions MathFunctions.cxx mysqrt.cxx) - -.. raw:: html - -
- -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 - -
TODO 2: Click to show/hide answer - -.. code-block:: cmake - :caption: TODO 2: CMakeLists.txt - :name: CMakeLists.txt-add_subdirectory - - add_subdirectory(MathFunctions) - -.. raw:: html - -
- -Next, the new library target is linked to the executable target using -:command:`target_link_libraries`. - -.. raw:: html - -
TODO 3: Click to show/hide answer - -.. code-block:: cmake - :caption: TODO 3: CMakeLists.txt - :name: CMakeLists.txt-target_link_libraries - - target_link_libraries(Tutorial PUBLIC MathFunctions) - -.. raw:: html - -
- -Finally we need to specify the library's header file location. -Modify the existing :command:`target_include_directories` call -to add the ``MathFunctions`` subdirectory as an include directory -so that the ``MathFunctions.h`` header file can be found. - -.. raw:: html - -
TODO 4: Click to show/hide answer - -.. 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 - -
- -Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``: - -.. raw:: html - -
TODO 5: Click to show/hide answer - -.. literalinclude:: Step3/tutorial.cxx - :caption: TODO 5: tutorial.cxx - :name: CMakeLists.txt-include-MathFunctions.h - :language: cmake - :start-after: #include - :end-before: #include "TutorialConfig.h" - -.. raw:: html - -
- -Lastly, replace ``sqrt`` with the wrapper function ``mathfunctions::sqrt``. - -.. raw:: html - -
TODO 6: Click to show/hide answer - -.. literalinclude:: Step3/tutorial.cxx - :caption: TODO 6: tutorial.cxx - :name: CMakeLists.txt-option - :language: cmake - :start-after: double const inputValue = std::stod(argv[1]); - :end-before: std::cout - -.. raw:: html - -
- -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 14``. - -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``. - -Lastly, prevent ``mysqrt.cxx`` from being compiled when ``USE_MYMATH`` is on -by making it its own library inside of the ``USE_MYMATH`` block of -``MathFunctions/CMakeLists.txt``. - -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 ` or :manual:`ccmake ` -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 ` and -:manual:`ccmake ` with a default value of ``ON`` that can be -changed by the user. - -.. raw:: html - -
TODO 7: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 8: Click to show/hide answer - -.. code-block:: cmake - :caption: TODO 8: MathFunctions/CMakeLists.txt - :name: CMakeLists.txt-USE_MYMATH - - if (USE_MYMATH) - target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") - endif() - -.. raw:: html - -
- -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. - -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 - -
TODO 9: Click to show/hide answer - -.. 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 - -
- -Next, we need to include ``mysqrt.h`` if ``USE_MYMATH`` is defined. - -.. raw:: html - -
TODO 10: Click to show/hide answer - -.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx - :caption: TODO 10: MathFunctions/MathFunctions.cxx - :name: MathFunctions-USE_MYMATH-if-include - :language: c++ - :start-after: include - :end-before: namespace mathfunctions - -.. raw:: html - -
- -Finally, we need to include ``cmath`` now that we are using ``std::sqrt``. - -.. raw:: html - -
TODO 11: Click to show/hide answer - -.. code-block:: c++ - :caption: TODO 11 : MathFunctions/MathFunctions.cxx - :name: tutorial.cxx-include_cmath - - #include - -.. raw:: html - -
- -At this point, if ``USE_MYMATH`` is ``OFF``, ``mysqrt.cxx`` would not be used -but it will still be compiled because the ``MathFunctions`` target has -``mysqrt.cxx`` listed under sources. - -There are a few ways to fix this. The first option is to use -:command:`target_sources` to add ``mysqrt.cxx`` from within the ``USE_MYMATH`` -block. Another option is to create an additional library within the -``USE_MYMATH`` block which is responsible for compiling ``mysqrt.cxx``. For -the sake of this tutorial, we are going to create an additional library. - -First, from within ``USE_MYMATH`` create a library called ``SqrtLibrary`` -that has sources ``mysqrt.cxx``. - -.. raw:: html - -
TODO 12: Click to show/hide answer - -.. literalinclude:: Step3/MathFunctions/CMakeLists.txt - :caption: TODO 12 : MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-add_library-SqrtLibrary - :language: cmake - :start-after: # library that just does sqrt - :end-before: # TODO 7: Link - -.. raw:: html - -
- -Next, we link ``SqrtLibrary`` onto ``MathFunctions`` when ``USE_MYMATH`` is -enabled. - -.. raw:: html - -
TODO 13: Click to show/hide answer - -.. literalinclude:: Step3/MathFunctions/CMakeLists.txt - :caption: TODO 13 : MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-target_link_libraries-SqrtLibrary - :language: cmake - :start-after: to tutorial_compiler_flags - :end-before: endif() - -.. raw:: html - -
- -Finally, we can remove ``mysqrt.cxx`` from our ``MathFunctions`` library -source list because it will be pulled in when ``SqrtLibrary`` is included. - -.. raw:: html - -
TODO 14: Click to show/hide answer - -.. literalinclude:: Step3/MathFunctions/CMakeLists.txt - :caption: TODO 14 : MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-remove-mysqrt.cxx-MathFunctions - :language: cmake - :end-before: # TODO 1: - -.. raw:: html - -
- -With these changes, the ``mysqrt`` function is now completely optional to -whoever is building and using the ``MathFunctions`` library. Users can toggle -``USE_MYMATH`` to manipulate what library is used in the build. diff --git a/Help/guide/tutorial/CMake Language Fundamentals.rst b/Help/guide/tutorial/CMake Language Fundamentals.rst new file mode 100644 index 0000000..e5dc68d --- /dev/null +++ b/Help/guide/tutorial/CMake Language Fundamentals.rst @@ -0,0 +1,583 @@ +Step 2: CMake Language Fundamentals +=================================== + +In the previous step we rushed through and handwaved several aspects of the +CMake language which is used within ``CMakeLists.txt`` in order to get useful, +building programs as soon as possible. However, in the wild we encounter +a great deal more complexity than simply describing lists of source and +header files. + +To deal with this complexity CMake provides a Turing-complete domain-specific +language for describing the process of building software. Understanding the +fundamentals of this language will be necessary as we write more complex +CMLs and other CMake files. The language is formally known as +":manual:`CMake Language `", or more colloquially as CMakeLang. + +.. note:: + The CMake Language is not well suited to describing things which are not + related to building software. While it has some features for general purpose + use, developers should use caution when solving problems not directly related + to their build in CMake Language. + + Oftentimes the correct answer is to write a tool in a general purpose + programming language which solves the problem, and teach CMake how to invoke + that tool as part of the build process. Code generation, cryptographic + signature utilities, and even ray-tracers have been written in CMake Language, + but this is not a recommended practice. + +Because we want to fully explore the language features, this step is an +exception to the tutorial sequencing. It neither builds on ``Step1``, nor is the +starting point for ``Step3``. This will be a sandbox to explore language +features without building any software. We'll pick back up with the Tutorial +program in ``Step3``. + +.. note:: + This tutorial endeavors to demonstrate best practices and solutions to real + problems. However, for this one step we're going to be re-implementing some + built-in CMake functions. In "real life", do not write your own + :command:`list(APPEND)`. + +Background +^^^^^^^^^^ + +The only fundamental types in CMakeLang are strings and lists. Every object in +CMake is a string, and lists are themselves strings which contain semicolons +as separators. Any command which appears to operate on something other than a +string, whether they be booleans, numbers, JSON objects, or otherwise, is in +fact consuming a string, doing some internal conversion logic (in a language +other than CMakeLang), and then converting back to a string for any potential +output. + +We can create a variable, which is to say a name for a string, using the +:command:`set` command. + +.. code-block:: cmake + + set(var "World!") + +A variable's value can be accessed using brace expansion, for example if we want +to use the :command:`message` command to print the string named by ``var``. + +.. code-block:: cmake + + set(var "World!") + message("Hello ${var}") + +.. code-block:: console + + $ cmake -P CMakeLists.txt + Hello World! + +.. note:: + :option:`cmake -P` is called "script mode", it informs CMake this file is not + intended to have a :command:`project` command. We're not building any + software, instead using CMake only as a command interpreter. + +Because CMakeLang has only strings, conditionals are entirely by convention of +which strings are considered true and which are considered false. These are +*supposed* to be intuitive, "True", "On", "Yes", and (strings representing) +non-zero numbers are truthy, while "False" "Off", "No", "0", "Ignore", +"NotFound", and the empty string are all considered false. + +However, some of the rules are more complex than that, so taking some time +to consult the :command:`if` documentation on expressions is worthwhile. It's +recommended to stick to a single pair for a given context, such as +"True"/"False" or "On"/"Off". + +As mentioned, lists are strings containing semicolons. The :command:`list` +command is useful for manipulating these, and many structures within CMake +expect to operate with this convention. As an example, we can use the +:command:`foreach` command to iterate over a list. + +.. code-block:: cmake + + set(stooges "Moe;Larry") + list(APPEND stooges "Curly") + + message("Stooges contains: ${stooges}") + + foreach(stooge IN LISTS stooges) + message("Hello, ${stooge}") + endforeach() + +.. code-block:: console + + $ cmake -P CMakeLists.txt + Stooges contains: Moe;Larry;Curly + Hello, Moe + Hello, Larry + Hello, Curly + +Exercise 1 - Macros, Functions, and Lists +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +CMake allows us to craft our own functions and macros. This can be very helpful +when constructing lots of similar targets, like tests, for which we will want +to call similar sets of commands over and over again. We do so with +:command:`function` and :command:`macro`. + +.. code-block:: cmake + + macro(MyMacro MacroArgument) + message("${MacroArgument}\n\t\tFrom Macro") + endmacro() + + function(MyFunc FuncArgument) + MyMacro("${FuncArgument}\n\tFrom Function") + endfunction() + + MyFunc("From TopLevel") + +.. code-block:: console + + $ cmake -P CMakeLists.txt + From TopLevel + From Function + From Macro + +Like with many languages, the difference between functions and macros is one +of scope. In CMakeLang, both :command:`function` and :command:`macro` can "see" +all the variables created in all the frames above them. However, a +:command:`macro` acts semantically like a text replacement, similar to C/C++ +macros, so any side effects the macro creates are visible in their calling +context. If we create or change a variable in a macro, the caller will see the +change. + +:command:`function` creates its own variable scope, so side effects are not +visible to the caller. In order to propagate changes to the parent which called +the function, we must use ``set( PARENT_SCOPE)``, which works the +same as :command:`set` but for variables belonging to the caller's context. + +.. note:: + In CMake 3.25, the :command:`return(PROPAGATE)` option was added, which + works the same as :command:`set(PARENT_SCOPE)` but provides slightly better + ergonomics. + +While not necessary for this exercise, it bears mentioning that :command:`macro` +and :command:`function` both support variadic arguments via the ``ARGV`` +variable, a list containing all arguments passed to the command, and the +``ARGN`` variable, containing all arguments past the last expected argument. + +We're not going to build any targets in this exercise, so instead we'll +construct our own version of :command:`list(APPEND)`, which adds a value to a +list. + +Goal +---- + +Implement a macro and a function which append a value to a list, without using +the :command:`list(APPEND)` command. + +The desired usage of these commands is as follows: + +.. code-block:: cmake + + set(Letters "Alpha;Beta") + MacroAppend(Letters "Gamma") + message("Letters contains: ${Letters}") + +.. code-block:: console + + $ cmake -P Exercise1.cmake + Letters contains: Alpha;Beta;Gamma + +.. note:: + The extension for these exercises is ``.cmake``, that's the standard extension + for CMakeLang files when not contained in a ``CMakeLists.txt`` + +Helpful Resources +----------------- + +* :command:`macro` +* :command:`function` +* :command:`set` +* :command:`if` + +Files to Edit +------------- + +* ``Exercise1.cmake`` + +Getting Started +---------------- + +The source code for ``Exercise1.cmake`` is provided in the +``Help/guide/tutorial/Step2`` directory. It contains tests to verify the +append behavior described above. + +.. note:: + You're not expected to handle the case of an empty or undefined list to + append to. However, as a bonus, the case is tested if you want to try out + your understanding of CMakeLang conditionals. + +Complete ``TODO 1`` and ``TODO 2``. + +Build and Run +------------- + +We're going to use script mode to run these exercises. First navigate to the +``Help/guide/tutorial/Step2`` folder then you can run the code with: + +.. code-block:: console + + cmake -P Exercise1.cmake + +The script will report if the commands were implemented correctly. + +Solution +-------- + +This problem relies on an understanding of the mechanisms of CMake variables. +CMake variables are names for strings; or put another way, a CMake variable +is itself a string which can brace expand into a different string. + +This leads to a common pattern in CMake code where functions and macros aren't +passed values, but rather, they are passed the names of variables which contain +those values. Thus ``ListVar`` does not contain the *value* of the list we need +to append to, it contains the *name* of a list, which contains the value we +need to append to. + +When expanding the variable with ``${ListVar}``, we will get the name of the +list. If we expand that name with ``${${ListVar}}``, we will get the values +the list contains. + +To implement ``MacroAppend``, we need only combine this understanding of +``ListVar`` with our knowledge of the :command:`set` command. + +.. raw:: html + +
TODO 1: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 1: Exercise1.cmake + :name: Exercise1.cmake-MacroAppend + + macro(MacroAppend ListVar Value) + set(${ListVar} "${${ListVar}};${Value}") + endmacro() + +.. raw:: html + +
+ +We don't need to worry about scope here, because a macro operates in the same +scope as its parent. + +``FuncAppend`` is almost identical, in fact it could be implemented in the +same one liner but with an added ``PARENT_SCOPE``, but the instructions ask +us to implement it in terms of ``MacroAppend``. + +.. raw:: html + +
TODO 2: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 2: Exercise1.cmake + :name: Exercise1.cmake-FuncAppend + + function(FuncAppend ListVar Value) + MacroAppend(${ListVar} ${Value}) + set(${ListVar} "${${ListVar}}" PARENT_SCOPE) + endfunction() + +.. raw:: html + +
+ +``MacroAppend`` transforms ``ListVar`` for us, but it won't propagate the result +to the parent scope. Because this is a function, we need to do so ourselves +with :command:`set(PARENT_SCOPE)`. + +Exercise 2 - Conditionals and Loops +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The two most common flow control elements in any structured programming +language are conditionals and their close sibling loops. CMakeLang is no +different. As previously mentioned, the truthiness of a given CMake string is a +convention established by the :command:`if` command. + +When given a string, :command:`if` will first check if it is one of the known +constant values previously discussed. If the string isn't one of those values +the command assumes it is a variable, and checks the brace-expanded contents of +that variable to determine the result of the conditional. + +.. code-block:: cmake + + if(True) + message("Constant Value: True") + else() + message("Constant Value: False") + endif() + + if(ConditionalValue) + message("Undefined Variable: True") + else() + message("Undefined Variable: False") + endif() + + set(ConditionalValue True) + + if(ConditionalValue) + message("Defined Variable: True") + else() + message("Defined Variable: False") + endif() + +.. code-block:: console + + $ cmake -P ConditionalValue.cmake + Constant Value: True + Undefined Variable: False + Defined Variable: True + +.. note:: + This is a good a time as any to discuss quoting in CMake. All objects in + CMake are strings, thus the double quote, ``"``, is often unnecessary. + CMake knows the object is a string, everything is a string. + + However, it is needed in some contexts. Strings containing whitespace require + double quotes, else they are treated like lists; CMake will concatenate the + elements together with semicolons. The reverse is also true, when + brace-expanding lists it is necessary to do so inside quotes if we want to + *preserve* the semicolons. Otherwise CMake will expand the list items into + space-separate strings. + + A handful of commands, such as :command:`if`, recognize the difference + between quoted and unquoted strings. :command:`if` will only check that the + given string represents a variable when the string is unquoted. + +Finally, :command:`if` provides several useful comparison modes such as +``STREQUAL`` for string matching, ``DEFINED`` for checking the existence of +a variable, and ``MATCHES`` for regular expression checks. It also supports the +typical logical operators, ``NOT``, ``AND``, and ``OR``. + +In addition to conditionals CMake provides two loop structures, +:command:`while`, which follows the same rules as :command:`if` for checking a +loop variable, and the more useful :command:`foreach`, which iterates over lists +of strings and was demonstrated in the `Background`_ section. + +For this exercise, we're going to use loops and conditionals to solve some +simple problems. We'll be using the aforementioned ``ARGN`` variable from +:command:`function` as the list to operate on. + +Goal +---- + +Loop over a list, and return all the strings containing the string ``Foo``. + +.. note:: + Those who read the command documentation will be aware that this is + :command:`list(FILTER)`, resist the temptation to use it. + +Helpful Resources +----------------- + +* :command:`function` +* :command:`foreach` +* :command:`if` +* :command:`list` + +Files to Edit +------------- + +* ``Exercise2.cmake`` + +Getting Started +---------------- + +The source code for ``Exercise2.cmake`` is provided in the ``Help/guide/tutorial/Step2`` +directory. It contains tests to verify the append behavior described above. + +.. note:: + You should use the :command:`list(APPEND)` command this time to collect your + final result into a list. The input can be consumed from the ``ARGN`` variable + of the provided function. + +Complete ``TODO 3``. + +Build and Run +------------- + +Navigate to the ``Help/guide/tutorial/Step2`` folder then you can run the code with: + +.. code-block:: console + + cmake -P Exercise2.cmake + +The script will report if the ``FilterFoo`` function was implemented correctly. + +Solution +-------- + +We need to do three things, loop over the ``ARGN`` list, check if a given +item in that list matches ``"Foo"``, and if so append it to the ``OutVar`` +list. + +While there are a couple ways we could invoke :command:`foreach`, the +recommended way is to allow the command to do the variable expansion for us +via ``IN LISTS`` to access the ``ARGN`` list items. + +The :command:`if` comparison we need is ``MATCHES`` which will check if +``"FOO"`` exists in the item. All that remains is to append the item to the +``OutVar`` list. The trickiest part is remembering that ``OutVar`` *names* a +list, it is not the list itself, so we need to access it via ``${OutVar}``. + +.. raw:: html + +
TODO 3: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 3: Exercise2.cmake + :name: Exercise2.cmake-FilterFoo + + function(FilterFoo OutVar) + + foreach(item IN LISTS ARGN) + if(item MATCHES Foo) + list(APPEND ${OutVar} ${item}) + endif() + endforeach() + + set(${OutVar} ${${OutVar}} PARENT_SCOPE) + endfunction() + +.. raw:: html + +
+ +Exercise 3 - Organizing with Include +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We have already discussed how to incorporate subdirectories containing their +own CMLs with :command:`add_subdirectory`. In later steps we will explore +the various way CMake code can be packaged and shared across projects. + +However for small CMake functions and utilities, it is often beneficial for them +to live in their own ``.cmake`` files outside the project CMLs and separate +from the rest of the build system. This allows for separation of concerns, +removing the project-specific elements from the utilities we are using to +describe them. + +To incorporate these separate ``.cmake`` files into our project, we use the +:command:`include` command. This command immediately begins interpreting the +contents of the :command:`include`'d file in the scope of the parent CML. It +is as if the entire file were being called as a macro. + +Traditionally, these kinds of ``.cmake`` files live in a folder named "cmake" +inside the project root. For this exercise, we'll use the ``Step2`` folder instead. + +Goal +---- + +Use the functions from Exercises 1 and 2 to build and filter our own list of items. + +Helpful Resources +----------------- + +* :command:`include` + +Files to Edit +------------- + +* ``Exercise3.cmake`` + +Getting Started +---------------- + +The source code for ``Exercise3.cmake`` is provided in the ``Help/guide/tutorial/Step2`` +directory. It contains tests to verify the correct usage of our functions +from the previous two exercises. + +.. note:: + Actually it reuses tests from Exercise2.cmake, reusable code is good for + everyone. + +Complete ``TODO 4`` through ``TODO 7``. + +Build and Run +------------- + +Navigate to the ``Help/guide/tutorial/Step2`` folder then you can run the code with: + +.. code-block:: console + + cmake -P Exercise3.cmake + +The script will report if the functions were invoked and composed correctly. + +Solution +-------- + +The :command:`include` command will interpret the included file completely, +including the tests from the first two exercises. We don't want to run these +tests again. Thanks to some forethought, these files check a variable called +``SKIP_TESTS`` prior to running their tests, setting this to ``True`` will +get us the behavior we want. + +.. raw:: html + +
TODO 4: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 4: Exercise3.cmake + :name: Exercise3.cmake-SKIP_TESTS + + set(SKIP_TESTS True) + +.. raw:: html + +
+ +Now we're ready to :command:`include` the previous exercises to grab their +functions. + +.. raw:: html + +
TODO 5: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 5: Exercise3.cmake + :name: Exercise3.cmake-include + + include(Exercise1.cmake) + include(Exercise2.cmake) + +.. raw:: html + +
+ +Now that ``FuncAppend`` is available to us, we can use it to append new elements +to the ``InList``. + +.. raw:: html + +
TODO 6: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 6: Exercise3.cmake + :name: Exercise3.cmake-FuncAppend + + FuncAppend(InList FooBaz) + FuncAppend(InList QuxBaz) + +.. raw:: html + +
+ +Finally, we can use ``FilterFoo`` to filter the full list. The tricky part to +remember here is that our ``FilterFoo`` wants to operate on list values via +``ARGN``, so we need to expand the ``InList`` when we call ``FilterFoo``. + +.. raw:: html + +
TODO 7: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 7: Exercise3.cmake + :name: Exercise3.cmake-FilterFoo + + FilterFoo(OutList ${InList}) + +.. raw:: html + +
diff --git a/Help/guide/tutorial/Complete/CMakeLists.txt b/Help/guide/tutorial/Complete/CMakeLists.txt deleted file mode 100644 index 181c25e..0000000 --- a/Help/guide/tutorial/Complete/CMakeLists.txt +++ /dev/null @@ -1,127 +0,0 @@ -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 "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) - -# 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_GENERATOR "TGZ") -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 deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Complete/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Complete/Config.cmake.in b/Help/guide/tutorial/Complete/Config.cmake.in deleted file mode 100644 index 17cbabd..0000000 --- a/Help/guide/tutorial/Complete/Config.cmake.in +++ /dev/null @@ -1,4 +0,0 @@ - -@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 deleted file mode 100644 index 85760e5..0000000 --- a/Help/guide/tutorial/Complete/License.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 1654564..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt +++ /dev/null @@ -1,62 +0,0 @@ -# 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 - $ - $ - ) - -# 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") - - include(MakeTable.cmake) # generates Table.h - - # 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} - ) - - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() - -# link MathFunctions to tutorial_compiler_flags -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.cmake b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake deleted file mode 100644 index 12865a9..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - ) diff --git a/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx deleted file mode 100644 index f85b278..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include -#include -#include - -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(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 deleted file mode 100644 index c0991b9..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx +++ /dev/null @@ -1,20 +0,0 @@ - -#include "MathFunctions.h" - -#include - -#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 deleted file mode 100644 index 3fb547b..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.h +++ /dev/null @@ -1,14 +0,0 @@ - -#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 deleted file mode 100644 index 8153f18..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#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(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 deleted file mode 100644 index e1c42ef..0000000 --- a/Help/guide/tutorial/Complete/MathFunctions/mysqrt.h +++ /dev/null @@ -1,6 +0,0 @@ - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Complete/MultiCPackConfig.cmake b/Help/guide/tutorial/Complete/MultiCPackConfig.cmake deleted file mode 100644 index c2583df..0000000 --- a/Help/guide/tutorial/Complete/MultiCPackConfig.cmake +++ /dev/null @@ -1,6 +0,0 @@ -include("release/CPackConfig.cmake") - -set(CPACK_INSTALL_CMAKE_PROJECTS - "debug;Tutorial;ALL;/" - "release;Tutorial;ALL;/" - ) diff --git a/Help/guide/tutorial/Complete/SimpleTest/CMakeLists.txt b/Help/guide/tutorial/Complete/SimpleTest/CMakeLists.txt new file mode 100644 index 0000000..96ad781 --- /dev/null +++ b/Help/guide/tutorial/Complete/SimpleTest/CMakeLists.txt @@ -0,0 +1,53 @@ +# A very simple test framework for demonstrating how dependencies work +cmake_minimum_required(VERSION 3.23) + +project(SimpleTest + VERSION 0.0.1 +) + +add_library(SimpleTest INTERFACE) +target_sources(SimpleTest + INTERFACE + FILE_SET HEADERS + FILES + SimpleTest.h +) +target_compile_features(SimpleTest INTERFACE cxx_std_20) + +target_compile_definitions(SimpleTest INTERFACE "SIMPLETEST_CONFIG=$") + +find_package(TransitiveDep REQUIRED) +target_link_libraries(SimpleTest + INTERFACE + TransitiveDep::TransitiveDep +) + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +install( + TARGETS SimpleTest + EXPORT SimpleTestTargets + FILE_SET HEADERS +) + +install( + EXPORT SimpleTestTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimpleTest + NAMESPACE SimpleTest:: +) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/SimpleTestConfigVersion.cmake + COMPATIBILITY ExactVersion + ARCH_INDEPENDENT +) + +install( + FILES + cmake/simpletest_discover_impl.cmake + cmake/simpletest_discover_tests.cmake + cmake/SimpleTestConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/SimpleTestConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimpleTest +) diff --git a/Help/guide/tutorial/Complete/SimpleTest/CMakePresets.json b/Help/guide/tutorial/Complete/SimpleTest/CMakePresets.json new file mode 100644 index 0000000..816d8a3 --- /dev/null +++ b/Help/guide/tutorial/Complete/SimpleTest/CMakePresets.json @@ -0,0 +1,16 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "SimpleTest Preset", + "description": "Preset to use with the tutorial's SimpleTest library", + "binaryDir": "${sourceDir}/build", + "installDir": "${sourceParentDir}/install", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20", + "CMAKE_PREFIX_PATH": "${sourceParentDir}/install" + } + } + ] +} diff --git a/Help/guide/tutorial/Complete/SimpleTest/SimpleTest.h b/Help/guide/tutorial/Complete/SimpleTest/SimpleTest.h new file mode 100644 index 0000000..ced6562 --- /dev/null +++ b/Help/guide/tutorial/Complete/SimpleTest/SimpleTest.h @@ -0,0 +1,155 @@ +#pragma once + +#include +#include +#include + +namespace SimpleTest { + +using TestFunc = void (*)(); + +using Registry = std::map>; +inline Registry g_registry; + +inline Registry& registry() +{ + return g_registry; +} + +struct failure +{ + char const* file; + int line; + char const* expr; +}; + +struct Registrar +{ + template + Registrar(char const (&name)[N], TestFunc f) + { + auto [it, inserted] = + registry().emplace(std::string_view{ name, N ? (N - 1) : 0 }, f); + if (!inserted) { + std::printf("[ WARN ] duplicate test name: %.*s\n", + int(it->first.size()), it->first.data()); + } + } +}; + +inline Registry const& all() +{ + return registry(); +} +inline TestFunc find(std::string_view name) +{ + auto it = registry().find(name); + return it == registry().end() ? nullptr : it->second; +} + +} + +#define SIMPLETEST_STRINGIFY(a) #a +#define SIMPLETEST_XSTRINGIFY(a) SIMPLETEST_STRINGIFY(a) +#define SIMPLETEST_CONCAT_(a, b) a##b +#define SIMPLETEST_CONCAT(a, b) SIMPLETEST_CONCAT_(a, b) + +#define TEST(name_literal) \ + static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)(); \ + static ::SimpleTest::Registrar SIMPLETEST_CONCAT(_simpletest_reg_, \ + __LINE__)( \ + name_literal, &SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)); \ + static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)() + +// Minimal assertion +#define REQUIRE(expr) \ + do { \ + if (!(expr)) \ + throw ::SimpleTest::failure{ __FILE__, __LINE__, #expr }; \ + } while (0) + +int main(int argc, char** argv) +{ + using namespace ::SimpleTest; + + std::string_view arg1 = + (argc >= 2) ? std::string_view{ argv[1] } : std::string_view{}; + + if (arg1 == "--list") { + bool first = true; + for (auto const& [name, _] : registry()) { + if (!first) + std::printf(","); + std::printf("%.*s", int(name.size()), name.data()); + first = false; + } + std::printf("\n"); + return 0; + } + + if (arg1 == "--test") { + if (argc < 3) { + std::printf("usage: %s [--list] [--test ]\n", argv[0]); + return 2; + } + +#ifdef SIMPLETEST_CONFIG + std::printf("SimpleTest built with config: " SIMPLETEST_XSTRINGIFY( + SIMPLETEST_CONFIG) "\n"); +#endif + + std::string_view name{ argv[2] }; + auto it = registry().find(name); + if (it == registry().end()) { + std::printf("[ NOTFOUND ] %s\n", argv[2]); + return 2; + } + + int failed = 0; + std::printf("[ RUN ] %.*s\n", int(it->first.size()), + it->first.data()); + try { + it->second(); + std::printf("[ OK] %.*s\n", int(it->first.size()), + it->first.data()); + } catch (failure const& f) { + std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(it->first.size()), + it->first.data(), f.file, f.line, f.expr); + failed = 1; + } catch (...) { + std::printf("[ FAILED ] %.*s : unknown exception\n", + int(it->first.size()), it->first.data()); + failed = 1; + } + return failed; + } + + if (argc > 1) { + std::printf("usage: %s [--list] [--test ]\n", argv[0]); + return 2; + } + +#ifdef SIMPLETEST_CONFIG + std::printf("SimpleTest built with config: " SIMPLETEST_XSTRINGIFY( + SIMPLETEST_CONFIG) "\n"); +#endif + + // Default: run all tests. + int failed = 0; + for (auto const& [name, func] : all()) { + std::printf("[ RUN ] %.*s\n", int(name.size()), name.data()); + try { + func(); + std::printf("[ OK ] %.*s\n", int(name.size()), name.data()); + } catch (failure const& f) { + std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(name.size()), + name.data(), f.file, f.line, f.expr); + failed = 1; + } catch (...) { + std::printf("[ FAILED ] %.*s : unknown exception\n", int(name.size()), + name.data()); + failed = 1; + } + } + return failed; +} diff --git a/Help/guide/tutorial/Complete/SimpleTest/cmake/SimpleTestConfig.cmake b/Help/guide/tutorial/Complete/SimpleTest/cmake/SimpleTestConfig.cmake new file mode 100644 index 0000000..6c7ffb5 --- /dev/null +++ b/Help/guide/tutorial/Complete/SimpleTest/cmake/SimpleTestConfig.cmake @@ -0,0 +1,5 @@ +include(CMakeFindDependencyMacro) +find_dependency(TransitiveDep) + +include(${CMAKE_CURRENT_LIST_DIR}/SimpleTestTargets.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/simpletest_discover_tests.cmake) diff --git a/Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_impl.cmake b/Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_impl.cmake new file mode 100644 index 0000000..7d3a22b --- /dev/null +++ b/Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_impl.cmake @@ -0,0 +1,32 @@ +if(NOT DEFINED TEST_EXE OR NOT DEFINED OUT_FILE) +# noqa: spellcheck off + message(FATAL_ERROR "simpletest_discover: need -DTEST_EXE and -DOUT_FILE") +# noqa: spellcheck on +endif() + +execute_process( + COMMAND ${TEST_EXE} --list + RESULT_VARIABLE _rc + OUTPUT_VARIABLE _out + ERROR_VARIABLE _err + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if(NOT _rc EQUAL 0) + file(WRITE ${OUT_FILE} "# simpletest: --list failed (rc=${_rc})\n") + message(FATAL_ERROR "simpletest_discover: '${TEST_EXE} --list' failed (${_rc})\n${_err}") +endif() + +if(_out STREQUAL "") + file(WRITE ${OUT_FILE} "# simpletest: no tests\n") + return() +endif() + +string(REPLACE "," ";" _names "${_out}") + +file(WRITE ${OUT_FILE} "# Auto-generated by simpletest_discover_impl.cmake\n") +foreach(_name IN LISTS _names) + file(APPEND ${OUT_FILE} + "add_test([=[${_name}]=] \"${TEST_EXE}\" \"--test\" \"${_name}\")\n" + ) +endforeach() diff --git a/Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_tests.cmake b/Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_tests.cmake new file mode 100644 index 0000000..e5cf059 --- /dev/null +++ b/Help/guide/tutorial/Complete/SimpleTest/cmake/simpletest_discover_tests.cmake @@ -0,0 +1,27 @@ +set(_simpletest_impl_script ${CMAKE_CURRENT_LIST_DIR}/simpletest_discover_impl.cmake) + +function(simpletest_discover_tests target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "simpletest_discover_tests: no such target '${target}'") + endif() + + set(_out ${CMAKE_CURRENT_BINARY_DIR}/${target}_ctests.cmake) + + if(NOT EXISTS ${_out}) + file(WRITE ${_out} "# Populated after building ${target}\n") + endif() + +# noqa: spellcheck off + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -DTEST_EXE=$ + -DOUT_FILE=${_out} + -P ${_simpletest_impl_script} + BYPRODUCTS ${_out} + COMMENT "SimpleTest: Discovering tests in ${target}" + VERBATIM + ) +# noqa: spellcheck on + + set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES ${_out}) +endfunction() diff --git a/Help/guide/tutorial/Complete/TutorialConfig.h.in b/Help/guide/tutorial/Complete/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Complete/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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/TutorialProject/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/CMakeLists.txt new file mode 100644 index 0000000..9a4ecbd --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/CMakePresets.json b/Help/guide/tutorial/Complete/TutorialProject/CMakePresets.json new file mode 100644 index 0000000..fee177b --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..e0f3ba2 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/CMakeLists.txt @@ -0,0 +1,55 @@ +add_library(MathFunctions) +add_library(Tutorial::MathFunctions ALIAS MathFunctions) + +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/Complete/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt new file mode 100644 index 0000000..6aa2a32 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx new file mode 100644 index 0000000..f85b278 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include +#include +#include + +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(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/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..4bf8051 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathFunctions.cxx @@ -0,0 +1,101 @@ +#include +#include + +#include + +#ifdef TUTORIAL_USE_SSE2 +# include +#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 + +double table_sqrt(double x) +{ + double result = sqrtTable[static_cast(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/Complete/TutorialProject/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..91cb176 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathFunctions.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include +#include + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..b20151f --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Complete/TutorialProject/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Complete/TutorialProject/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Complete/TutorialProject/Tests/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/Tests/CMakeLists.txt new file mode 100644 index 0000000..9b5bcd1 --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/Tests/TestMathFunctions.cxx b/Help/guide/tutorial/Complete/TutorialProject/Tests/TestMathFunctions.cxx new file mode 100644 index 0000000..166fd5d --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/Tests/TestMathFunctions.cxx @@ -0,0 +1,22 @@ +#include +#include + +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/Complete/TutorialProject/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Complete/TutorialProject/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..79b232b --- /dev/null +++ b/Help/guide/tutorial/Complete/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/Complete/TutorialProject/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Complete/TutorialProject/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..ac133d4 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/Tutorial/Tutorial.cxx @@ -0,0 +1,27 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include +#include + +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/Complete/TutorialProject/cmake/TutorialConfig.cmake b/Help/guide/tutorial/Complete/TutorialProject/cmake/TutorialConfig.cmake new file mode 100644 index 0000000..d13caa4 --- /dev/null +++ b/Help/guide/tutorial/Complete/TutorialProject/cmake/TutorialConfig.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TutorialTargets.cmake) diff --git a/Help/guide/tutorial/Complete/install/include/Unpackaged/Unpackaged.h b/Help/guide/tutorial/Complete/install/include/Unpackaged/Unpackaged.h new file mode 100644 index 0000000..9782b0a --- /dev/null +++ b/Help/guide/tutorial/Complete/install/include/Unpackaged/Unpackaged.h @@ -0,0 +1,3 @@ +#pragma once + +#define UNPACKAGED_HEADER_FOUND diff --git a/Help/guide/tutorial/Complete/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake b/Help/guide/tutorial/Complete/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake new file mode 100644 index 0000000..ef51145 --- /dev/null +++ b/Help/guide/tutorial/Complete/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake @@ -0,0 +1,50 @@ +# Abridged import written for the Tutorial + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8) + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +if(CMAKE_VERSION VERSION_LESS "3.0.0") + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 3.0.0...3.30) + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_cmake_targets_defined "") +set(_cmake_targets_not_defined "") +set(_cmake_expected_targets "") +foreach(_cmake_expected_target IN ITEMS TransitiveDep::TransitiveDep) + list(APPEND _cmake_expected_targets "${_cmake_expected_target}") + if(TARGET "${_cmake_expected_target}") + list(APPEND _cmake_targets_defined "${_cmake_expected_target}") + else() + list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}") + endif() +endforeach() +unset(_cmake_expected_target) +if(_cmake_targets_defined STREQUAL _cmake_expected_targets) + unset(_cmake_targets_defined) + unset(_cmake_targets_not_defined) + unset(_cmake_expected_targets) + unset(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT _cmake_targets_defined STREQUAL "") + string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}") + string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n") +endif() +unset(_cmake_targets_defined) +unset(_cmake_targets_not_defined) +unset(_cmake_expected_targets) + +# Create imported target TransitiveDep::TransitiveDep +add_library(TransitiveDep::TransitiveDep INTERFACE IMPORTED) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/Help/guide/tutorial/Complete/tutorial.cxx b/Help/guide/tutorial/Complete/tutorial.cxx deleted file mode 100644 index 78641b1..0000000 --- a/Help/guide/tutorial/Complete/tutorial.cxx +++ /dev/null @@ -1,26 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const outputValue = mathfunctions::sqrt(inputValue); - - std::cout << "The square root of " << inputValue << " is " << outputValue - << std::endl; - return 0; -} diff --git a/Help/guide/tutorial/Configuration and Cache Variables.rst b/Help/guide/tutorial/Configuration and Cache Variables.rst new file mode 100644 index 0000000..23d5997 --- /dev/null +++ b/Help/guide/tutorial/Configuration and Cache Variables.rst @@ -0,0 +1,606 @@ +Step 3: Configuration and Cache Variables +========================================= + +CMake projects often have some project-specific configuration variables which +users and packagers are interested in. CMake has many ways that an invoking +user or process can communicate these configuration choices, but the most +fundamental of them are :option:`-D ` flags. + +In this step we'll explore the ins and out of how to provide project +configuration options from within a CML, and how to invoke CMake to take +advantage of configuration options provided by both CMake and individual +projects. + +Background +^^^^^^^^^^ + +If we had a CMake project for compression software which supported multiple +compression algorithms, we might want to let the packager of the project decide +which algorithms to enable when they build our software. We can do so by +consuming variables set via :option:`-D ` flags. + +.. code-block:: cmake + + if(COMPRESSION_SOFTWARE_USE_ZLIB) + message("I will use Zlib!") + # ... + endif() + + if(COMPRESSION_SOFTWARE_USE_ZSTD) + message("I will use Zstd!") + # ... + endif() + +.. code-block:: console + + $ cmake -B build \ + -DCOMPRESSION_SOFTWARE_USE_ZLIB=ON \ + -DCOMPRESSION_SOFTWARE_USE_ZSTD=OFF + ... + I will use Zlib! + +Of course, we will want to provide reasonable defaults for these configuration +choices, and a way to communicate the purpose of a given option. This function +is provided by the :command:`option` command. + +.. code-block:: cmake + + option(COMPRESSION_SOFTWARE_USE_ZLIB "Support Zlib compression" ON) + option(COMPRESSION_SOFTWARE_USE_ZSTD "Support Zstd compression" ON) + + if(COMPRESSION_SOFTWARE_USE_ZLIB) + # Same as before + # ... + +.. code-block:: console + + $ cmake -B build \ + -DCOMPRESSION_SOFTWARE_USE_ZLIB=OFF + ... + I will use Zstd! + +The names created by :option:`-D ` flags and :command:`option` are +not normal variables, they are **cache** variables. Cache variables are globally +visible variables which are *sticky*, their value is difficult to change after +it is initially set. In fact they are so sticky that, in project mode, CMake +will save and restore cache variables across multiple configurations. If a +cache variable is set once, it will remain until another :option:`-D ` +flag preempts the saved variable. + +.. note:: + CMake itself has dozens of normal and cache variables used for configuration. + These are documented at :manual:`cmake-variables(7)` and operate in the same + manner as project-provided variables for configuration. + +:command:`set` can also be used to manipulate cache variables, but will not +change a variable which has already been created. + +.. code-block:: cmake + + set(StickyCacheVariable "I will not change" CACHE STRING "") + set(StickyCacheVariable "Overwrite StickyCache" CACHE STRING "") + + message("StickyCacheVariable: ${StickyCacheVariable}") + +.. code-block:: console + + $ cmake -P StickyCacheVariable.cmake + StickyCacheVariable: I will not change + +Because :option:`-D ` flags are processed before any other commands, +they take precedence for setting the value of a cache variable. + +.. code-block:: console + + $ cmake \ + -DStickyCacheVariable="Commandline always wins" \ + -P StickyCacheVariable.cmake + StickyCacheVariable: Commandline always wins + +While cache variables cannot ordinarily be changed, they can be *shadowed* by +normal variables. We can observe this by :command:`set`'ing a variable to have +the same name as a cache variable, and then using :command:`unset` to remove +the normal variable. + +.. code-block:: cmake + + set(ShadowVariable "In the shadows" CACHE STRING "") + set(ShadowVariable "Hiding the cache variable") + message("ShadowVariable: ${ShadowVariable}") + + unset(ShadowVariable) + message("ShadowVariable: ${ShadowVariable}") + +.. code-block:: console + + $ cmake -P ShadowVariable.cmake + ShadowVariable: Hiding the cache variable + ShadowVariable: In the shadows + +Exercise 1 - Using Options +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We can imagine a scenario where consumers really want our ``MathFunctions`` +library, and the ``Tutorial`` utility is a "take it or leave it" add-on. In +that case, we might want to add an option to allow consumers to disable +building our ``Tutorial`` binary, building only the ``MathFunctions`` library. + +With our knowledge of options, conditionals, and cache variables we have all +the pieces we need to make this configuration available. + +Goal +---- + +Add an option named ``TUTORIAL_BUILD_UTILITIES`` to control if the ``Tutorial`` +binary is configured and built. + +.. note:: + CMake allows us to determine which targets are built after configuration. Our + users could ask for the ``MathFunctions`` library alone without ``Tutorial``. + CMake also has mechanisms to exclude targets from ``ALL``, the default target + which builds all the other available targets. + + However, options which completely exclude targets from the configuration are + convenient and popular, especially if configuring those targets involves + heavy-weight steps which might take some time. + + It also simplifies :command:`install()` logic, which we'll discuss in later + steps, if targets the packager is uninterested in are completely excluded. + +Helpful Resources +----------------- + +* :command:`option` +* :command:`if` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +The ``Help/guide/tutorial/Step3`` folder contains the complete, recommended +solution to ``Step1`` and the relevant ``TODOs`` for this step. Take a minute +to review and refamiliarize yourself with the ``Tutorial`` project. + +When you feel you have an understanding of the current code, start with +``TODO 1`` and complete through ``TODO 2``. + +Build and Run +------------- + +We can now reconfigure our project. However, this time we want to control the +configuration via :option:`-D ` flags. We again start by navigating +to ``Help/guide/tutorial/Step3`` and invoking CMake, but this time with our +configuration options. + +.. code-block:: console + + cmake -B build -DTUTORIAL_BUILD_UTILITIES=OFF + +We can now build as usual. + +.. code-block:: console + + cmake --build build + +After the build we should observe no Tutorial executable is produced. Because +cache variables are sticky even a reconfigure shouldn't change this, despite +the default-``ON`` option. + +.. code-block:: console + + cmake -B build + cmake --build build + +Will not produce the Tutorial executable, the cache variables are "locked in". +To change this we have two options. First, we can edit the file which stores +the cache variables between CMake configuration runs, the "CMake Cache". This +file is ``build/CMakeCache.txt``, in it we can find the option cache variable. + +.. code-block:: cmake + + //Build the Tutorial executable + TUTORIAL_BUILD_UTILITIES:BOOL=OFF + +We can change this from ``OFF`` to ``ON``, rerun the build, and we will get +our ``Tutorial`` executable. + +.. note:: + ``CMakeCache.txt`` entries are of the form ``:=``, however + the "type" is only a hint. All objects in CMake are strings, regardless of + what the cache says. + +Alternatively, we can change the value of the cache variable on the command +line, because the command line runs before ``CMakeCache.txt`` is loaded its +value take precedence over those in the cache file. + +.. code-block:: console + + cmake -B build -DTUTORIAL_BUILD_UTILITIES=ON + cmake --build build + +Doing so we observe the value in ``CMakeCache.txt`` has flipped from ``OFF`` +to ``ON``, and that the ``Tutorial`` executable is built. + +Solution +-------- + +First we create our :command:`option` to provide our cache variable with a +reasonable default value. + +.. raw:: html + +
TODO 1: Click to show/hide answer + +.. literalinclude:: Step4/CMakeLists.txt + :caption: TODO 1: CMakeLists.txt + :name: CMakeLists.txt-option-TUTORIAL_BUILD_UTILITIES + :language: cmake + :start-at: option(TUTORIAL_BUILD_UTILITIES + :end-at: option(TUTORIAL_BUILD_UTILITIES + +.. raw:: html + +
+ +Then we can check the cache variable to conditionally enable the ``Tutorial`` +executable (by way of adding its subdirectory). + +.. raw:: html + +
TODO 2: Click to show/hide answer + +.. literalinclude:: Step4/CMakeLists.txt + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-if-TUTORIAL_BUILD_UTILITIES + :language: cmake + :start-at: if(TUTORIAL_BUILD_UTILITIES) + :end-at: endif() + +.. raw:: html + +
+ +Exercise 2 - ``CMAKE`` Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +CMake has several important normal and cache variables provided to allow +packagers to control the build. Decisions such as compilers, default flags, +search locations for packages, and much more are all controlled by CMake's +own configuration variables. + +Among the most important are language standards. As the language standard can +have significant impact on the ABI presented by a given package. For example, +it's quite common for libraries to use standard C++ templates on later +standards, and provide polyfills on earlier standards. If a library is consumed +under different standards then ABI incompatibilities between the standard +templates and the polyfills can result in incomprehensible errors and runtime +crashes. + +Ensuring all of our targets are built under the same language standard is +achieved with the :variable:`CMAKE__STANDARD` cache variables. For C++, +this is ``CMAKE_CXX_STANDARD``. + +.. note:: + Because these variables are so important, it is equally important that + developers not override or shadow them in their CMLs. Shadowing + :variable:`CMAKE__STANDARD` in a CML because the library wants C++20, + when the packager has decided to build the rest of their libraries and + applications with C++23, can lead to the aforementioned terrible, + incomprehensible errors. + + Do not :command:`set` ``CMAKE_`` globals without very strong reasons for + doing so. We'll discuss better methods for targets to communicate + requirements like definitions and minimum standards in later steps. + +In this exercise, we'll introduce some C++20 code into our library and +executable and build them with C++20 by setting the appropriate cache variable. + +Goal +---- + +Use ``std::format`` to format printed strings instead of stream operators. To +ensure availability of ``std::format``, configure CMake to use the C++20 +standard for C++ targets. + +Helpful Resources +----------------- + +* :option:`cmake -D` +* :variable:`CMAKE__STANDARD` +* :variable:`CMAKE_CXX_STANDARD` +* :prop_tgt:`CXX_STANDARD` +* `cppreference \ `_ + +Files to Edit +------------- + +* ``Tutorial/Tutorial.cxx`` +* ``MathFunctions/MathFunctions.cxx`` + +Getting Started +--------------- + +Continue to edit files from ``Step3``. Complete ``TODO 3`` through ``TODO 7``. +We'll be modifying our prints to use ``std::format`` instead of stream +operators. + +Ensure your cache variables are set such that the Tutorial executable will be +built, using any of the methods discussed in the previous exercise. + +Build and Run +------------- + +We need to reconfigure our project with the new standard, we can do this +using the same method as our ``TUTORIAL_BUILD_UTILITIES`` cache variable. + +.. code-block:: console + + cmake -B build -DCMAKE_CXX_STANDARD=20 + +.. note:: + Configuration variables are, by convention, prefixed with the provider of the + variable. CMake configuration variables are prefixed with ``CMAKE_``, while + projects should prefix their variables with ``_``. + + The tutorial configuration variables follow this convention, and are prefixed + with ``TUTORIAL_``. + +Now that we've configured with C++20, we can build as usual. + +.. code-block:: console + + cmake --build build + +Solution +-------- + +We need to include ```` and then use it. + +.. raw:: html + +
TODO 3-5: Click to show/hide answer + +.. literalinclude:: Step4/Tutorial/Tutorial.cxx + :caption: TODO 3: Tutorial/Tutorial.cxx + :name: Tutorial/Tutorial.cxx-include-format + :language: c++ + :start-at: #include + :end-at: #include + +.. literalinclude:: Step4/Tutorial/Tutorial.cxx + :caption: TODO 4: Tutorial/Tutorial.cxx + :name: Tutorial/Tutorial.cxx-format1 + :language: c++ + :start-at: if (argc < 2) { + :end-at: return 1; + :append: } + :dedent: 2 + +.. literalinclude:: Step4/Tutorial/Tutorial.cxx + :caption: TODO 5: Tutorial/Tutorial.cxx + :name: Tutorial/Tutorial.cxx-format3 + :language: c++ + :start-at: // calculate square root + :end-at: outputValue); + :dedent: 2 + +.. raw:: html + +
+ +And again for the ``MathFunctions`` library. + +.. raw:: html + +
TODO 6-7: Click to show/hide answer + +.. literalinclude:: Step4/MathFunctions/MathFunctions.cxx + :caption: TODO 6: MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-include-format + :language: c++ + :start-at: #include + :end-at: #include + +.. literalinclude:: Step4/MathFunctions/MathFunctions.cxx + :caption: TODO 7: MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-format + :language: c++ + :start-at: double delta + :end-at: std::format + :dedent: 4 + +.. raw:: html + +
+ +Exercise 3 - CMakePresets.json +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Managing these configuration values can quickly become overwhelming. In CI +systems it is appropriate to record these as part of a given CI step. For +example in a Github Actions CI step we might see something akin to the +following: + +.. code-block:: yaml + + - name: Configure and Build + run: | + cmake \ + -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_CXX_EXTENSIONS=ON \ + -DTUTORIAL_BUILD_UTILITIES=OFF \ + # Possibly many more options + # ... + + cmake --build build + +When developing code locally, typing all these options even once might be error +prone. If a fresh configuration is needed for any reason, doing so multiple +times could be exhausting. + +There are many and varied solutions to this problem, and your choice is +ultimately up to your preferences as a developer. CLI-oriented developers +commonly use task runners to invoke CMake with their desired options for a +project. Most IDEs also have a custom mechanism for controlling CMake +configuration. + +It would be impossible to fully enumerate every possible configuration workflow +here. Instead we will explore CMake's built-in solution, known as +:manual:`CMake Presets `. Presets give us a format to name +and express collections of CMake configuration options. + +.. note:: + Presets are capable of expressing entire CMake workflows, from + configuration, through building, all the way to installing the software + package. + + They are far more flexible than can we have room for here. We'll limit + ourselves to using them for configuration. + +CMake Presets come in two standard files, ``CMakePresets.json``, which is +intended to be a part of the project and tracked in source control; and +``CMakeUserPresets.json``, which is intended for local user configuration +and should not be tracked in source control. + +The simplest preset which would be of use to a developer does nothing more +than configure variables. + +.. code-block:: json + + { + "version": 4, + "configurePresets": [ + { + "name": "example-preset", + "cacheVariables": { + "EXAMPLE_FOO": "Bar", + "EXAMPLE_QUX": "Baz" + } + } + ] + } + +When invoking CMake, where previously we would have done: + +.. code-block:: console + + cmake -B build -DEXAMPLE_FOO=Bar -DEXAMPLE_QUX=Baz + +We can now use the preset: + +.. code-block:: console + + cmake -B build --preset example-preset + +CMake will search for files named ``CMakePresets.json`` and +``CMakeUserPresets.json``, and load the named configuration from them if +available. + +.. note:: + Command line flags can be mixed with presets. Command line flags have + precedence over values found in a preset. + +Presets also support limited macros, variables that can be brace-expanded +inside the preset. The only one of interest to us is the ``${sourceDir}`` macro, +which expands to the root directory of the project. We can use this to set our +build directory, skipping the :option:`-B ` flag when configuring +the project. + +.. code-block:: json + + { + "name": "example-preset", + "binaryDir": "${sourceDir}/build" + } + +Goal +---- + +Configure and build the tutorial using a CMake Preset instead of command line +flags. + +Helpful Resources +----------------- + +* :manual:`cmake-presets(7)` + +Files to Edit +------------- + +* ``CMakePresets.json`` + +Getting Started +--------------- + +Continue to edit files from ``Step3``. Complete ``TODO 8`` and ``TODO 9``. + +.. note:: + ``TODOs`` inside ``CMakePresets.json`` need to be *replaced*. There should + be no ``TODO`` keys left inside the file when you have completed the exercise. + +You can verify the preset is working correctly by deleting the existing build +folder before you configure, this will ensure you're not reusing the existing +CMake Cache for configuration. + +.. note:: + On CMake 3.24 and newer, the same effect can be achieved by configuring with + :option:`cmake --fresh`. + +All future configuration changes will be via the ``CMakePresets.json`` file. + +Build and Run +------------- + +We can now use the preset file to manage our configuration. + +.. code-block:: console + + cmake --preset tutorial + +Presets are capable of running the build step for us, but for this tutorial +we'll continue to run the build ourselves. + +.. code-block:: console + + cmake --build build + +Solution +-------- + +There are two changes we need to make, first we want to set the build +directory (also called the "binary directory") to the ``build`` subdirectory +of our project folder, and second we need to set the ``CMAKE_CXX_STANDARD`` to +``20``. + +.. raw:: html + +
TODO 8-9: Click to show/hide answer + +.. code-block:: json + :caption: TODO 8-9: CMakePresets.json + :name: CMakePresets.json-initial + + { + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20" + } + } + ] + } + +.. raw:: html + +
diff --git a/Help/guide/tutorial/Custom Commands and Generated Files.rst b/Help/guide/tutorial/Custom Commands and Generated Files.rst new file mode 100644 index 0000000..49c6c07 --- /dev/null +++ b/Help/guide/tutorial/Custom Commands and Generated Files.rst @@ -0,0 +1,279 @@ +Step 7: Custom Commands and Generated Files +=========================================== + +Code generation is a ubiquitous mechanism for extending programming languages +beyond the bounds of their language model. CMake has first-class support for +Qt's Meta-Object Compiler, but very few other code generators are notable +enough to warrant that kind of effort. + +Instead, code generators tend to be bespoke and usage specific. CMake provides +facilities for describing the usage of a code generator, so projects can +add support for their individual needs. + +In this step, we will use :command:`add_custom_command` to add support for a +code generator within the tutorial project. + +Background +^^^^^^^^^^ + +Any step in the build process can generally be described in terms of its inputs +and outputs. CMake assumes that code generators and other custom processes +operate on the same principle. In this way, the code generator acts identically +to compilers, linkers, and other elements of the toolchain; when the inputs are +newer than the outputs (or the outputs don't exist), a user-specified command +will be run to update the outputs. + +.. note:: + This model assumes the outputs of a process are known before it is run. CMake + lacks the ability to describe code generators where the name and location of + the outputs depends on the *content* of the input. Various hacks exist to + shim this functionality into CMake, but they are outside the scope of this + tutorial. + +Describing a code generator (or any custom process) is usually performed in +two parts. First, the inputs and outputs are described independently of the +CMake target model, concerned only with the generation process itself. Second, +the outputs are associated with a CMake target to insert them into the CMake +target model. + +For sources, this is as simple as adding the generated files to the source list +of a ``STATIC``, ``SHARED``, or ``OBJECT`` library. For header-only generators, +it's often necessary to use an intermediary target created via +:command:`add_custom_target` to add the header file generation to the +build stage (because ``INTERFACE`` libraries have no build step). + +Exercise 1 - Using a Code Generator +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The primary mechanism for describing a code generator is the +:command:`add_custom_command` command. A "command", for the purpose of +:command:`add_custom_command` is either an executable available in the build +environment or a CMake executable target name. + +.. code-block:: cmake + + add_executable(Tool) + # ... + add_custom_command( + OUTPUT Generated.cxx + COMMAND Tool -i input.txt -o Generated.cxx + DEPENDS Tool input.txt + VERBATIM + ) + # ... + add_library(GeneratedObject OBJECT) + target_sources(GeneratedObject + PRIVATE + Generated.cxx + ) + +Most of the keywords are self-explanatory, with the exception of ``VERBATIM``. +This argument is effectively mandatory for legacy reasons that are uninteresting +to explain in a modern context. The curious should consult the +:command:`add_custom_command` documentation for additional details. + +The ``Tool`` executable target appears both in the ``COMMAND`` and ``DEPENDS`` +parameters. While ``COMMAND`` is sufficient for the code to build correctly, +adding the ``Tool`` itself as a dependency of the custom command ensure that +if ``Tool`` is updated, the custom command will be rerun. + +For header-only file generation, additional commands are necessary because the +library itself has no build step. We can use :command:`add_custom_target` to +create an "artificial" build step for the library. We then force the custom +target to be run before any targets which link the library with the command +:command:`add_dependencies`. + +.. code-block:: cmake + + add_custom_target(RunGenerator DEPENDS Generated.h) + + add_library(GeneratedLib INTERFACE) + target_sources(GeneratedLib + INTERFACE + FILE_SET HEADERS + BASE_DIRS + ${CMAKE_CURRENT_BINARY_DIR} + FILES + ${CMAKE_CURRENT_BINARY_DIR}/Generated.h + ) + + add_dependencies(GeneratedLib RunGenerator) + +.. note:: + We add the :variable:`CMAKE_CURRENT_BINARY_DIR`, a variable which names the + current location in the build tree where our artifacts are being placed, to + the base directories because that's the working directory our code generator + will be run inside of. Listing the ``FILES`` is unnecessary for the build and + done so here only for clarity. + +Goal +---- + +Add a generated table of pre-computed square roots to the ``MathFunctions`` +library. + +Helpful Resources +----------------- + +* :command:`add_executable` +* :command:`add_library` +* :command:`target_sources` +* :command:`add_custom_command` +* :command:`add_custom_target` +* :command:`add_dependencies` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MakeTable/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.cxx`` + +Getting Started +--------------- + +The ``MathFunctions`` library has been edited to use a pre-computed table when +given a number less than 10. However, the hardcoded table is not particularly +accurate, containing only the nearest truncated integer value. + +The ``MakeTable.cxx`` source file describes a program which will generate a +better table. It takes a single argument as input, the file name of the table +to be generated. + +Complete ``TODO 1`` through ``TODO 10``. + +Build and Run +------------- + +No special configuration is needed, configure and build as usual. Note that +the ``MakeTable`` executable is sequenced before ``MathFunctions``. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +Verify the output of ``Tutorial`` now uses the pre-computed table for values +less than 10. + +Solution +-------- + +First we add a new executable to generate the tables, adding the +``MakeTable.cxx`` file as a source. + +.. raw:: html + +
TODO 1-2: Click to show/hide answer + +.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt + :caption: TODO 1-2: MathFunctions/MakeTable/CMakeLists.txt + :name: MathFunctions/MakeTable/CMakeLists.txt-add_executable + :language: cmake + :start-at: add_executable + :end-at: MakeTable.cxx + :append: ) + +.. raw:: html + +
+ +Then we add a custom command which produces the table, and custom target which +depends on the table. + +.. raw:: html + +
TODO 3-4: Click to show/hide answer + +.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt + :caption: TODO 3-4: MathFunctions/MakeTable/CMakeLists.txt + :name: MathFunctions/MakeTable/CMakeLists.txt-add_custom_command + :language: cmake + :start-at: add_custom_command + :end-at: add_custom_target + +.. raw:: html + +
+ +We need to add an interface library which describes the output which will +appear in :variable:`CMAKE_CURRENT_BINARY_DIR`. The ``FILES`` parameter is +optional. + +.. raw:: html + +
TODO 5-6: Click to show/hide answer + +.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt + :caption: TODO 5-6: MathFunctions/MakeTable/CMakeLists.txt + :name: MathFunctions/MakeTable/CMakeLists.txt-add_library + :language: cmake + :start-at: add_library + :end-at: SqrtTable.h + :append: ) + +.. raw:: html + +
+ +Now that all the targets are described, we can force the custom target to run +before any dependents of the interface library by associating them with +:command:`add_dependencies`. + +.. raw:: html + +
TODO 7: Click to show/hide answer + +.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt + :caption: TODO 7: MathFunctions/MakeTable/CMakeLists.txt + :name: MathFunctions/MakeTable/CMakeLists.txt-add_dependencies + :language: cmake + :start-at: add_dependencies + :end-at: add_dependencies + +.. raw:: html + +
+ +We are ready to add the interface library to the linked libraries of +``MathFunctions``, and add the entire ``MakeTable`` folder to the project. + +.. raw:: html + +
TODO 8-9: Click to show/hide answer + +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt + :caption: TODO 8: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-link-sqrttable + :language: cmake + :start-at: target_link_libraries(MathFunctions + :end-at: ) + +.. literalinclude:: Step8/MathFunctions/CMakeLists.txt + :caption: TODO 9: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add-maketable + :language: cmake + :start-at: add_subdirectory(MakeTable + :end-at: add_subdirectory(MakeTable + +.. raw:: html + +
+ +Finally, we update the ``MathFunctions`` library itself to take advantage of +the generated table. + +.. raw:: html + +
TODO 10: Click to show/hide answer + +.. literalinclude:: Step8/MathFunctions/MathFunctions.cxx + :caption: TODO 10: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-include-sqrttable + :language: c++ + :start-at: #include + :end-at: { + +.. raw:: html + +
diff --git a/Help/guide/tutorial/Finding Dependencies.rst b/Help/guide/tutorial/Finding Dependencies.rst new file mode 100644 index 0000000..ffd79c0 --- /dev/null +++ b/Help/guide/tutorial/Finding Dependencies.rst @@ -0,0 +1,529 @@ +Step 10: Finding Dependencies +============================= + +In C/C++ software development, managing build dependencies is consistently +one of the highest ranked challenges facing modern developers. CMake provides +an extensive toolset for discovering and validating dependencies of different +kinds. + +However, for correctly packaged projects there is no need to use these advanced +tools. Many popular library and utility projects today produce correct install +trees, like the one we set up in ``Step 9``, which are easy is to integrate +into CMake. + +In this best-case scenario, we only need the :command:`find_package` to +import dependencies into our project. + +Background +^^^^^^^^^^ + +There are five principle commands used for discovering dependencies with +CMake, the first four are: + + :command:`find_file` + Finds and reports the full path to a named file, this tends to be the + most flexible of the ``find`` commands. + + :command:`find_library` + Finds and reports the full path to a static archive or shared object + suitable for use with :command:`target_link_libraries`. + + :command:`find_path` + Finds and reports the full path to a directory *containing* a file. This + is most commonly used for headers in combination with + :command:`target_include_directories`. + + :command:`find_program` + Finds and reports and invocable name or path for a program. Often used in + combination with :command:`execute_process` or :command:`add_custom_command`. + +These commands should be considered "backup", used when the primary find command +is unsuitable. The primary find command is :command:`find_package`. It uses +comprehensive built-in heuristics and upstream-provided packaging files to +provide the best interface to the requested dependency. + +Exercise 1 - Using ``find_package()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The search paths and behaviors used by :command:`find_package` are fully +described in its documentation, but much too verbose to replicate here. Suffice +to say it searches well known, lesser known, obscure, and user-provided +locations attempting to find a package which meets the requirements given to it. + +.. code-block:: cmake + + find_package(ForeignLibrary) + +The best way to use :command:`find_package` is to ensure all dependencies have +been installed to a single install tree prior to the build, and then make the +location of that install tree known to :command:`find_package` via the +:variable:`CMAKE_PREFIX_PATH` variable. + +.. note:: + Building and installing dependencies can itself be an immense amount of labor. + While this tutorial will do so for illustration purposes, it is **extremely** + recommended that a package manager be used for project-local dependency + management. + +:command:`find_package` accepts several parameters besides the package to be +found. The most notable are: + +* A positional ```` argument, for describing a version to be checked + against the package's config version file. This should be used sparingly, + it is better to control the version of the dependency being installed via + a package manager than possibly break the build on otherwise innocuous + version updates. + + If the package is known to rely on an older version of a dependency, it + may be appropriate to use a version requirement. + +* ``REQUIRED`` for non-optional dependencies which should abort the build + if not found. + +* ``QUIET`` for optional dependencies which should not report anything to + users when not found. + +:command:`find_package` reports its results via ``_FOUND`` +variables, which will be set to a true or false value for found and not found +packages respectively. + +Goal +---- + +Integrate an externally installed test framework into the Tutorial project. + +Helpful Resources +----------------- + +* :command:`find_package` +* :command:`target_link_libraries` + +Files to Edit +------------- + +* ``TutorialProject/CMakePresets.json`` +* ``TutorialProject/Tests/CMakeLists.txt`` +* ``TutorialProject/Tests/TestMathFunctions.cxx`` + +Getting Started +--------------- + +The ``Step10`` folder is organized differently than previous steps. The tutorial +project we need to edit is under ``Step10/TutorialProject``. Another project +is now present, ``SimpleTest``, as well as a partially populated install tree +which we will use in later exercises. You do not need to edit anything in these +other directories for this exercise, all ``TODOs`` and solution steps are for +``TutorialProject``. + +The ``SimpleTest`` package provides two useful constructs, the +``SimpleTest::SimpleTest`` target to be linked into a test binary, and the +``simpletest_discover_tests`` function for automatically adding tests to +CTest. + +Similar to other test frameworks, ``simpletest_discover_tests`` only needs +to be passed the name of the executable target containing the tests. + +.. code-block:: cmake + + simpletest_discover_tests(MyTestExe) + +The ``TestMathFunctions.cxx`` file has been updated to use the ``SimpleTest`` +framework in the vein of GoogleTest or Catch2. Perform ``TODO 1`` through +``TODO 5`` in order to use the new test framework. + +.. note:: + It may go without saying, but ``SimpleTest`` is a very poor test framework + which only facially resembles a functional testing library. While much of + the CMake code in this tutorial could be used unaltered in other projects, + you should not use ``SimpleTest`` outside this tutorial, or try to learn from + the CMake code it provides. + +Build and Run +------------- + +First we must install the ``SimpleTest`` framework. Navigate to the +``Help/guide/Step10/SimpleTest`` directory and run the following commands + +.. code-block:: console + + cmake --preset tutorial + cmake --install build + +.. note:: + The ``SimpleTest`` preset sets up everything needed to install ``SimpleTest`` + for the tutorial. For reasons that are beyond the scope of this tutorial, + there is no need to build or provide any other configuration for + ``SimpleTest``. + +We can observe that the ``Step10/install`` directory has now been populated by +the ``SimpleTest`` header and package files. + +Now we can configure and build the Tutorial project as per usual, navigating to +the ``Help/guide/Step10/TutorialProject`` and running: + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +Verify that the ``SimpleTest`` framework has been consumed correctly by running +the tests with CTest. + +Solution +-------- + +First we call :command:`find_package` to discover the ``SimpleTest`` package. +We do this with ``REQUIRED`` because the tests cannot build without +``SimpleTest``. + +.. raw:: html + +
TODO 1 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tests/CMakeLists.txt + :caption: TODO 1: TutorialProject/Tests/CMakeLists.txt + :name: TutorialProject/Tests/CMakeLists.txt-find_package + :language: cmake + :start-at: find_package + :end-at: find_package + +.. raw:: html + +
+ +Next we add the ``SimpleTest::SimpleTest`` target to ``TestMathFunctions`` + +.. raw:: html + +
TODO 2 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tests/CMakeLists.txt + :caption: TODO 2: TutorialProject/Tests/CMakeLists.txt + :name: TutorialProject/Tests/CMakeLists.txt-link-simple-test + :language: cmake + :start-at: target_link_libraries(TestMathFunctions + :end-at: ) + +.. raw:: html + +
+ +Now we can replace our test description code with a call to +``simpletest_discover_tests``. + +.. raw:: html + +
TODO 3 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tests/CMakeLists.txt + :caption: TODO 3: TutorialProject/Tests/CMakeLists.txt + :name: TutorialProject/Tests/CMakeLists.txt-simpletest_discover_tests + :language: cmake + :start-at: simpletest_discover_tests + :end-at: simpletest_discover_tests + +.. raw:: html + +
+ +We ensure :command:`find_package` can discover ``SimpleTest`` by +adding the install tree to :variable:`CMAKE_PREFIX_PATH`. + +.. raw:: html + +
TODO 4 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/CMakePresets.json + :caption: TODO 4: TutorialProject/CMakePresets.json + :name: TutorialProject/CMakePresets.json-CMAKE_PREFIX_PATH + :language: json + :start-at: cacheVariables + :end-at: TUTORIAL_ENABLE_IPO + :dedent: 6 + :append: } + +.. raw:: html + +
+ +Finally, we update the tests to use the macros provided by ``SimpleTest`` by +removing the placeholders and including the appropriate header. + +.. raw:: html + +
TODO 5 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tests/TestMathFunctions.cxx + :caption: TODO 5: TutorialProject/Tests/TestMathFunctions.cxx + :name: TutorialProject/Tests/TestMathFunctions.cxx-simpletest + :language: c++ + :start-at: #include + :end-at: { + +.. raw:: html + +
+ +Exercise 2 - Transitive Dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Libraries often build on one another. A multimedia application may depend on a +library which provides support for various container formats, which may in turn +rely on one or more other libraries for compression algorithms. + +We need to express these transitive requirements inside the package config +files we place in the install tree. We do so with the +:module:`CMakeFindDependencyMacro` module, which provides a safe mechanism for +installed packages to recursively discover one another. + +.. code-block:: cmake + + include(CMakeFindDependencyMacro) + find_dependency(zlib) + +:module:`find_dependency() ` also forwards arguments +from the top-level :command:`find_package` call. If :command:`find_package` is +called with ``QUIET`` or ``REQUIRED``, +:module:`find_dependency() ` will also use ``QUIET`` +and/or ``REQUIRED``. + +Goal +---- + +Add a dependency to ``SimpleTest`` and ensure that packages which rely on +``SimpleTest`` also discover this transitive dependency. + +Helpful Resources +----------------- + +* :module:`CMakeFindDependencyMacro` +* :command:`find_package` +* :command:`target_link_libraries` + +Files to Edit +------------- + +* ``SimpleTest/CMakeLists.txt`` +* ``SimpleTest/cmake/SimpleTestConfig.cmake`` + +Getting Started +--------------- + +For this step we will only be editing the ``SimpleTest`` project. The transitive +dependency, ``TransitiveDep``, is a dummy dependency which provides no behavior. +However CMake doesn't know this and the ``TutorialProject`` tests will fail to +configure and build if CMake cannot find all required dependencies. + +The ``TransitiveDep`` package has already been installed to the +``Step10/install`` tree. We do not need to install it as we did with +``SimpleTest``. + +Complete ``TODO 6`` through ``TODO 8``. + +Build and Run +------------- + +We need to reinstall the SimpleTest framework. Navigate to the +``Help/guide/Step10/SimpleTest`` directory and run the same commands as before. + +.. code-block:: console + + cmake --preset tutorial + cmake --install build + +Now we can reconfigure and rebuild the ``TutorialProject``, navigate to +``Help/guide/Step10/TutorialProject`` and perform the usual steps to do so. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +If the build passed we have likely successfully propagated the transitive +dependency. Verify this by searching the ``CMakeCache.txt`` of +``TutorialProject`` for an entry named ``TransitiveDep_DIR``. This demonstrates +the ``TutorialProject`` searched for an found ``TransitiveDep`` even though it +has no direct requirement for it. + +Solution +-------- + +First we call :command:`find_package` to discover the ``TransitiveDep`` package. +We use ``REQUIRED`` to verify we have found ``TransitiveDep``. + +.. raw:: html + +
TODO 6 Click to show/hide answer + +.. literalinclude:: Step11/SimpleTest/CMakeLists.txt + :caption: TODO 6: SimpleTest/CMakeLists.txt + :name: SimpleTest/CMakeLists.txt-find_package + :language: cmake + :start-at: find_package + :end-at: find_package + +.. raw:: html + +
+ +Next we add the ``TransitiveDep::TransitiveDep`` target to ``SimpleTest``. + +.. raw:: html + +
TODO 7 Click to show/hide answer + +.. literalinclude:: Step11/SimpleTest/CMakeLists.txt + :caption: TODO 7: SimpleTest/CMakeLists.txt + :name: SimpleTest/CMakeLists.txt-link-transitive-dep + :language: cmake + :start-at: target_link_libraries(SimpleTest + :end-at: ) + +.. raw:: html + +
+ +.. note:: + If we built ``TutorialProject`` at this point, we would expect the + configuration to fail due to the ``TransitiveDep::TransitiveDep`` target + being unavailable inside that project. + +Finally, we include the :module:`CMakeFindDependencyMacro` and call +:module:`find_dependency() ` inside the ``SimpleTest`` +package config file to propagate the transitive dependency. + +.. raw:: html + +
TODO 8 Click to show/hide answer + +.. literalinclude:: Step11/SimpleTest/cmake/SimpleTestConfig.cmake + :caption: TODO 8: SimpleTest/cmake/SimpleTestConfig.cmake + :name: SimpleTest/cmake/SimpleTestConfig.cmake-find_dependency + :language: cmake + :start-at: include + :end-at: find_dependency + +.. raw:: html + +
+ + + +Exercise 3 - Finding Other Kinds of Files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In a perfect world every dependency we care about would be packaged correctly, +or at least some other developer would have written a module that discovers it +for us. We do no live in a perfect world, and sometimes we will have to get +our hands dirty and discover build requirements manually. + +For this we have the other find commands enumerated earlier in the step, such +as :command:`find_path`. + +.. code-block:: cmake + + find_path(PackageIncludeFolder Package.h REQUIRED + PATH_SUFFIXES + Package + ) + target_include_directories(MyApp + PRIVATE + ${PackageIncludeFolder} + ) + +Goal +---- + +Add an unpackaged header to the ``Tutorial`` executable of the +``TutorialProject``. + +Helpful Resources +----------------- + +* :command:`find_path` +* :command:`target_include_directories` + +Files to Edit +------------- + +* ``TutorialProject/Tutorial/CMakeLists.txt`` +* ``TutorialProject/Tutorial/Tutorial.cxx`` + +Getting Started +--------------- + +For this step we will only be editing the ``TutorialProject`` project. The +unpackaged header, ``Unpackaged/Unpackaged.h`` has already been installed to the +``Step10/install`` tree. + +Complete ``TODO 9`` through ``TODO 11``. + +Build and Run +------------- + +There are no special build steps for this exercise, navigate to +``Help/guide/Step10/TutorialProject`` and perform the usual build. + +.. code-block:: console + + cmake --build build + +If the build passed we have successfully added the ``Unpackaged`` include +directory to the project. + +Solution +-------- + +First we call :command:`find_path` to discover the ``Unpackaged`` include +directory. We use ``REQUIRED`` because building ``Tutorial`` will fail if +we cannot locate the ``Unpackaged.h`` header. + +.. raw:: html + +
TODO 9 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tutorial/CMakeLists.txt + :caption: TODO 9: TutorialProject/Tutorial/CMakeLists.txt + :name: TutorialProject/Tutorial/CMakeLists.txt-find_path + :language: cmake + :start-at: find_path + :end-at: ) + +.. raw:: html + +
+ +Next we add the discovered path to ``Tutorial`` using +:command:`target_include_directories`. + +.. raw:: html + +
TODO 10 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tutorial/CMakeLists.txt + :caption: TODO 10: TutorialProject/Tutorial/CMakeLists.txt + :name: TutorialProject/Tutorial/CMakeLists.txt-target_include_directories + :language: cmake + :start-at: target_include_directories + :end-at: ) + +.. raw:: html + +
+ +Finally, we edit ``Tutorial.cxx`` to include the discovered header. + +.. raw:: html + +
TODO 11 Click to show/hide answer + +.. literalinclude:: Step11/TutorialProject/Tutorial/Tutorial.cxx + :caption: TODO 11: TutorialProject/Tutorial/Tutorial.cxx + :name: TutorialProject/Tutorial/Tutorial.cxx-include-unpackaged + :language: c++ + :start-at: #include + :end-at: #include + +.. raw:: html + +
diff --git a/Help/guide/tutorial/Getting Started with CMake.rst b/Help/guide/tutorial/Getting Started with CMake.rst new file mode 100644 index 0000000..ae78dc6 --- /dev/null +++ b/Help/guide/tutorial/Getting Started with CMake.rst @@ -0,0 +1,805 @@ +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 `. +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 ` executable or the +:manual:`cmake-gui ` 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 ` 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 `, passing it the same relative path +we did with the :option:`-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 + +
TODO 1: Click to show/hide answer + +.. 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 + +
+ +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 + +
TODO 2: Click to show/hide answer + +.. literalinclude:: Step3/CMakeLists.txt + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-project + :language: cmake + :start-at: project + :end-at: project + +.. raw:: html + +
+ +Now we can setup our executable target for the Tutorial with :command:`add_executable`. + +.. raw:: html + +
TODO 3: Click to show/hide answer + +.. 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 + +
+ +Finally, we can associate our source file with the Tutorial executable target +using :command:`target_sources`. + +.. raw:: html + +
TODO 4: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 4: CMakeLists.txt + :name: CMakeLists.txt-target_sources + + target_sources(Tutorial + PRIVATE + Tutorial/Tutorial.cxx + ) + + +.. raw:: html + +
+ +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 ` 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 `` is the name of the ``FILE_SET``. This is a handle which + we can use to describe the collection in other contexts. + +* ``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 + +
TODO 5: Click to show/hide answer + +.. 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 + +
+ +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 `` 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 `` or similar. + +.. raw:: html + +
TODO 6: Click to show/hide answer + +.. 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 + +
+ +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 `. +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 + +
TODO 7: Click to show/hide answer + +.. 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 + +
+ +.. 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 + +
TODO 8-9: Click to show/hide answer + +.. 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 + +
+ +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 ` 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 + +
TODO 10-11: Click to show/hide answer + +.. 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 + +
+ +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 + +
TODO 12-13: Click to show/hide answer + +.. 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 + +
diff --git a/Help/guide/tutorial/In-Depth CMake Library Concepts.rst b/Help/guide/tutorial/In-Depth CMake Library Concepts.rst new file mode 100644 index 0000000..e6c15e6 --- /dev/null +++ b/Help/guide/tutorial/In-Depth CMake Library Concepts.rst @@ -0,0 +1,446 @@ +Step 5: In-Depth CMake Library Concepts +======================================= + +While executables are mostly one-size-fits-all, libraries come in many +different forms. There are static archives, shared objects, modules, +object libraries, header-only libraries, and libraries which describe advanced +CMake properties to be inherited by other targets, just to name a few. + +In this step you will learn about some of the most common kinds of libraries +that CMake can describe. This will cover most of the in-project uses of +:command:`add_library`. Libraries which are imported from dependencies (or +exported by the project to be consumed as a dependency) will be covered in +later steps. + +Background +^^^^^^^^^^ + +As we learned in ``Step1``, the :command:`add_library` command accepts the name +of the library target to be created as its first argument. The second +argument is an optional ```` for which the following values are valid: + + ``STATIC`` + A :ref:`Static Library `: + an archive of object files for use when linking other targets. + + ``SHARED`` + A :ref:`Shared Library `: + a dynamic library that may be linked by other targets and loaded + at runtime. + + ``MODULE`` + A :ref:`Module Library `: + a plugin that may not be linked by other targets, but may be + dynamically loaded at runtime using dlopen-like functionality. + + ``OBJECT`` + An :ref:`Object Library `: + a collection of object files which have not been archived or linked + into a library. + + ``INTERFACE`` + An :ref:`Interface Library `: + a library target which specifies usage requirements for dependents but + does not compile sources and does not produce a library artifact on disk. + +In addition, there are ``IMPORTED`` libraries which describe library targets +from foreign projects or modules, imported into the current project. We will +cover these briefly in later steps. + +``MODULE`` libraries are most commonly found in plugin systems, or as extensions +to runtime-loading languages like Python or Javascript. They act very similar to +normal shared libraries, except they cannot be directly linked by other targets. +They are sufficiently similar that we won't cover them in further depth here. + +Exercise 1 - Static and Shared +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While the :command:`add_library` command supports explicitly setting ``STATIC`` +or ``SHARED``, and this is sometimes necessary, it is best to leave the second +argument empty for most "normal" libraries which can operate as either. + +When not given a type, :command:`add_library` will create either a ``STATIC`` +or ``SHARED`` library depending on the value of :variable:`BUILD_SHARED_LIBS`. +If :variable:`BUILD_SHARED_LIBS` is true, a ``SHARED`` library will be created, +otherwise it will be ``STATIC``. + +.. code-block:: cmake + + add_library(MyLib-static STATIC) + add_library(MyLib-shared SHARED) + + # Depends on BUILD_SHARED_LIBRARY + add_library(MyLib) + +This is desirable behavior, as it allows packagers to determine what kind of +library will be produced, and ensure dependents link to that version of the +library without needing to modify their source code. In some contexts, fully +static builds are appropriate, and in others shared libraries are desirable. + +.. note:: + CMake does not define the :variable:`BUILD_SHARED_LIBS` variable by default, + meaning without project or user intervention :command:`add_library` will + produce ``STATIC`` libraries. + +By leaving the second argument to :command:`add_library()` blank, projects +provide additional flexibility to their packagers and downstream dependents. + +Goal +---- + +Build ``MathFunctions`` as a shared library. + +.. note:: + On Windows, you might see warnings about an empty DLL, as ``MathFunctions`` + doesn't export any symbols. + +Helpful Resources +----------------- + +* :variable:`BUILD_SHARED_LIBS` + +Files to Edit +------------- + +There are no files to edit. + +Getting Started +--------------- + +The ``Help/guide/tutorial/Step5`` directory contains the complete, recommended +solution to ``Step4``. This step is about building the ``MathFunctions`` +library, there are no ``TODOs`` necessary. You can proceed directly to the +build step. + +Build and Run +------------- + +We can configure using our preset, turning on :variable:`BUILD_SHARED_LIBS` with +a :option:`-D ` flag. + +.. code-block:: console + + cmake --preset tutorial -DBUILD_SHARED_LIBS=ON + +Then we can build only the ``MathFunctions`` library with +:option:`-t `. + +.. code-block:: console + + cmake --build build -t MathFunctions + +Verify a shared library is produced for ``MathFunctions`` then reset +:variable:`BUILD_SHARED_LIBS`, either by reconfiguring with +``-DBUILD_SHARED_LIBS=OFF`` or deleting the ``CMakeCache.txt``. + +Solution +-------- + +There are no changes to the project for this exercise. + +Exercise 2 - Interface Libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Interface libraries are those which only communicate usage requirements for +other targets, they do not build or produce any artifacts of their own. As such +all the properties of an interface library must themselves be interface +properties, specified with the ``INTERFACE`` :ref:`scope keywords `. + +.. code-block:: cmake + + add_library(MyInterface INTERFACE) + target_compile_definitions(MyInterface INTERFACE MYINTERFACE_COMPILE_DEF) + +The most common kind of interface library in C++ development is a header-only +library. Such libraries do not build anything, only providing the flags +necessary to discover their headers. + +Goal +---- + +Add a header-only library to the tutorial project, and use it inside the +``Tutorial`` executable. + +Helpful Resources +----------------- + +* :command:`add_library` +* :command:`target_sources` + +Files to Edit +------------- + +* ``MathFunctions/MathLogger/CMakeLists.txt`` +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.cxx`` + +Getting Started +--------------- + +In our previous discussions of :command:`target_sources(FILE_SET)`, we noted +we can omit the ``TYPE`` parameter if the file set's name is the same as the +file set's type. We also said we can omit the ``BASE_DIRS`` parameter if +we want to use the current source directory as the only base directory. + +We're ready to introduce a third shortcut, we only need to include the ``FILES`` +parameter if the headers are intended to be installed, such as public headers +of a library. + +The ``MathLogger`` headers in this exercise are only used internally by the +``MathFunctions`` implementation. They will not be installed. This should +make for a very abbreviated call to :command:`target_sources(FILE_SET)`. + +.. note:: + The headers will be discovered by the compiler's dependency scanner to ensure + correct incremental builds. It can be useful to list header files in these + contexts anyway, as the list can be used to generate metadata some IDEs + rely on. + +You can begin editing the ``Step5`` directory. Complete ``TODO 1`` through +``TODO 7``. + +Build and Run +------------- + +The preset has already been updated to use ``mathfunctions::sqrt`` instead of +``std::sqrt``. We can build and configure as usual. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +Verify that the ``Tutorial`` output now uses the logging framework. + +Solution +-------- + +First we add a new ``INTERFACE`` library named ``MathLogger``. + +.. raw:: html + +
TODO 1: Click to show/hide answer + +.. literalinclude:: Step6/MathFunctions/MathLogger/CMakeLists.txt + :caption: TODO 1: MathFunctions/MathLogger/CMakeLists.txt + :name: MathFunctions/MathLogger/CMakeLists.txt-add_library + :language: cmake + :start-at: add_library + :end-at: add_library + +.. raw:: html + +
+ +Then we add the appropriate :command:`target_sources` call to capture the +header information. We give this file set the name ``HEADERS`` so we can +omit the ``TYPE``, we don't need ``BASE_DIRS`` as we will use the default +of the current source directory, and we can exclude the ``FILES`` list because +we don't intend to install the library. + +.. raw:: html + +
TODO 2: Click to show/hide answer + +.. literalinclude:: Step6/MathFunctions/MathLogger/CMakeLists.txt + :caption: TODO 2: MathFunctions/MathLogger/CMakeLists.txt + :name: MathFunctions/MathLogger/CMakeLists.txt-target_sources + :language: cmake + :start-at: target_sources( + :end-at: ) + +.. raw:: html + +
+ +Now we can add the ``MathLogger`` library to the ``MathFunctions`` linked +libraries, and at the ``MathLogger`` folder to the project. + +.. raw:: html + +
TODO 3-4: Click to show/hide answer + +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :caption: TODO 3: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-link-mathlogger + :language: cmake + :start-at: target_link_libraries( + :end-at: MathLogger + :append: ) + +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :caption: TODO 4: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add-mathlogger + :language: cmake + :start-at: add_subdirectory(MathLogger + :end-at: add_subdirectory(MathLogger + +.. raw:: html + +
+ +Finally we can update ``MathFunctions.cxx`` to take advantage of the new logger. + +.. raw:: html + +
TODO 5-7: Click to show/hide answer + +.. literalinclude:: Step6/MathFunctions/MathFunctions.cxx + :caption: TODO 5: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-mathlogger-header + :language: c++ + :start-at: cmath + :end-at: MathLogger + +.. literalinclude:: Step6/MathFunctions/MathFunctions.cxx + :caption: TODO 6: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-mathlogger-logger + :language: c++ + :start-at: mathlogger::Logger Logger + :end-at: mathlogger::Logger Logger + +.. literalinclude:: Step6/MathFunctions/MathFunctions.cxx + :caption: TODO 7: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-mathlogger-code + :language: c++ + :start-at: Logger.Log(std::format("Computing sqrt of {} to be {}\n" + :end-at: std::format + :dedent: 4 + +.. raw:: html + +
+ +Exercise 3 - Object Libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Object libraries have several advanced uses, but also tricky nuances which +are difficult to fully enumerate in the scope of this tutorial. + +.. code-block:: cmake + + add_library(MyObjects OBJECT) + +The most obvious drawback to object libraries is the objects themselves cannot +be transitively linked. If an object library appears in the +:prop_tgt:`INTERFACE_LINK_LIBRARIES` of a target, the dependents which link that +target will not "see" the objects. The object library will act like an +``INTERFACE`` library in such contexts. In the general case, object libraries +are only suitable for ``PRIVATE`` or ``PUBLIC`` consumption via +:command:`target_link_libraries`. + +A common use case for object libraries is coalescing several library targets +into a single archive or shared library object. Even within a single project +libraries may be maintained as different targets for a variety of reasons, such +as belonging to different teams within an organization. However, it may be +desirable to distribute these as a single consumer-facing binary. Object +libraries make this possible. + +Goal +---- + +Add several object libraries to the ``MathFunctions`` library. + +Helpful Resources +----------------- + +* :command:`target_link_libraries` +* :command:`add_subdirectory` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.h`` +* ``Tutorial/Tutorial.cxx`` + +Getting Started +--------------- + +Several extensions for our ``MathFunctions`` library have been made available +(we can imagine these coming from other teams in our organization). Take +a minute to look at the targets made available in ``MathFunctions/MathExtensions``. +Then complete ``TODO 8`` through ``TODO 11``. + +Build and Run +------------- + +There's no reconfiguration needed, we can build as usual. + +.. code-block:: console + + cmake --build build + +Verify the output of ``Tutorial`` now includes the verification message. Also +take a minute to inspect the build directory under +``build/MathFunctions/MathExtensions``. You should find that, unlike +``MathFunctions``, no archives are produced for any of the object libraries. + +Solution +-------- + +First we will add links for all the object libraries to ``MathFunctions``. +These are ``PUBLIC``, because we want the objects to be added to the +``MathFunctions`` library as part of its own build step, and we want the +headers to be available to consumers of the library. + +Then we add the ``MathExtensions`` subdirectoy to the project. + +.. raw:: html + +
TODO 8-9: Click to show/hide answer + +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :caption: TODO 8: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-link-objects + :language: cmake + :start-at: target_link_libraries( + :end-at: ) + +.. literalinclude:: Step6/MathFunctions/CMakeLists.txt + :caption: TODO 9: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-add-objs + :language: cmake + :start-at: add_subdirectory(MathExtensions + :end-at: add_subdirectory(MathExtensions + +.. raw:: html + +
+ + +To make the extensions available to consumers, we include their headers in the +``MathFunctions.h`` header. + +.. raw:: html + +
TODO 10: Click to show/hide answer + +.. literalinclude:: Step6/MathFunctions/MathFunctions.h + :caption: TODO 10: MathFunctions/MathFunctions.h + :name: MathFunctions/MathFunctions.h-include-objects + :language: c++ + :start-at: OpAdd + :end-at: OpSub + +.. raw:: html + +
+ +Finally we can take advantage of the extensions in the ``Tutorial`` program. + +.. raw:: html + +
TODO 11: Click to show/hide answer + +.. literalinclude:: Step6/Tutorial/Tutorial.cxx + :caption: TODO 11: Tutorial/Tutorial.cxx + :name: Tutorial/Tutorial.cxx-use-objects + :language: c++ + :start-at: OpMul + :end-at: checkValue); + :dedent: 2 + +.. raw:: html + +
diff --git a/Help/guide/tutorial/In-Depth CMake Target Commands.rst b/Help/guide/tutorial/In-Depth CMake Target Commands.rst new file mode 100644 index 0000000..f78a48f --- /dev/null +++ b/Help/guide/tutorial/In-Depth CMake Target Commands.rst @@ -0,0 +1,532 @@ +Step 4: In-Depth CMake Target Commands +====================================== + +There are several target commands within CMake we can use to describe +requirements. As a reminder, a target command is one which modifies the +properties of the target it is applied to. These properties describe +requirements needed to build the software, such as sources, compile flags, +and output names; or properties necessary to consume the target, such as header +includes, library directories, and linkage rules. + +.. note:: + As discussed in ``Step1``, properties required to build a target should be + described with the ``PRIVATE`` :ref:`scope keyword `, + those required to consume the target with ``INTERFACE``, and properties needed + for both are described with ``PUBLIC``. + +In this step we will go over all the available target commands in CMake. Not all +target commands are created equal. We have already discussed the two most +important target commands, :command:`target_sources` and +:command:`target_link_libraries`. Of the remaining commands, some are almost +as common as these two, others have more advanced applications, and a couple +should only be used as a last resort when other options are not available. + +Background +^^^^^^^^^^ + +Before going any further, let's name all of the CMake target commands. We'll +split these into three groups: the recommended and generally useful commands, +the advanced and cautionary commands, and the "footgun" commands which should +be avoided unless necessary. + ++-----------------------------------------+--------------------------------------+---------------------------------------+ +| Common/Recommended | Advanced/Caution | Esoteric/Footguns | ++=========================================+======================================+=======================================+ +| :command:`target_compile_definitions` | :command:`get_target_property` | :command:`target_include_directories` | +| :command:`target_compile_features` | :command:`set_target_properties` | :command:`target_link_directories` | +| :command:`target_link_libraries` | :command:`target_compile_options` | | +| :command:`target_sources` | :command:`target_link_options` | | +| | :command:`target_precompile_headers` | | ++-----------------------------------------+--------------------------------------+---------------------------------------+ + +.. note:: + There's no such thing as a "bad" CMake target command. They all have valid + use cases. This categorization is provided to give newcomers a simple + intuition about which commands they should consider first when tackling + a problem. + +We'll demonstrate most of these in the following exercises. The three we won't +be using are :command:`get_target_property`, :command:`set_target_properties` +and :command:`target_precompile_headers`, so we will briefly discuss their +purpose here. + +The :command:`get_target_property` and :command:`set_target_properties` commands +give direct access to a target's properties by name. They can even be used +to attach arbitrary property names to a target. + +.. code-block:: cmake + + add_library(Example) + set_target_properties(Example + PROPERTIES + Key Value + Hello World + ) + + get_target_property(KeyVar Example Key) + get_target_property(HelloVar Example Hello) + + message("Key: ${KeyVar}") + message("Hello: ${HelloVar}") + +.. code-block:: console + + $ cmake -B build + ... + Key: Value + Hello: World + +The full list of target properties which are semantically meaningful to CMake +are documented at :manual:`cmake-properties(7)`, however most of these should +be modified with their dedicated commands. For example, it is unnecessary to +directly manipulate ``LINK_LIBRARIES`` and ``INTERFACE_LINK_LIBRARIES``, as +these are handled by :command:`target_link_libraries`. + +Conversely, some lesser-used properties are only accessible via these commands. +The :prop_tgt:`DEPRECATION` property, used to attach deprecation notices to +targets, can only be set via :command:`set_target_properties`; as can the +:prop_tgt:`ADDITIONAL_CLEAN_FILES`, for describing additional files to be +removed by CMake's ``clean`` target; and other properties of this sort. + +The :command:`target_precompile_headers` command takes a list of header files, +similar to :command:`target_sources`, and creates a precompiled header from +them. This precompiled header is then force included into all translation +units in the target. This can be useful for build performance. + +Exercise 1 - Features and Definitions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In earlier steps we cautioned against globally setting +:variable:`CMAKE__STANDARD` and overriding packagers' decision concerning +which language standard to use. On the other hand, many libraries have a +minimum required feature set they need in order to build, and for these it +is appropriate to use the :command:`target_compile_features` command to +communicate those requirements. + +.. code-block:: cmake + + target_compile_features(MyApp PRIVATE cxx_std_20) + +The :command:`target_compile_features` command describes a minimum language +standard as a target property. If the :variable:`CMAKE__STANDARD` is above +this version, or the compiler default already provides this language standard, +no action is taken. If additional flags are necessary to enable the standard, +these will be added by CMake. + +.. note:: + :command:`target_compile_features` manipulates the same style of interface and + non-interface properties as the other target commands. This means it is + possible to *inherit* a language standard requirement specified with + ``INTERFACE`` or ``PUBLIC`` scope keywords. + + If language features are used only in implementation files, then the + respective compile features should be ``PRIVATE``. If the target's headers + use the features, then ``PUBLIC`` or ``INTERFACE`` should be used. + +For C++, the compile features are of the form ``cxx_std_YY`` where ``YY`` is +the standardization year, e.g. ``14``, ``17``, ``20``, etc. + +The :command:`target_compile_definitions` command describes compile definitions +as target properties. It is the most common mechanism for communicating build +configuration information to the source code itself. As with all properties, +the scope keywords apply as we have discussed. + +.. code-block:: cmake + + target_compile_definitions(MyLibrary + PRIVATE + MYLIBRARY_USE_EXPERIMENTAL_IMPLEMENTATION + + PUBLIC + MYLIBRARY_EXCLUDE_DEPRECATED_FUNCTIONS + ) + +It is neither required nor desired that we attach ``-D`` prefixes to compile +definitions described with :command:`target_compile_definitions`. CMake will +determine the correct flag for the current compiler. + +Goal +---- + +Use :command:`target_compile_features` and :command:`target_compile_definitions` +to communicate language standard and compile definition requirements. + +Helpful Resources +----------------- + +* :command:`target_compile_features` +* :command:`target_compile_definitions` +* :command:`option` +* :command:`if` + +Files to Edit +------------- + +* ``Tutorial/CMakeLists.txt`` +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.cxx`` +* ``CMakePresets.json`` + +Getting Started +--------------- + +The ``Help/guide/tutorial/Step4`` directory contains the complete, recommended +solution to ``Step3`` and relevant ``TODOs`` for this step. Complete ``TODO 1`` +through ``TODO 8``. + +Build and Run +------------- + +We can run CMake using our ``tutorial`` preset, and then build as usual. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +Verify that the output of ``Tutorial`` is what we would expect for ``std::sqrt``. + +Solution +-------- + +First we add a new option to the top-level CML. + +.. raw:: html + +
TODO 1: Click to show/hide answer + +.. literalinclude:: Step5/CMakeLists.txt + :caption: TODO 1: CMakeLists.txt + :name: CMakeLists.txt-TUTORIAL_USE_STD_SQRT + :language: cmake + :start-at: option(TUTORIAL_BUILD_UTILITIES + :end-at: option(TUTORIAL_USE_STD_SQRT + +.. raw:: html + +
+ +Then we add the compile feature and definitions to ``MathFunctions``. + +.. raw:: html + +
TODO 2-3: Click to show/hide answer + +.. literalinclude:: Step5/MathFunctions/CMakeLists.txt + :caption: TODO 2-3: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-target_compile_features + :language: cmake + :start-at: target_compile_features + :end-at: endif() + +.. raw:: html + +
+ +And the compile feature for ``Tutorial``. + +.. raw:: html + +
TODO 4: Click to show/hide answer + +.. literalinclude:: Step5/Tutorial/CMakeLists.txt + :caption: TODO 4: Tutorial/CMakeLists.txt + :name: Tutorial/CMakeLists.txt-target_compile_features + :language: cmake + :start-at: target_compile_features + :end-at: target_compile_features + +.. raw:: html + +
+ +Now we can modify ``MathFunctions`` to take advantage of the new definition. + +.. raw:: html + +
TODO 5-6: Click to show/hide answer + +.. literalinclude:: Step5/MathFunctions/MathFunctions.cxx + :caption: TODO 5: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-cmath + :language: c++ + :start-at: cmath + :end-at: format + :append: #include + +.. literalinclude:: Step5/MathFunctions/MathFunctions.cxx + :caption: TODO 6: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-std-sqrt + :language: c++ + :start-at: double sqrt(double x) + :end-at: } + +.. raw:: html + +
+ +Finally we can update our ``CMakePresets.json``. We don't need to set +``CMAKE_CXX_STANDARD`` anymore, but we do want to try out our new +compile definition. + +.. raw:: html + +
TODO 7-8: Click to show/hide answer + +.. code-block:: json + :caption: TODO 7-8: CMakePresets.json + :name: CMakePresets.json-std-sqrt + + "cacheVariables": { + "TUTORIAL_USE_STD_SQRT": "ON" + } + +.. raw:: html + +
+ +Exercise 2 - Compile and Link Options +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes, we need to exercise specific control over the exact options being +passed on the compile and link line. These situations are addressed by +:command:`target_compile_options` and :command:`target_link_options`. + +.. code:: c++ + + target_compile_options(MyApp PRIVATE -Wall -Werror) + target_link_options(MyApp PRIVATE -T LinksScript.ld) + +There are several problems with unconditionally calling +:command:`target_compile_options` or :command:`target_link_options`. The primary +problem is compiler flags are specific to the compiler frontend being used. In +order to ensure that our project supports multiple compiler frontends, we must +only pass compatible flags to the compiler. + +We can achieve this by checking the :variable:`CMAKE__COMPILER_FRONTEND_VARIANT` +variable which tells us the style of flags supported by the compiler frontend. + +.. note:: + Prior to CMake 3.26, :variable:`CMAKE__COMPILER_FRONTEND_VARIANT` was + only set for compilers with multiple frontend variants. In versions after + CMake 3.26 checking this variable alone is sufficient. + + However this tutorial targets CMake 3.23. As such, the logic is more + complicated than we have time for here. This tutorial step already includes + correct logic for checking the compiler variant for MSVC, GCC, Clang, and + AppleClang on CMake 3.23. + +Even if a compiler accepts the flags we pass, the semantics of compiler flags +change over time. This is especially true with regards to warnings. Projects +should not turn warnings-as-error flags by default, as this can break their +build on otherwise innocuous compiler warnings included in later releases. + +.. note:: + For errors and warnings, consider placing flags in :variable:`CMAKE__FLAGS` + for local development builds and during CI runs (via preset or + :option:`-D ` flags). We know exactly which compiler and + toolchain are being used in these contexts, so we can customize the behavior + precisely without risking build breakages on other platforms. + +Goal +---- + +Add appropriate warning flags to the ``Tutorial`` executable for MSVC-style and +GNU-style compiler frontends. + +Helpful Resources +----------------- + +* :command:`target_compile_options` + +Files to Edit +------------- + +* ``Tutorial/CMakeLists.txt`` + +Getting Started +--------------- + +Continue editing files in the ``Step4`` directory. The conditional for checking +the frontend variant has already been written. Complete ``TODO 9`` and +``TODO 10`` to add warning flags to ``Tutorial``. + +Build and Run +------------- + +Since we have already configured for this step, we can build with the usual +command. + +.. code-block:: cmake + + cmake --build build + +This should reveal a simple warning in the build. You can go ahead and fix it. + +Solution +-------- + +We need to add two compile options to ``Tutorial``, one MSVC-style flag and +one GNU-style flag. + +.. raw:: html + +
TODO 9-10: Click to show/hide answer + +.. literalinclude:: Step5/Tutorial/CMakeLists.txt + :caption: TODO 9-10: Tutorial/CMakeLists.txt + :name: Tutorial/CMakeLists.txt-target_compile_options + :language: cmake + :start-at: if( + :end-at: endif() + +.. raw:: html + +
+ +Exercise 3 - Include and Link Directories +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. note:: + This exercise requires building an archive using a compiler directly on the + command line. It is not used in later steps. It is included only to + demonstrate a use case for :command:`target_include_directories` and + :command:`target_link_directories`. + + If you cannot complete this exercise for whatever reason feel free to treat + it as informational-only, or skip it entirely. + +It is generally unnecessary to directly describe include and link directories, +as these requirements are inherited when linking together targets generated +within CMake, or from external dependencies imported into CMake with commands +we will cover in later steps. + +If we happen to have some libraries or header files which are not described +by a CMake target which we need to bring into the build, perhaps pre-compiled +binaries provided by a vendor, we can incorporate with the +:command:`target_link_directories` and :command:`target_include_directories` +commands. + +.. code-block:: cmake + + target_link_directories(MyApp PRIVATE Vendor/lib) + target_include_directories(MyApp PRIVATE Vendor/include) + + +These commands use properties which map to the ``-L`` and ``-I`` compiler flags +(or whatever flags the compiler uses for link and include directories). + +Of course, passing a link directory doesn't tell the compiler to link anything +into the build. For that we need :command:`target_link_libraries`. When +:command:`target_link_libraries` is given an argument which does not map to +a target name, it will add the string directly to the link line as a library +to be linked into the build (prepending any appropriate flags, such a ``-l``). + +Goal +---- + +Describe a pre-compiled, vendored, static library and its headers inside a +project using :command:`target_link_directories` and +:command:`target_include_directories`. + +Helpful Resources +----------------- + +* :command:`target_link_directories` +* :command:`target_include_directories` +* :command:`target_link_libraries` + +Files to Edit +------------- + +* ``Vendor/CMakeLists.txt`` +* ``Tutorial/CMakeLists.txt`` + +Getting Started +--------------- + +You will need to build the vendor library into a static archive to complete this +exercise. Navigate to the ``Help/guide/tutorial/Step4/Vendor/lib`` directory +and build the code as appropriate for your platform. On Unix-like operating +systems the appropriate commands are usually: + +.. code-block:: console + + g++ -c Vendors.cxx + ar rvs libVendor.a Vendor.o + +Then complete ``TODO 11`` through ``TODO 14``. + +.. note:: + ``VendorLib`` is an ``INTERFACE`` library, meaning it has no build requirements + (because it has already been built). All of its properties should also be + interface properties. + + We'll discuss ``INTERFACE`` libraries in greater depth during the next step. + + +Build and Run +------------- + +If you have successfully built ``libVendor``, you can rebuild ``Tutorial`` +using the normal command. + +.. code-block:: console + + cmake --build build + +Running ``Tutorial`` should now output a message about the acceptability of the +result to the vendor. + +Solution +-------- + +We need to use the target link and include commands to describe the archive +and its headers as ``INTERFACE`` requirements of ``VendorLib``. + +.. raw:: html + +
TODO 11-13: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 11-13: Vendor/CMakeLists.txt + :name: Vendor/CMakeLists.txt + + target_include_directories(VendorLib + INTERFACE + include + ) + + target_link_directories(VendorLib + INTERFACE + lib + ) + + target_link_libraries(VendorLib + INTERFACE + Vendor + ) + +.. raw:: html + +
+ +Then we can add ``VendorLib`` to ``Tutorial``'s linked libraries. + +.. raw:: html + +
TODO 14: Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 14: Tutorial/CMakeLists.txt + :name: Tutorial/CMakeLists.txt-VendorLib + + target_link_libraries(Tutorial + PRIVATE + MathFunctions + VendorLib + ) + +.. raw:: html + +
diff --git a/Help/guide/tutorial/In-Depth System Introspection.rst b/Help/guide/tutorial/In-Depth System Introspection.rst new file mode 100644 index 0000000..73fe0f2 --- /dev/null +++ b/Help/guide/tutorial/In-Depth System Introspection.rst @@ -0,0 +1,418 @@ +Step 6: In-Depth System Introspection +===================================== + +In order to discover information about the system environment and the toolchain, +CMake will often compile small test programs to verify the availability of +compiler flags, headers, and builtins or other language constructs. + +In this step, we will take advantage of the same test program mechanisms that +CMake uses in our own project code. + +Background +^^^^^^^^^^ + +An old trick going back to the oldest days of configuration and build systems +is to verify the availability of some feature by compiling a small program +which uses that feature. + +CMake makes this unnecessary for many contexts. As we will address in later +steps, if CMake can find a library dependency, we can rely on it having all +the facilities (headers, code generators, test utilities, etc) we expect it to +have. Conversely, if CMake can't find a dependency, attempting to use the +dependency anyway will almost certainly fail. + +However, there are other kinds of information about the toolchain which CMake +doesn't communicate readily. For these advanced cases, we can write our own +test programs and compile commands to check for availability. + +CMake provides modules to simplify these checks. These are documented at +:manual:`cmake-modules(7)`. Any module that begins with ``Check`` is a system +introspection module we can use to interrogate the toolchain and system +environment. Some notable ones include: + + ``CheckIncludeFiles`` + Check one or more C/C++ header files. + + ``CheckCompilerFlag`` + Check whether the compiler supports a given flag. + + ``CheckSourceCompiles`` + Checks whether source code can be built for a given language. + + ``CheckIPOSupported`` + Check whether the compiler supports interprocedural optimization (IPO/LTO). + + +Exercise 1 - Check Include File +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A fast and easy check to perform is if a given header file is available on +a certain platform, for which CMake provides :module:`CheckIncludeFiles`. This +is most appropriate for system and intrinsic headers, which may not be provided +by a specific package by are expected to be available in many build environments. + +.. code-block:: cmake + + include(CheckIncludeFiles) + check_include_files(sys/socket.h HAVE_SYS_SOCKET_H LANGUAGE CXX) + +.. note:: + These functions are not immediately available in CMake, they must be added via + :command:`include`'ing their associated module (aka, a CMakeLang file). Many + modules live inside CMake's own ``Modules`` folder. This built-in ``Modules`` + folder is one of the places CMake searches when evaluating an :command:`include` + command. You can think of these modules like standard library headers, they're + expected to be available. + +Once a header file is known to exist, we can communicate that to our code using +the same mechanisms of conditionals and target commands already covered. + +Goal +---- + +Check if the x86 SSE2 intrinsic header is available, and if so use it to +improve ``mathfunctions::sqrt``. + +Helpful Resources +----------------- + +* :module:`CheckIncludeFiles` +* :command:`target_compile_definitions` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` +* ``MathFunctions/MathFunctions.cxx`` + +Getting Started +--------------- + +The ``Help/guide/tutorial/Step6`` directory contains the complete, recommended +solution to ``Step5`` and relevant ``TODOs`` for this step. It also contains +specialized implementations of the ``sqrt`` function for various conditions, +which you will find in ``MathFunctions/MathFunctions.cxx``. + +Complete ``TODO 1`` through ``TODO 3``. Note that some ``#ifdef`` directives +have already been added to the library, which will change its operation as we +work through the step. + +Build and Run +------------- + +We can use our usual commands to configure. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +In the output of the configuration step we should observe CMake checking for +the ``emmintrin.h`` header. + +.. code-block:: console + + -- Looking for include file emmintrin.h + -- Looking for include file emmintrin.h - found + +If the header is available on your system, verify the ``Tutorial`` output +contains the message about using SSE2. Conversely, if the header is not +available you should see the usual behavior from ``Tutorial``. + +Solution +-------- + +First we include and use the ``CheckIncludeFiles`` module, verifying the +``emmintrin.h`` header is available. + +.. raw:: html + +
TODO 1: Click to show/hide answer + +.. literalinclude:: Step7/MathFunctions/CMakeLists.txt + :caption: TODO 1: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-check-include-files + :language: cmake + :start-at: include(CheckIncludeFiles + :end-at: check_include_files( + +.. raw:: html + +
+ +Then we use the result of the check to conditionally set a compile definition +on ``MathFunctions``. + +.. raw:: html + +
TODO 2: Click to show/hide answer + +.. literalinclude:: Step7/MathFunctions/CMakeLists.txt + :caption: TODO 2: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-define-use-sse2 + :language: cmake + :start-at: if(HAS_EMMINTRIN) + :end-at: endif() + +.. raw:: html + +
+ +Finally we can conditionally include the header in the ``MathFunctions`` library. + +.. raw:: html + +
TODO 3: Click to show/hide answer + +.. literalinclude:: Step7/MathFunctions/MathFunctions.cxx + :caption: TODO 3: MathFunctions/MathFunctions.cxx + :name: MathFunctions/MathFunctions.cxx-include-sse2 + :language: c++ + :start-at: #ifdef TUTORIAL_USE_SSE2 + :end-at: #endif + +.. raw:: html + +
+ + +Exercise 2 - Check Source Compiles +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes it is insufficient to merely check for a header. This is especially +true when no header is available to check, such is the case with +compiler-builtins. For these scenarios we have :module:`CheckSourceCompiles`. + +.. code-block:: cmake + + include(CheckSourceCompiles) + check_source_compiles(CXX + " + int main() { + int a, b, c; + __builtin_add_overflow(a, b, &c); + } + " + HAS_CHECKED_ADDITION + ) + +.. note:: + By default :module:`CheckSourceCompiles` builds and links an executable. The + code to be check must provide a valid ``int main()`` in order to succeed. + +After performing the check, this system introspection can be applied identically +to how we discussed with header files. + +Goal +---- + +Check if the GNU SSE2 builtins are available, and if so use them to improve +``mathfunctions::sqrt``. + +Helpful Resources +----------------- + +* :module:`CheckSourceCompiles` +* :command:`target_compile_definitions` + +Files to Edit +------------- + +* ``MathFunctions/CMakeLists.txt`` + +Getting Started +--------------- + +Complete ``TODO 4`` and ``TODO 5``. No code changes to the ``MathFunctions`` +implementation are necessary, as these have already been provided. + +Build and Run +------------- + +We need only rebuild the tutorial. + +.. code-block:: console + + cmake --build build + +.. note:: + If a check fails and you think it should succeed, you will need to clear the + CMake Cache by deleting the ``CMakeCache.txt`` file. CMake will not rerun + compile checks on subsequent runs if it has a cached result. + +In the output of the configuration step we should observe CMake checking if the +provided source code compiles, which will be reported under the variable name +we provided to ``check_source_compiles()``. + +.. code-block:: console + + -- Performing Test HAS_GNU_BUILTIN + -- Performing Test HAS_GNU_BUILTIN - Success + +If the builtins are available on your compiler, verify the ``Tutorial`` output +contains the message about using GNU-builting. Conversely, if the builtins are +not available you should see the previous behavior from ``Tutorial``. + +Solution +-------- + +First we include and use the ``CheckSourceCompiles`` module, verifying the +provided source code can be built. + +.. + pygments doesn't like the [=[ ]=] literals in the following + literalinclude, so use :language: none + +.. raw:: html + +
TODO 4: Click to show/hide answer + +.. literalinclude:: Step7/MathFunctions/CMakeLists.txt + :caption: TODO 4: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-check-source-compiles + :language: none + :start-at: include(CheckSourceCompiles + :end-at: HAS_GNU_BUILTIN + :append: ) + +.. raw:: html + +
+ +Then we use the result of the check to conditionally set a compile definition +on ``MathFunctions``. + +.. raw:: html + +
TODO 5: Click to show/hide answer + +.. literalinclude:: Step7/MathFunctions/CMakeLists.txt + :caption: TODO 5: MathFunctions/CMakeLists.txt + :name: MathFunctions/CMakeLists.txt-define-use-gnu-builtin + :language: cmake + :start-at: if(HAS_GNU_BUILTIN) + :end-at: endif() + +.. raw:: html + +
+ +Exercise 3 - Check Interprocedural Optimization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Interprocedural and link time optimizations can provide significant performance +improvements to some software. CMake has the capacity to check for the +availability of IPO flags via :module:`CheckIPOSupported`. + +.. code-block:: cmake + + include(CheckIPOSupported) + check_ipo_supported() # fatal error if IPO is not supported + set_target_properties(MyApp + PROPERTIES + INTERPROCEDURAL_OPTIMIZATION TRUE + ) + +.. note:: + There a couple important caveats with regard to in-project IPO configuration: + + * CMake does not know about every IPO/LTO flag on every compiler, better + results can often be achieved with individual tuning for a known toolchain. + * Setting the :prop_tgt:`INTERPROCEDURAL_OPTIMIZATION` property on a target + does not alter any of the targets it links to, or dependencies from other + projects. IPO can only "see" into other targets which are also compiled + appropriately. + + For these reasons, serious consideration should be given to manually setting + up IPO/LTO flags across all projects in the dependency tree via external + mechanisms (presets, :option:`-D ` flags, + :manual:`toolchain files `, etc) instead of in-project + control. + +However, especially for extremely large projects, it can be useful to have +an in-project mechanism to use IPO whenever it is available. + +Goal +---- + +Enable IPO for the entire tutorial project when it is available from the +toolchain. + +Helpful Resources +----------------- + +* :module:`CheckIPOSupported` +* :variable:`CMAKE_INTERPROCEDURAL_OPTIMIZATION` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +Continue editing the files in ``Step6``. Complete ``TODO 6`` and ``TODO 7``. + +Build and Run +------------- + +We need only rebuild the tutorial. + +.. code-block:: console + + cmake --build build + +If IPO is unavailable, we will see an error message during configuration. +Otherwise nothing will change. + +.. note:: + Regardless of the result of the IPO check, we shouldn't expect any change + in behavior from ``Tutorial`` or ``MathFunctions``. + +Solution +-------- + +The first ``TODO`` is easy, we add another option to our project. + +.. raw:: html + +
TODO 6: Click to show/hide answer + +.. literalinclude:: Step7/CMakeLists.txt + :caption: TODO 6: MathFunctions/CMakeLists.txt + :name: CMakeLists.txt-enable-ipo + :language: cmake + :start-at: option(TUTORIAL_ENABLE_IPO + :end-at: option(TUTORIAL_ENABLE_IPO + +.. raw:: html + +
+ +The next step is involved, however the documentation for :module:`CheckIPOSupported` +has an almost complete example of what we need to do. The only difference is +we are going to enable IPO project-wide instead of for a single target. + +.. raw:: html + +
TODO 7: Click to show/hide answer + +.. literalinclude:: Step7/CMakeLists.txt + :caption: TODO 7: CMakeLists.txt + :name: CMakeLists.txt-check-ipo + :language: cmake + :start-at: if(TUTORIAL_ENABLE_IPO) + :end-at: endif() + :append: endif() + +.. raw:: html + +
+ +.. note:: + Normally we have discouraged setting ``CMAKE_`` variables inside the project. + Here, we are controlling that behavior with an :command:`option()`. This + allows packagers to opt-out of our override. This is an imperfect, but + acceptable solution to situations where we want to provide options to control + project-wide behavior controlled by ``CMAKE_`` variables. diff --git a/Help/guide/tutorial/Installation Commands and Concepts.rst b/Help/guide/tutorial/Installation Commands and Concepts.rst new file mode 100644 index 0000000..1229630 --- /dev/null +++ b/Help/guide/tutorial/Installation Commands and Concepts.rst @@ -0,0 +1,596 @@ +Step 9: Installation Commands and Concepts +========================================== + +Projects need to do more than build and test their code, they need to make it +available to consumers. The layout of files in the build tree is unsuitable +for consumption by other projects, binaries are in unexpected places, header +files are located far away in the source tree, and there's no clear way +to discover what targets are provided or how to use them. + +This translation, moving artifacts from the source and build trees into a final +layout suitable for consumption, is known as installation. CMake supports a +complete installation workflow as part of the project description, controlling +both the layout of artifacts in the install tree, and reconstructing targets +for other CMake projects which want to consume the libraries provided by the +install tree. + +Background +^^^^^^^^^^ + +All CMake installation goes through a single command, :command:`install`, which +is split into many subcommands responsible for various aspects of the +installation process. For target-based CMake workflows, it is mostly sufficient +to rely on installing targets themselves with :command:`install(TARGETS)` +instead of resorting to manually moving files with :command:`install(FILES)` +or :command:`install(DIRECTORY)`. + +.. note:: + This is why we need to add ``FILES`` to header sets which are intended to be + installed. CMake needs to be able to locate the files when their associated + target is installed. + +CMake divides target-based installation into various artifact kinds. The +available artifact kinds (in CMake 3.23) are: + + ``ARCHIVE`` + Static libraries (``.a`` / ``.lib``), DLL import libraries (``.lib``), and + a handful of other "archive-like" objects. + + ``LIBRARY`` + Shared libraries (``.so``), modules, and other dynamically loadable + objects. **Not** Window's DLL files (``.dll``) or MacOS frameworks. + + ``RUNTIME`` + Executables of all kinds except MacOS bundles; and Window's DLLs (``.dll``). + + ``OBJECT`` + Objects from ``OBJECT`` libraries. + + ``FRAMEWORK`` + Both static and shared MacOS frameworks + + ``BUNDLE`` + MacOS bundle executables + + ``PUBLIC_HEADER`` / ``PRIVATE_HEADER`` / ``RESOURCE`` + Files described by the :prop_tgt:`PUBLIC_HEADER`, :prop_tgt:`PRIVATE_HEADER` + and :prop_tgt:`RESOURCE` target properties, typically used with MacOS + frameworks + + ``FILE_SET `` + A file set associated with the target. This is how headers are typically + installed. + +Most important artifact kinds have known destinations which CMake will default +to unless instructed to do otherwise. For example, ``RUNTIME`` will be installed +to the location named by :module:`CMAKE_INSTALL_BINDIR `, if +the variable is available, otherwise they default to ``bin``. + +The full list of artifact kind default destinations is described in the +following table. + +=============================== =============================== ====================== + Target Type Variable Built-In Default +=============================== =============================== ====================== +``RUNTIME`` ``${CMAKE_INSTALL_BINDIR}`` ``bin`` +``LIBRARY`` ``${CMAKE_INSTALL_LIBDIR}`` ``lib`` +``ARCHIVE`` ``${CMAKE_INSTALL_LIBDIR}`` ``lib`` +``PRIVATE_HEADER`` ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include`` +``PUBLIC_HEADER`` ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include`` +``FILE_SET`` (type ``HEADERS``) ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include`` +=============================== =============================== ====================== + +For the most part, projects should leave the defaults alone unless they need to +install to a specific subdirectory of a default location. + +CMake does not define the ``CMAKE_INSTALL_`` variables by default. If a +project wishes to dictate installing to a subdirectory of one of these +locations, it is necessary to include the :module:`GNUInstallDirs` module, which +will provide values for all ``CMAKE_INSTALL_`` variables that have not +already been defined. + +Exercise 1 - Installing Artifacts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For modern, target-based CMake projects installation of artifacts is trivial +and consists of a single call to :command:`install(targets)`. + +.. code-block:: cmake + + install( + TARGETS MyApp MyLib + + FILE_SET HEADERS + FILE_SET AnotherHeaderFileSet + ) + +Most artifact kinds are installed by default and do not need to be listed in +the :command:`install` command. However, ``FILE_SET``s must be named to let +CMake know you want to install. In the above example we install two file +sets, one named ``HEADERS`` and another named ``AnotherHeaderFileSet``. + +When named, an artifact kind can be given various options, such as a destination. + +.. code-block:: cmake + + include(GNUInstallDirs) + + install( + TARGETS MyApp MyLib + + RUNTIME + DESTINATION ${CMAKE_INSTALL_BINDIR}/Subfolder + + FILE_SET HEADERS + ) + +This will install the ``MyApp`` target to ``bin/Subfolder`` (if the packager +hasn't changed :module:`CMAKE_INSTALL_BINDIR `). + +Importantly, if the ``OBJECT`` artifact kind is never given a destination, it +will act like an ``INTERFACE`` library, only installing its headers. + +Goal +---- + +Install the artifacts for the libraries and executables (except tests) described +in the tutorial project. + +Helpful Resources +----------------- + +* :command:`install` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +The ``Help/guide/tutorial/Step9`` directory contains the complete, recommended +solution to ``Step8``. Complete ``TODO 1`` and ``TODO 2``. + +Build and Run +------------- + +No special configuration is needed, configure and build as usual. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +We can verify the installation is correct with :option:`cmake --install`. + +.. code-block:: console + + cmake --install build --prefix install + +The ``install`` folder should be populated correctly for our artifacts. + +Solution +-------- + +First we add an :command:`install(TARGETS)` for the conditionally built, +thus conditionally installed, ``Tutorial`` executable. + +.. raw:: html + +
TODO 1 Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 1: CMakeLists.txt + :name: CMakeLists.txt-install-tutorial + + if(TUTORIAL_BUILD_UTILITIES) + add_subdirectory(Tutorial) + install( + TARGETS Tutorial + ) + endif() + +.. raw:: html + +
+ +Then we can install the rest of the targets. + +.. raw:: html + +
TODO 2 Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 2: CMakeLists.txt + :name: CMakeLists.txt-install-libs + + install( + TARGETS MathFunctions OpAdd OpMul OpSub MathLogger SqrtTable + FILE_SET HEADERS + ) + +.. raw:: html + +
+ +.. note:: + We could add :command:`install(TARGETS)` commands locally to each subfolder + where the targets are defined. This would be typical in very large projects + where keeping track of all the installable targets is difficult. + +It might seem unnecessary to install the ``SqrtTable`` and ``MathLogger``, +and it is at this stage. Due to how CMake models target relationships, when we +reconstruct the target model in the next exercise we will need these targets to +be available. + +Exercise 2 - Exporting Targets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This raw collection of installed files is a good start, but we lose the CMake +target model. These are effectively no better than the pre-compiled vendored +libraries we discussed in ``Step 4``. We need some way for other projects to +reconstruct our targets from what we have provided in the install tree. + +The mechanism CMake provides to solve this is a CMakeLang file known as a +"target export file". It is created by the :command:`install(EXPORT)` +command. + +.. code-block:: cmake + + install( + TARGETS MyApp MyLib + EXPORT MyProjectTargets + ) + + include(GNUInstallDirs) + + install( + EXPORT MyProjectTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject + NAMESPACE MyProject:: + ) + +There are several parts to the above example. Firstly the +:command:`install(TARGETS)` command takes an export name, basically a list to +add the installed targets to. + +Later, the :command:`install(EXPORT)` command consumes this list of targets +to generate the target export file. This will be a file named +``.cmake`` located in the provided ``DESTINATION``. The +``DESTINATION`` provided in this example is the conventional one, but any +location searched by the :command:`find_package` command is valid. + +Finally, the targets created by the target export file will be prefixed with the +``NAMESPACE`` string, ie they will be of the form ````. +It is conventional for this to be the project name followed by two colons. + +For reasons that will become more obvious in future steps, we typically don't +consume this file directly. Instead we have a file named +``Config.cmake`` consume it via :command:`include()`. + +.. code-block:: cmake + + include(${CMAKE_CURRENT_LIST_DIR}/MyProjectTargets.cmake) + +.. note:: + The :variable:`CMAKE_CURRENT_LIST_DIR` variable names the directory that the + currently running CMake Language file is inside of, regardless of how that + file was included or launched. + +Then this file is installed alongside the target export with +:command:`install(FILES)`. + +.. code-block:: cmake + + install( + FILES + cmake/MyProjectConfig.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject + ) + +.. note:: + The name of this file and its location are dictated by the discovery + semantics of the :command:`find_package` command, which we will discuss more + in the next step. + +Goal +---- + +Export the Tutorial project targets so other projects may consume them. + +Helpful Resources +----------------- + +* :command:`install` +* :module:`GNUInstallDirs` +* :variable:`CMAKE_CURRENT_LIST_DIR` + +Files to Edit +------------- + +* ``CMakeLists.txt`` +* ``cmake/TutorialConfig.cmake`` + +Getting Started +--------------- + +Continue editing the files in the ``Help/guide/tutorial/Step9`` directory. +Complete ``TODO 3`` through ``TODO 8``. + +Build and Run +------------- + +The build command is sufficient to reconfigure the project. + +.. code-block:: console + + cmake --build build + +We can verify the installation is correct with :option:`cmake --install`. + +.. note:: + + As with CTest, when using multi-config generator, eg Visual Studio, it will be + necessary to specify a configuration with + ``cmake --install --config ``, where + ```` is a value like ``Debug`` or ``Release``. This is true whenever + using a multi-config generator, and won't be called out specifically in + future commands. + +.. code-block:: console + + cmake --install build --prefix install + +.. note:: + CMake won't update files which have not changed, only installing new or + updated files from the build and source trees. + +The ``install`` folder should be populated correctly for our artifacts and +export files. We'll demonstrate how to use these files in the next step. + +Solution +-------- + +First we add the ``Tutorial`` target to the ``TutorialTargets`` export. + +.. raw:: html + +
TODO 3 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/CMakeLists.txt + :caption: TODO 3: CMakeLists.txt + :name: CMakeLists.txt-install-tutorial-export + :language: cmake + :start-at: install( + :end-at: ) + +.. raw:: html + +
+ +Soon we will need access to the ``CMAKE_INSTALL_`` variables, so next +we include the :module:`GNUInstallDirs` module. + +.. raw:: html + +
TODO 4 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/CMakeLists.txt + :caption: TODO 4: CMakeLists.txt + :name: CMakeLists.txt-gnuinstalldirss + :language: cmake + :start-at: include(GNUInstallDirs) + :end-at: include(GNUInstallDirs) + +.. raw:: html + +
+ +Now we add the rest of our targets to the ``TutorialTargets`` export. + +.. raw:: html + +
TODO 5 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/CMakeLists.txt + :caption: TODO 5: CMakeLists.txt + :name: CMakeLists.txt-install-libs-export + :language: cmake + :start-at: TARGETS MathFunctions + :end-at: ) + :prepend: install( + +.. raw:: html + +
+ +Next we install the export itself, to generate our target export file. + +.. raw:: html + +
TODO 6 Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 6: CMakeLists.txt + :name: CMakeLists.txt-install-export + + install( + EXPORT TutorialTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial + NAMESPACE Tutorial:: + ) + +.. raw:: html + +
+ +And then we install our "config" file, which we will use to include our target +export file. + +.. raw:: html + +
TODO 7 Click to show/hide answer + +.. code-block:: cmake + :caption: TODO 7: CMakeLists.txt + :name: CMakeLists.txt-install-config + + install( + FILES + cmake/TutorialConfig.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial + ) + +.. raw:: html + +
+ +Finally we can add the necessary :command:`include` command to the config file. + +.. raw:: html + +
TODO 8 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/cmake/TutorialConfig.cmake + :caption: TODO 8: cmake/TutorialConfig.cmake + :name: cmake/TutorialConfig.cmake + :language: cmake + :start-at: include + :end-at: include + +.. raw:: html + +
+ +Exercise 3 - Exporting a Version File +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When importing CMake targets from a target export file, there is no way to +"bail out" or "undo" the operation. If it turns out a package is a wrong or +incompatible version for the one we requested, we'll be stuck with any +side-effects incurred while we learned that version information. + +The answer CMake provides for this problem is a light-weight version file which +only describes this version compatibility information, which can be checked +before CMake commits to fully importing the file. + +CMake provides helper modules and scripts for generating these version files, +namely the :module:`CMakePackageConfigHelpers` module. + +.. code-block:: cmake + + include(CMakePackageConfigHelpers) + + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake + COMPATIBILITY ExactVersion + ) + +The available versions are: + +* ``AnyNewerVersion`` +* ``SameMajorVersion`` +* ``SameMinorVersion`` +* ``ExactVersion`` + +Additionally packages can mark themselves as ``ARCH_INDEPENDENT``, intended for +packages which ship no binaries which would tie them to a specific machine +architecture. + +By default, the ``VERSION`` used by ``write_basic_package_version_file()`` is +the ``VERSION`` number given to the :command:`project` command. + +Goal +---- + +Export a version file for the Tutorial project. + +Helpful Resources +----------------- + +* :command:`project` +* :command:`install` +* :module:`CMakePackageConfigHelpers` +* :variable:`PROJECT_VERSION` + +Files to Edit +------------- + +* ``CMakeLists.txt`` + +Getting Started +--------------- + +Continue editing the files in the ``Help/guide/tutorial/Step9`` directory. +Complete ``TODO 9`` through ``TODO 12``. + +Build and Run +------------- + +Rebuild and install as done previously. + +.. code-block:: console + + cmake --build build + cmake --install build --prefix install + +The ``install`` folder should be populated correctly with our newly generated +and installed version file. + +Solution +-------- + +First we add a ``VERSION`` parameter to the :command:`project` command. + +.. raw:: html + +
TODO 9 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/CMakeLists.txt + :caption: TODO 9: CMakeLists.txt + :name: CMakeLists.txt-project-version + :language: cmake + :start-at: project( + :end-at: ) + +.. raw:: html + +
+ +Next we include the :module:`CMakePackageConfigHelpers` modules and use it +to generate the config version file. + +.. raw:: html + +
TODO 10-11 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/CMakeLists.txt + :caption: TODO 10-11: CMakeLists.txt + :name: CMakeLists.txt-write_basic_package_version_file + :language: cmake + :start-at: include(CMakePackageConfigHelpers + :end-at: COMPATIBILITY ExactVersion + :append: ) + +.. raw:: html + +
+ +Finally we add the config version file to the list of files to be installed. + +.. raw:: html + +
TODO 12 Click to show/hide answer + +.. literalinclude:: Step10/TutorialProject/CMakeLists.txt + :caption: TODO 12: CMakeLists.txt + :name: CMakeLists.txt-install-version-config + :language: cmake + :start-at: FILES + :end-at: ) + :prepend: install( + +.. raw:: html + +
diff --git a/Help/guide/tutorial/Installing and Testing.rst b/Help/guide/tutorial/Installing and Testing.rst deleted file mode 100644 index 7a59fcb..0000000 --- a/Help/guide/tutorial/Installing and Testing.rst +++ /dev/null @@ -1,311 +0,0 @@ -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 ` executable or the -:manual:`cmake-gui ` to configure the project and then build it -with your chosen build tool. - -Then, run the install step by using the :option:`--install ` -option of the :manual:`cmake ` 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 ` 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 ` 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 - -
TODO 1: Click to show/hide answer - -.. 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 - -
- -and - -.. raw:: html - -
TODO 2: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 3,4: Click to show/hide answer - -.. 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 - -
- -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 ` - -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 \ ` 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 - -
TODO 5: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 6: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 7: Click to show/hide answer - -.. 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 - -
- -The next test we will add verifies the computed value is truly the -square root. - -.. raw:: html - -
TODO 8: Click to show/hide answer - -.. 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 - -
- -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 - -
TODO 9: Click to show/hide answer - -.. 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 - -
diff --git a/Help/guide/tutorial/Miscellaneous Features.rst b/Help/guide/tutorial/Miscellaneous Features.rst new file mode 100644 index 0000000..51fbe8e --- /dev/null +++ b/Help/guide/tutorial/Miscellaneous Features.rst @@ -0,0 +1,188 @@ +Step 11: Miscellaneous Features +=============================== + +Some features don't fit well or aren't important enough to receive attention +in the main tutorial, but deserve mention. These exercises collect some of those +features. They should be considered "bonuses". + +There are many CMake features that are not covered by the tutorial, some of +which are considered essential to the projects which use them. Others are +in common use by packagers but see little discussion among software developers +producing local builds. + +This list is not an exhaustive discussion of what remains of CMake's +capabilities. It may grow or shrink with time and relevance. + +Exercise 1: Target Aliases +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This tutorial focuses on installing dependencies and consuming them from an +install tree. It also recommends the use of package managers to facilitate +this process. However, for a variety of reasons both historical and +contemporary this is not always how CMake projects are consumed. + +It is possible to vendor a dependency's source code entirely in a parent project +and consume it with :command:`add_subdirectory`. When performed, the target +names exposed are those used within the project, not those exported via +:command:`install(EXPORT)`. The target names will not have the namespace string +that command prefixes to targets. + +Some projects wish to support this workflow with an interface consistent with +the one presented to :command:`find_package` consumers. CMake supports this via +:command:`add_library(ALIAS)` and :command:`add_executable(ALIAS)`. + +.. code-block:: cmake + + add_library(MyLib INTERFACE) + add_library(MyProject::MyLib ALIAS MyLib) + +Goal +---- + +Add a library alias for the ``MathFunctions`` library. + +Helpful Resources +----------------- + +* :command:`add_library` + +Files to Edit +------------- + +* ``TutorialProject/MathFunctions/CMakeLists.txt`` + +Getting Started +--------------- + +For this step we will only be editing the ``TutorialProject`` project in the +``Step11`` folder. Complete ``TODO 1``. + +Build and Run +------------- + +To build the project we first need configure and install ``SimpleTest``. +Navigate to ``Help/guide/Step11/SimpleTest`` and run the appropriate commands. + +.. code-block:: console + + cmake --preset tutorial + cmake --install build + +Then navigate to ``Help/guide/Step11/TutorialProject`` and perform the usual build. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +There should be no observable change in behavior from adding the alias. + +Solution +-------- + +We add a single line to the ``MathFunctions`` CML. + +.. raw:: html + +
TODO 1 Click to show/hide answer + +.. literalinclude:: Complete/TutorialProject/MathFunctions/CMakeLists.txt + :caption: TODO 1: TutorialProject/MathFunctions/CMakeLists.txt + :name: TutorialProject/MathFunctions/CMakeLists.txt-alias + :language: cmake + :start-at: ALIAS + :end-at: ALIAS + +.. raw:: html + +
+ +Exercise 2: Generator Expressions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:manual:`Generator expressions ` are a +complicated domain-specific language supported in some contexts within CMake. +They are most easily understood as deferred-evaluation conditionals, they +express requirements where the inputs to determine the correct behavior are not +known during the CMake configuration stage. + +.. note:: + This is where generator expressions get their name, they are evaluated when + the underlying build system is being generated. + +Generator expressions were commonly used in combination with +:command:`target_include_directories` to express include directory requirements +across the build and install tree, but file sets have superseded this use case. +Their most common applications now are in multi-config generators and +intricate dependency injection systems. + +.. code-block:: cmake + + target_compile_definitions(MyApp PRIVATE "MYAPP_BUILD_CONFIG=$") + +Goal +---- + +Add a generator expression to ``SimpleTest`` that checks the build configuration +inside a compile definition. + +Helpful Resources +----------------- + +* :command:`target_compile_definitions` +* :manual:`cmake-generator-expressions(7)` + +Files to Edit +------------- + +* ``SimpleTest/CMakeLists.txt`` + +Getting Started +--------------- + +For this step we will only be editing the ``SimpleTest`` project in the +``Step11`` folder. Complete ``TODO 2``. + +Build and Run +------------- + +To build the project we first need configure and install ``SimpleTest``. +Navigate to ``Help/guide/Step11/SimpleTest`` and run the appropriate commands. + +.. code-block:: console + + cmake --preset tutorial + cmake --install build + +Then navigate to ``Help/guide/Step11/TutorialProject`` and perform the usual build. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +When running the ``TestMathFunctions`` binary directly, we should a message +naming the build configuration used to build the executable (not necessarily the +same as configuration used to configure ``SimpleTest``). On single configuration +generators, the build configuration can be changed by setting +:variable:`CMAKE_BUILD_TYPE`. + +Solution +-------- + +We add a single line to the ``SimpleTest`` CML. + +.. raw:: html + +
TODO 2 Click to show/hide answer + +.. literalinclude:: Complete/SimpleTest/CMakeLists.txt + :caption: TODO 2: SimpleTest/CMakeLists.txt + :name: SimpleTest/CMakeLists.txt-target_compile_definitions + :language: cmake + :start-at: target_compile_definitions + :end-at: target_compile_definitions + +.. raw:: html + +
diff --git a/Help/guide/tutorial/Packaging Debug and Release.rst b/Help/guide/tutorial/Packaging Debug and Release.rst deleted file mode 100644 index 6036969..0000000 --- a/Help/guide/tutorial/Packaging Debug and Release.rst +++ /dev/null @@ -1,86 +0,0 @@ -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 ` 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 ` 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 deleted file mode 100644 index 4cca679..0000000 --- a/Help/guide/tutorial/Packaging an Installer.rst +++ /dev/null @@ -1,64 +0,0 @@ -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_GENERATOR` and -:variable:`CPACK_SOURCE_GENERATOR` variables select the generators used for -binary and source installations, respectively. - -Finally we include the :module:`CPack module ` 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 ` executable. To build a binary distribution, from the -binary directory run: - -.. code-block:: console - - cpack - -To specify the binary generator, use the :option:`-G ` option. For -multi-config builds, use :option:`-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 ` -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 deleted file mode 100644 index a2f5e2a..0000000 --- a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst +++ /dev/null @@ -1,61 +0,0 @@ -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 - :start-at: # state that SqrtLibrary need PIC when the default is shared libraries - :end-at: ) - -Define ``EXPORTING_MYMATH`` stating we are using ``declspec(dllexport)`` when -building on Windows. - -.. literalinclude:: Step11/MathFunctions/CMakeLists.txt - :caption: MathFunctions/CMakeLists.txt - :name: MathFunctions/CMakeLists.txt-dll-export - :language: cmake - :start-at: # define the symbol stating we are using the declspec(dllexport) when - :end-at: target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH") - -**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 index 6fcce90..19020c2 100644 --- a/Help/guide/tutorial/Step1/CMakeLists.txt +++ b/Help/guide/tutorial/Step1/CMakeLists.txt @@ -1,16 +1,21 @@ -# TODO 1: Set the minimum required version of CMake to be 3.10 +# TODO1: Set the minimum required version of CMake to be 3.23 -# TODO 2: Create a project named Tutorial +# TODO2: Create a project named Tutorial -# TODO 7: Set the project version number as 1.0 in the above project command +# TODO3: Add an executable target called Tutorial to the project -# TODO 6: Set the variable CMAKE_CXX_STANDARD to 11 -# and the variable CMAKE_CXX_STANDARD_REQUIRED to True +# TODO4: Add the Tutorial/Tutorial.cxx source file to the Tutorial target -# TODO 8: Use configure_file to configure and copy TutorialConfig.h.in to -# TutorialConfig.h +# TODO7: Add the MathFunctions library as a linked dependency +# to the Tutorial target -# TODO 3: Add an executable called Tutorial to the project -# Hint: Be sure to specify the source file as tutorial.cxx +# TODO11: Add the Tutorial subdirectory to the project -# TODO 9: Use target_include_directories to include ${PROJECT_BINARY_DIR} +# TODO5: Add a library target called MathFunctions to the project + +# TODO6: Add the source and header file located in Step1/MathFunctions to the +# MathFunctions target, note that the intended way to include the +# MathFunctions header is: +# #include + +# TODO13: Add the MathFunctions subdirectory to the project diff --git a/Help/guide/tutorial/Step1/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step1/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..0816408 --- /dev/null +++ b/Help/guide/tutorial/Step1/MathFunctions/CMakeLists.txt @@ -0,0 +1,2 @@ +# TODO12: Move all the MathFunctions target commands to this CMakeLists.txt. +# Ensure that all paths are updated to be relative to this new location. diff --git a/Help/guide/tutorial/Step1/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step1/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..4bce912 --- /dev/null +++ b/Help/guide/tutorial/Step1/MathFunctions/MathFunctions.cxx @@ -0,0 +1,31 @@ +#include + +namespace { +// 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; +} +} + +namespace mathfunctions { +double sqrt(double x) +{ + return mysqrt(x); +} +} diff --git a/Help/guide/tutorial/Step1/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step1/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..d5c2f22 --- /dev/null +++ b/Help/guide/tutorial/Step1/MathFunctions/MathFunctions.h @@ -0,0 +1,5 @@ +#pragma once + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step1/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step1/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..93d5b5f --- /dev/null +++ b/Help/guide/tutorial/Step1/Tutorial/CMakeLists.txt @@ -0,0 +1,2 @@ +# TODO10: Move all the Tutorial target commands to this CMakeLists.txt. Ensure +# that all paths are updated to be relative to this new location. diff --git a/Help/guide/tutorial/Step1/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step1/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..8cc0f2a --- /dev/null +++ b/Help/guide/tutorial/Step1/Tutorial/Tutorial.cxx @@ -0,0 +1,23 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +// TODO8: Include the MathFunctions header + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + double const inputValue = std::stod(argv[1]); + + // TODO9: Use the mathfunctions::sqrt function + // calculate square root + double const outputValue = std::sqrt(inputValue); + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; +} diff --git a/Help/guide/tutorial/Step1/TutorialConfig.h.in b/Help/guide/tutorial/Step1/TutorialConfig.h.in deleted file mode 100644 index 990bfbd..0000000 --- a/Help/guide/tutorial/Step1/TutorialConfig.h.in +++ /dev/null @@ -1,2 +0,0 @@ -// 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 deleted file mode 100644 index 18077b6..0000000 --- a/Help/guide/tutorial/Step1/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include // TODO 5: Remove this line -#include -#include - -// 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]) - double const inputValue = atof(argv[1]); - - // calculate square root - double const 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 deleted file mode 100644 index 40fee8d..0000000 --- a/Help/guide/tutorial/Step10/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -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 "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) - -# 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_GENERATOR "TGZ") -set(CPACK_SOURCE_GENERATOR "TGZ") -include(CPack) diff --git a/Help/guide/tutorial/Step10/CTestConfig.cmake b/Help/guide/tutorial/Step10/CTestConfig.cmake deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step10/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step10/License.txt b/Help/guide/tutorial/Step10/License.txt deleted file mode 100644 index 85760e5..0000000 --- a/Help/guide/tutorial/Step10/License.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 210563a..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# 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") - - include(MakeTable.cmake) # generates Table.h - - # 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} - ) - - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() - -# link MathFunctions to tutorial_compiler_flags -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.cmake b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake deleted file mode 100644 index 12865a9..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - ) diff --git a/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx deleted file mode 100644 index f85b278..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include -#include -#include - -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(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 deleted file mode 100644 index c0991b9..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx +++ /dev/null @@ -1,20 +0,0 @@ - -#include "MathFunctions.h" - -#include - -#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 deleted file mode 100644 index 1e916e1..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h +++ /dev/null @@ -1,3 +0,0 @@ -namespace mathfunctions { -double sqrt(double x); -} diff --git a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx deleted file mode 100644 index 8153f18..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#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(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 deleted file mode 100644 index e1c42ef..0000000 --- a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.h +++ /dev/null @@ -1,6 +0,0 @@ - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step10/SimpleTest/CMakeLists.txt b/Help/guide/tutorial/Step10/SimpleTest/CMakeLists.txt new file mode 100644 index 0000000..6a73ff4 --- /dev/null +++ b/Help/guide/tutorial/Step10/SimpleTest/CMakeLists.txt @@ -0,0 +1,52 @@ +# A very simple test framework for demonstrating how dependencies work +cmake_minimum_required(VERSION 3.23) + +project(SimpleTest + VERSION 0.0.1 +) + +add_library(SimpleTest INTERFACE) +target_sources(SimpleTest + INTERFACE + FILE_SET HEADERS + FILES + SimpleTest.h +) +target_compile_features(SimpleTest INTERFACE cxx_std_20) + +# TODO6: Find the TransitiveDep package with find_package. The SimpleTest +# build should fail if TransitiveDep cannot be found. + +# TODO7: Add the TransitiveDep::TransitiveDep target to the SimpleTest interface +# library's links. Remember that interface libraries can only have +# interface properties. + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +install( + TARGETS SimpleTest + EXPORT SimpleTestTargets + FILE_SET HEADERS +) + +install( + EXPORT SimpleTestTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimpleTest + NAMESPACE SimpleTest:: +) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/SimpleTestConfigVersion.cmake + COMPATIBILITY ExactVersion + ARCH_INDEPENDENT +) + +install( + FILES + cmake/simpletest_discover_impl.cmake + cmake/simpletest_discover_tests.cmake + cmake/SimpleTestConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/SimpleTestConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimpleTest +) diff --git a/Help/guide/tutorial/Step10/SimpleTest/CMakePresets.json b/Help/guide/tutorial/Step10/SimpleTest/CMakePresets.json new file mode 100644 index 0000000..816d8a3 --- /dev/null +++ b/Help/guide/tutorial/Step10/SimpleTest/CMakePresets.json @@ -0,0 +1,16 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "SimpleTest Preset", + "description": "Preset to use with the tutorial's SimpleTest library", + "binaryDir": "${sourceDir}/build", + "installDir": "${sourceParentDir}/install", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20", + "CMAKE_PREFIX_PATH": "${sourceParentDir}/install" + } + } + ] +} diff --git a/Help/guide/tutorial/Step10/SimpleTest/SimpleTest.h b/Help/guide/tutorial/Step10/SimpleTest/SimpleTest.h new file mode 100644 index 0000000..ca290d3 --- /dev/null +++ b/Help/guide/tutorial/Step10/SimpleTest/SimpleTest.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include + +namespace SimpleTest { + +using TestFunc = void (*)(); + +using Registry = std::map>; +inline Registry g_registry; + +inline Registry& registry() +{ + return g_registry; +} + +struct failure +{ + char const* file; + int line; + char const* expr; +}; + +struct Registrar +{ + template + Registrar(char const (&name)[N], TestFunc f) + { + auto [it, inserted] = + registry().emplace(std::string_view{ name, N ? (N - 1) : 0 }, f); + if (!inserted) { + std::printf("[ WARN ] duplicate test name: %.*s\n", + int(it->first.size()), it->first.data()); + } + } +}; + +inline Registry const& all() +{ + return registry(); +} +inline TestFunc find(std::string_view name) +{ + auto it = registry().find(name); + return it == registry().end() ? nullptr : it->second; +} + +} + +#define SIMPLETEST_CONCAT_(a, b) a##b +#define SIMPLETEST_CONCAT(a, b) SIMPLETEST_CONCAT_(a, b) + +#define TEST(name_literal) \ + static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)(); \ + static ::SimpleTest::Registrar SIMPLETEST_CONCAT(_simpletest_reg_, \ + __LINE__)( \ + name_literal, &SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)); \ + static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)() + +// Minimal assertion +#define REQUIRE(expr) \ + do { \ + if (!(expr)) \ + throw ::SimpleTest::failure{ __FILE__, __LINE__, #expr }; \ + } while (0) + +int main(int argc, char** argv) +{ + using namespace ::SimpleTest; + + std::string_view arg1 = + (argc >= 2) ? std::string_view{ argv[1] } : std::string_view{}; + + if (arg1 == "--list") { + bool first = true; + for (auto const& [name, _] : registry()) { + if (!first) + std::printf(","); + std::printf("%.*s", int(name.size()), name.data()); + first = false; + } + std::printf("\n"); + return 0; + } + + if (arg1 == "--test") { + if (argc < 3) { + std::printf("usage: %s [--list] [--test ]\n", argv[0]); + return 2; + } + +#ifdef SIMPLETEST_CONFIG + std::printf("SimpleTest built with config: %s\n", SIMPLETEST_CONFIG); +#endif + + std::string_view name{ argv[2] }; + auto it = registry().find(name); + if (it == registry().end()) { + std::printf("[ NOTFOUND ] %s\n", argv[2]); + return 2; + } + + int failed = 0; + std::printf("[ RUN ] %.*s\n", int(it->first.size()), + it->first.data()); + try { + it->second(); + std::printf("[ OK] %.*s\n", int(it->first.size()), + it->first.data()); + } catch (failure const& f) { + std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(it->first.size()), + it->first.data(), f.file, f.line, f.expr); + failed = 1; + } catch (...) { + std::printf("[ FAILED ] %.*s : unknown exception\n", + int(it->first.size()), it->first.data()); + failed = 1; + } + return failed; + } + + if (argc > 1) { + std::printf("usage: %s [--list] [--test ]\n", argv[0]); + return 2; + } + +#ifdef SIMPLETEST_CONFIG + std::printf("SimpleTest built with config: %s\n", SIMPLETEST_CONFIG); +#endif + + // Default: run all tests. + int failed = 0; + for (auto const& [name, func] : all()) { + std::printf("[ RUN ] %.*s\n", int(name.size()), name.data()); + try { + func(); + std::printf("[ OK ] %.*s\n", int(name.size()), name.data()); + } catch (failure const& f) { + std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(name.size()), + name.data(), f.file, f.line, f.expr); + failed = 1; + } catch (...) { + std::printf("[ FAILED ] %.*s : unknown exception\n", int(name.size()), + name.data()); + failed = 1; + } + } + return failed; +} diff --git a/Help/guide/tutorial/Step10/SimpleTest/cmake/SimpleTestConfig.cmake b/Help/guide/tutorial/Step10/SimpleTest/cmake/SimpleTestConfig.cmake new file mode 100644 index 0000000..aba0f75 --- /dev/null +++ b/Help/guide/tutorial/Step10/SimpleTest/cmake/SimpleTestConfig.cmake @@ -0,0 +1,6 @@ +# TODO8: Include the CMakeFindDependencyMacro and use find_dependency to find +# the TransitiveDep package. + + +include(${CMAKE_CURRENT_LIST_DIR}/SimpleTestTargets.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/simpletest_discover_tests.cmake) diff --git a/Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_impl.cmake b/Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_impl.cmake new file mode 100644 index 0000000..7d3a22b --- /dev/null +++ b/Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_impl.cmake @@ -0,0 +1,32 @@ +if(NOT DEFINED TEST_EXE OR NOT DEFINED OUT_FILE) +# noqa: spellcheck off + message(FATAL_ERROR "simpletest_discover: need -DTEST_EXE and -DOUT_FILE") +# noqa: spellcheck on +endif() + +execute_process( + COMMAND ${TEST_EXE} --list + RESULT_VARIABLE _rc + OUTPUT_VARIABLE _out + ERROR_VARIABLE _err + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if(NOT _rc EQUAL 0) + file(WRITE ${OUT_FILE} "# simpletest: --list failed (rc=${_rc})\n") + message(FATAL_ERROR "simpletest_discover: '${TEST_EXE} --list' failed (${_rc})\n${_err}") +endif() + +if(_out STREQUAL "") + file(WRITE ${OUT_FILE} "# simpletest: no tests\n") + return() +endif() + +string(REPLACE "," ";" _names "${_out}") + +file(WRITE ${OUT_FILE} "# Auto-generated by simpletest_discover_impl.cmake\n") +foreach(_name IN LISTS _names) + file(APPEND ${OUT_FILE} + "add_test([=[${_name}]=] \"${TEST_EXE}\" \"--test\" \"${_name}\")\n" + ) +endforeach() diff --git a/Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_tests.cmake b/Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_tests.cmake new file mode 100644 index 0000000..e5cf059 --- /dev/null +++ b/Help/guide/tutorial/Step10/SimpleTest/cmake/simpletest_discover_tests.cmake @@ -0,0 +1,27 @@ +set(_simpletest_impl_script ${CMAKE_CURRENT_LIST_DIR}/simpletest_discover_impl.cmake) + +function(simpletest_discover_tests target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "simpletest_discover_tests: no such target '${target}'") + endif() + + set(_out ${CMAKE_CURRENT_BINARY_DIR}/${target}_ctests.cmake) + + if(NOT EXISTS ${_out}) + file(WRITE ${_out} "# Populated after building ${target}\n") + endif() + +# noqa: spellcheck off + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -DTEST_EXE=$ + -DOUT_FILE=${_out} + -P ${_simpletest_impl_script} + BYPRODUCTS ${_out} + COMMENT "SimpleTest: Discovering tests in ${target}" + VERBATIM + ) +# noqa: spellcheck on + + set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES ${_out}) +endfunction() diff --git a/Help/guide/tutorial/Step10/TutorialConfig.h.in b/Help/guide/tutorial/Step10/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step10/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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/TutorialProject/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/CMakeLists.txt new file mode 100644 index 0000000..d9ca663 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/CMakeLists.txt @@ -0,0 +1,63 @@ +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) + install( + TARGETS Tutorial + EXPORT TutorialTargets + ) +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/Step10/TutorialProject/CMakePresets.json b/Help/guide/tutorial/Step10/TutorialProject/CMakePresets.json new file mode 100644 index 0000000..3926cac --- /dev/null +++ b/Help/guide/tutorial/Step10/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": { + "TODO4": "Add ${sourceParentDir}/install to CMAKE_PREFIX_PATH", + "TUTORIAL_USE_STD_SQRT": "OFF", + "TUTORIAL_ENABLE_IPO": "OFF" + } + } + ] +} diff --git a/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/CMakeLists.txt new file mode 100644 index 0000000..8673342 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/CMakeLists.txt @@ -0,0 +1,54 @@ +add_library(MathFunctions) + +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/Step10/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MakeTable/CMakeLists.txt new file mode 100644 index 0000000..6aa2a32 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx new file mode 100644 index 0000000..f85b278 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MakeTable/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include +#include +#include + +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(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/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathFunctions.cxx new file mode 100644 index 0000000..4bf8051 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathFunctions.cxx @@ -0,0 +1,101 @@ +#include +#include + +#include + +#ifdef TUTORIAL_USE_SSE2 +# include +#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 + +double table_sqrt(double x) +{ + double result = sqrtTable[static_cast(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/Step10/TutorialProject/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathFunctions.h new file mode 100644 index 0000000..91cb176 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathFunctions.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include +#include + +namespace mathfunctions { +double sqrt(double x); +} diff --git a/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..b20151f --- /dev/null +++ b/Help/guide/tutorial/Step10/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/Step10/TutorialProject/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Step10/TutorialProject/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Step10/TutorialProject/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt new file mode 100644 index 0000000..1f4482c --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/Tests/CMakeLists.txt @@ -0,0 +1,31 @@ +add_executable(TestMathFunctions) + +target_sources(TestMathFunctions + PRIVATE + TestMathFunctions.cxx +) + +# TODO1: Find the SimpleTest package. This should be a required dependency when +# building tests. + +# TODO2: Add the SimpleTest::SimpleTest target to Test MathFunctions + +target_link_libraries(TestMathFunctions + PRIVATE + MathFunctions +) + +# TODO3: Replace MathFunctionTest and all the calls to it with +# simpletest_discover_tests called on TestMathFunctions + +function(MathFunctionTest op) + add_test( + NAME ${op} + COMMAND TestMathFunctions ${op} + ) +endfunction() + +MathFunctionTest(add) +MathFunctionTest(mul) +MathFunctionTest(sqrt) +MathFunctionTest(sub) diff --git a/Help/guide/tutorial/Step10/TutorialProject/Tests/TestMathFunctions.cxx b/Help/guide/tutorial/Step10/TutorialProject/Tests/TestMathFunctions.cxx new file mode 100644 index 0000000..68a3419 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/Tests/TestMathFunctions.cxx @@ -0,0 +1,28 @@ +#include + +// TODO5: Replace the following 5 lines with #include +#define TEST(x) namespace +#define REQUIRE(x) +int main() +{ +} + +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/Step10/TutorialProject/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step10/TutorialProject/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..05eac5c --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/Tutorial/CMakeLists.txt @@ -0,0 +1,36 @@ +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() + +# TODO9: Find the path to the folder containing Unpackaged.h. The build should +# fail if this path is not discovered. Note that Unpackaged.h is stored +# in a subdirectory named "Unpackaged". + +# TODO10: Add the discovered path to the Tutorial executable target's +# include directories. diff --git a/Help/guide/tutorial/Step10/TutorialProject/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step10/TutorialProject/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..3b8f911 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/Tutorial/Tutorial.cxx @@ -0,0 +1,28 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +// TODO11: Include the Unpackaged.h header + +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/Step10/TutorialProject/cmake/TutorialConfig.cmake b/Help/guide/tutorial/Step10/TutorialProject/cmake/TutorialConfig.cmake new file mode 100644 index 0000000..d13caa4 --- /dev/null +++ b/Help/guide/tutorial/Step10/TutorialProject/cmake/TutorialConfig.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/TutorialTargets.cmake) diff --git a/Help/guide/tutorial/Step10/install/include/Unpackaged/Unpackaged.h b/Help/guide/tutorial/Step10/install/include/Unpackaged/Unpackaged.h new file mode 100644 index 0000000..9782b0a --- /dev/null +++ b/Help/guide/tutorial/Step10/install/include/Unpackaged/Unpackaged.h @@ -0,0 +1,3 @@ +#pragma once + +#define UNPACKAGED_HEADER_FOUND diff --git a/Help/guide/tutorial/Step10/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake b/Help/guide/tutorial/Step10/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake new file mode 100644 index 0000000..ef51145 --- /dev/null +++ b/Help/guide/tutorial/Step10/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake @@ -0,0 +1,50 @@ +# Abridged import written for the Tutorial + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8) + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +if(CMAKE_VERSION VERSION_LESS "3.0.0") + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 3.0.0...3.30) + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_cmake_targets_defined "") +set(_cmake_targets_not_defined "") +set(_cmake_expected_targets "") +foreach(_cmake_expected_target IN ITEMS TransitiveDep::TransitiveDep) + list(APPEND _cmake_expected_targets "${_cmake_expected_target}") + if(TARGET "${_cmake_expected_target}") + list(APPEND _cmake_targets_defined "${_cmake_expected_target}") + else() + list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}") + endif() +endforeach() +unset(_cmake_expected_target) +if(_cmake_targets_defined STREQUAL _cmake_expected_targets) + unset(_cmake_targets_defined) + unset(_cmake_targets_not_defined) + unset(_cmake_expected_targets) + unset(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT _cmake_targets_defined STREQUAL "") + string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}") + string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n") +endif() +unset(_cmake_targets_defined) +unset(_cmake_targets_not_defined) +unset(_cmake_expected_targets) + +# Create imported target TransitiveDep::TransitiveDep +add_library(TransitiveDep::TransitiveDep INTERFACE IMPORTED) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/Help/guide/tutorial/Step10/tutorial.cxx b/Help/guide/tutorial/Step10/tutorial.cxx deleted file mode 100644 index ca5110c..0000000 --- a/Help/guide/tutorial/Step10/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 deleted file mode 100644 index 9214c88..0000000 --- a/Help/guide/tutorial/Step11/CMakeLists.txt +++ /dev/null @@ -1,85 +0,0 @@ -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 "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) - -# 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_GENERATOR "TGZ") -set(CPACK_SOURCE_GENERATOR "TGZ") -include(CPack) diff --git a/Help/guide/tutorial/Step11/CTestConfig.cmake b/Help/guide/tutorial/Step11/CTestConfig.cmake deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step11/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step11/License.txt b/Help/guide/tutorial/Step11/License.txt deleted file mode 100644 index 85760e5..0000000 --- a/Help/guide/tutorial/Step11/License.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index eacc538..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -# 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") - - include(MakeTable.cmake) # generates Table.h - - # 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} - ) - - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() - -# link MathFunctions to tutorial_compiler_flags -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.cmake b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake deleted file mode 100644 index 12865a9..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - ) diff --git a/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx deleted file mode 100644 index f85b278..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include -#include -#include - -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(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 deleted file mode 100644 index c0991b9..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx +++ /dev/null @@ -1,20 +0,0 @@ - -#include "MathFunctions.h" - -#include - -#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 deleted file mode 100644 index 3fb547b..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.h +++ /dev/null @@ -1,14 +0,0 @@ - -#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 deleted file mode 100644 index 8153f18..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#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(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 deleted file mode 100644 index e1c42ef..0000000 --- a/Help/guide/tutorial/Step11/MathFunctions/mysqrt.h +++ /dev/null @@ -1,6 +0,0 @@ - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step11/SimpleTest/CMakeLists.txt b/Help/guide/tutorial/Step11/SimpleTest/CMakeLists.txt new file mode 100644 index 0000000..866640e --- /dev/null +++ b/Help/guide/tutorial/Step11/SimpleTest/CMakeLists.txt @@ -0,0 +1,54 @@ +# A very simple test framework for demonstrating how dependencies work +cmake_minimum_required(VERSION 3.23) + +project(SimpleTest + VERSION 0.0.1 +) + +add_library(SimpleTest INTERFACE) +target_sources(SimpleTest + INTERFACE + FILE_SET HEADERS + FILES + SimpleTest.h +) +target_compile_features(SimpleTest INTERFACE cxx_std_20) + +# TODO2: Add a compile definition that sets SIMPLETEST_CONFIG=$ +# on the SimpleTest target + +find_package(TransitiveDep REQUIRED) +target_link_libraries(SimpleTest + INTERFACE + TransitiveDep::TransitiveDep +) + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +install( + TARGETS SimpleTest + EXPORT SimpleTestTargets + FILE_SET HEADERS +) + +install( + EXPORT SimpleTestTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimpleTest + NAMESPACE SimpleTest:: +) + +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/SimpleTestConfigVersion.cmake + COMPATIBILITY ExactVersion + ARCH_INDEPENDENT +) + +install( + FILES + cmake/simpletest_discover_impl.cmake + cmake/simpletest_discover_tests.cmake + cmake/SimpleTestConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/SimpleTestConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SimpleTest +) diff --git a/Help/guide/tutorial/Step11/SimpleTest/CMakePresets.json b/Help/guide/tutorial/Step11/SimpleTest/CMakePresets.json new file mode 100644 index 0000000..816d8a3 --- /dev/null +++ b/Help/guide/tutorial/Step11/SimpleTest/CMakePresets.json @@ -0,0 +1,16 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "SimpleTest Preset", + "description": "Preset to use with the tutorial's SimpleTest library", + "binaryDir": "${sourceDir}/build", + "installDir": "${sourceParentDir}/install", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "20", + "CMAKE_PREFIX_PATH": "${sourceParentDir}/install" + } + } + ] +} diff --git a/Help/guide/tutorial/Step11/SimpleTest/SimpleTest.h b/Help/guide/tutorial/Step11/SimpleTest/SimpleTest.h new file mode 100644 index 0000000..ced6562 --- /dev/null +++ b/Help/guide/tutorial/Step11/SimpleTest/SimpleTest.h @@ -0,0 +1,155 @@ +#pragma once + +#include +#include +#include + +namespace SimpleTest { + +using TestFunc = void (*)(); + +using Registry = std::map>; +inline Registry g_registry; + +inline Registry& registry() +{ + return g_registry; +} + +struct failure +{ + char const* file; + int line; + char const* expr; +}; + +struct Registrar +{ + template + Registrar(char const (&name)[N], TestFunc f) + { + auto [it, inserted] = + registry().emplace(std::string_view{ name, N ? (N - 1) : 0 }, f); + if (!inserted) { + std::printf("[ WARN ] duplicate test name: %.*s\n", + int(it->first.size()), it->first.data()); + } + } +}; + +inline Registry const& all() +{ + return registry(); +} +inline TestFunc find(std::string_view name) +{ + auto it = registry().find(name); + return it == registry().end() ? nullptr : it->second; +} + +} + +#define SIMPLETEST_STRINGIFY(a) #a +#define SIMPLETEST_XSTRINGIFY(a) SIMPLETEST_STRINGIFY(a) +#define SIMPLETEST_CONCAT_(a, b) a##b +#define SIMPLETEST_CONCAT(a, b) SIMPLETEST_CONCAT_(a, b) + +#define TEST(name_literal) \ + static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)(); \ + static ::SimpleTest::Registrar SIMPLETEST_CONCAT(_simpletest_reg_, \ + __LINE__)( \ + name_literal, &SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)); \ + static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)() + +// Minimal assertion +#define REQUIRE(expr) \ + do { \ + if (!(expr)) \ + throw ::SimpleTest::failure{ __FILE__, __LINE__, #expr }; \ + } while (0) + +int main(int argc, char** argv) +{ + using namespace ::SimpleTest; + + std::string_view arg1 = + (argc >= 2) ? std::string_view{ argv[1] } : std::string_view{}; + + if (arg1 == "--list") { + bool first = true; + for (auto const& [name, _] : registry()) { + if (!first) + std::printf(","); + std::printf("%.*s", int(name.size()), name.data()); + first = false; + } + std::printf("\n"); + return 0; + } + + if (arg1 == "--test") { + if (argc < 3) { + std::printf("usage: %s [--list] [--test ]\n", argv[0]); + return 2; + } + +#ifdef SIMPLETEST_CONFIG + std::printf("SimpleTest built with config: " SIMPLETEST_XSTRINGIFY( + SIMPLETEST_CONFIG) "\n"); +#endif + + std::string_view name{ argv[2] }; + auto it = registry().find(name); + if (it == registry().end()) { + std::printf("[ NOTFOUND ] %s\n", argv[2]); + return 2; + } + + int failed = 0; + std::printf("[ RUN ] %.*s\n", int(it->first.size()), + it->first.data()); + try { + it->second(); + std::printf("[ OK] %.*s\n", int(it->first.size()), + it->first.data()); + } catch (failure const& f) { + std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(it->first.size()), + it->first.data(), f.file, f.line, f.expr); + failed = 1; + } catch (...) { + std::printf("[ FAILED ] %.*s : unknown exception\n", + int(it->first.size()), it->first.data()); + failed = 1; + } + return failed; + } + + if (argc > 1) { + std::printf("usage: %s [--list] [--test ]\n", argv[0]); + return 2; + } + +#ifdef SIMPLETEST_CONFIG + std::printf("SimpleTest built with config: " SIMPLETEST_XSTRINGIFY( + SIMPLETEST_CONFIG) "\n"); +#endif + + // Default: run all tests. + int failed = 0; + for (auto const& [name, func] : all()) { + std::printf("[ RUN ] %.*s\n", int(name.size()), name.data()); + try { + func(); + std::printf("[ OK ] %.*s\n", int(name.size()), name.data()); + } catch (failure const& f) { + std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(name.size()), + name.data(), f.file, f.line, f.expr); + failed = 1; + } catch (...) { + std::printf("[ FAILED ] %.*s : unknown exception\n", int(name.size()), + name.data()); + failed = 1; + } + } + return failed; +} diff --git a/Help/guide/tutorial/Step11/SimpleTest/cmake/SimpleTestConfig.cmake b/Help/guide/tutorial/Step11/SimpleTest/cmake/SimpleTestConfig.cmake new file mode 100644 index 0000000..6c7ffb5 --- /dev/null +++ b/Help/guide/tutorial/Step11/SimpleTest/cmake/SimpleTestConfig.cmake @@ -0,0 +1,5 @@ +include(CMakeFindDependencyMacro) +find_dependency(TransitiveDep) + +include(${CMAKE_CURRENT_LIST_DIR}/SimpleTestTargets.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/simpletest_discover_tests.cmake) diff --git a/Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_impl.cmake b/Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_impl.cmake new file mode 100644 index 0000000..7d3a22b --- /dev/null +++ b/Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_impl.cmake @@ -0,0 +1,32 @@ +if(NOT DEFINED TEST_EXE OR NOT DEFINED OUT_FILE) +# noqa: spellcheck off + message(FATAL_ERROR "simpletest_discover: need -DTEST_EXE and -DOUT_FILE") +# noqa: spellcheck on +endif() + +execute_process( + COMMAND ${TEST_EXE} --list + RESULT_VARIABLE _rc + OUTPUT_VARIABLE _out + ERROR_VARIABLE _err + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if(NOT _rc EQUAL 0) + file(WRITE ${OUT_FILE} "# simpletest: --list failed (rc=${_rc})\n") + message(FATAL_ERROR "simpletest_discover: '${TEST_EXE} --list' failed (${_rc})\n${_err}") +endif() + +if(_out STREQUAL "") + file(WRITE ${OUT_FILE} "# simpletest: no tests\n") + return() +endif() + +string(REPLACE "," ";" _names "${_out}") + +file(WRITE ${OUT_FILE} "# Auto-generated by simpletest_discover_impl.cmake\n") +foreach(_name IN LISTS _names) + file(APPEND ${OUT_FILE} + "add_test([=[${_name}]=] \"${TEST_EXE}\" \"--test\" \"${_name}\")\n" + ) +endforeach() diff --git a/Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_tests.cmake b/Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_tests.cmake new file mode 100644 index 0000000..e5cf059 --- /dev/null +++ b/Help/guide/tutorial/Step11/SimpleTest/cmake/simpletest_discover_tests.cmake @@ -0,0 +1,27 @@ +set(_simpletest_impl_script ${CMAKE_CURRENT_LIST_DIR}/simpletest_discover_impl.cmake) + +function(simpletest_discover_tests target) + if(NOT TARGET ${target}) + message(FATAL_ERROR "simpletest_discover_tests: no such target '${target}'") + endif() + + set(_out ${CMAKE_CURRENT_BINARY_DIR}/${target}_ctests.cmake) + + if(NOT EXISTS ${_out}) + file(WRITE ${_out} "# Populated after building ${target}\n") + endif() + +# noqa: spellcheck off + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} + -DTEST_EXE=$ + -DOUT_FILE=${_out} + -P ${_simpletest_impl_script} + BYPRODUCTS ${_out} + COMMENT "SimpleTest: Discovering tests in ${target}" + VERBATIM + ) +# noqa: spellcheck on + + set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES ${_out}) +endfunction() diff --git a/Help/guide/tutorial/Step11/TutorialConfig.h.in b/Help/guide/tutorial/Step11/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step11/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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/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 +#include +#include + +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(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 +#include + +#include + +#ifdef TUTORIAL_USE_SSE2 +# include +#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 + +double table_sqrt(double x) +{ + double result = sqrtTable[static_cast(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 +#include +#include + +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 + +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 + +#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 +#include + +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 +#include + +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 +#include +#include + +#include +#include + +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) diff --git a/Help/guide/tutorial/Step11/install/include/Unpackaged/Unpackaged.h b/Help/guide/tutorial/Step11/install/include/Unpackaged/Unpackaged.h new file mode 100644 index 0000000..9782b0a --- /dev/null +++ b/Help/guide/tutorial/Step11/install/include/Unpackaged/Unpackaged.h @@ -0,0 +1,3 @@ +#pragma once + +#define UNPACKAGED_HEADER_FOUND diff --git a/Help/guide/tutorial/Step11/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake b/Help/guide/tutorial/Step11/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake new file mode 100644 index 0000000..ef51145 --- /dev/null +++ b/Help/guide/tutorial/Step11/install/lib/cmake/TransitiveDep/TransitiveDepConfig.cmake @@ -0,0 +1,50 @@ +# Abridged import written for the Tutorial + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8) + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +if(CMAKE_VERSION VERSION_LESS "3.0.0") + message(FATAL_ERROR "CMake >= 3.0.0 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 3.0.0...3.30) + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_cmake_targets_defined "") +set(_cmake_targets_not_defined "") +set(_cmake_expected_targets "") +foreach(_cmake_expected_target IN ITEMS TransitiveDep::TransitiveDep) + list(APPEND _cmake_expected_targets "${_cmake_expected_target}") + if(TARGET "${_cmake_expected_target}") + list(APPEND _cmake_targets_defined "${_cmake_expected_target}") + else() + list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}") + endif() +endforeach() +unset(_cmake_expected_target) +if(_cmake_targets_defined STREQUAL _cmake_expected_targets) + unset(_cmake_targets_defined) + unset(_cmake_targets_not_defined) + unset(_cmake_expected_targets) + unset(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT _cmake_targets_defined STREQUAL "") + string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}") + string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n") +endif() +unset(_cmake_targets_defined) +unset(_cmake_targets_not_defined) +unset(_cmake_expected_targets) + +# Create imported target TransitiveDep::TransitiveDep +add_library(TransitiveDep::TransitiveDep INTERFACE IMPORTED) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/Help/guide/tutorial/Step11/tutorial.cxx b/Help/guide/tutorial/Step11/tutorial.cxx deleted file mode 100644 index ca5110c..0000000 --- a/Help/guide/tutorial/Step11/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 deleted file mode 100644 index a84590f..0000000 --- a/Help/guide/tutorial/Step12/CMakeLists.txt +++ /dev/null @@ -1,124 +0,0 @@ -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 "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) - -# 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}") -set(CPACK_GENERATOR "TGZ") -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 includes the exports -configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake" - INSTALL_DESTINATION "lib/cmake/MathFunctions" - 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 deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step12/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step12/Config.cmake.in b/Help/guide/tutorial/Step12/Config.cmake.in deleted file mode 100644 index 17cbabd..0000000 --- a/Help/guide/tutorial/Step12/Config.cmake.in +++ /dev/null @@ -1,4 +0,0 @@ - -@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 deleted file mode 100644 index 85760e5..0000000 --- a/Help/guide/tutorial/Step12/License.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 8aa5904..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -# 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 - $ - $ - ) - -# 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") - - include(MakeTable.cmake) # generates Table.h - - # 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} - ) - - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() - -# link MathFunctions to tutorial_compiler_flags -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.cmake b/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake deleted file mode 100644 index 12865a9..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - ) diff --git a/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx deleted file mode 100644 index f85b278..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include -#include -#include - -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(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 deleted file mode 100644 index c0991b9..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx +++ /dev/null @@ -1,20 +0,0 @@ - -#include "MathFunctions.h" - -#include - -#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 deleted file mode 100644 index 3fb547b..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.h +++ /dev/null @@ -1,14 +0,0 @@ - -#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 deleted file mode 100644 index 8153f18..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#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(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 deleted file mode 100644 index e1c42ef..0000000 --- a/Help/guide/tutorial/Step12/MathFunctions/mysqrt.h +++ /dev/null @@ -1,6 +0,0 @@ - -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 deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step12/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index 78641b1..0000000 --- a/Help/guide/tutorial/Step12/tutorial.cxx +++ /dev/null @@ -1,26 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 deleted file mode 100644 index 0a06ed7..0000000 --- a/Help/guide/tutorial/Step2/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -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/Exercise1.cmake b/Help/guide/tutorial/Step2/Exercise1.cmake new file mode 100644 index 0000000..3816de6 --- /dev/null +++ b/Help/guide/tutorial/Step2/Exercise1.cmake @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.23) + + +# TODO1: Implement MacroAppend +macro(MacroAppend ListVar Value) + +endmacro() + +# TODO2: Call MacroAppend, then return the value from FuncAppend +function(FuncAppend ListVar Value) + +endfunction() + + + +# Testing for the above, final expected value is "Alpha;Beta;Gamma;Delta" +if(SKIP_TESTS) + return() +endif() + +set(Original "Beta;Gamma") +set(Expected "Alpha;Beta;Gamma;Delta") + +set(BeginList ${Original}) +set(EndList "Alpha") + +MacroAppend(BeginList "Delta") +foreach(value IN LISTS BeginList) + MacroAppend(EndList ${value}) +endforeach() + +if(BeginList STREQUAL Original) + message("MacroAppend unimplemented or did nothing") +elseif(NOT EndList STREQUAL Expected) + message(WARNING "MacroAppend error, final value: ${EndList}") +else() + message("MacroAppend correct") +endif() + +set(BeginList ${Original}) +set(EndList "Alpha") + +FuncAppend(BeginList "Delta") +foreach(value IN LISTS BeginList) + FuncAppend(EndList ${value}) +endforeach() + +if(BeginList STREQUAL Original) + message("FuncAppend unimplemented or did nothing") +elseif(NOT EndList STREQUAL Expected) + message(WARNING "FuncAppend error, final value: ${EndList}") +else() + message("FuncAppend correct") +endif() + +# Bonus Tests + +FuncAppend(UndefinedList "Test") + +set(EmptyList "") +FuncAppend(EmptyList "Test") + +set(FalseList "False") +FuncAppend(FalseList "Test") + +if( + (UndefinedList STREQUAL "Test") AND + (EmptyList STREQUAL "Test") AND + (FalseList STREQUAL "False;Test") +) + message("You implemented the empty list case, well done!") +endif() diff --git a/Help/guide/tutorial/Step2/Exercise2.cmake b/Help/guide/tutorial/Step2/Exercise2.cmake new file mode 100644 index 0000000..ee4e9e8 --- /dev/null +++ b/Help/guide/tutorial/Step2/Exercise2.cmake @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.23) + + +function(FilterFoo OutVar) +# TODO3: Search all the variables in the argument list passed to FilterFoo, +# and place those containing "Foo" into the list named by "OutVar" + + set(${OutVar} ${${OutVar}} PARENT_SCOPE) +endfunction() + + + +# Testing for the above +function(check_contains var) + if(NOT var IN_LIST OutList) + message(WARNING "OutList does not contain: ${var}") + set(Failed True PARENT_SCOPE) + endif() +endfunction() + +function(check_nonfoo) + list(FILTER ARGN EXCLUDE REGEX Foo) + if(NOT ARGN STREQUAL "") + message(WARNING "OutList contains extra item(s): ${ARGN}") + set(Failed True PARENT_SCOPE) + endif() +endfunction() + +if(SKIP_TESTS) + return() +endif() + +set(InList FooBar BarBaz FooBaz BazBar QuxFoo BazQux) + +FilterFoo(OutList ${InList}) + +if(NOT DEFINED OutList) + message("FilterFoo unimplemented or does nothing") + return() +endif() + +set(Failed False) + +check_contains(FooBar) +check_contains(FooBaz) +check_contains(QuxFoo) +check_nonfoo(${OutList}) + +if(NOT Failed) + message("Success!") +endif() diff --git a/Help/guide/tutorial/Step2/Exercise3.cmake b/Help/guide/tutorial/Step2/Exercise3.cmake new file mode 100644 index 0000000..439aa25 --- /dev/null +++ b/Help/guide/tutorial/Step2/Exercise3.cmake @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.23) + + +# TODO4: Set the SKIP_TESTS variable to a true value, so that the tests from +# Exercise1 and Exercise2 are skipped + + +# TODO5: Include Exercise1.cmake and Exercise2.cmake + + +set(InList FooBar QuxBar) + +# TODO6: Append FooBaz and QuxBaz to InList with FuncAppend + + +if(NOT InList STREQUAL "FooBar;QuxBar;FooBaz;QuxBaz") + message(WARNING "Append failed, InList contains: ${InList}") +endif() + + +# TODO7: Filter InList with FilterFoo, use OutList as the output variable + + +check_contains(FooBar) +check_contains(FooBaz) +check_nonfoo(${OutList}) + +if(NOT Failed) + message("Success!") +endif() diff --git a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt deleted file mode 100644 index c3cd806..0000000 --- a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# TODO 14: Remove mysqrt.cxx from the list of sources - -# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx -# and mysqrt.cxx -# 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 - -# TODO 12: When USE_MYMATH is ON, add a library for SqrtLibrary with -# source mysqrt.cxx - -# TODO 13: When USE_MYMATH is ON, link SqrtLibrary to the MathFunctions Library diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx deleted file mode 100644 index 781d0ec..0000000 --- a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx +++ /dev/null @@ -1,15 +0,0 @@ -#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 deleted file mode 100644 index d5c2f22..0000000 --- a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h +++ /dev/null @@ -1,5 +0,0 @@ -#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 deleted file mode 100644 index ba0ac64..0000000 --- a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,28 +0,0 @@ -#include "mysqrt.h" - -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#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 deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step2/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index b4063c8..0000000 --- a/Help/guide/tutorial/Step2/tutorial.cxx +++ /dev/null @@ -1,29 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -// 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 - double const inputValue = std::stod(argv[1]); - - // TODO 6: Replace sqrt with mathfunctions::sqrt - - // calculate square root - double const 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 index ac3e9f1..916ad08 100644 --- a/Help/guide/tutorial/Step3/CMakeLists.txt +++ b/Help/guide/tutorial/Step3/CMakeLists.txt @@ -1,40 +1,18 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.23) -# set the project name and version -project(Tutorial VERSION 1.0) +project(Tutorial) -# TODO 4: 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() +# TODO1: Add a default ON option for a cache variable named: +# TUTORIAL_BUILD_UTILITIES. +# option() requires a doc string as its second argument, set this to +# something like: +# "Build the Tutorial executable" -# 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) +# TODO2: Add a conditional statement around add_subdirectory(Tutorial). Only +# build the Tutorial target if TUTORIAL_BUILD_UTILITIES is ON (or +# otherwise truthy). -# TODO 2: Remove EXTRA_INCLUDES list +add_subdirectory(Tutorial) -# add the MathFunctions library add_subdirectory(MathFunctions) -list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions") - -# add the executable -add_executable(Tutorial tutorial.cxx) - -# TODO 5: Link Tutorial to tutorial_compiler_flags - -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/CMakePresets.json b/Help/guide/tutorial/Step3/CMakePresets.json new file mode 100644 index 0000000..33f5968 --- /dev/null +++ b/Help/guide/tutorial/Step3/CMakePresets.json @@ -0,0 +1,14 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "TODO8": "Set the build directory", + "cacheVariables": { + "TODO9": "Set the C++ standard version" + } + } + ] +} diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt index 74c553f..dbc7f95 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt @@ -1,22 +1,11 @@ -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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 +target_sources(MathFunctions + PRIVATE + MathFunctions.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") - - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ) - - # TODO 6: Link SqrtLibrary to tutorial_compiler_flags - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() - -# TODO 7: Link MathFunctions to tutorial_compiler_flags + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx index dc28b4b..5f519b4 100644 --- a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx @@ -1,19 +1,35 @@ -#include "MathFunctions.h" +#include -#include +// TODO6: Include -#ifdef USE_MYMATH -# include "mysqrt.h" -#endif +namespace { +// 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; + + // TODO7: Convert the print to use std::format + std::cout << "Computing sqrt of " << x << " to be " << result << std::endl; + } + return result; +} +} 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 + return mysqrt(x); } } diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx deleted file mode 100644 index ba0ac64..0000000 --- a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,28 +0,0 @@ -#include "mysqrt.h" - -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step3/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step3/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..0dc1f58 --- /dev/null +++ b/Help/guide/tutorial/Step3/Tutorial/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(Tutorial) + +target_sources(Tutorial + PRIVATE + Tutorial.cxx +) + +target_link_libraries(Tutorial + PRIVATE + MathFunctions +) diff --git a/Help/guide/tutorial/Step3/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step3/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..c630f30 --- /dev/null +++ b/Help/guide/tutorial/Step3/Tutorial/Tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number + +// TODO3: Include + +#include +#include + +#include + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + // TODO4: Convert the print to use std::format + std::cout << "Usage: " << argv[0] << " number" << std::endl; + return 1; + } + + // convert input to double + double const inputValue = std::stod(argv[1]); + + // calculate square root + double const outputValue = mathfunctions::sqrt(inputValue); + // TODO5: Convert the print to use std::format + std::cout << "The square root of " << inputValue << " is " << outputValue + << std::endl; +} diff --git a/Help/guide/tutorial/Step3/TutorialConfig.h.in b/Help/guide/tutorial/Step3/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step3/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step3/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 index fba9766..7d09051 100644 --- a/Help/guide/tutorial/Step4/CMakeLists.txt +++ b/Help/guide/tutorial/Step4/CMakeLists.txt @@ -1,44 +1,15 @@ -# TODO 1: Update the minimum required version to 3.15 +cmake_minimum_required(VERSION 3.23) -cmake_minimum_required(VERSION 3.10) +project(Tutorial) -# set the project name and version -project(Tutorial VERSION 1.0) +option(TUTORIAL_BUILD_UTILITIES "Build the Tutorial executable" ON) -# specify the C++ standard -add_library(tutorial_compiler_flags INTERFACE) -target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) +# TODO1: Add a default-OFF option named TUTORIAL_USE_STD_SQRT, with a doc +# string of "Use std::sqrt" -# TODO 2: 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 +if(TUTORIAL_BUILD_UTILITIES) + add_subdirectory(Tutorial) +endif() -# TODO 3: 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 4: 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) - -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_subdirectory(Vendor) diff --git a/Help/guide/tutorial/Step4/CMakePresets.json b/Help/guide/tutorial/Step4/CMakePresets.json new file mode 100644 index 0000000..6c47447 --- /dev/null +++ b/Help/guide/tutorial/Step4/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": { + "TODO7": "Remove the CMAKE_CXX_STANDARD (and this TODO)", + "CMAKE_CXX_STANDARD": "20", + "TODO8": "Set TUTORIAL_USE_STD_SQRT to ON" + } + } + ] +} diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt index 6931898..213aafb 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt @@ -1,27 +1,16 @@ -# create the MathFunctions library -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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} - ) +target_sources(MathFunctions + PRIVATE + MathFunctions.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") + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ) +# TODO2: Add a compile feature for C++20 support to MathFunctions - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() - -# link MathFunctions to tutorial_compiler_flags -target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) +# TODO3: Add a conditional which checks TUTORIAL_USE_STD_SQRT and if +# ON, set a compile definition on MathFunctions of the same name diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx index dc28b4b..4635cc4 100644 --- a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx @@ -1,19 +1,38 @@ -#include "MathFunctions.h" +// TODO5: Include -#include +#include +#include -#ifdef USE_MYMATH -# include "mysqrt.h" -#endif +namespace { +// 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 << std::format("Computing sqrt of {} to be {}\n", x, result); + } + return result; +} +} 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 + // TODO6: Check if TUTORIAL_USE_STD_SQRT is defined, if so use std::sqrt + // instead of mysqrt + + return mysqrt(x); } } diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx deleted file mode 100644 index ba0ac64..0000000 --- a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,28 +0,0 @@ -#include "mysqrt.h" - -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step4/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step4/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..65a1e74 --- /dev/null +++ b/Help/guide/tutorial/Step4/Tutorial/CMakeLists.txt @@ -0,0 +1,33 @@ +add_executable(Tutorial) + +target_sources(Tutorial + PRIVATE + Tutorial.cxx +) + +# TODO14: Add VendorLib to Tutorial + +target_link_libraries(Tutorial + PRIVATE + MathFunctions +) + +# TODO4: Add a compile feature for C++20 support to Tutorial + + +if( + (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR + (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") +) + + # TODO9: Add the /W3 compile flag to Tutorial + +elseif( + (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR + (CMAKE_CXX_COMPILER_ID MATCHES "Clang") +) + + # TODO10: Add the -Wall compile flag to Tutorial + + +endif() diff --git a/Help/guide/tutorial/Step4/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step4/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..2bb79bf --- /dev/null +++ b/Help/guide/tutorial/Step4/Tutorial/Tutorial.cxx @@ -0,0 +1,36 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +#ifdef TUTORIAL_USE_VENDORLIB +# include +#endif + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + std::cout << std::format("Usage: {} number\n", argv[0]); + return 1; + } + + int unused; + + // 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); + +#ifdef TUTORIAL_USE_VENDORLIB + if (CheckSqrt(inputValue, outputValue)) { + std::cout << "Sqrt verified by vendor\n"; + } else { + std::cout << "Sqrt rejected by vendor\n"; + } +#endif +} diff --git a/Help/guide/tutorial/Step4/TutorialConfig.h.in b/Help/guide/tutorial/Step4/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step4/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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/Vendor/CMakeLists.txt b/Help/guide/tutorial/Step4/Vendor/CMakeLists.txt new file mode 100644 index 0000000..5850a8c --- /dev/null +++ b/Help/guide/tutorial/Step4/Vendor/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(VendorLib INTERFACE) +target_compile_definitions(VendorLib INTERFACE TUTORIAL_USE_VENDORLIB) + +# TODO11: Add the include directory to VendorLib + +# TODO12: Add the lib directory to VendorLib + +# TODO13: Add the Vendor archive to VendorLib diff --git a/Help/guide/tutorial/Step4/Vendor/include/Vendor.h b/Help/guide/tutorial/Step4/Vendor/include/Vendor.h new file mode 100644 index 0000000..78742c4 --- /dev/null +++ b/Help/guide/tutorial/Step4/Vendor/include/Vendor.h @@ -0,0 +1,3 @@ +#pragma once + +bool CheckSqrt(double val, double sqrt); diff --git a/Help/guide/tutorial/Step4/Vendor/lib/Vendor.cxx b/Help/guide/tutorial/Step4/Vendor/lib/Vendor.cxx new file mode 100644 index 0000000..a6e6c9a --- /dev/null +++ b/Help/guide/tutorial/Step4/Vendor/lib/Vendor.cxx @@ -0,0 +1,8 @@ +bool CheckSqrt(double val, double sqrt) +{ + double pow2 = sqrt * sqrt; + double delta = val > pow2 ? val - pow2 : pow2 - val; + + // Close enough! + return delta < 0.1; +} diff --git a/Help/guide/tutorial/Step4/tutorial.cxx b/Help/guide/tutorial/Step4/tutorial.cxx deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step4/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 index ad814f6..f5ed532 100644 --- a/Help/guide/tutorial/Step5/CMakeLists.txt +++ b/Help/guide/tutorial/Step5/CMakeLists.txt @@ -1,59 +1,12 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.23) -# set the project name and version -project(Tutorial VERSION 1.0) +project(Tutorial) -# specify the C++ standard -add_library(tutorial_compiler_flags INTERFACE) -target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) +option(TUTORIAL_BUILD_UTILITIES "Build the Tutorial executable" ON) +option(TUTORIAL_USE_STD_SQRT "Use std::sqrt" OFF) -# add compiler warning flags just when building this project via -# the BUILD_INTERFACE genex -set(gcc_like_cxx "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) +if(TUTORIAL_BUILD_UTILITIES) + add_subdirectory(Tutorial) +endif() -# 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/CMakePresets.json b/Help/guide/tutorial/Step5/CMakePresets.json new file mode 100644 index 0000000..b8ca15f --- /dev/null +++ b/Help/guide/tutorial/Step5/CMakePresets.json @@ -0,0 +1,14 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "TUTORIAL_USE_STD_SQRT": "OFF" + } + } + ] +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt index 61b3899..7aebf48 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt @@ -1,34 +1,29 @@ -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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} - ) +target_sources(MathFunctions + PRIVATE + MathFunctions.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") + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ) +# TODO3: Add a link to MathLogger for the MathFunctions library. Note that +# MathLogger will only be used in the MathFunctions implementation, +# not the headers - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) +# TODO8: Add links to MathLogger for the OpAdd, OpMul, and OpSub libraries. +# Note that their headers will be exposed in the MathFunctions.h +# header, and must be available to consumers. - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) -endif() +target_compile_features(MathFunctions PRIVATE cxx_std_20) -# link MathFunctions to tutorial_compiler_flags -target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) +if(TUTORIAL_USE_STD_SQRT) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_STD_SQRT) +endif() -# 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 +# TODO4: Add the MathLogger subdirectory -# TODO 2: Install the library headers to the include folder. -# Hint: Use the FILES and DESTINATION parameters +# TODO9: Add the MathExtensions subdirectory diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step5/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Step5/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/Step5/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx index dc28b4b..811209f 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx @@ -1,19 +1,45 @@ -#include "MathFunctions.h" - #include +#include -#ifdef USE_MYMATH -# include "mysqrt.h" -#endif +// TODO5: Replace with + +#include + +namespace { + +// TODO6: Instantiate a logger inside the anonymous namespace + +// 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; + + // TODO7: Use the logger to log the message + std::cout << std::format("Computing sqrt of {} to be {}\n", x, result); + } + return result; +} +} namespace mathfunctions { double sqrt(double x) { -// which square root function should we use? -#ifdef USE_MYMATH - return detail::mysqrt(x); -#else +#ifdef TUTORIAL_USE_STD_SQRT return std::sqrt(x); +#else + return mysqrt(x); #endif } } diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h index d5c2f22..fcc469b 100644 --- a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h @@ -1,5 +1,7 @@ #pragma once +// TODO10: Include , , and + namespace mathfunctions { double sqrt(double x); } diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..1077c7f --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/CMakeLists.txt @@ -0,0 +1,6 @@ + +# TODO1: Add an INTERFACE library called MathLogger + + +# TODO2: Add an appropriate FILE_SET to MathLogger to capture the headers in +# this directory diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Step5/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Step5/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Step5/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx deleted file mode 100644 index ba0ac64..0000000 --- a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,28 +0,0 @@ -#include "mysqrt.h" - -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step5/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step5/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..3daa5ea --- /dev/null +++ b/Help/guide/tutorial/Step5/Tutorial/CMakeLists.txt @@ -0,0 +1,29 @@ +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() diff --git a/Help/guide/tutorial/Step5/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step5/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..e3e3604 --- /dev/null +++ b/Help/guide/tutorial/Step5/Tutorial/Tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +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); + + // TODO11: Check the calculated square root using mathfunctions::OpMul to + // square the outputValue. Output the result with the format: + // "The square of {} is {}\n" +} diff --git a/Help/guide/tutorial/Step5/TutorialConfig.h.in b/Help/guide/tutorial/Step5/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step5/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step5/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 index a86d60a..f699687 100644 --- a/Help/guide/tutorial/Step6/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/CMakeLists.txt @@ -1,71 +1,22 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.23) -# set the project name and version -project(Tutorial VERSION 1.0) +project(Tutorial) -# specify the C++ standard -add_library(tutorial_compiler_flags INTERFACE) -target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) +option(TUTORIAL_BUILD_UTILITIES "Build the Tutorial executable" ON) +option(TUTORIAL_USE_STD_SQRT "Use std::sqrt" OFF) -# add compiler warning flags just when building this project via -# the BUILD_INTERFACE genex -set(gcc_like_cxx "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) +# TODO6: Add a default-ON option named TUTORIAL_ENABLE_IPO with a doc string: +# "Check for and use IPO support" -# configure a header file to pass some of the CMake settings -# to the source code -configure_file(TutorialConfig.h.in TutorialConfig.h) +# TODO7: Include and use the CheckIPOSupported module to enable IPO if +# TUTORIAL_ENABLE_IPO is True. To enable IPO, use: +# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION True) +# Otherwise, follow the examples in the CheckIPOSupported documentation. +# Specifically, follow the documentation example to emit an error message +# if IPO is unavailable. -# 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 - ) +if(TUTORIAL_BUILD_UTILITIES) + add_subdirectory(Tutorial) +endif() -# 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") +add_subdirectory(MathFunctions) diff --git a/Help/guide/tutorial/Step6/CMakePresets.json b/Help/guide/tutorial/Step6/CMakePresets.json new file mode 100644 index 0000000..b8ca15f --- /dev/null +++ b/Help/guide/tutorial/Step6/CMakePresets.json @@ -0,0 +1,14 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "TUTORIAL_USE_STD_SQRT": "OFF" + } + } + ] +} diff --git a/Help/guide/tutorial/Step6/CTestConfig.cmake b/Help/guide/tutorial/Step6/CTestConfig.cmake deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step6/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt index 8499a51..bac1e65 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt @@ -1,35 +1,49 @@ -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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} - ) +target_sources(MathFunctions + PRIVATE + MathFunctions.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") + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ) +target_link_libraries(MathFunctions + PRIVATE + MathLogger - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + PUBLIC + OpAdd + OpMul + OpSub +) - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +target_compile_features(MathFunctions PRIVATE cxx_std_20) + +if(TUTORIAL_USE_STD_SQRT) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_STD_SQRT) endif() -# link MathFunctions to tutorial_compiler_flags -target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) +# TODO1: Include the CheckIncludeFiles module and use it to check for +# the emmintrin.h header. -# 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) +# TODO2: If emmintrin.h is available, add a compile definition to MathFunctions +# named TUTORIAL_USE_SSE2. This will only be needed by the MathFunctions +# implementation file. + +# TODO4: Include the CheckSourceCompiles module and use it to check if the +# following program compiles: +# +# typedef double v2df __attribute__((vector_size(16))); +# int main() { +# __builtin_ia32_sqrtsd(v2df{}); +# } + +# TODO5: If the GNU builtins are available, add a compile definition to +# MathFunctions named TUTORIAL_USE_GNU_BUILTIN. This will only be needed +# by the MathFunctions implementation file. + +add_subdirectory(MathLogger) +add_subdirectory(MathExtensions) diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step6/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx index dc28b4b..eae6c32 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx @@ -1,19 +1,79 @@ -#include "MathFunctions.h" - #include +#include + +#include + +// TODO3: If the TUTORIAL_USE_SSE2 definition is set, include +// the header + +namespace { -#ifdef USE_MYMATH -# include "mysqrt.h" +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; +} + +double mysqrt(double 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) { -// which square root function should we use? -#ifdef USE_MYMATH - return detail::mysqrt(x); -#else +#ifdef TUTORIAL_USE_STD_SQRT return std::sqrt(x); +#else + return mysqrt(x); #endif } } diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h index d5c2f22..91cb176 100644 --- a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + namespace mathfunctions { double sqrt(double x); } diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..b20151f --- /dev/null +++ b/Help/guide/tutorial/Step6/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/Step6/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Step6/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Step6/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Step6/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx deleted file mode 100644 index ba0ac64..0000000 --- a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,28 +0,0 @@ -#include "mysqrt.h" - -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step6/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step6/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..3daa5ea --- /dev/null +++ b/Help/guide/tutorial/Step6/Tutorial/CMakeLists.txt @@ -0,0 +1,29 @@ +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() diff --git a/Help/guide/tutorial/Step6/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step6/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..44b7831 --- /dev/null +++ b/Help/guide/tutorial/Step6/Tutorial/Tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +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/Step6/TutorialConfig.h.in b/Help/guide/tutorial/Step6/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step6/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step6/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 index 97ec6aa..2502a38 100644 --- a/Help/guide/tutorial/Step7/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/CMakeLists.txt @@ -1,70 +1,24 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.23) -# set the project name and version -project(Tutorial VERSION 1.0) +project(Tutorial) -# specify the C++ standard -add_library(tutorial_compiler_flags INTERFACE) -target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) +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) -# add compiler warning flags just when building this project via -# the BUILD_INTERFACE genex -set(gcc_like_cxx "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) +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() -# 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 - ) +if(TUTORIAL_BUILD_UTILITIES) + add_subdirectory(Tutorial) +endif() -# 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") +add_subdirectory(MathFunctions) diff --git a/Help/guide/tutorial/Step7/CMakePresets.json b/Help/guide/tutorial/Step7/CMakePresets.json new file mode 100644 index 0000000..f393977 --- /dev/null +++ b/Help/guide/tutorial/Step7/CMakePresets.json @@ -0,0 +1,15 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "TUTORIAL_USE_STD_SQRT": "OFF", + "TUTORIAL_ENABLE_IPO": "OFF" + } + } + ] +} diff --git a/Help/guide/tutorial/Step7/CTestConfig.cmake b/Help/guide/tutorial/Step7/CTestConfig.cmake deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step7/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt index a0b3037..3782f66 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt @@ -1,55 +1,54 @@ -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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} - ) +target_sources(MathFunctions + PRIVATE + MathFunctions.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") + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ) +# TODO8: Add the interface library to MathFunctions +target_link_libraries(MathFunctions + PRIVATE + MathLogger - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) + PUBLIC + OpAdd + OpMul + OpSub +) - # TODO 1: Include CheckCXXSourceCompiles +target_compile_features(MathFunctions PRIVATE cxx_std_20) - # 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 - # 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 SqrtLibrary target. +if(TUTORIAL_USE_STD_SQRT) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_STD_SQRT) +endif() - # Hint: Use target_compile_definitions() +include(CheckIncludeFiles) +check_include_files(emmintrin.h HAS_EMMINTRIN LANGUAGE CXX) - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +if(HAS_EMMINTRIN) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_SSE2) endif() -# link MathFunctions to tutorial_compiler_flags -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) +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() -install(TARGETS ${installable_libs} DESTINATION lib) -# install include headers -install(FILES MathFunctions.h DESTINATION include) + +add_subdirectory(MathLogger) +add_subdirectory(MathExtensions) +# TODO9: Add the MakeTable subdirectory to the project diff --git a/Help/guide/tutorial/Step7/MathFunctions/MakeTable/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/MakeTable/CMakeLists.txt new file mode 100644 index 0000000..cef2be7 --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MakeTable/CMakeLists.txt @@ -0,0 +1,15 @@ +# TODO1: Add a MakeTable executable + +# TODO2: Add MakeTable.cxx to the MakeTable executable + +# TODO3: Add a custom command which invokes MakeTable to generate SqrtTable.h + +# TODO4: Add a custom target which depends on SqrtTable.h + +# TODO5: Add an INTERFACE library to describe the SqrtTable header + +# TODO6: Add the current binary directory (and optionally the SqrtTable.h FILE) +# to a header file set of the interface library + +# TODO7: Use add_dependencies to ensure the custom target always runs before +# targets that depend on the interface library diff --git a/Help/guide/tutorial/Step7/MathFunctions/MakeTable/MakeTable.cxx b/Help/guide/tutorial/Step7/MathFunctions/MakeTable/MakeTable.cxx new file mode 100644 index 0000000..f85b278 --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MakeTable/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include +#include +#include + +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(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/Step7/MathFunctions/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step7/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx index dc28b4b..9da2656 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx @@ -1,19 +1,102 @@ -#include "MathFunctions.h" - #include +#include + +#include + +#ifdef TUTORIAL_USE_SSE2 +# include +#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; + } -#ifdef USE_MYMATH -# include "mysqrt.h" + 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; +} + +// TODO10: Replace this hardcoded sqrtTable with #include +double sqrtTable[] = { 0, 1, 1, 2, 2, 2, 2, 3, 3, 3 }; + +double table_sqrt(double x) +{ + double result = sqrtTable[static_cast(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) { -// which square root function should we use? -#ifdef USE_MYMATH - return detail::mysqrt(x); -#else +#ifdef TUTORIAL_USE_STD_SQRT return std::sqrt(x); +#else + return mysqrt(x); #endif } } diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h index d5c2f22..91cb176 100644 --- a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + namespace mathfunctions { double sqrt(double x); } diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..b20151f --- /dev/null +++ b/Help/guide/tutorial/Step7/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/Step7/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Step7/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Step7/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Step7/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx deleted file mode 100644 index 465b43a..0000000 --- a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,38 +0,0 @@ -#include "mysqrt.h" - -// TODO 4: include cmath -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step7/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step7/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..3daa5ea --- /dev/null +++ b/Help/guide/tutorial/Step7/Tutorial/CMakeLists.txt @@ -0,0 +1,29 @@ +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() diff --git a/Help/guide/tutorial/Step7/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step7/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..44b7831 --- /dev/null +++ b/Help/guide/tutorial/Step7/Tutorial/Tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +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/Step7/TutorialConfig.h.in b/Help/guide/tutorial/Step7/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step7/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step7/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 index 97ec6aa..af50f6b 100644 --- a/Help/guide/tutorial/Step8/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/CMakeLists.txt @@ -1,70 +1,29 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.23) -# set the project name and version -project(Tutorial VERSION 1.0) +project(Tutorial) -# specify the C++ standard -add_library(tutorial_compiler_flags INTERFACE) -target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11) +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) -# add compiler warning flags just when building this project via -# the BUILD_INTERFACE genex -set(gcc_like_cxx "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) +# TODO6: Add a default-ON option named BUILD_TESTING with a doc string of: +# "Enable testing and build tests" -# configure a header file to pass some of the CMake settings -# to the source code -configure_file(TutorialConfig.h.in TutorialConfig.h) +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() -# 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 - ) +if(TUTORIAL_BUILD_UTILITIES) + add_subdirectory(Tutorial) +endif() -# enable testing -include(CTest) +# TODO7: Conditional on the value of BUILD_TESTING, enable testing and add the +# Tests subdirectory to the project -# 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") +add_subdirectory(MathFunctions) diff --git a/Help/guide/tutorial/Step8/CMakePresets.json b/Help/guide/tutorial/Step8/CMakePresets.json new file mode 100644 index 0000000..f393977 --- /dev/null +++ b/Help/guide/tutorial/Step8/CMakePresets.json @@ -0,0 +1,15 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "TUTORIAL_USE_STD_SQRT": "OFF", + "TUTORIAL_ENABLE_IPO": "OFF" + } + } + ] +} diff --git a/Help/guide/tutorial/Step8/CTestConfig.cmake b/Help/guide/tutorial/Step8/CTestConfig.cmake deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step8/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt index b14d180..8673342 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt @@ -1,59 +1,54 @@ -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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") +target_sources(MathFunctions + PRIVATE + MathFunctions.cxx - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ) + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) +target_link_libraries(MathFunctions + PRIVATE + MathLogger + SqrtTable - # does this system provide the log and exp functions? - include(CheckCXXSourceCompiles) - check_cxx_source_compiles(" - #include - int main() { - std::log(1.0); - return 0; - } - " HAVE_LOG) - check_cxx_source_compiles(" - #include - int main() { - std::exp(1.0); - return 0; - } - " HAVE_EXP) + PUBLIC + OpAdd + OpMul + OpSub +) - # add compile definitions - if(HAVE_LOG AND HAVE_EXP) - target_compile_definitions(SqrtLibrary - PRIVATE "HAVE_LOG" "HAVE_EXP" - ) - endif() +target_compile_features(MathFunctions PRIVATE cxx_std_20) - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +if(TUTORIAL_USE_STD_SQRT) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_STD_SQRT) 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} - ) +include(CheckIncludeFiles) +check_include_files(emmintrin.h HAS_EMMINTRIN LANGUAGE CXX) -# link MathFunctions to tutorial_compiler_flags -target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) +if(HAS_EMMINTRIN) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_SSE2) +endif() -# install libs -set(installable_libs MathFunctions tutorial_compiler_flags) -if(TARGET SqrtLibrary) - list(APPEND installable_libs SqrtLibrary) +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() -install(TARGETS ${installable_libs} DESTINATION lib) -# install include headers -install(FILES MathFunctions.h DESTINATION include) + +add_subdirectory(MathLogger) +add_subdirectory(MathExtensions) +add_subdirectory(MakeTable) diff --git a/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx deleted file mode 100644 index f85b278..0000000 --- a/Help/guide/tutorial/Step8/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include -#include -#include - -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(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/MakeTable/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/MakeTable/CMakeLists.txt new file mode 100644 index 0000000..6aa2a32 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MakeTable/MakeTable.cxx b/Help/guide/tutorial/Step8/MathFunctions/MakeTable/MakeTable.cxx new file mode 100644 index 0000000..f85b278 --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MakeTable/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include +#include +#include + +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(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/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step8/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx index dc28b4b..4bf8051 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx @@ -1,19 +1,101 @@ -#include "MathFunctions.h" - #include +#include + +#include + +#ifdef TUTORIAL_USE_SSE2 +# include +#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; + } -#ifdef USE_MYMATH -# include "mysqrt.h" + 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 + +double table_sqrt(double x) +{ + double result = sqrtTable[static_cast(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) { -// which square root function should we use? -#ifdef USE_MYMATH - return detail::mysqrt(x); -#else +#ifdef TUTORIAL_USE_STD_SQRT return std::sqrt(x); +#else + return mysqrt(x); #endif } } diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h index d5c2f22..91cb176 100644 --- a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + namespace mathfunctions { double sqrt(double x); } diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..b20151f --- /dev/null +++ b/Help/guide/tutorial/Step8/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/Step8/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Step8/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Step8/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Step8/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx deleted file mode 100644 index 28ab042..0000000 --- a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,36 +0,0 @@ -#include "mysqrt.h" - -#include -#include - -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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step8/Tests/CMakeLists.txt b/Help/guide/tutorial/Step8/Tests/CMakeLists.txt new file mode 100644 index 0000000..ce2ca46 --- /dev/null +++ b/Help/guide/tutorial/Step8/Tests/CMakeLists.txt @@ -0,0 +1,13 @@ +# TODO1: Add an executable target for the tests + +# TODO2: Add the TestMathFunctions.cxx file to the test target's sources + +# TODO3: Add the MathFunctions library to the test target's link libraries + +# TODO4: Write a function that takes a single operation name as an argument. +# The function will call add_test() with the operation name as the NAME +# of the test, and a COMMAND of the form: + + +# TODO5: Call the function for all four supported operations: +# add, mul, sqrt, sub diff --git a/Help/guide/tutorial/Step8/Tests/TestMathFunctions.cxx b/Help/guide/tutorial/Step8/Tests/TestMathFunctions.cxx new file mode 100644 index 0000000..bda0bba --- /dev/null +++ b/Help/guide/tutorial/Step8/Tests/TestMathFunctions.cxx @@ -0,0 +1,24 @@ +#include + +#include + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + return -1; + } + + std::string op(argv[1]); + + if (op == "add") { + return mathfunctions::OpAdd(1.0, 1.0) != 2.0; + } else if (op == "mul") { + return mathfunctions::OpMul(5.0, 5.0) != 25.0; + } else if (op == "sqrt") { + return mathfunctions::sqrt(25.0) != 5.0; + } else if (op == "sub") { + return mathfunctions::OpSub(5.0, 1.0) != 4.0; + } + + return -1; +} diff --git a/Help/guide/tutorial/Step8/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step8/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..3daa5ea --- /dev/null +++ b/Help/guide/tutorial/Step8/Tutorial/CMakeLists.txt @@ -0,0 +1,29 @@ +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() diff --git a/Help/guide/tutorial/Step8/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step8/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..44b7831 --- /dev/null +++ b/Help/guide/tutorial/Step8/Tutorial/Tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +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/Step8/TutorialConfig.h.in b/Help/guide/tutorial/Step8/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step8/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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 deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step8/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const 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 index 97ec6aa..ae34014 100644 --- a/Help/guide/tutorial/Step9/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/CMakeLists.txt @@ -1,70 +1,57 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.23) + +# TODO9: Add a VERSION parameter to the project() command for version 1.0.0 +project(Tutorial) + +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) + # TODO1: Install the Tutorial target + + # TODO3: Add the Tutorial target to the TutorialTargets export +endif() + +if(BUILD_TESTING) + enable_testing() + add_subdirectory(Tests) +endif() -# 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 "$") -set(msvc_cxx "$") -target_compile_options(tutorial_compiler_flags INTERFACE - "$<${gcc_like_cxx}:$>" - "$<${msvc_cxx}:$>" -) - -# 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) +# TODO4: Include the GNUInstallDirs module -# 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}" - ) +# TODO2: Install the MathFunctions, OpAdd, OpMul, OpSub, SqrtTable, and +# MathLogger targets. Ensure you name their header file set so the +# headers will be installed. -# add the install targets -install(TARGETS Tutorial DESTINATION bin) -install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" - DESTINATION include - ) +# TODO5: Add the targets from TODO2 to the TutorialTargets export -# enable testing -include(CTest) +# TODO6: Install the TutorialTargets export to: +# ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial +# And give them a namespace of "Tutorial::" -# does the application run -add_test(NAME Runs COMMAND Tutorial 25) +# TODO10: Include CMakePackageConfigHelpers -# does the usage message work? -add_test(NAME Usage COMMAND Tutorial) -set_tests_properties(Usage - PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" - ) +# TODO11: Use write_basic_package_version_file to write a +# TutorialConfigVersion.cmake file to the CMAKE_CURRENT_BINARY_DIR. +# The version compatibility should be ExactVersion. -# 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() +# TODO12: Add the generated TutorialConfigVersion.cmake file to the file list +# of the TODO7 install() command. -# 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") +# TODO7: Install the config file at cmake/TutorialConfig.cmake to the same +# destination as the TutorialTargets export. diff --git a/Help/guide/tutorial/Step9/CMakePresets.json b/Help/guide/tutorial/Step9/CMakePresets.json new file mode 100644 index 0000000..f393977 --- /dev/null +++ b/Help/guide/tutorial/Step9/CMakePresets.json @@ -0,0 +1,15 @@ +{ + "version": 4, + "configurePresets": [ + { + "name": "tutorial", + "displayName": "Tutorial Preset", + "description": "Preset to use with the tutorial", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "TUTORIAL_USE_STD_SQRT": "OFF", + "TUTORIAL_ENABLE_IPO": "OFF" + } + } + ] +} diff --git a/Help/guide/tutorial/Step9/CTestConfig.cmake b/Help/guide/tutorial/Step9/CTestConfig.cmake deleted file mode 100644 index b2922fe..0000000 --- a/Help/guide/tutorial/Step9/CTestConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CTEST_NIGHTLY_START_TIME "00:00:00 EST") - -set(CTEST_SUBMIT_URL "https://my.cdash.org/submit.php?project=CMakeTutorial") diff --git a/Help/guide/tutorial/Step9/License.txt b/Help/guide/tutorial/Step9/License.txt deleted file mode 100644 index 85760e5..0000000 --- a/Help/guide/tutorial/Step9/License.txt +++ /dev/null @@ -1,2 +0,0 @@ -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 index 5addc6d..8673342 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt +++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt @@ -1,44 +1,54 @@ -add_library(MathFunctions MathFunctions.cxx) +add_library(MathFunctions) -# 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} - ) +target_sources(MathFunctions + PRIVATE + MathFunctions.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") + PUBLIC + FILE_SET HEADERS + FILES + MathFunctions.h +) - # generate Table.h - include(MakeTable.cmake) +target_link_libraries(MathFunctions + PRIVATE + MathLogger + SqrtTable - # library that just does sqrt - add_library(SqrtLibrary STATIC - mysqrt.cxx - ${CMAKE_CURRENT_BINARY_DIR}/Table.h - ) + PUBLIC + OpAdd + OpMul + OpSub +) - # state that we depend on our binary dir to find Table.h - target_include_directories(SqrtLibrary PRIVATE - ${CMAKE_CURRENT_BINARY_DIR} - ) +target_compile_features(MathFunctions PRIVATE cxx_std_20) - # link SqrtLibrary to tutorial_compiler_flags - target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags) - - target_link_libraries(MathFunctions PRIVATE SqrtLibrary) +if(TUTORIAL_USE_STD_SQRT) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_STD_SQRT) endif() -# link MathFunctions to tutorial_compiler_flags -target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags) +include(CheckIncludeFiles) +check_include_files(emmintrin.h HAS_EMMINTRIN LANGUAGE CXX) + +if(HAS_EMMINTRIN) + target_compile_definitions(MathFunctions PRIVATE TUTORIAL_USE_SSE2) +endif() -# install libs -set(installable_libs MathFunctions tutorial_compiler_flags) -if(TARGET SqrtLibrary) - list(APPEND installable_libs SqrtLibrary) +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() -install(TARGETS ${installable_libs} DESTINATION lib) -# install include headers -install(FILES MathFunctions.h DESTINATION include) + +add_subdirectory(MathLogger) +add_subdirectory(MathExtensions) +add_subdirectory(MakeTable) diff --git a/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake deleted file mode 100644 index 12865a9..0000000 --- a/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - ) diff --git a/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx deleted file mode 100644 index f85b278..0000000 --- a/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cxx +++ /dev/null @@ -1,25 +0,0 @@ -// A simple program that builds a sqrt table -#include -#include -#include - -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(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/MakeTable/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/MakeTable/CMakeLists.txt new file mode 100644 index 0000000..6aa2a32 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MakeTable/MakeTable.cxx b/Help/guide/tutorial/Step9/MathFunctions/MakeTable/MakeTable.cxx new file mode 100644 index 0000000..f85b278 --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MakeTable/MakeTable.cxx @@ -0,0 +1,25 @@ +// A simple program that builds a sqrt table +#include +#include +#include + +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(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/MathExtensions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/CMakeLists.txt new file mode 100644 index 0000000..b113786 --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(OpAdd) +add_subdirectory(OpMul) +add_subdirectory(OpSub) diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/CMakeLists.txt new file mode 100644 index 0000000..f35da81 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/OpAdd.cxx new file mode 100644 index 0000000..ea11496 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpAdd/OpAdd.h b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpAdd/OpAdd.h new file mode 100644 index 0000000..9c9efc3 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpMul/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpMul/CMakeLists.txt new file mode 100644 index 0000000..f494fc6 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpMul/OpMul.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpMul/OpMul.cxx new file mode 100644 index 0000000..c8eb016 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpMul/OpMul.h b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpMul/OpMul.h new file mode 100644 index 0000000..52b467b --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpSub/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpSub/CMakeLists.txt new file mode 100644 index 0000000..1a108fd --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpSub/OpSub.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpSub/OpSub.cxx new file mode 100644 index 0000000..b7b35da --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathExtensions/OpSub/OpSub.h b/Help/guide/tutorial/Step9/MathFunctions/MathExtensions/OpSub/OpSub.h new file mode 100644 index 0000000..1406733 --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx index dc28b4b..4bf8051 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx +++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx @@ -1,19 +1,101 @@ -#include "MathFunctions.h" - #include +#include + +#include + +#ifdef TUTORIAL_USE_SSE2 +# include +#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; + } -#ifdef USE_MYMATH -# include "mysqrt.h" + 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 + +double table_sqrt(double x) +{ + double result = sqrtTable[static_cast(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) { -// which square root function should we use? -#ifdef USE_MYMATH - return detail::mysqrt(x); -#else +#ifdef TUTORIAL_USE_STD_SQRT return std::sqrt(x); +#else + return mysqrt(x); #endif } } diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h index d5c2f22..91cb176 100644 --- a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h +++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + namespace mathfunctions { double sqrt(double x); } diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathLogger/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/CMakeLists.txt new file mode 100644 index 0000000..b20151f --- /dev/null +++ b/Help/guide/tutorial/Step9/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/Step9/MathFunctions/MathLogger/MathFormatting.h b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathFormatting.h new file mode 100644 index 0000000..3b6d61c --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathFormatting.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +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/Step9/MathFunctions/MathLogger/MathLogger.h b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathLogger.h new file mode 100644 index 0000000..ef7b31a --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathLogger.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#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/Step9/MathFunctions/MathLogger/MathOutput.h b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathOutput.h new file mode 100644 index 0000000..63f0aeb --- /dev/null +++ b/Help/guide/tutorial/Step9/MathFunctions/MathLogger/MathOutput.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace mathlogger { +inline void WriteLog(std::string const& msg) +{ + std::cout << msg; +} +} diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx deleted file mode 100644 index 477d715..0000000 --- a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx +++ /dev/null @@ -1,37 +0,0 @@ -#include "mysqrt.h" - -#include - -// 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(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 deleted file mode 100644 index 593d41e..0000000 --- a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace mathfunctions { -namespace detail { -double mysqrt(double x); -} -} diff --git a/Help/guide/tutorial/Step9/Tests/CMakeLists.txt b/Help/guide/tutorial/Step9/Tests/CMakeLists.txt new file mode 100644 index 0000000..c6750ae --- /dev/null +++ b/Help/guide/tutorial/Step9/Tests/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable(TestMathFunctions) + +target_sources(TestMathFunctions + PRIVATE + TestMathFunctions.cxx +) + +target_link_libraries(TestMathFunctions + PRIVATE + MathFunctions +) + +function(MathFunctionTest op) + add_test( + NAME ${op} + COMMAND TestMathFunctions ${op} + ) +endfunction() + +MathFunctionTest(add) +MathFunctionTest(mul) +MathFunctionTest(sqrt) +MathFunctionTest(sub) diff --git a/Help/guide/tutorial/Step9/Tests/TestMathFunctions.cxx b/Help/guide/tutorial/Step9/Tests/TestMathFunctions.cxx new file mode 100644 index 0000000..bda0bba --- /dev/null +++ b/Help/guide/tutorial/Step9/Tests/TestMathFunctions.cxx @@ -0,0 +1,24 @@ +#include + +#include + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + return -1; + } + + std::string op(argv[1]); + + if (op == "add") { + return mathfunctions::OpAdd(1.0, 1.0) != 2.0; + } else if (op == "mul") { + return mathfunctions::OpMul(5.0, 5.0) != 25.0; + } else if (op == "sqrt") { + return mathfunctions::sqrt(25.0) != 5.0; + } else if (op == "sub") { + return mathfunctions::OpSub(5.0, 1.0) != 4.0; + } + + return -1; +} diff --git a/Help/guide/tutorial/Step9/Tutorial/CMakeLists.txt b/Help/guide/tutorial/Step9/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..3daa5ea --- /dev/null +++ b/Help/guide/tutorial/Step9/Tutorial/CMakeLists.txt @@ -0,0 +1,29 @@ +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() diff --git a/Help/guide/tutorial/Step9/Tutorial/Tutorial.cxx b/Help/guide/tutorial/Step9/Tutorial/Tutorial.cxx new file mode 100644 index 0000000..44b7831 --- /dev/null +++ b/Help/guide/tutorial/Step9/Tutorial/Tutorial.cxx @@ -0,0 +1,26 @@ +// A simple program that computes the square root of a number +#include +#include +#include + +#include + +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/Step9/TutorialConfig.h.in b/Help/guide/tutorial/Step9/TutorialConfig.h.in deleted file mode 100644 index 7e4d7fa..0000000 --- a/Help/guide/tutorial/Step9/TutorialConfig.h.in +++ /dev/null @@ -1,3 +0,0 @@ -// 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/cmake/TutorialConfig.cmake b/Help/guide/tutorial/Step9/cmake/TutorialConfig.cmake new file mode 100644 index 0000000..f8e61ea --- /dev/null +++ b/Help/guide/tutorial/Step9/cmake/TutorialConfig.cmake @@ -0,0 +1,2 @@ +# TODO8: Include the TutorialTargets.cmake file, it will be located in the same +# list directory as this file when it is run. diff --git a/Help/guide/tutorial/Step9/tutorial.cxx b/Help/guide/tutorial/Step9/tutorial.cxx deleted file mode 100644 index 48be3c6..0000000 --- a/Help/guide/tutorial/Step9/tutorial.cxx +++ /dev/null @@ -1,27 +0,0 @@ -// A simple program that computes the square root of a number -#include -#include -#include - -#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 - double const inputValue = std::stod(argv[1]); - - double const outputValue = mathfunctions::sqrt(inputValue); - - std::cout << "The square root of " << inputValue << " is " << outputValue - << std::endl; - return 0; -} diff --git a/Help/guide/tutorial/Testing and CTest.rst b/Help/guide/tutorial/Testing and CTest.rst new file mode 100644 index 0000000..360b383 --- /dev/null +++ b/Help/guide/tutorial/Testing and CTest.rst @@ -0,0 +1,232 @@ +Step 8: Testing and CTest +========================= + +Testing is, historically, not the role of the build system. At best it might +have a specific target which maps to building and running the project's tests. + +In the CMake ecosystem, the opposite is true. CMake's testing ecosystem is +known as CTest. This ecosystem is both deceivingly simple and incredibly +powerful. In fact it is so powerful it deserves its own full tutorial to +describe everything we could achieve with it. + +This is not that tutorial. In this step, we will scratch the surface of some +of the facilities that CTest provides. + +Background +^^^^^^^^^^ + +At its core, CTest is a task launcher which runs commands and reports if they +have returned zero or non-zero values. This is the level we will be dealing +with CTest at. + +CMake provides direct integration with CTest via the :command:`enable_testing` +and :command:`add_test` commands. These allow CMake to setup the necessary +infrastructure in the build folder for CTest to discover, run, and report +on various tests we might be interested in. + +After setting up and building tests, the easiest way to invoke CTest is to run +it directly on the build directory with: + +.. code-block:: console + + ctest --test-dir build + +Which will run all available tests. Specific tests can be run with regular +expressions. + +.. code-block:: console + + ctest --test-dir build -R SpecificTest + +CTest also has advanced mechanisms for scripting, fixtures, sanitizers, +job servers, metric reportings, and much more. See the :manual:`ctest(1)` +manual for more information. + +Exercise 1 - Adding Tests +^^^^^^^^^^^^^^^^^^^^^^^^^ + +CTest convention dictates the building and running of tests be based on a +default-``ON`` variable named :variable:`BUILD_TESTING`. When using the full +suite of CTest capabilities via the :module:`CTest` module, this +:command:`option` is setup for us. When using a more stripped-down approach to +testing, it's expected the project will setup the option (or at least one of a +similar name) on its own. + +When :variable:`BUILD_TESTING` is true, the :command:`enable_testing` command +should be called in the root CML. + +.. code-block:: cmake + + enable_testing() + +This will generate all the necessary metadata into the build tree for CTest to +find and run tests. + +Once that has been done, the :command:`add_test` command can be used to create +a test anywhere in the project. The semantics of this command are similar to +:command:`add_custom_command`; we can name an executable target as the "command". + +.. code-block:: cmake + + add_test( + NAME MyAppWithTestFlag + COMMAND MyApp --test + ) + +Goal +---- + +Add tests for the MathFunctions library to the project and run them with CTest. + +Helpful Resources +----------------- + +* :variable:`BUILD_TESTING` +* :command:`enable_testing` +* :command:`function` +* :command:`add_test` + +Files to Edit +------------- + +* ``Tests/CMakeLists.txt`` +* ``CMakeLists.txt`` + +Getting Started +--------------- + +A testing program has been written in the file ``Tests/TestMathFunctions.cxx``. +This program takes a single command line argument, the math function to be +tested, with valid values of ``add``, ``mul``, ``sqrt``, and ``sub``. The return +code is zero if the operation is recognized and the calculated value is valid, +otherwise it is non-zero. + +Complete ``TODO 1`` through ``TODO 7``. + +Build and Run +------------- + +No special configuration is needed, configure and build as usual. + +.. code-block:: console + + cmake --preset tutorial + cmake --build build + +Verify all the tests pass with CTest. + +.. note:: + + If using a multi-config generator, eg Visual Studio, it will be necessary to + specify a configuration with ``ctest -C ``, where + ```` is a value like ``Debug`` or ``Release``. This is true whenever + using a multi-config generator, and won't be called out specifically in + future commands. + +.. code-block:: console + + ctest --test-dir build + +You can run individual tests with the :option:`-R ` flag. + +.. code-block:: console + + ctest --test-dir build -R sqrt + +Solution +-------- + +First we add a new executable for the tests. + +.. raw:: html + +
TODO 1-2: Click to show/hide answer + +.. literalinclude:: Step9/Tests/CMakeLists.txt + :caption: TODO 1-2: Tests/CMakeLists.txt + :name: Tests/CMakeLists.txt-add_executable + :language: cmake + :start-at: add_executable + :end-at: TestMathFunctions.cxx + :append: ) + +.. raw:: html + +
+ +Then we link in the library we are testing. + +.. raw:: html + +
TODO 3: Click to show/hide answer + +.. literalinclude:: Step9/Tests/CMakeLists.txt + :caption: TODO 3: Tests/CMakeLists.txt + :name: Tests/CMakeLists.txt-target_link_libraries + :language: cmake + :start-at: target_link_libraries(TestMathFunctions + :end-at: ) + +.. raw:: html + +
+ +We need to call :command:`add_test` for each of the valid operations, but this +would get repetitive, so we write a :command:`function` to do it for us. + +.. raw:: html + +
TODO 4: Click to show/hide answer + +.. literalinclude:: Step9/Tests/CMakeLists.txt + :caption: TODO 4: Tests/CMakeLists.txt + :name: Tests/CMakeLists.txt-function + :language: cmake + :start-at: function + :end-at: endfunction + +.. raw:: html + +
+ +Now we can use our :command:`function` to add all the tests. + +.. raw:: html + +
TODO 5: Click to show/hide answer + +.. literalinclude:: Step9/Tests/CMakeLists.txt + :caption: TODO 5: Tests/CMakeLists.txt + :name: Tests/CMakeLists.txt-add_test + :language: cmake + :start-at: MathFunctionTest(add + :end-at: MathFunctionTest(sub + +.. raw:: html + +
+ +Finally, we can add the :variable:`BUILD_TESTING` option and conditionally +enable building and running tests in the top-level CML. + +.. raw:: html + +
TODO 6-7: Click to show/hide answer + +.. literalinclude:: Step9/CMakeLists.txt + :caption: TODO 6: CMakeLists.txt + :name: CMakeLists.txt-BUILD_TESTING + :language: cmake + :start-at: option(BUILD_TESTING + :end-at: option(BUILD_TESTING + +.. literalinclude:: Step9/CMakeLists.txt + :caption: TODO 7: CMakeLists.txt + :name: CMakeLists.txt-enable_testing + :language: cmake + :start-at: if(BUILD_TESTING) + :end-at: endif() + +.. raw:: html + +
diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst index adedbcd..4f990c5 100644 --- a/Help/guide/tutorial/index.rst +++ b/Help/guide/tutorial/index.rst @@ -20,18 +20,17 @@ 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 + Getting Started with CMake + CMake Language Fundamentals + Configuration and Cache Variables + In-Depth CMake Target Commands + In-Depth CMake Library Concepts + In-Depth System Introspection + Custom Commands and Generated Files + Testing and CTest + Installation Commands and Concepts + Finding Dependencies + Miscellaneous Features .. Whenever a step above is renamed or removed, leave forwarding text in diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 25383f2..e89d747 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -584,11 +584,12 @@ add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}) add_RunCMake_test(TargetObjects) add_RunCMake_test(TargetProperties) add_RunCMake_test(ToolchainFile) + if(NOT CMake_TEST_EXTERNAL_CMAKE) set(Tutorial_ARGS TEST_DIR Tutorial -DTutorial_SOURCE_DIR=${CMake_SOURCE_DIR}/Help/guide/tutorial) - add_RunCMake_test(Tutorial-NoMath ${Tutorial_ARGS} -DTutorial_USE_MYMATH=OFF) - add_RunCMake_test(Tutorial-MyMath ${Tutorial_ARGS} -DTutorial_USE_MYMATH=ON) + add_RunCMake_test(Tutorial ${Tutorial_ARGS}) endif() + add_RunCMake_test(find_dependency) add_RunCMake_test(CompileDefinitions) add_RunCMake_test(CompileWarningAsError diff --git a/Tests/RunCMake/Tutorial/CMakeLists.txt b/Tests/RunCMake/Tutorial/CMakeLists.txt new file mode 100644 index 0000000..2c5c2ef --- /dev/null +++ b/Tests/RunCMake/Tutorial/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.23) +project(${RunCMake_TEST} CXX) + +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/Tutorial/Inspect.cmake b/Tests/RunCMake/Tutorial/Inspect.cmake new file mode 100644 index 0000000..3d1f0aa --- /dev/null +++ b/Tests/RunCMake/Tutorial/Inspect.cmake @@ -0,0 +1,8 @@ +try_compile(out + SOURCES ${CMAKE_CURRENT_LIST_DIR}/inspect.cpp + CXX_STANDARD 20 +) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" + "set(can_build_cxx20_tutorial ${out})" +) diff --git a/Tests/RunCMake/Tutorial/RunCMakeTest.cmake b/Tests/RunCMake/Tutorial/RunCMakeTest.cmake index 5516e72..ab5d050 100644 --- a/Tests/RunCMake/Tutorial/RunCMakeTest.cmake +++ b/Tests/RunCMake/Tutorial/RunCMakeTest.cmake @@ -1,39 +1,83 @@ include(RunCMake) +run_cmake(Inspect) +include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake") + function(run_tutorial_step name) - set(RunCMake_TEST_SOURCE_DIR ${Tutorial_SOURCE_DIR}/${name}) - set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build) - if(Tutorial_USE_MYMATH) - set(math MyMath) + if(ARGV1 STREQUAL "TUTORIALPROJECT_SUBDIR") + set(RunCMake_TEST_SOURCE_DIR ${Tutorial_SOURCE_DIR}/${name}/TutorialProject) else() - set(math NoMath) + set(RunCMake_TEST_SOURCE_DIR ${Tutorial_SOURCE_DIR}/${name}) endif() + + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}/build) set(config Release) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) - set(exe ${RunCMake_TEST_BINARY_DIR}/${config}/Tutorial) + set(exe ${RunCMake_TEST_BINARY_DIR}/Tutorial/${config}/Tutorial) else() - set(exe ${RunCMake_TEST_BINARY_DIR}/Tutorial) + set(exe ${RunCMake_TEST_BINARY_DIR}/Tutorial/Tutorial) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config}) endif() - list(APPEND RunCMake_TEST_OPTIONS -DUSE_MYMATH:BOOL=${Tutorial_USE_MYMATH}) + + if(ARGV1 STREQUAL "NO_PRESET") + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_CXX_STANDARD=11) + else() + list(APPEND RunCMake_TEST_OPTIONS --preset tutorial) + endif() + + if(ARGV2 STREQUAL "SETUP_CMAKE_PREFIX") + list(APPEND RunCMake_TEST_OPTIONS + "-DCMAKE_PREFIX_PATH=${Tutorial_SOURCE_DIR}/${name}/install\;${RunCMake_BINARY_DIR}/${name}/install" + ) + endif() + + list(APPEND RunCMake_TEST_OPTIONS -B ${RunCMake_TEST_BINARY_DIR}) + run_cmake(${name}-configure) + unset(RunCMake_TEST_OPTIONS) set(RunCMake_TEST_NO_CLEAN 1) set(RunCMake_TEST_OUTPUT_MERGE 1) run_cmake_command(${name}-build ${CMAKE_COMMAND} --build . --config ${config}) + unset(RunCMake_TEST_OUTPUT_MERGE) - if(EXISTS ${RunCMake_SOURCE_DIR}/${name}-${math}-run-stdout.txt) - set(RunCMake-stdout-file ${name}-${math}-run-stdout.txt) - else() - set(RunCMake-stdout-file Step-${math}-run-stdout.txt) - endif() + set(RunCMake-stdout-file ${name}-run-stdout.txt) run_cmake_command(${name}-run ${exe} 25) + endfunction() -if(NOT Tutorial_USE_MYMATH) - run_tutorial_step(Step2) +run_tutorial_step(Step3 NO_PRESET) + +if(NOT can_build_cxx20_tutorial) + return() endif() -foreach(step RANGE 3 12) - run_tutorial_step(Step${step}) + +foreach(num RANGE 4 9) + run_tutorial_step(Step${num}) endforeach() -run_tutorial_step(Complete) + +run_tutorial_step(Step10 TUTORIALPROJECT_SUBDIR) + +function(install_simpletest name) + set(RunCMake_TEST_SOURCE_DIR ${Tutorial_SOURCE_DIR}/${name}/SimpleTest) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}/build-simpletest) + set(config Release) + + list(APPEND RunCMake_TEST_OPTIONS + -B ${RunCMake_TEST_BINARY_DIR} + -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/${name}/install + --preset tutorial + ) + run_cmake(${name}-simpletest-configure) + + set(RunCMake_TEST_NO_CLEAN 1) + set(RunCMake_TEST_OUTPUT_MERGE 1) + run_cmake_command(${name}-simpletest-install ${CMAKE_COMMAND} --install ${RunCMake_TEST_BINARY_DIR} --config ${config}) +endfunction() + +install_simpletest(Step11) +run_tutorial_step(Step11 TUTORIALPROJECT_SUBDIR SETUP_CMAKE_PREFIX) + +install_simpletest(Complete) +run_tutorial_step(Complete TUTORIALPROJECT_SUBDIR SETUP_CMAKE_PREFIX) diff --git a/Tests/RunCMake/Tutorial/Step-MyMath-run-stdout.txt b/Tests/RunCMake/Tutorial/Step-MyMath-run-stdout.txt deleted file mode 100644 index 5e3442a..0000000 --- a/Tests/RunCMake/Tutorial/Step-MyMath-run-stdout.txt +++ /dev/null @@ -1,2 +0,0 @@ -^(Computing sqrt of 25 to be [0-9.]+ -)+The square root of 25 is 5$ diff --git a/Tests/RunCMake/Tutorial/Step-NoMath-run-stdout.txt b/Tests/RunCMake/Tutorial/Step-NoMath-run-stdout.txt deleted file mode 100644 index 75bb544..0000000 --- a/Tests/RunCMake/Tutorial/Step-NoMath-run-stdout.txt +++ /dev/null @@ -1 +0,0 @@ -^The square root of 25 is 5$ diff --git a/Tests/RunCMake/Tutorial/Step8-MyMath-run-stdout.txt b/Tests/RunCMake/Tutorial/Step8-MyMath-run-stdout.txt deleted file mode 100644 index 19c4f47..0000000 --- a/Tests/RunCMake/Tutorial/Step8-MyMath-run-stdout.txt +++ /dev/null @@ -1,2 +0,0 @@ -^Computing sqrt of 25 to be 5 using log and exp -The square root of 25 is 5$ diff --git a/Tests/RunCMake/Tutorial/inspect.cpp b/Tests/RunCMake/Tutorial/inspect.cpp new file mode 100644 index 0000000..e24c2f5 --- /dev/null +++ b/Tests/RunCMake/Tutorial/inspect.cpp @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() +{ + std::string s{ std::format("inspect") }; + std::string_view v{ s }; +} -- cgit v0.12