-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsection_fortran_cmake.tex
331 lines (256 loc) · 13.8 KB
/
section_fortran_cmake.tex
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
% --------------------------------------------------------------------
\section{CMake}
% --------------------------------------------------------------------
When projects become large the time needed for maintaining the build system increases. This is often due to the fact that different OS environments needs to be handled in different ways and this has to be included in the makefile. CMake is a tool that can generate targeted makefiles and project files for most existing development environments. CMake works by parsing special files, CMakeLists.txt, and generating the needed makefiles and project files.
% --------------------------------------------------------------------
\subsection{Compiling code with cmake}
% --------------------------------------------------------------------
To use CMake, a CMakeLists.txt file has to be created. This is a normal text files with special CMake statements in it. Usually this files starts with a \cmfunc{cmake\_minimum\_required(VERSION 2.6)}. This prevents the CMakeLists.txt file to be used by a too old cmake. The first actual statement is usually \cmfunc{project(...)}-function defining the name of the project.
\cmmode
\begin{lstlisting}
cmake_minimum_required(VERSION 2.6)
project(simple)
\end{lstlisting}
The name of the project is not the same as the executable but is used when generating project files for development environments.
CMake by default does not support Fortran, so a special function, \cmfunc{enable\_language}-function is used to enable this:
\begin{lstlisting}
enable_language(Fortran)
\end{lstlisting}
To create an executable the \cmfunc{add\_executable}-function is used. This command takes an executable name as the first argument and a list of source files.
\begin{lstlisting}
add_executable(simple myprog.f90)
\end{lstlisting}
The completed CMakeLists.txt file then becomes:
\begin{lstlisting}
cmake_minimum_required(VERSION 2.6)
project(simple)
enable_language(Fortran)
add_executable(simple myprog.f90)
\end{lstlisting}
Now when we have a CMakeLists.txt file it is possible to run \cli{cmake .} in the same directory to create the needed makefiles to build the project:
\cmdmode
\begin{lstlisting}
$ ls
CMakeLists.txt myprog.f90
$ cmake .
-- The C compiler identification is GNU 4.2.1
-- The CXX compiler identification is Clang 4.0.0
-- Checking whether C compiler has -isysroot
-- Checking whether C compiler has -isysroot - yes
-- Checking whether C compiler supports OSX deployment target flag
-- Checking whether C compiler supports OSX deployment target flag - yes
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- The Fortran compiler identification is GNU
-- Check for working Fortran compiler: /opt/local/bin/gfortran
-- Check for working Fortran compiler: /opt/local/bin/gfortran -- works
-- Detecting Fortran compiler ABI info
-- Detecting Fortran compiler ABI info - done
-- Checking whether /opt/local/bin/gfortran supports Fortran 90
-- Checking whether /opt/local/bin/gfortran supports Fortran 90 -- yes
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/.../simple
$ ls
CMakeCache.txt CMakeLists.txt cmake_install.cmake
CMakeFiles Makefile myprog.f90
\end{lstlisting}
As show in the above output, cmake, has generated a lot of files one of them being a normal makefile. To build the project, the normal make command can be used.
\begin{lstlisting}
$ make
Scanning dependencies of target simple
[100%] Building Fortran object CMakeFiles/simple.dir/myprog.f90.o
Linking Fortran executable simple
[100%] Built target simple
\end{lstlisting}
CMake generates a lot of files when run. Which can make the source tree quite cluttered. The recommended way of running CMake is to create a separate build directory and generate the build files in this directory. This is done in the following example:
\begin{lstlisting}
$ mkdir build
$ cd build
$ cmake ..
-- The C compiler identification is GNU 4.2.1
.
.
-- Generating done
-- Build files have been written to: /Users/.../simple/build
\end{lstlisting}
Make is then run in this directory as before. In this approach it is easy to remove the build files by removing the build directory.
% --------------------------------------------------------------------
\subsection{Building debug and release versions}
% --------------------------------------------------------------------
By default CMake generates build files for compiling debug versions of an applicaiton. That is using no optimisation and with debug symbols. Controlling the build type can be done by assigning the variable \cmvar{CMAKE\_BUILD\_TYPE} to either \cmvar{Release} or \cmvar{Debug} when executing CMake. Variables can be set on the command line by using the switch -D as shown in the following example:
\begin{lstlisting}
$ cmake -D CMAKE_BUILD_TYPE=Release ..
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/lindemann/Development/progsci_book/source/cmake_examples/simple/build
\end{lstlisting}
% --------------------------------------------------------------------
\subsection{Adding library dependencies}
% --------------------------------------------------------------------
In the previous examples the binaries have been built without any library dependencies. To add link dependencies, the \cmfunc{target\_link\_libraries} can be used. To add the libraries, \cmvar{libblas} and \cmvar{libm} as dependencies of the executable, the CMakeList.txt becomes:
\cmmode
\begin{lstlisting}
cmake_minimum_required(VERSION 2.6)
project(simple)
enable_language(Fortran)
add_executable(simple myprog.f90)
target_link_libraries(simple blas m)
\end{lstlisting}
To show what switches that are actually used when building the executable, the \cmvar{CMAKE\_VERBOSE\_MAKEFILE}, is set to \cmvar{ON}. This will show the actual commands used during the build.
\cmdmode
\begin{lstlisting}
$ mkdir build
$ cd build/
$ cmake -D CMAKE_VERBOSE_MAKEFILE=ON ..
-- The C compiler identification is GNU 4.2.1
-- The CXX compiler identification is Clang 4.0.0
...
-- Generating done
-- Build files have been written ...
$ make
...
/opt/local/bin/gfortran [...]/mymodule.f90.o -o multiple -lblas -lm
...
\end{lstlisting}
Which shows that the libraries have been added to the actual compilation command.
% --------------------------------------------------------------------
\subsection{Variables and conditional builds}
% --------------------------------------------------------------------
Often when compiling code under different platforms, special flags and commands have to be used. CMake supports conditional statements in the CMakeLists.txt files to handle these cases. To test for a Unix-build the following if statement can be used:
\cmmode
\begin{lstlisting}
if (UNIX)
message("This is a Unix build.")
endif (UNIX)
\end{lstlisting}
\cmvar{UNIX} is predefined variable that is true when building on Unix-type system. When running CMake on a Unix-type system will print ''This is a Unix build.'' on the console.
CMake also has an else-statement. The following code, creates a build target and adds different build options depending on the platform used:
\begin{lstlisting}
if (UNIX)
add_executable(multiple myprog.f90 mymodule.f90)
target_link_libraries(multiple blas m)
else (UNIX)
if (WIN32)
add_executable(multiple myprog.f90 mymodule.f90)
target_link_libraries(multiple blas32)
else (WIN32)
message("Not supported configuration.")
endif (WIN32)
endif (UNIX)
\end{lstlisting}
It is also possible to use variables in CMake. Variables can be both strings and lists of strings. A variable is created by using the \cmfunc{set}-function. The following example shows how a simple string variable is created:
\begin{lstlisting}
set(MYVAR "Hello, world!")
\end{lstlisting}
To use the actual value of a variable, it has to be preceded by a \$ enclosed by curly brackets as shown in the following example:
\begin{lstlisting}
set(MYVAR "Hello, world!")
message(${MYVAR})
\end{lstlisting}
This will print the contents of the variable, \cmvar{MYVAR}. If not enclosed it will print the name of the variable.
Variables can also be lists of values which can be iterated over. Creating a list is also done using the \cmfunc{set}-function, as shown in this example:
\begin{lstlisting}
set(MYLIST a b c)
message(${MYLIST})
\end{lstlisting}
Here, \cmvar{MYLIST}, containing 3 strings. The \cmfunc{message}-function will concatenate the items in the list and the resulting output of running cmake will be:
\cmdmode
\begin{lstlisting}
$ cmake ..
abc
-- Configuring done
-- Generating done
-- Build files have been written to: ...
\end{lstlisting}
Using a list variable it is also possible to do an iteration using a \cmfunc{for}-statement, which the following example shows:
\cmmode
\begin{lstlisting}
set(MYLIST a b c)
foreach(i ${MYLIST})
message(${i})
endforeach(i)
\end{lstlisting}
Running this using CMake produced the following output:
\cmdmode
\begin{lstlisting}
$ cmake ..
a
b
c
-- Configuring done
-- Generating done
-- Build files have been written to: ...
\end{lstlisting}
% --------------------------------------------------------------------
\subsection{Controlling optimisation options}
% --------------------------------------------------------------------
Optimisation options can differ between compilers. To control the optimisation options in CMake, conditional builds using if-statements can be used. First, the used compiler needs to be queried. The path to the actual compiler is stored in the \cmvar{CMAKE\_Fortran\_COMPILER}. To create an if-statement on the compiler the compiler command must be extracted from the compiler path. This can be accomplished using the \cmfunc{get\_filename\_component}
\cmmode
\begin{lstlisting}
get_filename_component (Fortran_COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME)
\end{lstlisting}
This command extracts the filename component of the path and stores it in the variable \cmvar{Fortran\_COMPILER\_NAME}. Next, an if-statement has to implemented that queries for different compilers. A string comparison can be done using the \cmfunc{STREQUAL} operator in CMake. Compilation flags for CMake are stored in \cmvar{CMAKE\_Fortran\_FLAGS\_RELEASE} for release mode flags and \cmvar{CMAKE\_Fortran\_FLAGS\_DEBUG} for debug flags. An example fo this king of conditional compilation statement is shown below (from \cite{cmakecond12}):
\cmmode
\begin{lstlisting}
if (Fortran_COMPILER_NAME STREQUAL "gfortran")
set (CMAKE_Fortran_FLAGS_RELEASE "-funroll-all-loops -fno-f2c -O3")
set (CMAKE_Fortran_FLAGS_DEBUG "-fno-f2c -O0 -g")
elseif (Fortran_COMPILER_NAME STREQUAL "ifort")
set (CMAKE_Fortran_FLAGS_RELEASE "-f77rtl -O3")
set (CMAKE_Fortran_FLAGS_DEBUG "-f77rtl -O0 -g")
elseif (Fortran_COMPILER_NAME STREQUAL "g77")
set (CMAKE_Fortran_FLAGS_RELEASE "-funroll-all-loops -fno-f2c -O3 -m32")
set (CMAKE_Fortran_FLAGS_DEBUG "-fno-f2c -O0 -g -m32")
else (Fortran_COMPILER_NAME STREQUAL "gfortran")
message ("No optimized Fortran compiler flags are known, we just try -O2...")
set (CMAKE_Fortran_FLAGS_RELEASE "-O2")
set (CMAKE_Fortran_FLAGS_DEBUG "-O0 -g")
endif (Fortran_COMPILER_NAME STREQUAL "gfortran")
\end{lstlisting}
% --------------------------------------------------------------------
\subsection{Generating project files for development environments}
% --------------------------------------------------------------------
CMake is not limited to generating makefiles, it can also generate project files for a number of graphical development environments. Supported generators in CMake can be listed by running the \cmfunc{cmake}-command without parameters. The following list is produced on a Mac OS X based machine:
\cmdmode
\begin{lstlisting}
enerators
The following generators are available on this platform:
Unix Makefiles = Generates standard UNIX makefiles.
Xcode = Generate Xcode project files.
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
Eclipse CDT4 - Unix Makefiles = Generates Eclipse CDT 4.0 project files.
KDevelop3 = Generates KDevelop 3 project files.
KDevelop3 - Unix Makefiles = Generates KDevelop 3 project files.
\end{lstlisting}
This lists covers most common development environments for Mac OS X. When running on a Windows machine, generators for Visual Studio and other development environments for that platform will be available as well.
To generate build files for a different generator the \cmvar{-G}-switch is used. In the following example build files for the Eclipse-environment are generated.
\begin{lstlisting}
$ mkdir build_eclipse
$ cd build_eclipse/
$ cmake -G "Eclipse CDT4 - Unix Makefiles" ../multiple/
-- The C compiler identification is GNU 4.2.1
-- The CXX compiler identification is Clang 4.0.0
-- Could not determine Eclipse version, assuming at least 3.6 (Helios). Adjust CMAKE_ECLIPSE_VERSION if this is wrong.
...
-- Generating done
-- Build files have been written to: ...
$ ls -la
total 112
drwxr-xr-x 8 lindemann staff 272 Aug 29 20:07 .
drwxr-xr-x 13 lindemann staff 442 Aug 29 20:06 ..
-rw-r--r-- 1 lindemann staff 14343 Aug 29 20:07 .cproject
-rw-r--r-- 1 lindemann staff 5527 Aug 29 20:07 .project
-rw-r--r-- 1 lindemann staff 17808 Aug 29 20:07 CMakeCache.txt
drwxr-xr-x 21 lindemann staff 714 Aug 29 20:07 CMakeFiles
-rw-r--r-- 1 lindemann staff 4770 Aug 29 20:07 Makefile
-rw-r--r-- 1 lindemann staff 1562 Aug 29 20:07 cmake_install.cmake
\end{lstlisting}
When generation is completed this directory can be added to a Eclipse workspace as a project.
Please note that in the above example we are using a build directory not located in the source tree. This is the recommended way for an Eclipse based project.