Browse Source

Merge topic 'update-tutorial'

0e2cdacf7b Tests: Update style of c++ code snippets in Tutorial directions
f2ddedfa58 Tests: Update CMake tutorial
438651506a Tests: Make ExternalProjectLocal independent of Tutorial directory

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

+ 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

+ 53 - 76
Tests/ExternalProjectLocal/CMakeLists.txt

@@ -20,71 +20,55 @@ set(binary_base "${base}/Build")
 set_property(DIRECTORY PROPERTY EP_BASE ${base})
 set_property(DIRECTORY PROPERTY EP_STEP_TARGETS configure build test)
 
-if(NOT DEFINED can_build_tutorial_step5)
-  set(can_build_tutorial_step5 1)
-
-  # The ExternalProject builds of Tutorial Step5 cannot be built
-  # correctly 2nd and later times in an in-source build...
-  # (because the CMakeCache.txt from the real in-source build of
-  # the Tests/Tutorial/Step5 directory gets copied when we do
-  # the "source directory copy" step... but it still refers to
-  # its original path which yields a configure error.) So:
-  #
-  if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
-    set(can_build_tutorial_step5 0)
-  endif()
-endif()
 
 # Local DIR:
 #
-if(can_build_tutorial_step5)
-  set(proj TutorialStep5-Local)
-  ExternalProject_Add(${proj}
-    URL "${CMAKE_CURRENT_SOURCE_DIR}/../Tutorial/Step5"
-    CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-    CMAKE_ARGS -G ${CMAKE_GENERATOR} <SOURCE_DIR>
-    TEST_BEFORE_INSTALL 1
-    LOG_INSTALL 1
-  )
-  set_property(TARGET ${proj} PROPERTY FOLDER "Local")
-  ExternalProject_Get_Property(${proj} install_dir)
-  set(TutorialStep5_install_dir ${install_dir})
-
-  set(proj TutorialStep5-Local-TestAfterInstall)
-  ExternalProject_Add(${proj}
-    URL "${CMAKE_CURRENT_SOURCE_DIR}/../Tutorial/Step5"
-    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -G ${CMAKE_GENERATOR} <SOURCE_DIR>
-    CMAKE_CACHE_DEFAULT_ARGS -DUSE_MYMATH:BOOL=OFF
-    TEST_AFTER_INSTALL 1
-    LOG_TEST 1
-  )
-  set_property(TARGET ${proj} PROPERTY FOLDER "Local")
-
-  set(proj TutorialStep5-Local-TestExcludeFromMainBefore)
-  ExternalProject_Add(${proj}
-    URL "${CMAKE_CURRENT_SOURCE_DIR}/../Tutorial/Step5"
-    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>  -G ${CMAKE_GENERATOR} <SOURCE_DIR>
-    CMAKE_CACHE_DEFAULT_ARGS -DUSE_MYMATH:BOOL=OFF
-    TEST_BEFORE_INSTALL 1
-    TEST_EXCLUDE_FROM_MAIN 1
-    STEP_TARGETS test
-    LOG_TEST 1
-  )
-  set_property(TARGET ${proj} PROPERTY FOLDER "Local")
-
-  set(proj TutorialStep5-Local-TestExcludeFromMainAfter)
-  ExternalProject_Add(${proj}
-    URL "${CMAKE_CURRENT_SOURCE_DIR}/../Tutorial/Step5"
-    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>  -G ${CMAKE_GENERATOR} <SOURCE_DIR>
-    CMAKE_CACHE_DEFAULT_ARGS -DUSE_MYMATH:BOOL=OFF
-    TEST_AFTER_INSTALL 1
-    TEST_EXCLUDE_FROM_MAIN 1
-    STEP_TARGETS test
-    LOG_TEST 1
-  )
-  set_property(TARGET ${proj} PROPERTY FOLDER "Local")
+set(proj TutorialStep5-Local)
+ExternalProject_Add(${proj}
+URL "${CMAKE_CURRENT_SOURCE_DIR}/Step5"
+CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+CMAKE_ARGS -G ${CMAKE_GENERATOR} <SOURCE_DIR>
+TEST_BEFORE_INSTALL 1
+LOG_INSTALL 1
+)
+set_property(TARGET ${proj} PROPERTY FOLDER "Local")
+ExternalProject_Get_Property(${proj} install_dir)
+set(TutorialStep5_install_dir ${install_dir})
+
+set(proj TutorialStep5-Local-TestAfterInstall)
+ExternalProject_Add(${proj}
+URL "${CMAKE_CURRENT_SOURCE_DIR}/Step5"
+CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -G ${CMAKE_GENERATOR} <SOURCE_DIR>
+CMAKE_CACHE_DEFAULT_ARGS -DUSE_MYMATH:BOOL=OFF
+TEST_AFTER_INSTALL 1
+LOG_TEST 1
+)
+set_property(TARGET ${proj} PROPERTY FOLDER "Local")
+
+set(proj TutorialStep5-Local-TestExcludeFromMainBefore)
+ExternalProject_Add(${proj}
+URL "${CMAKE_CURRENT_SOURCE_DIR}/Step5"
+CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>  -G ${CMAKE_GENERATOR} <SOURCE_DIR>
+CMAKE_CACHE_DEFAULT_ARGS -DUSE_MYMATH:BOOL=OFF
+TEST_BEFORE_INSTALL 1
+TEST_EXCLUDE_FROM_MAIN 1
+STEP_TARGETS test
+LOG_TEST 1
+)
+set_property(TARGET ${proj} PROPERTY FOLDER "Local")
+
+set(proj TutorialStep5-Local-TestExcludeFromMainAfter)
+ExternalProject_Add(${proj}
+URL "${CMAKE_CURRENT_SOURCE_DIR}/Step5"
+CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>  -G ${CMAKE_GENERATOR} <SOURCE_DIR>
+CMAKE_CACHE_DEFAULT_ARGS -DUSE_MYMATH:BOOL=OFF
+TEST_AFTER_INSTALL 1
+TEST_EXCLUDE_FROM_MAIN 1
+STEP_TARGETS test
+LOG_TEST 1
+)
+set_property(TARGET ${proj} PROPERTY FOLDER "Local")
 
