1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
# Mixing Static and Shared #
In this section we will show how by using the BUILD_SHARED_LIBS variable we can
control the default behavior of add_library, and allow control over how
libraries without an explicit type ( STATIC/SHARED/MODULE/OBJECT ) are built.
To accomplish this we need to add BUILD_SHARED_LIBS to the top level
CMakeLists.txt. We use the option command as it allows users to optionally
select if the value should be On or Off.
Next we are going to refactor MathFunctions to become a real library that
encapsulates using mysqrt or sqrt, instead of requiring the calling code
to do this logic. This will also mean that USE_MYMATH will not control building
MathFuctions, but instead will control the behavior of this library.
The first step is to update the starting section of the top level CMakeLists.txt
to look like:
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_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 PUBLIC MathFunctions)
Now that we have made MathFunctions always be used, we will need to update
the logic of that library. So, in MathFunctions/CMakeLists.txt we need to
create a SqrtLibrary that will conditionally be built when USE_MYMATH is
enabled. Now, since this is a tutorial, we are going to explicitly require
that SqrtLibrary is built statically.
The end result is that MathFunctions/CMakeLists.txt should look like:
# 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}
)
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)
Next, update MathFunctions/mysqrt.cxx to use the mathfunctions and detail namespaces:
#include <iostream>
#include "MathFunctions.h"
// 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)
{
...
return result;
}
}
}
We also need to make some changes in tutorial.cxx, so that it no longer uses USE_MYMATH:
1. Always include MathFunctions.h
2. Always use mathfunctions::sqrt
Finally, update MathFunctions/MathFunctions.h to use dll export defines:
#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);
}
At this point, if you build everything, you will notice that linking fails
as we are combining a static library without position enabled code with a
library that has position enabled code. This solution to this is to explicitly
set the POSITION_INDEPENDENT_CODE target property of SqrtLibrary to be True no
matter the build type.
Exercise: We modified MathFunctions.h to use dll export defines. Using CMake
documentation can you find a helper module to simplify this?
Exercise: Determine what command is enabling PIC for SqrtLibrary.
What happens if we remove said command?
|