Tests: Update CMake tutorial

Latest material from data.kitware.com -> Collections -> Courses -> CMake.
This commit is contained in:
Betsy McPhail
2019-01-20 11:28:39 -05:00
parent 438651506a
commit f2ddedfa58
112 changed files with 3118 additions and 623 deletions

View File

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

View File

@@ -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"
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"
)

View File

@@ -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" )

View File

@@ -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));
}

View File

@@ -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.

View File

@@ -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"
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
Tests/Tutorial/Readme.txt Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

@@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

@@ -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.

View File

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

View File

@@ -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}"
)

View File

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

View File

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

View File

@@ -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 compilers 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 isnt 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, Step1s sqrt or Step2s mysqrt?

View File

@@ -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;
double inputValue = std::stod(argv[1]);
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);
double outputValue = sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
return 0;
}

View File

@@ -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_executable(Tutorial tutorial.cxx)
# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
target_link_libraries(Tutorial ${EXTRA_LIBS})
# enable testing
enable_testing ()
# does the application run
add_test (TutorialRuns Tutorial 25)
# 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"
)
# 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}
)

View File

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

View File

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

View File

@@ -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.

View File

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

View File

@@ -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_executable(Tutorial tutorial.cxx)
# 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 (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}"
)

View File

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

View File

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

View File

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

View File

@@ -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”.

View File

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

View File

@@ -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)
# 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"
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
#define a macro to simplify adding tests
macro (do_test arg result)
add_test (TutorialComp${arg} Tutorial ${arg})
set_tests_properties (TutorialComp${arg}
# 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}
)
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")

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,
Step1s sqrt or Step5s 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?

View File

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

View File

@@ -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)
# 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"
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
#define a macro to simplify adding tests
macro (do_test arg result)
add_test (TutorialComp${arg} Tutorial ${arg})
set_tests_properties (TutorialComp${arg}
# 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}
)
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")

View File

@@ -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 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)
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.
# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
# TutorialConfig.h include is an implementation detail
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${Tutorial_BINARY_DIR}
)
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)

View File

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

View File

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

View File

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

View File

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

View File

@@ -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)
# 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"
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
#define a macro to simplify adding tests
macro (do_test arg result)
add_test (TutorialComp${arg} Tutorial ${arg})
set_tests_properties (TutorialComp${arg}
# 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}
)
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 "foo@bar.org")
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")

View File

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

View File

@@ -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
)
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.
# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
# TutorialConfig.h include is an implementation detail
# state that we depend on our binary dir to find Table.h
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${Tutorial_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)

View File

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

View File

@@ -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;

View File

@@ -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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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