Browse Source

Tests: Update CMake tutorial

Latest material from data.kitware.com -> Collections -> Courses -> CMake.
Betsy McPhail 7 years ago
parent
commit
f2ddedfa58
100 changed files with 2673 additions and 604 deletions
  1. 27 8
      Tests/CMakeLists.txt
  2. 116 0
      Tests/Tutorial/Complete/CMakeLists.txt
  3. 4 0
      Tests/Tutorial/Complete/Config.cmake.in
  4. 1 1
      Tests/Tutorial/Complete/License.txt
  5. 68 0
      Tests/Tutorial/Complete/MathFunctions/CMakeLists.txt
  6. 25 0
      Tests/Tutorial/Complete/MathFunctions/MakeTable.cxx
  7. 18 0
      Tests/Tutorial/Complete/MathFunctions/MathFunctions.cxx
  8. 14 0
      Tests/Tutorial/Complete/MathFunctions/MathFunctions.h
  9. 45 0
      Tests/Tutorial/Complete/MathFunctions/mysqrt.cxx
  10. 6 0
      Tests/Tutorial/Complete/MathFunctions/mysqrt.h
  11. 3 0
      Tests/Tutorial/Complete/TutorialConfig.h.in
  12. 25 0
      Tests/Tutorial/Complete/tutorial.cxx
  13. 51 0
      Tests/Tutorial/Consumer/CMakeLists.txt
  14. 14 0
      Tests/Tutorial/Consumer/Config.cmake.in
  15. 11 0
      Tests/Tutorial/Consumer/consumer.cxx
  16. 6 0
      Tests/Tutorial/Consumer/directions.txt
  17. 109 0
      Tests/Tutorial/MultiPackage/CMakeLists.txt
  18. 4 0
      Tests/Tutorial/MultiPackage/Config.cmake.in
  19. 2 0
      Tests/Tutorial/MultiPackage/License.txt
  20. 68 0
      Tests/Tutorial/MultiPackage/MathFunctions/CMakeLists.txt
  21. 25 0
      Tests/Tutorial/MultiPackage/MathFunctions/MakeTable.cxx
  22. 18 0
      Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.cxx
  23. 14 0
      Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.h
  24. 45 0
      Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.cxx
  25. 6 0
      Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.h
  26. 7 0
      Tests/Tutorial/MultiPackage/MultiCPackConfig.cmake
  27. 3 0
      Tests/Tutorial/MultiPackage/TutorialConfig.h.in
  28. 34 0
      Tests/Tutorial/MultiPackage/directions.txt
  29. 25 0
      Tests/Tutorial/MultiPackage/tutorial.cxx
  30. 16 0
      Tests/Tutorial/Readme.txt
  31. 1 18
      Tests/Tutorial/Step1/CMakeLists.txt
  32. 96 0
      Tests/Tutorial/Step1/directions.txt
  33. 9 8
      Tests/Tutorial/Step1/tutorial.cxx
  34. 77 0
      Tests/Tutorial/Step10/CMakeLists.txt
  35. 2 0
      Tests/Tutorial/Step10/License.txt
  36. 61 0
      Tests/Tutorial/Step10/MathFunctions/CMakeLists.txt
  37. 25 0
      Tests/Tutorial/Step10/MathFunctions/MakeTable.cxx
  38. 18 0
      Tests/Tutorial/Step10/MathFunctions/MathFunctions.cxx
  39. 14 0
      Tests/Tutorial/Step10/MathFunctions/MathFunctions.h
  40. 45 0
      Tests/Tutorial/Step10/MathFunctions/mysqrt.cxx
  41. 6 0
      Tests/Tutorial/Step10/MathFunctions/mysqrt.h
  42. 3 0
      Tests/Tutorial/Step10/TutorialConfig.h.in
  43. 38 0
      Tests/Tutorial/Step10/directions.txt
  44. 25 0
      Tests/Tutorial/Step10/tutorial.cxx
  45. 77 0
      Tests/Tutorial/Step11/CMakeLists.txt
  46. 2 0
      Tests/Tutorial/Step11/License.txt
  47. 60 0
      Tests/Tutorial/Step11/MathFunctions/CMakeLists.txt
  48. 25 0
      Tests/Tutorial/Step11/MathFunctions/MakeTable.cxx
  49. 18 0
      Tests/Tutorial/Step11/MathFunctions/MathFunctions.cxx
  50. 14 0
      Tests/Tutorial/Step11/MathFunctions/MathFunctions.h
  51. 45 0
      Tests/Tutorial/Step11/MathFunctions/mysqrt.cxx
  52. 6 0
      Tests/Tutorial/Step11/MathFunctions/mysqrt.h
  53. 3 0
      Tests/Tutorial/Step11/TutorialConfig.h.in
  54. 104 0
      Tests/Tutorial/Step11/directions.txt
  55. 25 0
      Tests/Tutorial/Step11/tutorial.cxx
  56. 14 20
      Tests/Tutorial/Step2/CMakeLists.txt
  57. 5 8
      Tests/Tutorial/Step2/MathFunctions/mysqrt.cxx
  58. 0 1
      Tests/Tutorial/Step2/TutorialConfig.h.in
  59. 102 0
      Tests/Tutorial/Step2/directions.txt
  60. 11 21
      Tests/Tutorial/Step2/tutorial.cxx
  61. 22 52
      Tests/Tutorial/Step3/CMakeLists.txt
  62. 0 3
      Tests/Tutorial/Step3/MathFunctions/CMakeLists.txt
  63. 5 8
      Tests/Tutorial/Step3/MathFunctions/mysqrt.cxx
  64. 26 0
      Tests/Tutorial/Step3/directions.txt
  65. 12 13
      Tests/Tutorial/Step3/tutorial.cxx
  66. 20 52
      Tests/Tutorial/Step4/CMakeLists.txt
  67. 5 2
      Tests/Tutorial/Step4/MathFunctions/CMakeLists.txt
  68. 5 18
      Tests/Tutorial/Step4/MathFunctions/mysqrt.cxx
  69. 0 4
      Tests/Tutorial/Step4/TutorialConfig.h.in
  70. 72 0
      Tests/Tutorial/Step4/directions.txt
  71. 12 13
      Tests/Tutorial/Step4/tutorial.cxx
  72. 42 44
      Tests/Tutorial/Step5/CMakeLists.txt
  73. 8 15
      Tests/Tutorial/Step5/MathFunctions/CMakeLists.txt
  74. 14 21
      Tests/Tutorial/Step5/MathFunctions/MakeTable.cxx
  75. 5 22
      Tests/Tutorial/Step5/MathFunctions/mysqrt.cxx
  76. 0 4
      Tests/Tutorial/Step5/TutorialConfig.h.in
  77. 69 0
      Tests/Tutorial/Step5/directions.txt
  78. 12 13
      Tests/Tutorial/Step5/tutorial.cxx
  79. 46 48
      Tests/Tutorial/Step6/CMakeLists.txt
  80. 11 21
      Tests/Tutorial/Step6/MathFunctions/CMakeLists.txt
  81. 14 21
      Tests/Tutorial/Step6/MathFunctions/MakeTable.cxx
  82. 12 19
      Tests/Tutorial/Step6/MathFunctions/mysqrt.cxx
  83. 111 0
      Tests/Tutorial/Step6/directions.txt
  84. 12 13
      Tests/Tutorial/Step6/tutorial.cxx
  85. 46 52
      Tests/Tutorial/Step7/CMakeLists.txt
  86. 1 1
      Tests/Tutorial/Step7/License.txt
  87. 19 14
      Tests/Tutorial/Step7/MathFunctions/CMakeLists.txt
  88. 14 21
      Tests/Tutorial/Step7/MathFunctions/MakeTable.cxx
  89. 6 12
      Tests/Tutorial/Step7/MathFunctions/mysqrt.cxx
  90. 40 0
      Tests/Tutorial/Step7/directions.txt
  91. 12 13
      Tests/Tutorial/Step7/tutorial.cxx
  92. 82 0
      Tests/Tutorial/Step8/CMakeLists.txt
  93. 2 0
      Tests/Tutorial/Step8/License.txt
  94. 29 0
      Tests/Tutorial/Step8/MathFunctions/CMakeLists.txt
  95. 25 0
      Tests/Tutorial/Step8/MathFunctions/MakeTable.cxx
  96. 1 0
      Tests/Tutorial/Step8/MathFunctions/MathFunctions.h
  97. 42 0
      Tests/Tutorial/Step8/MathFunctions/mysqrt.cxx
  98. 8 0
      Tests/Tutorial/Step8/TutorialConfig.h.in
  99. 38 0
      Tests/Tutorial/Step8/directions.txt
  100. 32 0
      Tests/Tutorial/Step8/tutorial.cxx

+ 27 - 8
Tests/CMakeLists.txt

@@ -1704,18 +1704,37 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
     DEPENDS ExternalProjectUpdateSetup )
 
   # do each of the tutorial steps
-  foreach(STP RANGE 1 7)
-    add_test(TutorialStep${STP} ${CMAKE_CTEST_COMMAND}
+  function(add_tutorial_test step_name use_mymath)
+    set(tutorial_test_name Tutorial${step_name})
+    set(tutorial_build_dir "${CMake_BINARY_DIR}/Tests/Tutorial/${step_name}")
+    if (use_mymath)
+      set(tutorial_build_options "")
+    else()
+      set(tutorial_test_name ${tutorial_test_name}_MYMATH)
+      set(tutorial_build_dir "${tutorial_build_dir}_MYMATH")
+      set(tutorial_build_options -DUSE_MYMATH:BOOL=OFF)
+    endif()
+    add_test(${tutorial_test_name} ${CMAKE_CTEST_COMMAND}
       --build-and-test
-      "${CMake_SOURCE_DIR}/Tests/Tutorial/Step${STP}"
-      "${CMake_BINARY_DIR}/Tests/Tutorial/Step${STP}"
-      --build-two-config
+      "${CMake_SOURCE_DIR}/Tests/Tutorial/${step_name}"
+      ${tutorial_build_dir}_Build
       ${build_generator_args}
       --build-project Tutorial
-      --build-options ${build_options}
+      --build-options ${build_options} ${tutorial_build_options}
       --test-command Tutorial 25.0)
-  endforeach()
-  list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Tutorial")
+    list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/${tutorial_build_dir}_Build")
+  endfunction()
+
+  if(NOT CMake_TEST_EXTERNAL_CMAKE)
+    foreach(STP RANGE 1 11)
+      add_tutorial_test(Step${STP} TRUE)
+    endforeach()
+    add_tutorial_test(Complete TRUE)
+    foreach(STP RANGE 3 11)
+      add_tutorial_test(Step${STP} FALSE)
+    endforeach()
+    add_tutorial_test(Complete FALSE)
+  endif()
 
   add_test(testing ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE}
     --build-and-test

+ 116 - 0
Tests/Tutorial/Complete/CMakeLists.txt

@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
+
+# 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}")
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
+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(
+  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  )
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# 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_test)
+
+# 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")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
+
+# install the configuration targets
+install(EXPORT MathFunctionsTargets
+  FILE MathFunctionsTargets.cmake
+  DESTINATION lib/cmake/MathFunctions
+)
+
+include(CMakePackageConfigHelpers)
+# generate the config file that 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"
+)

