summaryrefslogtreecommitdiff
path: root/CMakeLists.txt
blob: 80c529082db91c4a0f8a2ce5554c00945f135dc6 (plain)
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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
# This usage of CMake requires at least version 2.4.4 (earlier versions lack the CheckCXXCompilerFlag module)
cmake_minimum_required(VERSION 2.4.4 FATAL_ERROR)

# Detect is we are using CMake 2.6 or better, these versions include functions that require less work than CMake 2.4 does
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5)
  set(CMAKE26_OR_BETTER TRUE)
else(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5)
  set(CMAKE26_OR_BETTER FALSE)
endif(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5)

# If the user specifies -DCMAKE_BUILD_TYPE on the command line, take their definition
# and dump it in the cache along with proper documentation, otherwise set CMAKE_BUILD_TYPE
# to Debug prior to calling PROJECT()
# Only do this if not using Visual Studio
if(NOT MSVC)
  if(CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
  else(CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
  endif(CMAKE_BUILD_TYPE)
endif(NOT MSVC)

# Set the project as C++ primarily, but have C enabled for the checks required later
project(Anope CXX)
enable_language(C)

# If running under MinGW, we have to force the resource compiler settings (hopefully this will be fixed in a later version of CMake)
if(MINGW)
  set(CMAKE_RC_COMPILER_INIT windres)
  enable_language(RC)
  set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> <DEFINES> -o <OBJECT> <SOURCE>")
endif(MINGW)

# Include the checking functions used later in this CMakeLists.txt
include(CheckFunctionExists)
include(CheckIncludeFile)
include(CheckTypeSize)
include(CheckCXXCompilerFlag)
include(CheckLibraryExists)

# Add an optional variable for using run-cc.pl for building, Perl will be checked later regardless of this setting
option(USE_RUN_CC_PL "Use run-cc.pl for building" OFF)

# If using Visual Studio, set the C++ flags accordingly
if(MSVC)
  # Remove the default exception handling flags, also remove default warning level flag
  string(REPLACE "/EHsc " "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
  string(REPLACE "/GX " "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
  string(REPLACE "/W3 " "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
  # Set the compile flags to have warnings on the max setting (but disable a few annoying ones), exception handling turned on, the proper defines, and the include directories
  set(CXXFLAGS "${CXXFLAGS} /W4 /wd4251 /wd4706 /wd4800 /EHs /D_WIN32 /DMSVCPP /I\"${Anope_SOURCE_DIR}/include\" /I\"${Anope_BINARY_DIR}/include\" /I\"${Anope_BINARY_DIR}/lang\"")
  # Set the module-specific compile flags to include a define for module compiling
  set(MODULE_CXXFLAGS "${CXXFLAGS} /DMODULE_COMPILE")
# Otherwise, we're not using Visual Studio
else(MSVC)
  # Set the compile flags to have all warnings on (including shadowed variables) and the include directories
  set(CXXFLAGS "${CXXFLAGS} -Wall -Wshadow -I${Anope_SOURCE_DIR}/include -I${Anope_BINARY_DIR}/include -I${Anope_BINARY_DIR}/lang")
  # If on a *nix system, also set the compile flags to remove GNU extensions (favor ISO C++) as well as reject non-ISO C++ code, also remove all leading underscores in exported symbols
  if(UNIX)
    set(CXXFLAGS "${CXXFLAGS} -ansi -pedantic -fno-leading-underscore")
    # Set the module-specific compile flags to the same setting as the compile flags
    set(MODULE_CXXFLAGS "${CXXFLAGS}")
  # If we aren't on a *nix system, set the compile flags to include a define for Windows
  else(UNIX)
    set(CXXFLAGS "${CXXFLAGS} -D_WIN32")
    # Also, if we are building under MinGW, add another define for MinGW
    if(MINGW)
      set(CXXFLAGS "${CXXFLAGS} -DMINGW")
    endif(MINGW)
    # Set the module-specific compile flags to include a define for module compiling
    set(MODULE_CXXFLAGS "${CXXFLAGS} -DMODULE_COMPILE")
  endif(UNIX)
endif(MSVC)

# If CMake has found that the given system requires a special library for dl* calls, include it with the linker flags
if(CMAKE_DL_LIBS)
  set(LDFLAGS "${LDFLAGS} ${CMAKE_DL_LIBS}")
endif(CMAKE_DL_LIBS)

# Under MinGW, the -shared flag isn't properly set in the module-specific linker flags, add it from the C flags for shared libraries
if(MINGW)
  set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}")
endif(MINGW)

# Under Windows, we set the executable name for Anope to be anope
if(WIN32)
  set(PROGRAM_NAME anope)
# Under *nix, we set the executable name for Anope to be services
else(WIN32)
  set(PROGRAM_NAME services)
endif(WIN32)

# If we are not using Visual Studio, we'll run the following checks
if(NOT MSVC)
  # Check if the C++ compiler can accept the -pipe flag, and add it to the compile flags if it works
  check_cxx_compiler_flag(-pipe HAVE_PIPE_FLAG)
  if(HAVE_PIPE_FLAG)
    set(CXXFLAGS "${CXXFLAGS} -pipe")
  endif(HAVE_PIPE_FLAG)

  # The following are additional library checks, they are not required for Windows
  if(NOT WIN32)
    # Check if socket is within the socket library (if the library exists), and add it to the linker flags if needed
    check_library_exists(socket socket "" HAVE_SOCKET_LIB)
    if(HAVE_SOCKET_LIB)
      set(LDFLAGS "${LDFLAGS} -lsocket")
    endif(HAVE_SOCKET_LIB)
  endif(NOT WIN32)

  # Check if va_list can be copied as an array, and if it can, set the flag for it
  try_run(RUN_VA_LIST_AS_ARRAY COMPILE_VA_LIST_AS_ARRAY
    ${Anope_SOURCE_DIR} ${Anope_SOURCE_DIR}/va_list_check.c
  )
  if(COMPILE_VA_LIST_AS_ARRAY AND NOT RUN_VA_LIST_AS_ARRAY)
    set(HAVE_VA_LIST_AS_ARRAY 1)
  endif(COMPILE_VA_LIST_AS_ARRAY AND NOT RUN_VA_LIST_AS_ARRAY)
endif(NOT MSVC)

# If DEFUMASK wasn't passed to CMake, set a default depending on if RUNGROUP was passed in or not
if(NOT DEFUMASK)
  if(RUNGROUP)
    set(DEFUMASK "007")
  else(RUNGROUP)
    set(DEFUMASK "077")
  endif(RUNGROUP)
endif(NOT DEFUMASK)

# Check for the existance of the following include files
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
check_include_file(execinfo.h HAVE_BACKTRACE)
check_include_file(strings.h HAVE_STRINGS_H)
check_include_file(sys/select.h HAVE_SYS_SELECT_H)

# Check for the existance of the following functions
check_function_exists(gethostbyname HAVE_GETHOSTBYNAME)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_function_exists(setgrent HAVE_SETGRENT)
check_function_exists(strcasecmp HAVE_STRCASECMP)
check_function_exists(stricmp HAVE_STRICMP)
check_function_exists(strlcat HAVE_STRLCAT)
check_function_exists(strlcpy HAVE_STRLCPY)
check_function_exists(umask HAVE_UMASK)

# Check for the existance of the following types
check_type_size(uint8_t UINT8_T)
check_type_size(u_int8_t U_INT8_T)
check_type_size(int16_t INT16_T)
check_type_size(uint16_t UINT16_T)
check_type_size(u_int16_t U_INT16_T)
check_type_size(int32_t INT32_T)
check_type_size(uint32_t UINT32_T)
check_type_size(u_int32_t U_INT32_T)

# Only CMake 2.6.x and later contain the STRIP sub-command for string()
if(CMAKE26_OR_BETTER)
  # Strip the leading and trailing spaces from the compile flags
  if(CXXFLAGS)
    string(STRIP ${CXXFLAGS} CXXFLAGS)
  endif(CXXFLAGS)
  # Strip the leading and trailing spaces from the linker flags
  if(LDFLAGS)
    string(STRIP ${LDFLAGS} LDFLAGS)
  endif(LDFLAGS)
endif(CMAKE26_OR_BETTER)

# A macro to handle reading specific lines from a file
macro(read_from_file FILE REGEX STRINGS)
  if(CMAKE26_OR_BETTER)
    # For CMake 2.6.x or better, we can just use this function to get the lines that match the given regular expression
    file(STRINGS ${FILE} RESULT REGEX ${REGEX})
  else(CMAKE26_OR_BETTER)
    # For CMake 2.4.x, we need to do this manually, firsly we read the file in
    file(READ ${FILE} ALL_STRINGS)
    # Next we replace all newlines with semicolons
    string(REGEX REPLACE "\n" ";" ALL_STRINGS ${ALL_STRINGS})
    # Clear the result list
    set(RESULT)
    # Iterate through all the lines of the file
    foreach(STRING ${ALL_STRINGS})
      # Check for a match against the given regular expression
      string(REGEX MATCH ${REGEX} STRING_MATCH ${STRING})
      # If we had a match, append the match to the list
      if(STRING_MATCH)
        list(APPEND RESULT ${STRING})
      endif(STRING_MATCH)
    endforeach(STRING)
  endif(CMAKE26_OR_BETTER)
  # Set the given STRINGS variable to the result
  set(${STRINGS} ${RESULT})
endmacro(read_from_file)

# A macro to handle searching within a list
macro(find_in_list LIST ITEM_TO_FIND FOUND)
  if(CMAKE26_OR_BETTER OR ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} GREATER 2.4.7)
    # For CMake 2.6.x or better (as well as CMake 2.4.8 or better), we can use the FIND sub-command of list()
    list(FIND ${LIST} ${ITEM_TO_FIND} ITEM_FOUND)
  else(CMAKE26_OR_BETTER OR ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} GREATER 2.4.7)
    # For CMake 2.4.x before 2.4.8, we have to do this ourselves (NOTE: This is very slow due to a lack of break() as well), firstly we set that we a temporary boolean
    set(ITEM_FOUND -1)
    set(POS 0)
    # Iterate through the list
    foreach(ITEM ${${LIST}})
      # If the item we are looking at is the item we are trying to find, set that we've found the item
      if(${ITEM} STREQUAL ${ITEM_TO_FIND})
        set(ITEM_FOUND ${POS})
      endif(${ITEM} STREQUAL ${ITEM_TO_FIND})
      math(EXPR POS "${POS} + 1")
    endforeach(ITEM)
  endif(CMAKE26_OR_BETTER OR ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} GREATER 2.4.7)
  # Set the given FOUND variable to the result
  set(${FOUND} ${ITEM_FOUND})
endmacro(find_in_list)

# A macro to handle removing duplicates from a list
macro(remove_list_duplicates LIST)
  if(CMAKE26_OR_BETTER)
    # For CMake 2.6.x or better, this can be done automatically
    list(REMOVE_DUPLICATES ${LIST})
  else(CMAKE26_OR_BETTER)
    # For CMake 2.4.x, we have to do this ourselves, firstly we'll clear a temporary list
    set(NEW_LIST)
    # Iterate through the old list
    foreach(ITEM ${${LIST}})
      # Check if the item is in the new list
      find_in_list(NEW_LIST ${ITEM} FOUND_ITEM)
      if(FOUND_ITEM EQUAL -1)
        # If the item was not found, append it to the list
        list(APPEND NEW_LIST ${ITEM})
      endif(FOUND_ITEM EQUAL -1)
    endforeach(ITEM)
    # replace the old list with the new list
    set(${LIST} ${NEW_LIST})
  endif(CMAKE26_OR_BETTER)
endmacro(remove_list_duplicates)

# Search for the following programs
find_program(GREP grep)
find_program(SH sh)
find_program(CHGRP chgrp)
find_program(CHMOD chmod)
find_program(PERL perl)

# If perl is included on the system and the user wants to use run-cc.pl, change the commands for compiling and linking
if(PERL AND USE_RUN_CC_PL)
  set(CMAKE_CXX_COMPILE_OBJECT "${PERL} ${Anope_SOURCE_DIR}/run-cc.pl ${CMAKE_CXX_COMPILE_OBJECT}")
  set(CMAKE_CXX_LINK_EXECUTABLE "${PERL} ${Anope_SOURCE_DIR}/run-cc.pl ${CMAKE_CXX_LINK_EXECUTABLE}")
  set(CMAKE_CXX_CREATE_SHARED_MODULE "${PERL} ${Anope_SOURCE_DIR}/run-cc.pl ${CMAKE_CXX_CREATE_SHARED_MODULE}")
endif(PERL AND USE_RUN_CC_PL)

# If a INSTDIR wasn't passed in to CMake, set the default to the services directory under the user's home directory
if(NOT INSTDIR)
  set(INSTDIR "$ENV{HOME}/services")
endif(NOT INSTDIR)

# Set Anope's data install location to be the data directory under the install directory
set(DATADIR "${INSTDIR}/data")

# Only process Anope's version on Windows, it's needed for win32.rc as well as version branding at link time
if(WIN32)
  # Find all lines in version.log that start with VERSION_
  read_from_file(${Anope_SOURCE_DIR}/version.log "^VERSION_" VERSIONS)
  # Iterate through the strings found
  foreach(VERSION_STR ${VERSIONS})
    # Get the length of the string
    string(LENGTH ${VERSION_STR} VERSION_LEN)
    # Subtract 16 from the string's length (8 for VERSION_, 5 more for the type, 2 for the space and leading quote, 1 for the trailing quote)
    math(EXPR VERSION_NUM_LEN "${VERSION_LEN} - 16")
    # Extract the type from the string
    string(SUBSTRING ${VERSION_STR} 8 5 VERSION_TYPE)
    # Extract the actual value from the string
    string(SUBSTRING ${VERSION_STR} 15 ${VERSION_NUM_LEN} VERSION)
    # Set the version type to the value extract from above
    set(VERSION_${VERSION_TYPE} ${VERSION})
  endforeach(VERSION_STR ${VERSIONS})

  # Set the version variables based on what was found above
  set(VERSION_COMMA "${VERSION_MAJOR},${VERSION_MINOR},${VERSION_PATCH},${VERSION_BUILD}")
  set(VERSION_DOTTED "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD}")
  set(VERSION_FULL "${VERSION_DOTTED}${VERSION_EXTRA}")

  # Generate the win32.rc file using the above variables
  configure_file(${Anope_SOURCE_DIR}/src/win32.rc.cmake ${Anope_BINARY_DIR}/src/win32.rc)
endif(WIN32)

# Calculate dependencies for each header
# I would've done this inside the CMakeLists.txt for the include directory, but since it's added AFTER everything else, it won't help...

# Firstly, find all the header files
file(GLOB_RECURSE ALL_HEADERS "*.h")
# Iterate through the headers
foreach(HEADER ${ALL_HEADERS})
  # Don't process the file if it's in an obsolete directory
  if(NOT HEADER MATCHES ".*obsolete.*")
    list(APPEND TMP_HEADERS ${HEADER})
    # In addition, also set up a variable to store the fullpath of the header, in a variable prefixed with just the header's filename for easy access later
    get_filename_component(HEADER_FILENAME ${HEADER} NAME)
    set(${HEADER_FILENAME}_FULLPATH ${HEADER})
  endif(NOT HEADER MATCHES ".*obsolete.*")
endforeach(HEADER)
# Set the list of headers to be all the non-obsolete ones, then sort the list
set(ALL_HEADERS ${TMP_HEADERS})
list(SORT ALL_HEADERS)

# This function will take a #include line and extract the filename minus the quotes
macro(extract_include_filename INCLUDE FILENAME)
  # Detect if there is any trailing whitespace (basically see if the last character is a space or a tab)
  string(LENGTH ${INCLUDE} INCLUDE_LEN)
  math(EXPR LAST_CHAR_POS "${INCLUDE_LEN} - 1")
  string(SUBSTRING ${INCLUDE} ${LAST_CHAR_POS} 1 LAST_CHAR)
  # Only strip if the last character was a space or a tab
  if(LAST_CHAR STREQUAL " " OR LAST_CHAR STREQUAL "\t")
    # Strip away trailing whitespace from the line
    string(REGEX REPLACE "[ \t]*$" "" INCLUDE_STRIPPED ${INCLUDE})
  else(LAST_CHAR STREQUAL " " OR LAST_CHAR STREQUAL "\t")
    # Just copy INCLUDE to INCLUDE_STRIPPED so the below code doesn't complain about a lack of INCLUDE_STRIPPED
    set(INCLUDE_STRIPPED ${INCLUDE})
  endif(LAST_CHAR STREQUAL " " OR LAST_CHAR STREQUAL "\t")
  # Find the filename including the quotes, it should be at the end of the line after whitespace was stripped
  string(REGEX MATCH "\".*\"$" FILE ${INCLUDE_STRIPPED})
  # Get the length of the filename with quotes
  string(LENGTH ${FILE} FILENAME_LEN)
  # Subtract 2 from this length, for the quotes
  math(EXPR FILENAME_LEN "${FILENAME_LEN} - 2")
  # Overwrite the filename with a version sans quotes
  string(SUBSTRING ${FILE} 1 ${FILENAME_LEN} FILE)
  # Set the filename to the the given variable
  set(${FILENAME} "${FILE}")
endmacro(extract_include_filename)

# Preparse step 1: get filenames sans paths
# Iterate through the headers
foreach(HEADER ${ALL_HEADERS})
  # Find all the lines in the current header that have any form of #include on them, regardless of whitespace
  read_from_file(${HEADER} "^[ \t]*#[ \t]*include[ \t]*\".*\"[ \t]*$" INCLUDES)
  # Get the filename only of the header we just checked
  get_filename_component(HEADER_FILENAME ${HEADER} NAME)
  # Iterate through the strings containing #include (if any)
  foreach(INCLUDE ${INCLUDES})
    # Extract the filename from the #include line
    extract_include_filename(${INCLUDE} FILENAME)
    # Append this filename to the list of headers for the header we are checking
    list(APPEND ${HEADER_FILENAME}_HEADERS ${FILENAME})
  endforeach(INCLUDE)
endforeach(HEADER)

# Preparse step 2: for every header from above that had includes, recursively find the headers each header relies on
# Iterate through the headers (again)
foreach(HEADER ${ALL_HEADERS})
  # Get the filename only of the current header
  get_filename_component(HEADER_FILENAME ${HEADER} NAME)
  # If there were any include, we'll be checking them
  if(${HEADER_FILENAME}_HEADERS)
    # Set the variables, old for all previously found headers, new for all newly found headers
    set(OLD_HEADERS)
    set(HEADERS ${${HEADER_FILENAME}_HEADERS})
    set(NEW_HEADERS)
    # Loop as long as there are still headers to be parsed
    while(HEADERS)
      # Iterate through the list of the current headers
      foreach(CURR_HEADER ${HEADERS})
        # If that header has headers it relies on, we'll add them to the list of new headers
        if(${CURR_HEADER}_HEADERS)
          foreach(CURR_HEADERS_HEADER ${${CURR_HEADER}_HEADERS})
            list(APPEND NEW_HEADERS ${CURR_HEADERS_HEADER})
          endforeach(CURR_HEADERS_HEADER)
        endif(${CURR_HEADER}_HEADERS)
      endforeach(CURR_HEADER)
      # Append the headers we checked to the old headers
      list(APPEND OLD_HEADERS ${HEADERS})
      # Set the headers to check to the new headers (it may be empty and that'll exit the loop)
      set(HEADERS ${NEW_HEADERS})
      # Erase the new headers
      set(NEW_HEADERS)
    endwhile(HEADERS)
    # OLD_HEADERS will now contain all headers that the current header relies on, remove duplicate headers from the list and sort the list
    remove_list_duplicates(OLD_HEADERS)
    list(SORT OLD_HEADERS)
    # Set the current header's list of headers to the cleaned up list from above
    set(${HEADER_FILENAME}_HEADERS ${OLD_HEADERS})
  endif(${HEADER_FILENAME}_HEADERS)
endforeach(HEADER)

# The following headers are generated from CMake rules and won't be found with the above
list(APPEND ALL_HEADERS ${Anope_BINARY_DIR}/lang/language.h ${Anope_BINARY_DIR}/include/sysconf.h ${Anope_BINARY_DIR}/include/version.h)
set(language.h_FULLPATH ${Anope_BINARY_DIR}/lang/language.h)
set(sysconf.h_FULLPATH ${Anope_BINARY_DIR}/include/sysconf.h)
set(version.h_FULLPATH ${Anope_BINARY_DIR}/include/version.h)

# This function is used in most of the src (sub)directories to calculate the header file dependencies for the given source file
macro(calculate_depends SRC)
  # Find all the lines in the given source file that have any form of #include on them, regardless of whitespace
  read_from_file(${SRC} "^[ \t]*#[ \t]*include[ \t]*\".*\"[ \t]*$" INCLUDES)
  # Reset the list of headers to empty
  set(HEADERS)
  # Iterate through the strings containing #include (if any)
  foreach(INCLUDE ${INCLUDES})
    # Extract the filename from the #include line
    extract_include_filename(${INCLUDE} FILENAME)
    # Append the filename to the list of headers
    list(APPEND HEADERS ${FILENAME})
  endforeach(INCLUDE)
  # Set the list of new headers to empty (this will store all the headers that the above list depends on)
  set(NEW_HEADERS)
  # Iterate through the list of headers
  foreach(HEADER ${HEADERS})
    # If the current header has it's own headers to depend on, append those to the list of new headers
    if(${HEADER}_HEADERS)
      list(APPEND NEW_HEADERS ${${HEADER}_HEADERS})
    endif(${HEADER}_HEADERS)
  endforeach(HEADER)
  # If there were new headers, append them to the list of headers
  if(NEW_HEADERS)
    list(APPEND HEADERS ${NEW_HEADERS})
  endif(NEW_HEADERS)
  # If after all the above there is a list of header, we'll process them, converting them to full paths
  if(HEADERS)
    # Remove duplicate headers from the list and sort the list
    remove_list_duplicates(HEADERS)
    list(SORT HEADERS)
    # Set the list of full path headers to empty
    set(HEADERS_FULL)
    # Iterate through the list of headers
    foreach(HEADER ${HEADERS})
      # Append the full path of the header to the full path headers list
      list(APPEND HEADERS_FULL ${${HEADER}_FULLPATH})
    endforeach(HEADER)
    # Set the given source file to depend on the headers given
    set_source_files_properties(${SRC} PROPERTIES OBJECT_DEPENDS "${HEADERS_FULL}")
  endif(HEADERS)
endmacro(calculate_depends)

# Go into the following directories and run their CMakeLists.txt as well
add_subdirectory(data)
add_subdirectory(lang)
add_subdirectory(src)
add_subdirectory(include)

# At install time, create the following additional directories
install(CODE "file(MAKE_DIRECTORY \"${DATADIR}/backups\")")
install(CODE "file(MAKE_DIRECTORY \"${DATADIR}/logs\")")
install(CODE "file(MAKE_DIRECTORY \"${DATADIR}/modules/runtime\")")
# On non-Windows platforms, if RUNGROUP is set, change the permissions of the below directories, as well as the group of the data directory
if(NOT WIN32 AND RUNGROUP)
  install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"${DATADIR}/backups\")")
  install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"${DATADIR}/logs\")")
  install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"${DATADIR}/modules/runtime\")")
  install(CODE "execute_process(COMMAND ${CHGRP} -R ${RUNGROUP} \"${DATADIR}\")")
endif(NOT WIN32 AND RUNGROUP)