-
Notifications
You must be signed in to change notification settings - Fork 1
/
generate_cmakelists.py
384 lines (318 loc) · 14.3 KB
/
generate_cmakelists.py
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
#!/usr/bin/env python
import os
import platform
import sys
cmakelists_str = \
"""# This file is automatically generated by running
#
# generate-cmakelists
#
# Require CMake 2.8
cmake_minimum_required(VERSION 2.8)
set(PROJECT_NAME %(project_name)s)
project(${PROJECT_NAME})
# Set verbose output while testing CMake
#set(CMAKE_VERBOSE_MAKEFILE 1)
#
# Get 3rdparty configuration data (<3rdParty>Config.cmake must be in PROJECT_CMAKE_CONFIG_PATH)
# edit line below to give cmake a hint where to find a <3rdParty>Config.cmake in case of manually built
# and not properly installed 3rdParty Package
#set(<3rdParty>_DIR "/path/to/<3rdParty>Config.cmake")
#example:
#find_package(PCL 1.3 REQUIRED COMPONENTS <component_1> <component_2>)
#include_directories(${<3rdParty>_INCLUDE_DIRS})
#
# Default build type (can be overridden by user)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: Debug MinSizeRel Release RelWithDebInfo." FORCE)
endif()
#
# Compiler definitions from main project
#
add_definitions(${PROJECT_CXX_DEFINITIONS})
#
# Add special compiler flags
#
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PROJECT_CXX_FLAGS}")
#
#list all dependent files
#
#set(SRCS src_files)
%(source_files)s
#
#
# uncomment strings bellow if you want to generate subgroups\projects
#SOURCE_GROUP(${PROJECT_NAME} ${SRCS})
#
# you may need to build plain c files with g++, example - xerces
# if it is not the common case - comment this string
#set_source_files_properties(${SRCS} PROPERTIES LANGUAGE CXX )
#
# Include directories
#
include_directories(${PROJECT_INCLUDE_DIRS})
%(includes)s
#
# Executable
#
%(executables)s
#
# shared lib
#
%(library)s
#
# static libs
#
%(library_static)s
#
# Target libraries
#
%(target_libraries)s
set(TARGET_LOCATION ${EXECUTABLE_OUTPUT_PATH})
%(precompiled_headers)s
"""
#
# Some global defs, yeah, yeah, I also not a big fan of it
#
start_label = 'module_list_start'
end_label = 'module_list_end'
# add more suffixes if necessary
src_suffix = ('.c', '.cpp', '.cxx')
inc_suffix = ('.h', '.hpp')
# if we find main.* file treat this subproject's target as executable
# otherwise - create static\shared libraries
main_file_names = ('main.')
exclude_dirs = ['build', 'CMakeFiles']
add_exec = "add_executable(%s %s)"
add_lib = "add_library(%s SHARED %s)"
add_lib_static = "add_library(%s STATIC %s)"
add_includes = "include_directories(SYSTEM %s)"
target_link_libraries_str = "target_link_libraries(%s ${PROJECT_LIBRARIES} ${PROJECT_3RD_PARTY_LIBRARIES})"
if platform.system() == "Windows": # FIXME - re-factor it!!!
add_precompiled_header_support = "SET_SOURCE_FILES_PROPERTIES(${PCH_SRC_CPP} PROPERTIES COMPILE_FLAGS \"${PCH_SRC_CMPL_FLAG} /Yc\\\"${PCH_SRC_H}\\\"\")"
else:
add_precompiled_header_support = " ADD_PRECOMPILED_HEADER ( ${PROJECT_NAME} ${PCH_SRC} )"
should_generate_precompiled_headers = True
# TODO: describe in\out params
def get_all_subfolders_by_suffix(some_suffix, exclude_dirs):
cur_dir = os.getcwd()
# out_subprj_lst = set ( os.path.relpath(folder) for folder, subfolders, files in os.walk(cur_dir,topdown = True ) for file_ in files if os.path.splitext(file_)[1].endswith ( some_suffix ) )
# return out_subprj_lst
res_folders = []
for root, dirs, files in os.walk(cur_dir, topdown=True):
dirs[:] = [d for d in dirs if d not in exclude_dirs and not d.startswith('.')]
for d in dirs:
for name in os.listdir(os.path.join(root, d)):
if name.endswith(some_suffix):
new_entry = os.path.normpath(os.path.join(root, d))
if platform.system() == "Windows": # FIXME - re-factor it!!!
new_entry = new_entry.replace("\\", "/")
res_folders.append(new_entry)
break # proceed to next subfolder
return res_folders
def get_all_files_by_suffix(current_target_dir, exclude_dirs, src_suffix):
src_group = dict()
dir_list = set(os.path.normpath(folder) for folder, subfolders, files in os.walk(current_target_dir, topdown=True) for file_ in files if os.path.splitext(file_)[1].endswith(src_suffix))
for dir in dir_list:
should_add_group = False
current_target_src = []
for name in os.listdir(dir):
if name.endswith(src_suffix):
new_entry_folder = os.path.relpath(dir, current_target_dir) # NOTE - order is important
new_entry_file = os.path.normpath(os.path.join(new_entry_folder, name))
if platform.system() == "Windows": # FIXME - re-factor it!!!
new_entry_file = new_entry_file.replace("\\", "/")
current_target_src.append(new_entry_file)
should_add_group = True
if should_add_group:
src_group[os.path.relpath(dir, current_target_dir)] = current_target_src
return src_group
def get_index_of_substr(list_for_search, substr):
for i, s in enumerate(list_for_search):
if substr in s:
return i
return -1
def clean_previous_subdir_entry():
# erase all previous entries for add_subdirectory in order to generate them explicitly from the scratch
writing = True
with open('CMakeLists.txt') as f:
with open('CMakeLists_new.txt', 'a+') as out:
for line in f:
if writing:
if start_label in line:
writing = False
out.write(line)
buffer = [line]
else:
out.write(line)
elif end_label in line:
out.write(line)
writing = True
else:
buffer.append(line)
else:
if not writing:
# There wasn't a closing "I'm in hell", so write buffer contents
out.writelines(buffer)
os.remove('CMakeLists.txt')
os.rename('CMakeLists_new.txt', 'CMakeLists.txt')
# idea from:
# https://github.com/alogg/dolfin/blob/master/cmake/scripts/generate-cmakefiles
# TODO: describe in\out params
def generate_cmake_files(in_sub_dir_list, in_main_file_names, in_src_suffix, in_inc_dir):
clean_previous_subdir_entry()
sub_projects = []
root = os.getcwd()
for cur_sub_dir in in_sub_dir_list:
src_files = []
full_path = cur_sub_dir
root_name = os.path.split(root)[-1]
start_pos = cur_sub_dir.find(root_name)
subproject_name = cur_sub_dir[start_pos:]
project_name = subproject_name.replace('/', '-')
name_forms = dict(
project_name=project_name,
source_files="#SRCS - NOT SET",
executables="#executables - NOT_SET",
target_libraries="#target_libraries - NOT_SET",
library="#library - NOT_SET",
library_static="#library_static - NOT_SET",
includes="#include - NOT_SET",
precompiled_headers="#NOT_SET")
# FIXME - code duplication >_M
for f in os.listdir(full_path):
filename, extension = os.path.splitext(f)
if extension in in_src_suffix:
full_file_name = os.path.join(full_path, f)
if platform.system() == "Windows":
full_file_name = full_file_name.replace("\\", "/")
src_files.append(full_file_name)
# If no src files - continue
if not src_files:
continue
# set(SRCS %(src_files)s)
if should_generate_precompiled_headers:
name_forms["source_files"] = "set(SRCS ${PCH_SRC} " + "\n".join(src_files)+" )"
else:
name_forms["source_files"] = "set(SRCS " + "\n".join(src_files)+" )"
if in_main_file_names in src_files:
# If directory contains a main file we assume that only one
# executable should be generated for this directory and all other
# src files should be linked to this
name_forms["executables"] = add_exec % ("${PROJECT_NAME}", "${SRCS}")
name_forms["target_libraries"] = target_link_libraries_str % "${PROJECT_NAME}"
if should_generate_precompiled_headers:
name_forms["executables"] += os.linesep+"ADD_NATIVE_PRECOMPILED_HEADER(${PROJECT_NAME} ${PCH_SRC_H})"
else:
# If no main file in source files, we assume that we build library
# be compiled as an executable
name_forms["library"] = add_lib % ("${PROJECT_NAME}", "${SRCS}")
name_forms["library_static"] = add_lib_static % ("${PROJECT_NAME}-static", "${SRCS}")
if should_generate_precompiled_headers:
name_forms["library"] += os.linesep+"ADD_NATIVE_PRECOMPILED_HEADER(${PROJECT_NAME} ${PCH_SRC_H})"
name_forms["library_static"] += os.linesep+"ADD_NATIVE_PRECOMPILED_HEADER(${PROJECT_NAME}-static ${PCH_SRC_H})"
name_forms["target_libraries"] = target_link_libraries_str % "${PROJECT_NAME}"
name_forms["includes"] = add_includes % (' '.join(in_inc_dir))
if should_generate_precompiled_headers:
name_forms["precompiled_headers"] = add_precompiled_header_support
with open(os.path.join(cur_sub_dir, "CMakeLists.txt"), 'w') as f:
f.write(cmakelists_str % name_forms)
sub_projects.append(subproject_name)
# explicitly add aditional sub-directory to main CMakeLists.txt file
f = open("CMakeLists.txt", "r")
lines = f.readlines()
i = get_index_of_substr(lines, start_label)
if i > -1:
for item in sub_projects:
if platform.system() == "Windows":
start_pos = item.find(root)+len(root)-1
item = item[start_pos:]
item = item.replace("\\", "/")
else:
start_pos = item.find("/")+1
item = item[start_pos:]
new_entry = "add_subdirectory("+item+")"+os.linesep
lines.insert(i+1, new_entry)
f.close()
out = open('CMakeLists_new.txt', 'a+')
out.writelines(lines)
out.close()
os.remove('CMakeLists.txt')
os.rename('CMakeLists_new.txt', 'CMakeLists.txt')
def generate_cmake_files_deep_nested(target_list, main_file_names, src_suffix, includes):
cur_dir = os.getcwd()
for dir in target_list:
current_target_dir = os.path.normpath(os.path.join(cur_dir, dir))
current_target = os.path.basename(os.path.normpath(dir))
current_target_src = []
group_src = get_all_files_by_suffix(current_target_dir, exclude_dirs, src_suffix)
name_forms = dict(
project_name=current_target,
source_files="#SRCS - NOT SET",
executables="#executables - NOT_SET",
target_libraries="#target_libraries - NOT_SET",
library="#library - NOT_SET",
library_static="#library_static - NOT_SET",
includes="#include - NOT_SET",
precompiled_headers="#NOT_SET"
)
# generate groups for every nested sub-directory
# set(SRCS %(src_files)s)
# SOURCE_GROUP(${PROJECT_NAME} ${SRCS})
final_src_substituion = ""
for group in group_src:
if platform.system() == "Windows":
cur_group = os.path.normpath(group).replace('\\', '_')
else:
cur_group = os.path.normpath(group).replace('/', '_')
cur_group_src_files = group_src[group]
final_src_substituion += "set(" + cur_group + "_SRC " + "\n".join(cur_group_src_files) + " )" + os.linesep
final_src_substituion += "SOURCE_GROUP(" + cur_group + " FILES ${" + cur_group + "_SRC} )" + os.linesep
current_target_src = current_target_src + cur_group_src_files
if should_generate_precompiled_headers:
final_src_substituion += "set(SRCS ${PCH_SRC} " + "\n".join(current_target_src)+" )" + os.linesep
else:
final_src_substituion += "set(SRCS " + "\n".join(current_target_src) + " )" + os.linesep
name_forms["source_files"] = final_src_substituion
if main_file_names in current_target_src:
# If directory contains a main file we assume that only one
# executable should be generated for this directory and all other
# src files should be linked to this
name_forms["executables"] = add_exec % ("${PROJECT_NAME}", "${SRCS}")
if should_generate_precompiled_headers:
name_forms["executables"] += os.linesep+"ADD_NATIVE_PRECOMPILED_HEADER(${PROJECT_NAME} ${PCH_SRC_H})"
name_forms["target_libraries"] = target_link_libraries_str % "${PROJECT_NAME}"
else:
# If no main file in source files, we assume that we build library
# be compiled as an executable
name_forms["library"] = add_lib % ("${PROJECT_NAME}", "${SRCS}")
name_forms["library_static"] = add_lib_static % ("${PROJECT_NAME}-static", "${SRCS}")
if should_generate_precompiled_headers:
name_forms["library"] += os.linesep+"ADD_NATIVE_PRECOMPILED_HEADER(${PROJECT_NAME} ${PCH_SRC_H})"
name_forms["library_static"] += os.linesep+"ADD_NATIVE_PRECOMPILED_HEADER(${PROJECT_NAME}-static ${PCH_SRC_H})"
name_forms["target_libraries"] = target_link_libraries_str % "${PROJECT_NAME}"
name_forms["includes"] = add_includes % (' '.join(includes))
if should_generate_precompiled_headers:
name_forms["precompiled_headers"] = add_precompiled_header_support
with open(os.path.join(current_target_dir, "CMakeLists.txt"), 'w') as f:
f.write(cmakelists_str % name_forms)
#
# MAIN
#
if os.path.isfile("generated.flag"):
quit()
should_use_second_algo = False
target_list = sys.argv[1:]
cur_dir = os.getcwd()
if platform.system() == "Windows":
cur_dir = cur_dir.replace("\\", "/")
includes = get_all_subfolders_by_suffix(inc_suffix, exclude_dirs)
includes.append(cur_dir)
if len(target_list) > 0:
exclude_dirs += [os.path.basename(os.path.normpath(x)) for x in target_list]
should_use_second_algo = True
res_folder = get_all_subfolders_by_suffix(src_suffix, exclude_dirs)
generate_cmake_files(res_folder, main_file_names, src_suffix, includes)
if should_use_second_algo:
generate_cmake_files_deep_nested(target_list, main_file_names, src_suffix, includes)