+ 4 - 0
Tests/Tutorial/Complete/Config.cmake.in

@@ -0,0 +1,4 @@
+
+@PACKAGE_INIT@
+
+include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )

+ 1 - 1
Tests/Tutorial/Step6/License.txt → Tests/Tutorial/Complete/License.txt

@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tests/Tutorial/Step6...
+CMake/Tutorial/Step7...

+ 68 - 0
Tests/Tutorial/Complete/MathFunctions/CMakeLists.txt

@@ -0,0 +1,68 @@
+
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+                            $<INSTALL_INTERFACE:include>
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  # does this system provide the log and exp functions?
+  include(CheckSymbolExists)
+  set(CMAKE_REQUIRED_LIBRARIES "m")
+  check_symbol_exists(log "math.h" HAVE_LOG)
+  check_symbol_exists(exp "math.h" HAVE_EXP)
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_compile_definitions(SqrtLibrary PRIVATE
+                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
+                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
+                             )
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_compile_definitions(MathFunctions PRIVATE "$<$<BOOL:${USE_MYMATH}>:USE_MYMATH>")
+
+# 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(TARGETS MathFunctions
+        DESTINATION lib
+        EXPORT MathFunctionsTargets)
+install(FILES MathFunctions.h DESTINATION include)

+ 25 - 0
Tests/Tutorial/Complete/MathFunctions/MakeTable.cxx

@@ -0,0 +1,25 @@
+// A simple program that builds a sqrt table
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+int main(int argc, char* argv[])
+{
+  // make sure we have enough arguments
+  if (argc < 2) {
+    return 1;
+  }
+
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
+  }
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
+}

+ 18 - 0
Tests/Tutorial/Complete/MathFunctions/MathFunctions.cxx

@@ -0,0 +1,18 @@
+
+#include "MathFunctions.h"
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}

+ 14 - 0
Tests/Tutorial/Complete/MathFunctions/MathFunctions.h

@@ -0,0 +1,14 @@
+
+#if defined(_WIN32)
+#  if defined(EXPORTING_MYMATH)
+#    define DECLSPEC __declspec(dllexport)
+#  else
+#    define DECLSPEC __declspec(dllimport)
+#  endif
+#else // non windows
+#  define DECLSPEC
+#endif
+
+namespace mathfunctions {
+double DECLSPEC sqrt(double x);
+}

+ 45 - 0
Tests/Tutorial/Complete/MathFunctions/mysqrt.cxx

@@ -0,0 +1,45 @@
+#include "MathFunctions.h"
+#include <iostream>
+
+// include the generated table
+#include "Table.h"
+
+#include <cmath>
+
+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 = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
+            << std::endl;
+#else
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // if we have both log and exp then use them
+
+  // 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;
+}
+}
+}

+ 6 - 0
Tests/Tutorial/Complete/MathFunctions/mysqrt.h

@@ -0,0 +1,6 @@
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}

+ 3 - 0
Tests/Tutorial/Complete/TutorialConfig.h.in

@@ -0,0 +1,3 @@
+// the configured version number
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

+ 25 - 0
Tests/Tutorial/Complete/tutorial.cxx

@@ -0,0 +1,25 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}

+ 51 - 0
Tests/Tutorial/Consumer/CMakeLists.txt

@@ -0,0 +1,51 @@
+cmake_minimum_required(VERSION 3.3)
+
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED True)
+endif()
+
+
+function(find_external_dependency name)
+  set(${name}_ROOT ""  CACHE PATH "Root directory to find ${name}")
+  mark_as_advanced(${name}_DIR)
+  find_package(${name} PATHS ${${name}_ROOT} REQUIRED)
+endfunction()
+
+
+project(Consumer)
+
+find_external_dependency(MathFunctions)
+
+add_library(consumer consumer.cxx)
+target_link_libraries(consumer PUBLIC MathFunctions)
+
+# install the consumer library
+install(TARGETS consumer DESTINATION bin EXPORT ConsumerTargets)
+
+# install the configuration targets
+install(EXPORT ConsumerTargets
+  FILE ConsumerTargets.cmake
+  DESTINATION lib/cmake/Consumer
+)
+
+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}/ConsumerConfig.cmake"
+  INSTALL_DESTINATION "lib/cmake/example"
+  NO_SET_AND_CHECK_MACRO
+  NO_CHECK_REQUIRED_COMPONENTS_MACRO
+  )
+
+# install the configuration file
+install(FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/ConsumerConfig.cmake
+  DESTINATION lib/cmake/Consumer
+  )
+
+# generate the export targets for the build tree
+# needs to be after the install(TARGETS ) command
+export(EXPORT ConsumerTargets
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/ConsumerTargets.cmake"
+)

+ 14 - 0
Tests/Tutorial/Consumer/Config.cmake.in

@@ -0,0 +1,14 @@
+
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+
+function(find_external_dependency name)
+  set(${name}_ROOT ""  CACHE PATH "Root directory to find ${name}")
+  mark_as_advanced(${name}_DIR)
+  find_dependency(${name} PATHS ${${name}_ROOT} REQUIRED)
+endfunction()
+
+find_external_dependency(MathFunctions)
+
+include ( "${CMAKE_CURRENT_LIST_DIR}/ConsumerTargets.cmake" )

+ 11 - 0
Tests/Tutorial/Consumer/consumer.cxx

@@ -0,0 +1,11 @@
+// A simple function that computes the square root of a number
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "MathFunctions.h"
+
+double string_square_root(std::string const& value)
+{
+  return mathfunctions::sqrt(std::stod(value));
+}

+ 6 - 0
Tests/Tutorial/Consumer/directions.txt

@@ -0,0 +1,6 @@
+# Import a CMake Project#
+
+This examples shows how a project can find other CMake packages that
+generated Config.cmake files.
+
+It also shows how to state a projects external dependencies when generating a Config.cmake.

+ 109 - 0
Tests/Tutorial/MultiPackage/CMakeLists.txt

@@ -0,0 +1,109 @@
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
+
+# control how we mark up Debug libraries compared to Release libraries
+set(CMAKE_DEBUG_POSTFIX "-d")
+
+# 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}")
+
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
+# configure a header file to pass the version number only
+configure_file(
+  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  )
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# 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_test)
+
+# 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")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)
+
+# install the configuration targets
+install(EXPORT MathFunctionsTargets
+  FILE MathFunctionsTargets.cmake
+  DESTINATION lib/cmake/MathFunctions
+)
+
+include(CMakePackageConfigHelpers)
+# generate the config file that 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"
+)

+ 4 - 0
Tests/Tutorial/MultiPackage/Config.cmake.in

@@ -0,0 +1,4 @@
+
+@PACKAGE_INIT@
+
+include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )

+ 2 - 0
Tests/Tutorial/MultiPackage/License.txt

@@ -0,0 +1,2 @@
+This is the open source License.txt file introduced in
+CMake/Tutorial/Step7...

+ 68 - 0
Tests/Tutorial/MultiPackage/MathFunctions/CMakeLists.txt

@@ -0,0 +1,68 @@
+
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE
+                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+                            $<INSTALL_INTERFACE:include>
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  # does this system provide the log and exp functions?
+  include(CheckSymbolExists)
+  set(CMAKE_REQUIRED_LIBRARIES "m")
+  check_symbol_exists(log "math.h" HAVE_LOG)
+  check_symbol_exists(exp "math.h" HAVE_EXP)
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_compile_definitions(SqrtLibrary PRIVATE
+                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
+                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
+                             )
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_compile_definitions(MathFunctions PRIVATE "$<$<BOOL:${USE_MYMATH}>:USE_MYMATH>")
+
+# 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(TARGETS MathFunctions
+        DESTINATION lib
+        EXPORT MathFunctionsTargets)
+install(FILES MathFunctions.h DESTINATION include)

+ 25 - 0
Tests/Tutorial/MultiPackage/MathFunctions/MakeTable.cxx

@@ -0,0 +1,25 @@
+// A simple program that builds a sqrt table
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+int main(int argc, char* argv[])
+{
+  // make sure we have enough arguments
+  if (argc < 2) {
+    return 1;
+  }
+
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
+  }
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
+}

+ 18 - 0
Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.cxx

@@ -0,0 +1,18 @@
+
+#include "MathFunctions.h"
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}

+ 14 - 0
Tests/Tutorial/MultiPackage/MathFunctions/MathFunctions.h

@@ -0,0 +1,14 @@
+
+#if defined(_WIN32)
+#  if defined(EXPORTING_MYMATH)
+#    define DECLSPEC __declspec(dllexport)
+#  else
+#    define DECLSPEC __declspec(dllimport)
+#  endif
+#else // non windows
+#  define DECLSPEC
+#endif
+
+namespace mathfunctions {
+double DECLSPEC sqrt(double x);
+}

+ 45 - 0
Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.cxx

@@ -0,0 +1,45 @@
+#include "MathFunctions.h"
+#include <iostream>
+
+// include the generated table
+#include "Table.h"
+
+#include <cmath>
+
+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 = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
+            << std::endl;
+#else
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // if we have both log and exp then use them
+
+  // 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;
+}
+}
+}

+ 6 - 0
Tests/Tutorial/MultiPackage/MathFunctions/mysqrt.h

@@ -0,0 +1,6 @@
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}

+ 7 - 0
Tests/Tutorial/MultiPackage/MultiCPackConfig.cmake