-endif()
 
 
 # Local TAR:
@@ -209,12 +193,10 @@ enable_testing()
 #
 # BuildTree tests:
 #
-if(can_build_tutorial_step5)
-  add_test(TutorialStep5-Local-BuildTreeTest
-    "${binary_base}/TutorialStep5-Local/Tutorial" 42)
-  set_property(TEST TutorialStep5-Local-BuildTreeTest
-    APPEND PROPERTY LABELS Step5 BuildTree)
-endif()
+add_test(TutorialStep5-Local-BuildTreeTest
+"${binary_base}/TutorialStep5-Local/Tutorial" 42)
+set_property(TEST TutorialStep5-Local-BuildTreeTest
+APPEND PROPERTY LABELS Step5 BuildTree)
 
 add_test(TutorialStep1-LocalTAR-BuildTreeTest
   "${binary_base}/TutorialStep1-LocalTAR/EP-Tutorial" 36)
@@ -234,12 +216,7 @@ add_test(TutorialStep1-LocalNoDirTGZ-BuildTreeTest
 
 # InstallTree tests:
 #
-if(can_build_tutorial_step5)
-  add_test(TutorialStep5-InstallTreeTest
-    "${TutorialStep5_install_dir}/bin/Tutorial" 49)
-  set_property(TEST TutorialStep5-InstallTreeTest
-    APPEND PROPERTY LABELS Step5 InstallTree)
-endif()
-
-
-message(STATUS "can_build_tutorial_step5='${can_build_tutorial_step5}'")
+add_test(TutorialStep5-InstallTreeTest
+"${TutorialStep5_install_dir}/bin/Tutorial" 49)
+set_property(TEST TutorialStep5-InstallTreeTest
+APPEND PROPERTY LABELS Step5 InstallTree)

+ 71 - 0
Tests/ExternalProjectLocal/Step5/CMakeLists.txt

@@ -0,0 +1,71 @@
+cmake_minimum_required (VERSION 2.6)
+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)
+
+# 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 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})
+
+# 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)
+
+# 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 (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")

+ 17 - 0
Tests/ExternalProjectLocal/Step5/MathFunctions/CMakeLists.txt

@@ -0,0 +1,17 @@
+# 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_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  )
+
+install (TARGETS MathFunctions DESTINATION bin)
+install (FILES MathFunctions.h DESTINATION include)

+ 32 - 0
Tests/ExternalProjectLocal/Step5/MathFunctions/MakeTable.cxx

@@ -0,0 +1,32 @@
+// A simple program that builds a sqrt table
+#include <math.h>
+#include <stdio.h>
+
+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;
+  }
+
+  // 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;
+}

+ 1 - 0
Tests/ExternalProjectLocal/Step5/MathFunctions/MathFunctions.h

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

+ 40 - 0
Tests/ExternalProjectLocal/Step5/MathFunctions/mysqrt.cxx

@@ -0,0 +1,40 @@
+#include "MathFunctions.h"
+#include "TutorialConfig.h"
+#include <stdio.h>
+
+// include the generated table
+#include "Table.h"
+
+#include <math.h>
+
+// a hack square root calculation using simple operations
+double mysqrt(double x)
+{
+  if (x <= 0) {
+    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)];
+  }
+
+  // do ten iterations
+  int i;
+  for (i = 0; i < 10; ++i) {
+    if (result <= 0) {
+      result = 0.1;
+    }
+    delta = x - (result * result);
+    result = result + 0.5 * delta / result;
+    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
+  }
+
+  return result;
+}

+ 8 - 0
Tests/ExternalProjectLocal/Step5/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

+ 33 - 0
Tests/ExternalProjectLocal/Step5/tutorial.cxx

@@ -0,0 +1,33 @@
+// A simple program that computes the square root of a number
+#include "TutorialConfig.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef USE_MYMATH
+#  include "MathFunctions.h"
+#endif
+
+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]);
+    return 1;
+  }
+
+  double inputValue = atof(argv[1]);
+  double outputValue = 0;
+
+  if (inputValue >= 0) {
+#ifdef USE_MYMATH
+    outputValue = mysqrt(inputValue);
+#else
+    outputValue = sqrt(inputValue);
+#endif
+  }
+
+  fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
+  return 0;
+}

+ 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)

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

@@ -0,0 +1,95 @@
+# 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;
 }

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

@@ -0,0 +1,104 @@
+# 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)

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