1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""It is an AIDEGen sub task: generate the CLion project file. 18 19 Usage example: 20 json_path = common_util.get_blueprint_json_path( 21 constant.BLUEPRINT_CC_JSONFILE_NAME) 22 json_dict = common_util.get_soong_build_json_dict(json_path) 23 if 'modules' not in json_dict: 24 return 25 mod_info = json_dict['modules'].get('libui', {}) 26 if not mod_info: 27 return 28 CLionProjectFileGenerator(mod_info).generate_cmakelists_file() 29""" 30 31import logging 32import os 33 34from io import StringIO 35from io import TextIOWrapper 36 37from aidegen import constant 38from aidegen import templates 39from aidegen.lib import common_util 40from aidegen.lib import errors 41from aidegen.lib import native_module_info 42 43# Flags for writing to CMakeLists.txt section. 44_GLOBAL_COMMON_FLAGS = '\n# GLOBAL ALL FLAGS:\n' 45_LOCAL_COMMON_FLAGS = '\n# LOCAL ALL FLAGS:\n' 46_GLOBAL_CFLAGS = '\n# GLOBAL CFLAGS:\n' 47_LOCAL_CFLAGS = '\n# LOCAL CFLAGS:\n' 48_GLOBAL_C_ONLY_FLAGS = '\n# GLOBAL C ONLY FLAGS:\n' 49_LOCAL_C_ONLY_FLAGS = '\n# LOCAL C ONLY FLAGS:\n' 50_GLOBAL_CPP_FLAGS = '\n# GLOBAL CPP FLAGS:\n' 51_LOCAL_CPP_FLAGS = '\n# LOCAL CPP FLAGS:\n' 52_SYSTEM_INCLUDE_FLAGS = '\n# GLOBAL SYSTEM INCLUDE FLAGS:\n' 53 54# Keys for writing in module_bp_cc_deps.json 55_KEY_GLOBAL_COMMON_FLAGS = 'global_common_flags' 56_KEY_LOCAL_COMMON_FLAGS = 'local_common_flags' 57_KEY_GLOBAL_CFLAGS = 'global_c_flags' 58_KEY_LOCAL_CFLAGS = 'local_c_flags' 59_KEY_GLOBAL_C_ONLY_FLAGS = 'global_c_only_flags' 60_KEY_LOCAL_C_ONLY_FLAGS = 'local_c_only_flags' 61_KEY_GLOBAL_CPP_FLAGS = 'global_cpp_flags' 62_KEY_LOCAL_CPP_FLAGS = 'local_cpp_flags' 63_KEY_SYSTEM_INCLUDE_FLAGS = 'system_include_flags' 64 65# Dictionary maps keys to sections. 66_FLAGS_DICT = { 67 _KEY_GLOBAL_COMMON_FLAGS: _GLOBAL_COMMON_FLAGS, 68 _KEY_LOCAL_COMMON_FLAGS: _LOCAL_COMMON_FLAGS, 69 _KEY_GLOBAL_CFLAGS: _GLOBAL_CFLAGS, 70 _KEY_LOCAL_CFLAGS: _LOCAL_CFLAGS, 71 _KEY_GLOBAL_C_ONLY_FLAGS: _GLOBAL_C_ONLY_FLAGS, 72 _KEY_LOCAL_C_ONLY_FLAGS: _LOCAL_C_ONLY_FLAGS, 73 _KEY_GLOBAL_CPP_FLAGS: _GLOBAL_CPP_FLAGS, 74 _KEY_LOCAL_CPP_FLAGS: _LOCAL_CPP_FLAGS, 75 _KEY_SYSTEM_INCLUDE_FLAGS: _SYSTEM_INCLUDE_FLAGS 76} 77 78# Keys for parameter types. 79_KEY_FLAG = 'flag' 80_KEY_SYSTEM_ROOT = 'system_root' 81_KEY_RELATIVE = 'relative_file_path' 82 83# Constants for CMakeLists.txt. 84_MIN_VERSION_TOKEN = '@MINVERSION@' 85_PROJECT_NAME_TOKEN = '@PROJNAME@' 86_ANDOIR_ROOT_TOKEN = '@ANDROIDROOT@' 87_MINI_VERSION_SUPPORT = 'cmake_minimum_required(VERSION {})\n' 88_MINI_VERSION = '3.5' 89_KEY_CLANG = 'clang' 90_KEY_CPPLANG = 'clang++' 91_SET_C_COMPILER = 'set(CMAKE_C_COMPILER \"{}\")\n' 92_SET_CXX_COMPILER = 'set(CMAKE_CXX_COMPILER \"{}\")\n' 93_LIST_APPEND_HEADER = 'list(APPEND\n' 94_SOURCE_FILES_HEADER = 'SOURCE_FILES' 95_SOURCE_FILES_LINE = ' SOURCE_FILES\n' 96_END_WITH_ONE_BLANK_LINE = ')\n' 97_END_WITH_TWO_BLANK_LINES = ')\n\n' 98_SET_RELATIVE_PATH = 'set({} "{} {}={}")\n' 99_SET_ALL_FLAGS = 'set({} "{} {}")\n' 100_ANDROID_ROOT_SYMBOL = '${ANDROID_ROOT}' 101_SYSTEM = 'SYSTEM' 102_INCLUDE_DIR = 'include_directories({} \n' 103_SET_INCLUDE_FORMAT = ' "{}"\n' 104_CMAKE_C_FLAGS = 'CMAKE_C_FLAGS' 105_CMAKE_CXX_FLAGS = 'CMAKE_CXX_FLAGS' 106_USR = 'usr' 107_INCLUDE = 'include' 108_INCLUDE_SYSTEM = 'include_directories(SYSTEM "{}")\n' 109_GLOB_RECURSE_TMP_HEADERS = 'file (GLOB_RECURSE TMP_HEADERS\n' 110_ALL_HEADER_FILES = ' "{}/**/*.h"\n' 111_APPEND_SOURCE_FILES = "list (APPEND SOURCE_FILES ${TMP_HEADERS})\n\n" 112_ADD_EXECUTABLE_HEADER = '\nadd_executable({} {})' 113_PROJECT = 'project({})\n' 114_ADD_SUB = 'add_subdirectory({})\n' 115_DICT_EMPTY = 'mod_info is empty.' 116_DICT_NO_MOD_NAME_KEY = "mod_info does not contain 'module_name' key." 117_DICT_NO_PATH_KEY = "mod_info does not contain 'path' key." 118_MODULE_INFO_EMPTY = 'The module info dictionary is empty.' 119 120 121class CLionProjectFileGenerator: 122 """CLion project file generator. 123 124 Attributes: 125 mod_info: A dictionary of the target module's info. 126 mod_name: A string of module name. 127 mod_path: A string of module's path. 128 cc_dir: A string of generated CLion project file's directory. 129 cc_path: A string of generated CLion project file's path. 130 """ 131 132 def __init__(self, mod_info, parent_dir=None): 133 """ProjectFileGenerator initialize. 134 135 Args: 136 mod_info: A dictionary of native module's info. 137 parent_dir: The parent directory of this native module. The default 138 value is None. 139 """ 140 if not mod_info: 141 raise errors.ModuleInfoEmptyError(_MODULE_INFO_EMPTY) 142 self.mod_info = mod_info 143 self.mod_name = self._get_module_name() 144 self.mod_path = CLionProjectFileGenerator.get_module_path( 145 mod_info, parent_dir) 146 self.cc_dir = CLionProjectFileGenerator.get_cmakelists_file_dir( 147 os.path.join(self.mod_path, self.mod_name)) 148 if not os.path.exists(self.cc_dir): 149 os.makedirs(self.cc_dir) 150 self.cc_path = os.path.join(self.cc_dir, 151 constant.CLION_PROJECT_FILE_NAME) 152 153 def _get_module_name(self): 154 """Gets the value of the 'module_name' key if it exists. 155 156 Returns: 157 A string of the module's name. 158 159 Raises: 160 NoModuleNameDefinedInModuleInfoError if no 'module_name' key in 161 mod_info. 162 """ 163 mod_name = self.mod_info.get(constant.KEY_MODULE_NAME, '') 164 if not mod_name: 165 raise errors.NoModuleNameDefinedInModuleInfoError( 166 _DICT_NO_MOD_NAME_KEY) 167 return mod_name 168 169 @staticmethod 170 def get_module_path(mod_info, parent_dir=None): 171 """Gets the correct value of the 'path' key if it exists. 172 173 When a module with different paths, e.g., 174 'libqcomvoiceprocessingdescriptors': { 175 'path': [ 176 'device/google/bonito/voice_processing', 177 'device/google/coral/voice_processing', 178 'device/google/crosshatch/voice_processing', 179 'device/google/muskie/voice_processing', 180 'device/google/taimen/voice_processing' 181 ], 182 ... 183 } 184 it might be wrong if we always choose the first path. For example, in 185 this case if users command 'aidegen -i c device/google/coral' the 186 correct path they need should be the second one. 187 188 Args: 189 mod_info: A module's info dictionary. 190 parent_dir: The parent directory of this native module. The default 191 value is None. 192 193 Returns: 194 A string of the module's path. 195 196 Raises: 197 NoPathDefinedInModuleInfoError if no 'path' key in mod_info. 198 """ 199 mod_paths = mod_info.get(constant.KEY_PATH, []) 200 if not mod_paths: 201 raise errors.NoPathDefinedInModuleInfoError(_DICT_NO_PATH_KEY) 202 mod_path = mod_paths[0] 203 if parent_dir and len(mod_paths) > 1: 204 for path in mod_paths: 205 if common_util.is_source_under_relative_path(path, parent_dir): 206 mod_path = path 207 return mod_path 208 209 @staticmethod 210 @common_util.check_args(cc_path=str) 211 def get_cmakelists_file_dir(cc_path): 212 """Gets module's CMakeLists.txt file path to be created. 213 214 Return a string of $OUT/development/ide/clion/${cc_path}. 215 For example, if module name is 'libui'. The return path string would be: 216 out/development/ide/clion/frameworks/native/libs/ui/libui 217 218 Args: 219 cc_path: A string of absolute path of module's Android.bp file. 220 221 Returns: 222 A string of absolute path of module's CMakeLists.txt file to be 223 created. 224 """ 225 return os.path.join(common_util.get_android_root_dir(), 226 common_util.get_android_out_dir(), 227 constant.RELATIVE_NATIVE_PATH, cc_path) 228 229 def generate_cmakelists_file(self): 230 """Generates CLion project file from the target module's info.""" 231 with open(self.cc_path, 'w') as hfile: 232 self._write_cmakelists_file(hfile) 233 234 @common_util.check_args(hfile=(TextIOWrapper, StringIO)) 235 @common_util.io_error_handle 236 def _write_cmakelists_file(self, hfile): 237 """Writes CLion project file content with neccessary info. 238 239 Args: 240 hfile: A file handler instance. 241 """ 242 self._write_header(hfile) 243 self._write_c_compiler_paths(hfile) 244 self._write_source_files(hfile) 245 self._write_cmakelists_flags(hfile) 246 self._write_tail(hfile) 247 248 @common_util.check_args(hfile=(TextIOWrapper, StringIO)) 249 @common_util.io_error_handle 250 def _write_header(self, hfile): 251 """Writes CLion project file's header messages. 252 253 Args: 254 hfile: A file handler instance. 255 """ 256 content = templates.CMAKELISTS_HEADER.replace( 257 _MIN_VERSION_TOKEN, _MINI_VERSION) 258 content = content.replace(_PROJECT_NAME_TOKEN, self.mod_name) 259 content = content.replace( 260 _ANDOIR_ROOT_TOKEN, common_util.get_android_root_dir()) 261 hfile.write(content) 262 263 @common_util.check_args(hfile=(TextIOWrapper, StringIO)) 264 @common_util.io_error_handle 265 def _write_c_compiler_paths(self, hfile): 266 """Writes CMake compiler paths for C and Cpp to CLion project file. 267 268 Args: 269 hfile: A file handler instance. 270 """ 271 hfile.write(_SET_C_COMPILER.format( 272 native_module_info.NativeModuleInfo.c_lang_path)) 273 hfile.write(_SET_CXX_COMPILER.format( 274 native_module_info.NativeModuleInfo.cpp_lang_path)) 275 276 @common_util.check_args(hfile=(TextIOWrapper, StringIO)) 277 @common_util.io_error_handle 278 def _write_source_files(self, hfile): 279 """Writes source files' paths to CLion project file. 280 281 Args: 282 hfile: A file handler instance. 283 """ 284 if constant.KEY_SRCS not in self.mod_info: 285 logging.warning("No source files in %s's module info.", 286 self.mod_name) 287 return 288 root = common_util.get_android_root_dir() 289 source_files = self.mod_info[constant.KEY_SRCS] 290 hfile.write(_LIST_APPEND_HEADER) 291 hfile.write(_SOURCE_FILES_LINE) 292 for src in source_files: 293 if not os.path.exists(os.path.join(root, src)): 294 continue 295 hfile.write(''.join([_build_cmake_path(src, ' '), '\n'])) 296 hfile.write(_END_WITH_ONE_BLANK_LINE) 297 298 @common_util.check_args(hfile=(TextIOWrapper, StringIO)) 299 @common_util.io_error_handle 300 def _write_cmakelists_flags(self, hfile): 301 """Writes all kinds of flags in CLion project file. 302 303 Args: 304 hfile: A file handler instance. 305 """ 306 self._write_flags(hfile, _KEY_GLOBAL_COMMON_FLAGS, True, True) 307 self._write_flags(hfile, _KEY_LOCAL_COMMON_FLAGS, True, True) 308 self._write_flags(hfile, _KEY_GLOBAL_CFLAGS, True, True) 309 self._write_flags(hfile, _KEY_LOCAL_CFLAGS, True, True) 310 self._write_flags(hfile, _KEY_GLOBAL_C_ONLY_FLAGS, True, False) 311 self._write_flags(hfile, _KEY_LOCAL_C_ONLY_FLAGS, True, False) 312 self._write_flags(hfile, _KEY_GLOBAL_CPP_FLAGS, False, True) 313 self._write_flags(hfile, _KEY_LOCAL_CPP_FLAGS, False, True) 314 self._write_flags(hfile, _KEY_SYSTEM_INCLUDE_FLAGS, True, True) 315 316 @common_util.check_args(hfile=(TextIOWrapper, StringIO)) 317 @common_util.io_error_handle 318 def _write_tail(self, hfile): 319 """Writes CLion project file content with necessary info. 320 321 Args: 322 hfile: A file handler instance. 323 """ 324 hfile.write( 325 _ADD_EXECUTABLE_HEADER.format( 326 _cleanup_executable_name(self.mod_name), 327 _add_dollar_sign(_SOURCE_FILES_HEADER))) 328 329 @common_util.check_args( 330 hfile=(TextIOWrapper, StringIO), key=str, cflags=bool, cppflags=bool) 331 @common_util.io_error_handle 332 def _write_flags(self, hfile, key, cflags, cppflags): 333 """Writes CMake compiler paths of C, Cpp for different kinds of flags. 334 335 Args: 336 hfile: A file handler instance. 337 key: A string of flag type, e.g., 'global_common_flags' flag. 338 cflags: A boolean for setting 'CMAKE_C_FLAGS' flag. 339 cppflags: A boolean for setting 'CMAKE_CXX_FLAGS' flag. 340 """ 341 if key not in _FLAGS_DICT: 342 return 343 hfile.write(_FLAGS_DICT[key]) 344 params_dict = self._parse_compiler_parameters(key) 345 if params_dict: 346 _translate_to_cmake(hfile, params_dict, cflags, cppflags) 347 348 @common_util.check_args(flag=str) 349 def _parse_compiler_parameters(self, flag): 350 """Parses the specific flag data from a module_info dictionary. 351 352 Args: 353 flag: The string of key flag, e.g.: _KEY_GLOBAL_COMMON_FLAGS. 354 355 Returns: 356 A dictionary with compiled parameters. 357 """ 358 params = self.mod_info.get(flag, {}) 359 if not params: 360 return None 361 params_dict = { 362 constant.KEY_HEADER: [], 363 constant.KEY_SYSTEM: [], 364 _KEY_FLAG: [], 365 _KEY_SYSTEM_ROOT: '', 366 _KEY_RELATIVE: {} 367 } 368 for key, value in params.items(): 369 params_dict[key] = value 370 return params_dict 371 372 373@common_util.check_args(rel_project_path=str, mod_names=list) 374@common_util.io_error_handle 375def generate_base_cmakelists_file(cc_module_info, rel_project_path, mod_names): 376 """Generates base CLion project file for multiple CLion projects. 377 378 We create a multiple native project file: 379 {android_root}/development/ide/clion/{rel_project_path}/CMakeLists.txt 380 and use this project file to generate a link: 381 {android_root}/out/development/ide/clion/{rel_project_path}/CMakeLists.txt 382 383 Args: 384 cc_module_info: An instance of native_module_info.NativeModuleInfo. 385 rel_project_path: A string of the base project relative path. For 386 example: frameworks/native/libs/ui. 387 mod_names: A list of module names whose project were created under 388 rel_project_path. 389 390 Returns: 391 A symbolic link CLion project file path. 392 """ 393 root_dir = common_util.get_android_root_dir() 394 cc_dir = os.path.join(root_dir, constant.RELATIVE_NATIVE_PATH, 395 rel_project_path) 396 cc_out_dir = os.path.join(root_dir, common_util.get_android_out_dir(), 397 constant.RELATIVE_NATIVE_PATH, rel_project_path) 398 if not os.path.exists(cc_dir): 399 os.makedirs(cc_dir) 400 dst_path = os.path.join(cc_out_dir, constant.CLION_PROJECT_FILE_NAME) 401 if os.path.islink(dst_path): 402 os.unlink(dst_path) 403 src_path = os.path.join(cc_dir, constant.CLION_PROJECT_FILE_NAME) 404 if os.path.isfile(src_path): 405 os.remove(src_path) 406 with open(src_path, 'w') as hfile: 407 _write_base_cmakelists_file(hfile, cc_module_info, src_path, mod_names) 408 os.symlink(src_path, dst_path) 409 return dst_path 410 411 412@common_util.check_args( 413 hfile=(TextIOWrapper, StringIO), abs_project_path=str, mod_names=list) 414@common_util.io_error_handle 415def _write_base_cmakelists_file(hfile, cc_module_info, abs_project_path, 416 mod_names): 417 """Writes base CLion project file content. 418 419 When we write module info into base CLion project file, first check if the 420 module's CMakeLists.txt exists. If file exists, write content, 421 add_subdirectory({'relative_module_path'}) 422 423 Args: 424 hfile: A file handler instance. 425 cc_module_info: An instance of native_module_info.NativeModuleInfo. 426 abs_project_path: A string of the base project absolute path. 427 For example, 428 ${ANDROID_BUILD_TOP}/frameworks/native/libs/ui. 429 mod_names: A list of module names whose project were created under 430 abs_project_path. 431 """ 432 hfile.write(_MINI_VERSION_SUPPORT.format(_MINI_VERSION)) 433 project_dir = os.path.dirname(abs_project_path) 434 hfile.write(_PROJECT.format(os.path.basename(project_dir))) 435 root_dir = common_util.get_android_root_dir() 436 parent_dir = os.path.relpath(abs_project_path, root_dir) 437 for mod_name in mod_names: 438 mod_info = cc_module_info.get_module_info(mod_name) 439 mod_path = CLionProjectFileGenerator.get_module_path( 440 mod_info, parent_dir) 441 file_dir = CLionProjectFileGenerator.get_cmakelists_file_dir( 442 os.path.join(mod_path, mod_name)) 443 file_path = os.path.join(file_dir, constant.CLION_PROJECT_FILE_NAME) 444 if not os.path.isfile(file_path): 445 logging.warning("%s the project file %s doesn't exist.", 446 common_util.COLORED_INFO('Warning:'), file_path) 447 continue 448 link_project_dir = os.path.join(root_dir, 449 common_util.get_android_out_dir(), 450 os.path.relpath(project_dir, root_dir)) 451 rel_mod_path = os.path.relpath(file_dir, link_project_dir) 452 hfile.write(_ADD_SUB.format(rel_mod_path)) 453 454 455@common_util.check_args( 456 hfile=(TextIOWrapper, StringIO), params_dict=dict, cflags=bool, 457 cppflags=bool) 458def _translate_to_cmake(hfile, params_dict, cflags, cppflags): 459 """Translates parameter dict's contents into CLion project file format. 460 461 Args: 462 hfile: A file handler instance. 463 params_dict: A dict contains data to be translated into CLion 464 project file format. 465 cflags: A boolean is to set 'CMAKE_C_FLAGS' flag. 466 cppflags: A boolean is to set 'CMAKE_CXX_FLAGS' flag. 467 """ 468 _write_all_include_directories( 469 hfile, params_dict[constant.KEY_SYSTEM], True) 470 _write_all_include_directories( 471 hfile, params_dict[constant.KEY_HEADER], False) 472 473 if cflags: 474 _write_all_relative_file_path_flags(hfile, params_dict[_KEY_RELATIVE], 475 _CMAKE_C_FLAGS) 476 _write_all_flags(hfile, params_dict[_KEY_FLAG], _CMAKE_C_FLAGS) 477 478 if cppflags: 479 _write_all_relative_file_path_flags(hfile, params_dict[_KEY_RELATIVE], 480 _CMAKE_CXX_FLAGS) 481 _write_all_flags(hfile, params_dict[_KEY_FLAG], _CMAKE_CXX_FLAGS) 482 483 if params_dict[_KEY_SYSTEM_ROOT]: 484 path = os.path.join(params_dict[_KEY_SYSTEM_ROOT], _USR, _INCLUDE) 485 hfile.write(_INCLUDE_SYSTEM.format(_build_cmake_path(path))) 486 487 488@common_util.check_args(hfile=(TextIOWrapper, StringIO), is_system=bool) 489@common_util.io_error_handle 490def _write_all_include_directories(hfile, includes, is_system): 491 """Writes all included directories' paths to the CLion project file. 492 493 Args: 494 hfile: A file handler instance. 495 includes: A list of included file paths. 496 is_system: A boolean of whether it's a system flag. 497 """ 498 if not includes: 499 return 500 _write_all_includes(hfile, includes, is_system) 501 _write_all_headers(hfile, includes) 502 503 504@common_util.check_args( 505 hfile=(TextIOWrapper, StringIO), rel_paths_dict=dict, tag=str) 506@common_util.io_error_handle 507def _write_all_relative_file_path_flags(hfile, rel_paths_dict, tag): 508 """Writes all relative file path flags' parameters. 509 510 Args: 511 hfile: A file handler instance. 512 rel_paths_dict: A dict contains data of flag as a key and the relative 513 path string as its value. 514 tag: A string of tag, such as 'CMAKE_C_FLAGS'. 515 """ 516 for flag, path in rel_paths_dict.items(): 517 hfile.write( 518 _SET_RELATIVE_PATH.format(tag, _add_dollar_sign(tag), flag, 519 _build_cmake_path(path))) 520 521 522@common_util.check_args(hfile=(TextIOWrapper, StringIO), flags=list, tag=str) 523@common_util.io_error_handle 524def _write_all_flags(hfile, flags, tag): 525 """Wrties all flags to the project file. 526 527 Args: 528 hfile: A file handler instance. 529 flags: A list of flag strings to be added. 530 tag: A string to be added a dollar sign. 531 """ 532 for flag in flags: 533 hfile.write(_SET_ALL_FLAGS.format(tag, _add_dollar_sign(tag), flag)) 534 535 536def _add_dollar_sign(tag): 537 """Adds dollar sign to a string, e.g.: 'ANDROID_ROOT' -> '${ANDROID_ROOT}'. 538 539 Args: 540 tag: A string to be added a dollar sign. 541 542 Returns: 543 A dollar sign added string. 544 """ 545 return ''.join(['${', tag, '}']) 546 547 548def _build_cmake_path(path, tag=''): 549 """Joins tag, '${ANDROID_ROOT}' and path into a new string. 550 551 Args: 552 path: A string of a path. 553 tag: A string to be added in front of a dollar sign 554 555 Returns: 556 The composed string. 557 """ 558 return ''.join([tag, _ANDROID_ROOT_SYMBOL, os.path.sep, path]) 559 560 561@common_util.check_args(hfile=(TextIOWrapper, StringIO), is_system=bool) 562@common_util.io_error_handle 563def _write_all_includes(hfile, includes, is_system): 564 """Writes all included directories' paths to the CLion project file. 565 566 Args: 567 hfile: A file handler instance. 568 includes: A list of included file paths. 569 is_system: A boolean of whether it's a system flag. 570 """ 571 if not includes: 572 return 573 system = '' 574 if is_system: 575 system = _SYSTEM 576 hfile.write(_INCLUDE_DIR.format(system)) 577 for include in includes: 578 hfile.write(_SET_INCLUDE_FORMAT.format(_build_cmake_path(include))) 579 hfile.write(_END_WITH_TWO_BLANK_LINES) 580 581 582@common_util.check_args(hfile=(TextIOWrapper, StringIO)) 583@common_util.io_error_handle 584def _write_all_headers(hfile, includes): 585 """Writes all header directories' paths to the CLion project file. 586 587 Args: 588 hfile: A file handler instance. 589 includes: A list of included file paths. 590 """ 591 if not includes: 592 return 593 hfile.write(_GLOB_RECURSE_TMP_HEADERS) 594 for include in includes: 595 hfile.write(_ALL_HEADER_FILES.format(_build_cmake_path(include))) 596 hfile.write(_END_WITH_ONE_BLANK_LINE) 597 hfile.write(_APPEND_SOURCE_FILES) 598 599 600def _cleanup_executable_name(mod_name): 601 """Clean up an executable name to be suitable for CMake. 602 603 Replace the last '@' of a module name with '-' and make it become a suitable 604 executable name for CMake. 605 606 Args: 607 mod_name: A string of module name to be cleaned up. 608 609 Returns: 610 A string of the executable name. 611 """ 612 return mod_name[::-1].replace('@', '-', 1)[::-1] 613