@@ -0,0 +1,7 @@
+
+include("release/CPackConfig.cmake")
+
+set(CPACK_INSTALL_CMAKE_PROJECTS
+    "debug;Tutorial;ALL;/"
+    "release;Tutorial;ALL;/"
+    )

+ 3 - 0
Tests/Tutorial/MultiPackage/TutorialConfig.h.in

@@ -0,0 +1,3 @@
+// the configured version number
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

+ 34 - 0
Tests/Tutorial/MultiPackage/directions.txt

@@ -0,0 +1,34 @@
+# Packaging Debug and Release #
+
+By default CMake is model is that a build directory only contains a single
+configuration, be it Debug, Release, MinSizeRel, or RelWithDebInfo.
+
+But it is possible to setup CPack to bundle multiple build directories at the same
+time to build a package that contains multiple configurations of the same project.
+
+First we need to ahead and construct a directory called 'multi_config' this
+will contain all the builds that we want to package together.
+
+Second create a 'debug' and 'release' directory underneath 'multi_config'. At
+the end you should have a layout that looks like:
+
+─ multi_config
+    ├── debug
+    └── release
+
+Now we need to setup debug and release builds, which would roughly entail
+the following:
+
+  cd debug
+  cmake -DCMAKE_BUILD_TYPE=Debug ../../MultiPackage/
+  cmake --build .
+  cd ../release
+  cmake -DCMAKE_BUILD_TYPE=Release ../../MultiPackage/
+  cmake --build .
+  cd ..
+
+
+Now that both the debug and release builds are complete we can now use
+the custom MultiCPackConfig to package both builds into a single release.
+
+  cpack --config ../../MultiPackage/MultiCPackConfig.cmake

+ 25 - 0
Tests/Tutorial/MultiPackage/tutorial.cxx

@@ -0,0 +1,25 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}

+ 16 - 0
Tests/Tutorial/Readme.txt

@@ -0,0 +1,16 @@
+
+Step 0: A Starting Point
+Step 1: Configure a File and C++11 Controls
+Step 2: Adding a Library
+Step 3: Usage Requirements for Library
+Step 4: Installing and Testing
+Step 5: System Introspection
+Step 6: Custom Command and Generated File
+Step 7: Building an Installer
+Step 8: CDash submission
+Step 9: Mixing Static and Shared
+Step 10: Generator Expressions
+Step 11: Adding Export Configuration
+Complete: End result of Step 11
+Consumer: Example of Import Packages
+MultiPackage: How to package Debug and Release versions

+ 1 - 18
Tests/Tutorial/Step1/CMakeLists.txt

@@ -1,20 +1,3 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
-configure_file (
-  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
-  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  )
-
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-include_directories("${PROJECT_BINARY_DIR}")
-
-# add the executable
 add_executable(Tutorial tutorial.cxx)

+ 96 - 0
Tests/Tutorial/Step1/directions.txt

@@ -0,0 +1,96 @@
+# Adding a Version Number and Configured Header File #
+
+The first feature we will add is to provide our executable and project with a
+version number. While we could do this exclusively in the source code, using
+CMakeLists provides more flexibility.
+
+To add a version number we modify the CMakeLists file as follows:
+
+  cmake_minimum_required(VERSION 3.3)
+  project(Tutorial)
+
+  # the version number.
+  set(Tutorial_VERSION_MAJOR 1)
+  set(Tutorial_VERSION_MINOR 0)
+
+  # configure a header file to pass some of the CMake settings
+  # to the source code
+  configure_file(
+    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+    )
+
+  # add the executable
+  add_executable(Tutorial tutorial.cxx)
+
+  # 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}"
+                             )
+
+
+We then create a TutorialConfig.h.in file in the source tree with the
+following contents:
+
+  // the configured options and settings for Tutorial
+  #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+  #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+
+When CMake configures this header file the values for @Tutorial_VERSION_MAJOR@
+and @Tutorial_VERSION_MINOR@ will be replaced by the values from the CMakeLists
+file. Next we modify tutorial.cxx to include the configured header file and to
+make use of the version numbers. The resulting source code is listed below.
+
+  // A simple program that computes the square root of a number
+  #include <cmath>
+  #include <iostream>
+  #include <string>
+  #include <sstream>
+
+  #include "TutorialConfig.h"
+
+  int main (int argc, char *argv[])
+  {
+    if (argc < 2)
+      {
+      std::cout << argv[0] << " Version "
+                << Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR
+                << std::endl;
+      std::cout << "Usage: " << argv[0] << " number" << std::endl;
+      return 1;
+      }
+
+    double inputValue = atof(argv[1]);
+
+    double outputValue = sqrt(inputValue);
+    std::cout << "The square root of "
+              << inputValue << " is " << outputValue << std::endl;
+    return 0;
+  }
+
+# Adding C++11 support #
+
+Let's add some C++11 features to our project. We will need to explicitly state
+in the CMake code that it should use the correct flags. The easiest way to
+enable C++11 support for CMake is by using the CMAKE_CXX_STANDARD
+and CMAKE_CXX_STANDARD_REQUIRED variables.
+
+First, replace `atof` with `std::stod` in tutorial.cxx.
+
+Then, add the CMAKE_CXX_STANDARD and CMAKE_CXX_STANDARD_REQUIRED variables to
+the CMakeLists file. The STANADARD value should be set to 11, and REQUIRED
+should be set to True.
+
+
+# Build and Test #
+
+Run cmake or cmake-gui to configure the project and then build it with your
+chosen build tool
+
+cd to the directory where Tutorial was built (likely the make directory or
+a Debug or Release build configuration subdirectory) and run these commands:
+
+  Tutorial 4294967296
+  Tutorial 10
+  Tutorial

+ 9 - 8
Tests/Tutorial/Step1/tutorial.cxx

@@ -1,19 +1,20 @@
 // A simple program that computes the square root of a number
-#include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <string>
 
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
+
   double inputValue = atof(argv[1]);
+
   double outputValue = sqrt(inputValue);
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 77 - 0
Tests/Tutorial/Step10/CMakeLists.txt

@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
+
+# 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}")
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
+# configure a header file to pass the version number only
+configure_file(
+  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  )
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# 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_test)
+
+# 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")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)

+ 2 - 0
Tests/Tutorial/Step10/License.txt

@@ -0,0 +1,2 @@
+This is the open source License.txt file introduced in
+CMake/Tutorial/Step7...

+ 61 - 0
Tests/Tutorial/Step10/MathFunctions/CMakeLists.txt

@@ -0,0 +1,61 @@
+
+# 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)
+
+  # does this system provide the log and exp functions?
+  include(CheckSymbolExists)
+  set(CMAKE_REQUIRED_LIBRARIES "m")
+  check_symbol_exists(log "math.h" HAVE_LOG)
+  check_symbol_exists(exp "math.h" HAVE_EXP)
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  # state that SqrtLibrary need PIC when the default is shared libraries
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+  if(HAVE_LOG AND HAVE_EXP)
+    target_compile_definitions(SqrtLibrary
+                               PRIVATE "HAVE_LOG" "HAVE_EXP")
+  endif()
+
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+# define the symbol stating we are using the declspec(dllexport) when
+# building on windows
+target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
+
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)

+ 25 - 0
Tests/Tutorial/Step10/MathFunctions/MakeTable.cxx

@@ -0,0 +1,25 @@
+// A simple program that builds a sqrt table
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+int main(int argc, char* argv[])
+{
+  // make sure we have enough arguments
+  if (argc < 2) {
+    return 1;
+  }
+
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
+  }
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
+}

+ 18 - 0
Tests/Tutorial/Step10/MathFunctions/MathFunctions.cxx

@@ -0,0 +1,18 @@
+
+#include "MathFunctions.h"
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}

+ 14 - 0
Tests/Tutorial/Step10/MathFunctions/MathFunctions.h

@@ -0,0 +1,14 @@
+
+#if defined(_WIN32)
+#  if defined(EXPORTING_MYMATH)
+#    define DECLSPEC __declspec(dllexport)
+#  else
+#    define DECLSPEC __declspec(dllimport)
+#  endif
+#else // non windows
+#  define DECLSPEC
+#endif
+
+namespace mathfunctions {
+double DECLSPEC sqrt(double x);
+}

+ 45 - 0
Tests/Tutorial/Step10/MathFunctions/mysqrt.cxx

@@ -0,0 +1,45 @@
+#include "MathFunctions.h"
+#include <iostream>
+
+// include the generated table
+#include "Table.h"
+
+#include <cmath>
+
+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 = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
+            << std::endl;
+#else
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // if we have both log and exp then use them
+
+  // 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;
+}
+}
+}

+ 6 - 0
Tests/Tutorial/Step10/MathFunctions/mysqrt.h

@@ -0,0 +1,6 @@
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}

+ 3 - 0
Tests/Tutorial/Step10/TutorialConfig.h.in

@@ -0,0 +1,3 @@
+// the configured version number
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

+ 38 - 0
Tests/Tutorial/Step10/directions.txt

@@ -0,0 +1,38 @@
+# Adding Generator Expressions #
+
+Generator expressions are evaluated during build system generation to produce
+information specific to each build configuration.
+
+Generator expressions are allowed in the context of many target properties, such
+as LINK_LIBRARIES, INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS and others. They may
+also be used when using commands to populate those properties, such as
+target_link_libraries(), target_include_directories(),
+target_compile_definitions() and others.
+
+Generator expressions may to used to enable conditional linking, conditional
+definitions used when compiling, and 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 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.
+For example:
+
+  if(HAVE_LOG AND HAVE_EXP)
+    target_compile_definitions(SqrtLibrary
+                               PRIVATE "HAVE_LOG" "HAVE_EXP")
+  endif()
+
+Can be rewritten with generator expressions:
+
+  target_compile_definitions(SqrtLibrary PRIVATE
+                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
+                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
+                            )
+
+Note that "${HAVE_LOG}" is evaluated at CMake configure time while
+"$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>" is evaluated at build system generation time.

+ 25 - 0
Tests/Tutorial/Step10/tutorial.cxx

@@ -0,0 +1,25 @@
+// A simple program that computes the square root of a number
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}

+ 77 - 0
Tests/Tutorial/Step11/CMakeLists.txt

@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
+
+# 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}")
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
+# configure a header file to pass the version number only
+configure_file(
+  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  )
+
+# add the MathFunctions library
+add_subdirectory(MathFunctions)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial MathFunctions)
+
+# add the binary tree to the search path for include files
+# so that we will find TutorialConfig.h
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )
+
+# 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_test)
+
+# 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")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)

+ 2 - 0
Tests/Tutorial/Step11/License.txt

@@ -0,0 +1,2 @@
+This is the open source License.txt file introduced in
+CMake/Tutorial/Step7...

+ 60 - 0
Tests/Tutorial/Step11/MathFunctions/CMakeLists.txt

@@ -0,0 +1,60 @@
+
+# add the library that runs
+add_library(MathFunctions MathFunctions.cxx)
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+target_include_directories(MathFunctions
+                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+                           )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+  # does this system provide the log and exp functions?
+  include(CheckSymbolExists)
+  set(CMAKE_REQUIRED_LIBRARIES "m")
+  check_symbol_exists(log "math.h" HAVE_LOG)
+  check_symbol_exists(exp "math.h" HAVE_EXP)
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # add the command to generate the source code
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+    DEPENDS MakeTable
+    )
+
+  # library that just does sqrt
+  add_library(SqrtLibrary STATIC
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  # state that we depend on our binary dir to find Table.h
+  target_include_directories(SqrtLibrary PRIVATE
+                             ${CMAKE_CURRENT_BINARY_DIR}
+                             )
+
+  set_target_properties(SqrtLibrary PROPERTIES
+                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
+                        )
+
+  target_compile_definitions(SqrtLibrary PRIVATE
+                             "$<$<BOOL:${HAVE_LOG}>:HAVE_LOG>"
+                             "$<$<BOOL:${HAVE_EXP}>:HAVE_EXP>"
+                             )
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_compile_definitions(MathFunctions PRIVATE "$<$<BOOL:${USE_MYMATH}>:USE_MYMATH>")
+
+# define the symbol stating we are using the declspec(dllexport) when
+#building on windows
+target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
+
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)

+ 25 - 0
Tests/Tutorial/Step11/MathFunctions/MakeTable.cxx

@@ -0,0 +1,25 @@
+// A simple program that builds a sqrt table
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+int main(int argc, char* argv[])
+{
+  // make sure we have enough arguments
+  if (argc < 2) {
+    return 1;
+  }
+
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
+  }
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
+}

+ 18 - 0
Tests/Tutorial/Step11/MathFunctions/MathFunctions.cxx

@@ -0,0 +1,18 @@
+
+#include "MathFunctions.h"
+#include <cmath>
+
+#ifdef USE_MYMATH
+#  include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+#ifdef USE_MYMATH
+  return detail::mysqrt(x);
+#else
+  return std::sqrt(x);
+#endif
+}
+}

+ 14 - 0
Tests/Tutorial/Step11/MathFunctions/MathFunctions.h

@@ -0,0 +1,14 @@
+
+#if defined(_WIN32)
+#  if defined(EXPORTING_MYMATH)
+#    define DECLSPEC __declspec(dllexport)
+#  else
+#    define DECLSPEC __declspec(dllimport)
+#  endif
+#else // non windows
+#  define DECLSPEC
+#endif
+
+namespace mathfunctions {
+double DECLSPEC sqrt(double x);
+}

+ 45 - 0
Tests/Tutorial/Step11/MathFunctions/mysqrt.cxx

@@ -0,0 +1,45 @@
+#include "MathFunctions.h"
+#include <iostream>
+
+// include the generated table
+#include "Table.h"
+
+#include <cmath>
+
+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 = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
+            << std::endl;
+#else
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // if we have both log and exp then use them
+
+  // 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;
+}
+}
+}

+ 6 - 0
Tests/Tutorial/Step11/MathFunctions/mysqrt.h

@@ -0,0 +1,6 @@
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}

+ 3 - 0
Tests/Tutorial/Step11/TutorialConfig.h.in

@@ -0,0 +1,3 @@
+// the configured version number
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

+ 104 - 0
Tests/Tutorial/Step11/directions.txt

@@ -0,0 +1,104 @@
+# Adding Export Configuration #
+
+During Step 4 of the tutorial we added the ability for CMake to install the
+library and headers of the project. During Step 7 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 install(TARGETS) commands to not only specify
+a DESTINATION but also an EXPORT. The EXPORT keyword generates and installs 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:
+
+  install(TARGETS MathFunctions DESTINATION lib EXPORT MathFunctionsTargets)
+
+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:
+
+  # install the configuration targets
+  install(EXPORT MathFunctionsTargets
+    FILE MathFunctionsTargets.cmake
+    DESTINATION lib/cmake/MathFunctions
+  )
+
+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:
+
+  Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains
+  path:
+
+    "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions"
+
+  which is prefixed in the source directory.
+
+What CMake is trying to say is that during generating the export information
+it will export a path that is intrinsically tied to the current machine and
+will not be valid on other machines. The solution to this is to update the
+MathFunctions 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 target_include_directories
+call for MathFunctions to look like:
+
+  target_include_directories(MathFunctions
+                             INTERFACE
+                              $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+                              $<INSTALL_INTERFACE:include>
+                             )
+
+Once this has been updated, we can re-run CMake and see 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 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:
+
+  @PACKAGE_INIT@
+
+  include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )
+
+Then, to properly configure and install that file, add the following to the
+bottom of the top-level CMakeLists:
+
+  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
+    )
+
+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:
+
+  # 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"
+  )
+
+With this export call we now generate a Targets.cmake, allowing the configured
+MathFunctionsConfig.cmake in the build directory to be used by other projects,
+without needing it to be installed.

+ 25 - 0
Tests/Tutorial/Step11/tutorial.cxx

@@ -0,0 +1,25 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  double inputValue = std::stod(argv[1]);
+
+  const double outputValue = mathfunctions::sqrt(inputValue);
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}

+ 14 - 20
Tests/Tutorial/Step2/CMakeLists.txt

@@ -1,31 +1,25 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
 
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
 
 # configure a header file to pass some of the CMake settings
 # to the source code
-configure_file (
+configure_file(
   "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
   "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
-include_directories ("${PROJECT_BINARY_DIR}")
-
-# add the MathFunctions library?
-if (USE_MYMATH)
-  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
-  add_subdirectory (MathFunctions)
-  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
-endif ()
-
-# add the executable
-add_executable (Tutorial tutorial.cxx)
-target_link_libraries (Tutorial  ${EXTRA_LIBS})
+target_include_directories(Tutorial PUBLIC
+                           "${PROJECT_BINARY_DIR}"
+                           )

+ 5 - 8
Tests/Tutorial/Step2/MathFunctions/mysqrt.cxx

@@ -1,5 +1,5 @@
 #include "MathFunctions.h"
-#include <stdio.h>
+#include <iostream>
 
 // a hack square root calculation using simple operations
 double mysqrt(double x)
@@ -8,19 +8,16 @@ double mysqrt(double x)
     return 0;
   }
 
-  double result;
-  double delta;
-  result = x;
+  double result = x;
 
   // do ten iterations
-  int i;
-  for (i = 0; i < 10; ++i) {
+  for (int i = 0; i < 10; ++i) {
     if (result <= 0) {
       result = 0.1;
     }
-    delta = x - (result * result);
+    double delta = x - (result * result);
     result = result + 0.5 * delta / result;
-    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
   return result;
 }

+ 0 - 1
Tests/Tutorial/Step2/TutorialConfig.h.in

@@ -1,5 +1,4 @@
 // the configured options and settings for Tutorial
 #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
 #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
 

+ 102 - 0
Tests/Tutorial/Step2/directions.txt

@@ -0,0 +1,102 @@
+# Adding a Library #
+
+Now we will add a library to our project. This library will contain 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. It will have the following one line CMakeLists file:
+
+  add_library(MathFunctions mysqrt.cxx)
+
+The source file mysqrt.cxx has one function called mysqrt that provides similar
+functionality to the compiler’s sqrt function. To make use of the new library
+we add an add_subdirectory call in the top-level CMakeLists file so that the
+library will get built. We add the new library to the executable, and add the
+MathFunctions as an include directory so that mqsqrt.h header file can be
+found. The last few lines of the top-level CMakeLists file now look like:
+
+
+  add_subdirectory(MathFunctions)
+
+  #add the executable
+  add_executable(Tutorial tutorial.cxx)
+
+  target_link_libraries(Tutorial ${EXTRA_LIBS})
+
+
+Now let us make the MathFunctions library optional. While for the tutorial
+there really isn’t any need to do so, but with larger projects this is a common
+occurrence. The first step is to add an option to the top-level CMakeLists file.
+
+  option (USE_MYMATH
+          "Use tutorial provided math implementation" ON)
+
+This will show up in CMake GUI and ccmake with a default value of ON that can
+be changed by the user. This setting will be stored so that the user does not
+need to set the value each time they run CMake on this build directory.
+
+The next change is to make building and linking the MathFunctions library
+conditional. To do this we change the top-level CMakeLists file to look like
+the following:
+
+  cmake_minimum_required(VERSION 3.3)
+  project(Tutorial)
+
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+  # the version number.
+  set(Tutorial_VERSION_MAJOR 1)
+  set(Tutorial_VERSION_MINOR 0)
+
+  # configure a header file to pass some of the CMake settings
+  # to the source code
+  configure_file(
+    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+    )
+
+  # should we use our own math functions
+  option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+  # add the MathFunctions library?
+  if(USE_MYMATH)
+    add_subdirectory(MathFunctions)
+    list(APPEND EXTRA_LIBS MathFunctions)
+    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
+  endif(USE_MYMATH)
+
+  # add the executable
+  add_executable(Tutorial tutorial.cxx)
+
+  target_link_libraries(Tutorial ${EXTRA_LIBS})
+
+  # 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}
+                             )
+
+Note the use of the variables EXTRA_LIBS, and EXTRA_INCLUDES to collect
+up any optional libraries to later be linked into the executable. This is a
+classic approach when dealing with many optional components, we will cover the
+modern approach in the next step. For now the corresponding changes to the
+source code are fairly straightforward and leave us with:
+
+  #ifdef USE_MYMATH
+    double outputValue = mysqrt(inputValue);
+  #else
+    double outputValue = sqrt(inputValue);
+  #endif
+
+Since the source code now requires USE_MYMATH we can add it to the
+TutorialConfig.h.in. Simply add the following line:
+  #cmakedefine USE_MYMATH
+
+Run cmake or cmake-gui to configure the project and then build it with your
+chosen build tool and then run the built Tutorial executable.
+
+Which function gives better results, Step1’s sqrt or Step2’s mysqrt?

+ 11 - 21
Tests/Tutorial/Step2/tutorial.cxx

@@ -1,33 +1,23 @@
 // A simple program that computes the square root of a number
-#include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <cmath>
+#include <iostream>
+#include <string>
 
-#ifdef USE_MYMATH
-#  include "MathFunctions.h"
-#endif
+#include "TutorialConfig.h"
 
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
-  double inputValue = atof(argv[1]);
-  double outputValue = 0;
-
-  if (inputValue >= 0) {
-#ifdef USE_MYMATH
-    outputValue = mysqrt(inputValue);
-#else
-    outputValue = sqrt(inputValue);
-#endif
-  }
+  double inputValue = std::stod(argv[1]);
 
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  double outputValue = sqrt(inputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 22 - 52
Tests/Tutorial/Step3/CMakeLists.txt

@@ -1,68 +1,38 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
 # configure a header file to pass some of the CMake settings
 # to the source code
-configure_file (
+configure_file(
   "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
   "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-include_directories ("${PROJECT_BINARY_DIR}")
-
 # add the MathFunctions library?
-if (USE_MYMATH)
-  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
-  add_subdirectory (MathFunctions)
-  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
-endif ()
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
+endif(USE_MYMATH)
 
 # add the executable
-add_executable (Tutorial tutorial.cxx)
-target_link_libraries (Tutorial  ${EXTRA_LIBS})
-
-# 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 (TutorialRuns Tutorial 25)
+add_executable(Tutorial tutorial.cxx)
 
-# does it sqrt of 25
-add_test (TutorialComp25 Tutorial 25)
-set_tests_properties (TutorialComp25
-  PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5"
-  )
-
-# does it handle negative numbers
-add_test (TutorialNegative Tutorial -25)
-set_tests_properties (TutorialNegative
-  PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0"
-  )
-
-# does it handle small numbers
-add_test (TutorialSmall Tutorial 0.0001)
-set_tests_properties (TutorialSmall
-  PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01"
-  )
+target_link_libraries(Tutorial ${EXTRA_LIBS})
 
-# does the usage message work?
-add_test (TutorialUsage Tutorial)
-set_tests_properties (TutorialUsage
-  PROPERTIES
-  PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
+# 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}
+                           )

+ 0 - 3
Tests/Tutorial/Step3/MathFunctions/CMakeLists.txt

@@ -1,4 +1 @@
 add_library(MathFunctions mysqrt.cxx)
-
-install (TARGETS MathFunctions DESTINATION bin)
-install (FILES MathFunctions.h DESTINATION include)

+ 5 - 8
Tests/Tutorial/Step3/MathFunctions/mysqrt.cxx

@@ -1,5 +1,5 @@
 #include "MathFunctions.h"
-#include <stdio.h>
+#include <iostream>
 
 // a hack square root calculation using simple operations
 double mysqrt(double x)
@@ -8,19 +8,16 @@ double mysqrt(double x)
     return 0;
   }
 
-  double result;
-  double delta;
-  result = x;
+  double result = x;
 
   // do ten iterations
-  int i;
-  for (i = 0; i < 10; ++i) {
+  for (int i = 0; i < 10; ++i) {
     if (result <= 0) {
       result = 0.1;
     }
-    delta = x - (result * result);
+    double delta = x - (result * result);
     result = result + 0.5 * delta / result;
-    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
   return result;
 }

+ 26 - 0
Tests/Tutorial/Step3/directions.txt

@@ -0,0 +1,26 @@
+# Adding Usage Requirements for Library #
+
+Usage requirements allow for far better control over a library / 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:
+
+  - target_compile_definitions
+  - target_compile_options
+  - target_include_directories
+  - target_link_libraries
+
+First up is MathFunctions. We first state that anybody linking to MathFunctions
+needs to include the current source directory, while MathFunctions itself
+doesn't. So this can become an INTERFACE usage requirement.
+
+Remember INTERFACE means things that consumers require but the producer doesn't.
+
+  target_include_directories(MathFunctions
+            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+
+Now that we've specified usage requirements for MathFunctions we can safely remove
+our uses of the EXTRA_INCLUDES variable.
+
+Run cmake or cmake-gui to configure the project and then build it with your
+chosen build tool.

+ 12 - 13
Tests/Tutorial/Step3/tutorial.cxx

@@ -1,8 +1,9 @@
 // A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
 #include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
 
 #ifdef USE_MYMATH
 #  include "MathFunctions.h"
@@ -11,23 +12,21 @@
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
-  double inputValue = atof(argv[1]);
-  double outputValue = 0;
+  double inputValue = std::stod(argv[1]);
 
-  if (inputValue >= 0) {
 #ifdef USE_MYMATH
-    outputValue = mysqrt(inputValue);
+  double outputValue = mysqrt(inputValue);
 #else
-    outputValue = sqrt(inputValue);
+  double outputValue = sqrt(inputValue);
 #endif
-  }
 
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 20 - 52
Tests/Tutorial/Step4/CMakeLists.txt

@@ -1,68 +1,36 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
-
-# does this system provide the log and exp functions?
-include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
-check_function_exists (log HAVE_LOG)
-check_function_exists (exp HAVE_EXP)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
 # configure a header file to pass some of the CMake settings
 # to the source code
-configure_file (
+configure_file(
   "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
   "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-include_directories ("${PROJECT_BINARY_DIR}")
-
 # add the MathFunctions library?
-if (USE_MYMATH)
-  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
-  add_subdirectory (MathFunctions)
-  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
-endif ()
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif(USE_MYMATH)
 
 # add the executable
-add_executable (Tutorial tutorial.cxx)
-target_link_libraries (Tutorial  ${EXTRA_LIBS})
-
-# add the install targets
-install (TARGETS Tutorial DESTINATION bin)
-install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include)
-
-# enable testing
-enable_testing ()
+add_executable(Tutorial tutorial.cxx)
 
-# does the application run
-add_test (TutorialRuns Tutorial 25)
-
-# does the usage message work?
-add_test (TutorialUsage Tutorial)
-set_tests_properties (TutorialUsage
-  PROPERTIES
-  PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
-
-#define a macro to simplify adding tests
-macro (do_test arg result)
-  add_test (TutorialComp${arg} Tutorial ${arg})
-  set_tests_properties (TutorialComp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endmacro ()
-
-# do a bunch of result based tests
-do_test (25 "25 is 5")
-do_test (-25 "-25 is 0")
-do_test (0.0001 "0.0001 is 0.01")
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
 
+# 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}"
+                           )

+ 5 - 2
Tests/Tutorial/Step4/MathFunctions/CMakeLists.txt

@@ -1,4 +1,7 @@
 add_library(MathFunctions mysqrt.cxx)
 
-install (TARGETS MathFunctions DESTINATION bin)
-install (FILES MathFunctions.h DESTINATION include)
+# 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}
+          )

+ 5 - 18
Tests/Tutorial/Step4/MathFunctions/mysqrt.cxx

@@ -1,8 +1,5 @@
 #include "MathFunctions.h"
-#include "TutorialConfig.h"
-#include <stdio.h>
-
-#include <math.h>
+#include <iostream>
 
 // a hack square root calculation using simple operations
 double mysqrt(double x)
@@ -11,26 +8,16 @@ double mysqrt(double x)
     return 0;
   }
 
-  double result;
-
-// if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  result = exp(log(x) * 0.5);
-  fprintf(stdout, "Computing sqrt of %g to be %g using log\n", x, result);
-#else
-  double delta;
-  result = x;
+  double result = x;
 
   // do ten iterations
-  int i;
-  for (i = 0; i < 10; ++i) {
+  for (int i = 0; i < 10; ++i) {
     if (result <= 0) {
       result = 0.1;
     }
-    delta = x - (result * result);
+    double delta = x - (result * result);
     result = result + 0.5 * delta / result;
-    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
-#endif
   return result;
 }

+ 0 - 4
Tests/Tutorial/Step4/TutorialConfig.h.in

@@ -3,7 +3,3 @@
 #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
 #cmakedefine USE_MYMATH
 
-// does the platform provide exp and log functions?
-#cmakedefine HAVE_LOG
-#cmakedefine HAVE_EXP
-

+ 72 - 0
Tests/Tutorial/Step4/directions.txt

@@ -0,0 +1,72 @@
+# Installing and Testing #
+
+Now we can start adding testing support and install rules to our project.
+
+The install rules are fairly simple; for MathFunctions we install the library
+and header file, for the application we install the executable and configured
+header.
+
+So to MathFunctions/CMakeLists.txt we add:
+
+  install (TARGETS MathFunctions DESTINATION bin)
+  install (FILES MathFunctions.h DESTINATION include)
+
+And the to top-level CMakeLists.txt we add:
+
+  install(TARGETS Tutorial DESTINATION bin)
+  install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+          DESTINATION include
+          )
+
+That is all that is needed to create a basic local install of the tutorial.
+
+Run cmake or cmake-gui to configure the project and then build it with your
+chosen build tool. Then build the “install” target by typing 'make install'
+from the command line or build the INSTALL target from an IDE. This will
+install the appropriate header files, libraries, and executables.
+
+Verify that the installed Tutorial runs. Note: The CMake variable
+CMAKE_INSTALL_PREFIX is used to determine the root of where the files will
+be installed.
+
+Next let's test our application. Adding testing is an easy process. At the
+end of the top-level CMakeLists file we can add a number of basic tests to
+verify that the application is working correctly.
+
+  # 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_test)
+
+  # do a bunch of result based tests
+  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")
+
+The first test simply verifies that the application runs, does not segfault or
+otherwise crash, and has a zero return value. This is the basic form of a CTest
+test.
+
+The Usage test uses a regular expression to verify that the usage message
+is printed when an incorrect number of arguments are provided.
+
+Lastly, we have a function called do_test that simplifies running the
+application and verifying that the computed square root is correct for given
+input.
+
+To run tests, cd to the binary directory and run “ctest -N” and “ctest -VV”.

+ 12 - 13
Tests/Tutorial/Step4/tutorial.cxx

@@ -1,8 +1,9 @@
 // A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
 #include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
 
 #ifdef USE_MYMATH
 #  include "MathFunctions.h"
@@ -11,23 +12,21 @@
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
-  double inputValue = atof(argv[1]);
-  double outputValue = 0;
+  double inputValue = std::stod(argv[1]);
 
-  if (inputValue >= 0) {
 #ifdef USE_MYMATH
-    outputValue = mysqrt(inputValue);
+  double outputValue = mysqrt(inputValue);
 #else
-    outputValue = sqrt(inputValue);
+  double outputValue = sqrt(inputValue);
 #endif
-  }
 
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 42 - 44
Tests/Tutorial/Step5/CMakeLists.txt

@@ -1,72 +1,70 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
-
-# does this system provide the log and exp functions?
-include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
-check_function_exists (log HAVE_LOG)
-check_function_exists (exp HAVE_EXP)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
 # configure a header file to pass some of the CMake settings
 # to the source code
-configure_file (
+configure_file(
   "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
   "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-include_directories ("${PROJECT_BINARY_DIR}")
-
 # add the MathFunctions library?
-if (USE_MYMATH)
-  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
-  add_subdirectory (MathFunctions)
-  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
-endif ()
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
 
 # add the executable
-add_executable (Tutorial tutorial.cxx)
-target_link_libraries (Tutorial  ${EXTRA_LIBS})
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# 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)
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
 
 # enable testing
-enable_testing ()
+enable_testing()
 
 # does the application run
-add_test (TutorialRuns Tutorial 25)
+add_test(NAME Runs COMMAND Tutorial 25)
 
 # does the usage message work?
-add_test (TutorialUsage Tutorial)
-set_tests_properties (TutorialUsage
-  PROPERTIES
-  PASS_REGULAR_EXPRESSION "Usage:.*number"
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
   )
 
-#define a macro to simplify adding tests
-macro (do_test arg result)
-  add_test (TutorialComp${arg} Tutorial ${arg})
-  set_tests_properties (TutorialComp${arg}
+# 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}
     )
-endmacro ()
+endfunction(do_test)
 
 # do a bunch of result based tests
-do_test (4 "4 is 2")
-do_test (9 "9 is 3")
-do_test (5 "5 is 2.236")
-do_test (7 "7 is 2.645")
-do_test (25 "25 is 5")
-do_test (-25 "-25 is 0")
-do_test (0.0001 "0.0001 is 0.01")
-
+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")

+ 8 - 15
Tests/Tutorial/Step5/MathFunctions/CMakeLists.txt

@@ -1,17 +1,10 @@
-# first we add the executable that generates the table
-# add the binary tree directory to the search path for include files
-include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
+add_library(MathFunctions mysqrt.cxx)
 
-add_executable(MakeTable MakeTable.cxx )
-# 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
-  )
+# 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}
+          )
 
-# add the main library
-add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h  )
-
-install (TARGETS MathFunctions DESTINATION bin)
-install (FILES MathFunctions.h DESTINATION include)
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)

+ 14 - 21
Tests/Tutorial/Step5/MathFunctions/MakeTable.cxx

@@ -1,32 +1,25 @@
 // A simple program that builds a sqrt table
-#include <math.h>
-#include <stdio.h>
+#include <cmath>
+#include <fstream>
+#include <iostream>
 
 int main(int argc, char* argv[])
 {
-  int i;
-  double result;
-
   // make sure we have enough arguments
   if (argc < 2) {
     return 1;
   }
 
-  // open the output file
-  FILE* fout = fopen(argv[1], "w");
-  if (!fout) {
-    return 1;
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
   }
-
-  // create a source file with a table of square roots
-  fprintf(fout, "double sqrtTable[] = {\n");
-  for (i = 0; i < 10; ++i) {
-    result = sqrt(static_cast<double>(i));
-    fprintf(fout, "%g,\n", result);
-  }
-
-  // close the table with a zero
-  fprintf(fout, "0};\n");
-  fclose(fout);
-  return 0;
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
 }

+ 5 - 22
Tests/Tutorial/Step5/MathFunctions/mysqrt.cxx

@@ -1,11 +1,5 @@
 #include "MathFunctions.h"
-#include "TutorialConfig.h"
-#include <stdio.h>
-
-// include the generated table
-#include "Table.h"
-
-#include <math.h>
+#include <iostream>
 
 // a hack square root calculation using simple operations
 double mysqrt(double x)
@@ -14,27 +8,16 @@ double mysqrt(double x)
     return 0;
   }
 
-  double result;
-
-  // if we have both log and exp then use them
-  double delta;
-
-  // use the table to help find an initial value
-  result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
+  double result = x;
 
   // do ten iterations
-  int i;
-  for (i = 0; i < 10; ++i) {
+  for (int i = 0; i < 10; ++i) {
     if (result <= 0) {
       result = 0.1;
     }
-    delta = x - (result * result);
+    double delta = x - (result * result);
     result = result + 0.5 * delta / result;
-    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
-
   return result;
 }

+ 0 - 4
Tests/Tutorial/Step5/TutorialConfig.h.in

@@ -3,7 +3,3 @@
 #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
 #cmakedefine USE_MYMATH
 
-// does the platform provide exp and log functions?
-#cmakedefine HAVE_LOG
-#cmakedefine HAVE_EXP
-

+ 69 - 0
Tests/Tutorial/Step5/directions.txt

@@ -0,0 +1,69 @@
+# 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.
+
+If the platform has log and exp then we will use them to compute the square
+root in the mysqrt function. We first test for the availability of these
+functions using the CheckSymbolExists.cmake macro in the top-level CMakeLists
+file as follows:
+
+  # does this system provide the log and exp functions?
+  include(CheckSymbolExists)
+  set(CMAKE_REQUIRED_LIBRARIES "m")
+  check_symbol_exists(log "math.h" HAVE_LOG)
+  check_symbol_exists(exp "math.h" HAVE_EXP)
+
+Now let's add these defines to TutorialConfig.h.in so that we can use them
+from mysqrt.cxx:
+
+  // does the platform provide exp and log functions?
+  #cmakedefine HAVE_LOG
+  #cmakedefine HAVE_EXP
+
+Modify mysqrt.cxx to include math.h. Next, in the mysqrt function we can
+provide an alternate implementation based on log and exp if they are available
+on the system using the following code:
+
+  // if we have both log and exp then use them
+  #if defined(HAVE_LOG) && defined (HAVE_EXP)
+    double result = exp(log(x)*0.5);
+    std::cout << "Computing sqrt of " << x << " to be " << result << " using log" << std::endl;
+  #else
+    ...
+
+Run cmake or cmake-gui to configure the project and then build it with your
+chosen build tool.
+
+You will notice that even though HAVE_LOG and HAVE_EXP are both defined mysqrt
+isn't using them. We should realize quickly that we have forgotten to include
+TutorialConfig.h in mysqrt.cxx. We will also need to update
+MathFunctions/CMakeLists.txt with where it is located.
+
+So let's go ahead and update MathFunctions/CMakeLists.txt to look like:
+
+  add_library(MathFunctions mysqrt.cxx)
+
+  target_include_directories(MathFunctions
+            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+            PRIVATE ${Tutorial_BINARY_DIR}
+            )
+
+  install(TARGETS MathFunctions DESTINATION lib)
+  install(FILES MathFunctions.h DESTINATION include)
+
+Now all we need to do is include TutorialConfig.h in mysqrt.cxx
+
+At this point you should go ahead and build the project again.
+
+Run the built Tutorial executable. Which function gives better results now,
+Step1’s sqrt or Step5’s mysqrt?
+
+Exercise: Why is it important that we configure TutorialConfig.h.in after the
+checks for HAVE_LOG and HAVE_EXP? What would happen if we inverted the two?
+
+Exercise: Is there a better place for us to save the HAVE_LOG and HAVE_EXP
+values other than in TutorialConfig.h?

+ 12 - 13
Tests/Tutorial/Step5/tutorial.cxx

@@ -1,8 +1,9 @@
 // A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
 #include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
 
 #ifdef USE_MYMATH
 #  include "MathFunctions.h"
@@ -11,23 +12,21 @@
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
-  double inputValue = atof(argv[1]);
-  double outputValue = 0;
+  double inputValue = std::stod(argv[1]);
 
-  if (inputValue >= 0) {
 #ifdef USE_MYMATH
-    outputValue = mysqrt(inputValue);
+  double outputValue = mysqrt(inputValue);
 #else
-    outputValue = sqrt(inputValue);
+  double outputValue = sqrt(inputValue);
 #endif
-  }
 
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 46 - 48
Tests/Tutorial/Step6/CMakeLists.txt

@@ -1,78 +1,76 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
 
 # does this system provide the log and exp functions?
-include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
-check_function_exists (log HAVE_LOG)
-check_function_exists (exp HAVE_EXP)
+include(CheckSymbolExists)
+set(CMAKE_REQUIRED_LIBRARIES "m")
+check_symbol_exists(log "math.h" HAVE_LOG)
+check_symbol_exists(exp "math.h" HAVE_EXP)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
 # configure a header file to pass some of the CMake settings
 # to the source code
-configure_file (
+configure_file(
   "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
   "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-include_directories ("${PROJECT_BINARY_DIR}")
-
 # add the MathFunctions library?
-if (USE_MYMATH)
-  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
-  add_subdirectory (MathFunctions)
-  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
-endif ()
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
 
 # add the executable
-add_executable (Tutorial tutorial.cxx)
-target_link_libraries (Tutorial  ${EXTRA_LIBS})
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# 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)
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
 
 # enable testing
-enable_testing ()
+enable_testing()
 
 # does the application run
-add_test (TutorialRuns Tutorial 25)
+add_test(NAME Runs COMMAND Tutorial 25)
 
 # does the usage message work?
-add_test (TutorialUsage Tutorial)
-set_tests_properties (TutorialUsage
-  PROPERTIES
-  PASS_REGULAR_EXPRESSION "Usage:.*number"
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
   )
 
-#define a macro to simplify adding tests
-macro (do_test arg result)
-  add_test (TutorialComp${arg} Tutorial ${arg})
-  set_tests_properties (TutorialComp${arg}
+# 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}
     )
-endmacro ()
+endfunction(do_test)
 
 # do a bunch of result based tests
-do_test (4 "4 is 2")
-do_test (9 "9 is 3")
-do_test (5 "5 is 2.236")
-do_test (7 "7 is 2.645")
-do_test (25 "25 is 5")
-do_test (-25 "-25 is 0")
-do_test (0.0001 "0.0001 is 0.01")
-
-# build a CPack driven installer package
-include (InstallRequiredSystemLibraries)
-set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-include (CPack)
+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")

+ 11 - 21
Tests/Tutorial/Step6/MathFunctions/CMakeLists.txt

@@ -1,24 +1,14 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command (
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  DEPENDS MakeTable
-  COMMAND MakeTable
-  ARGS ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  )
-
-set_source_files_properties (
-  mysqrt.cxx PROPERTIES
-  OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  )
+add_library(MathFunctions mysqrt.cxx)
 
-# add the binary tree directory to the search path for include files
-include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
+# TutorialConfig.h include is an implementation detail
 
-# add the main library
-add_library(MathFunctions mysqrt.cxx)
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE ${Tutorial_BINARY_DIR}
+          )
 
-install (TARGETS MathFunctions DESTINATION bin)
-install (FILES MathFunctions.h DESTINATION include)
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)

+ 14 - 21
Tests/Tutorial/Step6/MathFunctions/MakeTable.cxx

@@ -1,32 +1,25 @@
 // A simple program that builds a sqrt table
-#include <math.h>
-#include <stdio.h>
+#include <cmath>
+#include <fstream>
+#include <iostream>
 
 int main(int argc, char* argv[])
 {
-  int i;
-  double result;
-
   // make sure we have enough arguments
   if (argc < 2) {
     return 1;
   }
 
-  // open the output file
-  FILE* fout = fopen(argv[1], "w");
-  if (!fout) {
-    return 1;
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
   }
-
-  // create a source file with a table of square roots
-  fprintf(fout, "double sqrtTable[] = {\n");
-  for (i = 0; i < 10; ++i) {
-    result = sqrt(static_cast<double>(i));
-    fprintf(fout, "%g,\n", result);
-  }
-
-  // close the table with a zero
-  fprintf(fout, "0};\n");
-  fclose(fout);
-  return 0;
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
 }

+ 12 - 19
Tests/Tutorial/Step6/MathFunctions/mysqrt.cxx

@@ -1,11 +1,8 @@
 #include "MathFunctions.h"
 #include "TutorialConfig.h"
-#include <stdio.h>
+#include <iostream>
 
-// include the generated table
-#include "Table.h"
-
-#include <math.h>
+#include <cmath>
 
 // a hack square root calculation using simple operations
 double mysqrt(double x)
@@ -14,27 +11,23 @@ double mysqrt(double x)
     return 0;
   }
 
-  double result;
-
   // if we have both log and exp then use them
-  double delta;
-
-  // use the table to help find an initial value
-  result = x;
-  if (x >= 1 && x < 10) {
-    result = sqrtTable[static_cast<int>(x)];
-  }
+#if defined(HAVE_LOG) && defined(HAVE_EXP)
+  double result = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
+            << std::endl;
+#else
+  double result = x;
 
   // do ten iterations
-  int i;
-  for (i = 0; i < 10; ++i) {
+  for (int i = 0; i < 10; ++i) {
     if (result <= 0) {
       result = 0.1;
     }
-    delta = x - (result * result);
+    double delta = x - (result * result);
     result = result + 0.5 * delta / result;
-    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
-
+#endif
   return result;
 }

+ 111 - 0
Tests/Tutorial/Step6/directions.txt

@@ -0,0 +1,111 @@
+# Adding a Custom Command and Generated File #
+
+In this section we will show how you can add a generated source file into the
+build process of an application. For this example, we will create a table of
+precomputed square roots as part of the build process, and then compile that
+table into our application.
+
+To accomplish this, we first need a program that will generate the table. In the
+MathFunctions subdirectory a new source file named MakeTable.cxx will do just that.
+
+  // A simple program that builds a sqrt table
+  #include <iostream>
+  #include <fstream>
+  #include <cmath>
+
+  int main (int argc, char *argv[])
+  {
+    // make sure we have enough arguments
+    if (argc < 2)
+      {
+      return 1;
+      }
+
+    std::ofstream fout(argv[1],std::ios_base::out);
+    const bool fileOpen = fout.is_open();
+    if(fileOpen)
+      {
+      fout << "double sqrtTable[] = {" << std::endl;
+      for (int i = 0; i < 10; ++i)
+        {
+        fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+        }
+      // close the table with a zero
+      fout << "0};" << std::endl;
+      fout.close();
+      }
+    return fileOpen ? 0 : 1; // return 0 if wrote the file
+  }
+
+Note that the table is produced as valid C++ code and that the output filename
+is passed in as an argument.
+
+The next step is to add the appropriate commands to MathFunctions’ CMakeLists
+file to build the MakeTable executable and then run it as part of the build
+process. A few commands are needed to accomplish this, as shown below:
+
+  # first we add the executable that generates the table
+  add_executable(MakeTable MakeTable.cxx)
+
+  # 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
+    )
+
+  # add the main library
+  add_library(MathFunctions
+              mysqrt.cxx
+              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+              )
+
+  target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PUBLIC ${Tutorial_BINARY_DIR}
+                 # add the binary tree directory to the search path for include files
+                 ${CMAKE_CURRENT_BINARY_DIR}
+          )
+
+  install(TARGETS MathFunctions DESTINATION lib)
+  install(FILES MathFunctions.h DESTINATION include)
+
+First, the executable for MakeTable is added as any other executable would be
+added. Then we add a custom command that specifies how to produce Table.h by
+running MakeTable. Next we have to let CMake know that mysqrt.cxx depends on
+the generated file Table.h. This is done by adding the generated Table.h to the
+list of sources for the library MathFunctions. 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.
+
+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:
+
+  if (x <= 0)
+      {
+      return 0;
+      }
+
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10)
+    {
+    result = sqrtTable[static_cast<int>(x)];
+    }
+
+  // do ten iterations
+  for (int i = 0; i < 10; ++i)
+    {
+    if (result <= 0)
+      {
+      result = 0.1;
+      }
+    double delta = x - (result*result);
+    result = result + 0.5*delta/result;
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
+    }
+
+Run cmake or 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.

+ 12 - 13
Tests/Tutorial/Step6/tutorial.cxx

@@ -1,8 +1,9 @@
 // A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
 #include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
 
 #ifdef USE_MYMATH
 #  include "MathFunctions.h"
@@ -11,23 +12,21 @@
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
-  double inputValue = atof(argv[1]);
-  double outputValue = 0;
+  double inputValue = std::stod(argv[1]);
 
-  if (inputValue >= 0) {
 #ifdef USE_MYMATH
-    outputValue = mysqrt(inputValue);
+  double outputValue = mysqrt(inputValue);
 #else
-    outputValue = sqrt(inputValue);
+  double outputValue = sqrt(inputValue);
 #endif
-  }
 
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 46 - 52
Tests/Tutorial/Step7/CMakeLists.txt

@@ -1,82 +1,76 @@
-cmake_minimum_required (VERSION 2.6)
-project (Tutorial)
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
 
-# The version number.
-set (Tutorial_VERSION_MAJOR 1)
-set (Tutorial_VERSION_MINOR 0)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
 
 # does this system provide the log and exp functions?
-include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
-check_function_exists (log HAVE_LOG)
-check_function_exists (exp HAVE_EXP)
+include(CheckSymbolExists)
+set(CMAKE_REQUIRED_LIBRARIES "m")
+check_symbol_exists(log "math.h" HAVE_LOG)
+check_symbol_exists(exp "math.h" HAVE_EXP)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
 # configure a header file to pass some of the CMake settings
 # to the source code
-configure_file (
+configure_file(
   "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
   "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
-# add the binary tree to the search path for include files
-# so that we will find TutorialConfig.h
-include_directories ("${PROJECT_BINARY_DIR}")
-
 # add the MathFunctions library?
-if (USE_MYMATH)
-  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
-  add_subdirectory (MathFunctions)
-  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
-endif ()
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif(USE_MYMATH)
 
 # add the executable
-add_executable (Tutorial tutorial.cxx)
-target_link_libraries (Tutorial  ${EXTRA_LIBS})
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# 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)
+install(TARGETS Tutorial DESTINATION bin)
+install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  DESTINATION include
+  )
 
 # enable testing
-enable_testing ()
+enable_testing()
 
 # does the application run
-add_test (TutorialRuns Tutorial 25)
+add_test(NAME Runs COMMAND Tutorial 25)
 
 # does the usage message work?
-add_test (TutorialUsage Tutorial)
-set_tests_properties (TutorialUsage
-  PROPERTIES
-  PASS_REGULAR_EXPRESSION "Usage:.*number"
+add_test(NAME Usage COMMAND Tutorial)
+set_tests_properties(Usage
+  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
   )
 
-#define a macro to simplify adding tests
-macro (do_test arg result)
-  add_test (TutorialComp${arg} Tutorial ${arg})
-  set_tests_properties (TutorialComp${arg}
+# 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}
     )
-endmacro ()
+endfunction(do_test)
 
 # do a bunch of result based tests
-do_test (4 "4 is 2")
-do_test (9 "9 is 3")
-do_test (5 "5 is 2.236")
-do_test (7 "7 is 2.645")
-do_test (25 "25 is 5")
-do_test (-25 "-25 is 0")
-do_test (0.0001 "0.0001 is 0.01")
-
-# build a CPack driven installer package
-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_PACKAGE_CONTACT       "[email protected]")
-include (CPack)
-
-# enable dashboard scripting
-include (CTest)
+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")

+ 1 - 1
Tests/Tutorial/Step7/License.txt

@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tests/Tutorial/Step6...
+CMake/Tutorial/Step7...

+ 19 - 14
Tests/Tutorial/Step7/MathFunctions/CMakeLists.txt

@@ -2,23 +2,28 @@
 add_executable(MakeTable MakeTable.cxx)
 
 # add the command to generate the source code
-add_custom_command (
+add_custom_command(
   OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
   DEPENDS MakeTable
-  COMMAND MakeTable
-  ARGS ${CMAKE_CURRENT_BINARY_DIR}/Table.h
   )
 
-set_source_files_properties (
-  mysqrt.cxx PROPERTIES
-  OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  )
-
-# add the binary tree directory to the search path for include files
-include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
-
 # add the main library
-add_library(MathFunctions mysqrt.cxx)
+add_library(MathFunctions
+            mysqrt.cxx
+            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+            )
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
+# TutorialConfig.h include is an implementation detail
+# state that we depend on our binary dir to find Table.h
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE ${Tutorial_BINARY_DIR}
+                  ${CMAKE_CURRENT_BINARY_DIR}
+          )
 
-install (TARGETS MathFunctions DESTINATION bin)
-install (FILES MathFunctions.h DESTINATION include)
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)

+ 14 - 21
Tests/Tutorial/Step7/MathFunctions/MakeTable.cxx

@@ -1,32 +1,25 @@
 // A simple program that builds a sqrt table
-#include <math.h>
-#include <stdio.h>
+#include <cmath>
+#include <fstream>
+#include <iostream>
 
 int main(int argc, char* argv[])
 {
-  int i;
-  double result;
-
   // make sure we have enough arguments
   if (argc < 2) {
     return 1;
   }
 
-  // open the output file
-  FILE* fout = fopen(argv[1], "w");
-  if (!fout) {
-    return 1;
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
   }
-
-  // create a source file with a table of square roots
-  fprintf(fout, "double sqrtTable[] = {\n");
-  for (i = 0; i < 10; ++i) {
-    result = sqrt(static_cast<double>(i));
-    fprintf(fout, "%g,\n", result);
-  }
-
-  // close the table with a zero
-  fprintf(fout, "0};\n");
-  fclose(fout);
-  return 0;
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
 }

+ 6 - 12
Tests/Tutorial/Step7/MathFunctions/mysqrt.cxx

@@ -1,11 +1,11 @@
 #include "MathFunctions.h"
 #include "TutorialConfig.h"
-#include <stdio.h>
+#include <iostream>
 
 // include the generated table
 #include "Table.h"
 
-#include <math.h>
+#include <cmath>
 
 // a hack square root calculation using simple operations
 double mysqrt(double x)
@@ -14,26 +14,20 @@ double mysqrt(double x)
     return 0;
   }
 
-  double result;
-
-  // if we have both log and exp then use them
-  double delta;
-
   // use the table to help find an initial value
-  result = x;
+  double result = x;
   if (x >= 1 && x < 10) {
     result = sqrtTable[static_cast<int>(x)];
   }
 
   // do ten iterations
-  int i;
-  for (i = 0; i < 10; ++i) {
+  for (int i = 0; i < 10; ++i) {
     if (result <= 0) {
       result = 0.1;
     }
-    delta = x - (result * result);
+    double delta = x - (result * result);
     result = result + 0.5 * delta / result;
-    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
 
   return result;

+ 40 - 0
Tests/Tutorial/Step7/directions.txt

@@ -0,0 +1,40 @@
+# Building 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
+the Installing and Testing section (Step 4), 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.
+
+  include(InstallRequiredSystemLibraries)
+  set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+  set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+  set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+  include(CPack)
+
+That is all there is to it. We start by including 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 makes use of the variables we set earlier in this tutorial. Finally
+we include the CPack module which will use these variables and some other
+properties of the system you are on to setup an installer.
+
+The next step is to build the project in the usual manner and then run CPack
+on it. To build a binary distribution you would run:
+
+  cpack
+
+To create a source distribution you would type:
+
+  cpack -C CPackSourceConfig.cmake
+
+Alternatively, run “make package” or right click the Package target and
+“Build Project” from an IDE.
+
+Run the installer executable found  in the binary directory. Then run the
+installed executable and verify that it works.

+ 12 - 13
Tests/Tutorial/Step7/tutorial.cxx

@@ -1,8 +1,9 @@
 // A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
 #include "TutorialConfig.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
 
 #ifdef USE_MYMATH
 #  include "MathFunctions.h"
@@ -11,23 +12,21 @@
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
-    fprintf(stdout, "%s Version %d.%d\n", argv[0], Tutorial_VERSION_MAJOR,
-            Tutorial_VERSION_MINOR);
-    fprintf(stdout, "Usage: %s number\n", argv[0]);
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
-  double inputValue = atof(argv[1]);
-  double outputValue = 0;
+  double inputValue = std::stod(argv[1]);
 
-  if (inputValue >= 0) {
 #ifdef USE_MYMATH
-    outputValue = mysqrt(inputValue);
+  double outputValue = mysqrt(inputValue);
 #else
-    outputValue = sqrt(inputValue);
+  double outputValue = sqrt(inputValue);
 #endif
-  }
 
-  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
   return 0;
 }

+ 82 - 0
Tests/Tutorial/Step8/CMakeLists.txt

@@ -0,0 +1,82 @@
+cmake_minimum_required(VERSION 3.3)
+project(Tutorial)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# the version number.
+set(Tutorial_VERSION_MAJOR 1)
+set(Tutorial_VERSION_MINOR 0)
+
+# does this system provide the log and exp functions?
+include(CheckSymbolExists)
+set(CMAKE_REQUIRED_LIBRARIES "m")
+check_symbol_exists(log "math.h" HAVE_LOG)
+check_symbol_exists(exp "math.h" HAVE_EXP)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+
+# configure a header file to pass some of the CMake settings
+# to the source code
+configure_file(
+  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
+  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
+  )
+
+# add the MathFunctions library?
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif(USE_MYMATH)
+
+# add the executable
+add_executable(Tutorial tutorial.cxx)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+
+# 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_test)
+
+# 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")
+
+include(InstallRequiredSystemLibraries)
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
+set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+include(CPack)

+ 2 - 0
Tests/Tutorial/Step8/License.txt

@@ -0,0 +1,2 @@
+This is the open source License.txt file introduced in
+CMake/Tutorial/Step7...

+ 29 - 0
Tests/Tutorial/Step8/MathFunctions/CMakeLists.txt

@@ -0,0 +1,29 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+
+# 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
+  )
+
+# add the main library
+add_library(MathFunctions
+            mysqrt.cxx
+            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+            )
+
+# state that anybody linking to us needs to include the current source dir
+# to find MathFunctions.h, while we don't.
+# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
+# TutorialConfig.h include is an implementation detail
+# state that we depend on our binary dir to find Table.h
+target_include_directories(MathFunctions
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE ${Tutorial_BINARY_DIR}
+                  ${CMAKE_CURRENT_BINARY_DIR}
+          )
+
+install(TARGETS MathFunctions DESTINATION lib)
+install(FILES MathFunctions.h DESTINATION include)

+ 25 - 0
Tests/Tutorial/Step8/MathFunctions/MakeTable.cxx

@@ -0,0 +1,25 @@
+// A simple program that builds a sqrt table
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+int main(int argc, char* argv[])
+{
+  // make sure we have enough arguments
+  if (argc < 2) {
+    return 1;
+  }
+
+  std::ofstream fout(argv[1], std::ios_base::out);
+  const bool fileOpen = fout.is_open();
+  if (fileOpen) {
+    fout << "double sqrtTable[] = {" << std::endl;
+    for (int i = 0; i < 10; ++i) {
+      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
+    }
+    // close the table with a zero
+    fout << "0};" << std::endl;
+    fout.close();
+  }
+  return fileOpen ? 0 : 1; // return 0 if wrote the file
+}

+ 1 - 0
Tests/Tutorial/Step8/MathFunctions/MathFunctions.h

@@ -0,0 +1 @@
+double mysqrt(double x);

+ 42 - 0
Tests/Tutorial/Step8/MathFunctions/mysqrt.cxx

@@ -0,0 +1,42 @@
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+#include <iostream>
+
+// include the generated table
+#include "Table.h"
+
+#include <cmath>
+
+// 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 = exp(log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result << " using log"
+            << std::endl;
+#else
+  // use the table to help find an initial value
+  double result = x;
+  if (x >= 1 && x < 10) {
+    result = sqrtTable[static_cast<int>(x)];
+  }
+
+  // if we have both log and exp then use them
+
+  // 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;
+}

+ 8 - 0
Tests/Tutorial/Step8/TutorialConfig.h.in

@@ -0,0 +1,8 @@
+// the configured options and settings for Tutorial
+#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
+#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
+
+// does the platform provide exp and log functions?
+#cmakedefine HAVE_LOG
+#cmakedefine HAVE_EXP

+ 38 - 0
Tests/Tutorial/Step8/directions.txt

@@ -0,0 +1,38 @@
+# Adding Support for a Dashboard #
+
+Adding support for submitting our test results to a dashboard is very easy. We
+already defined a number of tests for our project in the earlier steps of this
+tutorial. We just have to run those tests and submit them to a dashboard. To
+include support for dashboards we include the CTest module in our top-level
+CMakeLists.txt.
+
+Replace:
+  # enable testing
+  enable_testing()
+
+With:
+  # enable dashboard scripting
+  include(CTest)
+
+The CTest module will automatically call enable_testing(), so
+we can remove it from our CMake files.
+
+We will also need to create a CTestConfig.cmake file where we can specify the
+name of the project and where to submit the dashboard.
+
+  set(CTEST_PROJECT_NAME "CMakeTutorial")
+  set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
+
+  set(CTEST_DROP_METHOD "http")
+  set(CTEST_DROP_SITE "my.cdash.org/")
+  set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+  set(CTEST_DROP_SITE_CDASH TRUE)
+
+CTest will read in this file when it runs. To create a simple dashboard you can
+run cmake or cmake-gui to configure the project, but do not build it yet.
+Instead, change directory to the binary tree, and then run:
+ 'ctest [-VV] –D Experimental'. On Windows, build the EXPERIMENTAL target.
+
+Ctest will build and test the project and submit results to the Kitware public
+dashboard. The results of your dashboard will be uploaded to Kitware's public
+dashboard here: https://my.cdash.org/index.php?project=CMakeTutorial.

+ 32 - 0
Tests/Tutorial/Step8/tutorial.cxx

@@ -0,0 +1,32 @@
+// A simple program that computes the square root of a number
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#include "TutorialConfig.h"
+
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+int main(int argc, char* argv[])
+{
+  if (argc < 2) {
+    std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
+              << Tutorial_VERSION_MAJOR << std::endl;
+    std::cout << "Usage: " << argv[0] << " number" << std::endl;
+    return 1;
+  }
+
+  double inputValue = std::stod(argv[1]);
+
+#ifdef USE_MYMATH
+  double outputValue = mysqrt(inputValue);
+#else
+  double outputValue = sqrt(inputValue);
+#endif
+
+  std::cout << "The square root of " << inputValue << " is " << outputValue
+            << std::endl;
+  return 0;
+}

Some files were not shown because too many files changed in this diff