def __set_interprocedural_optimizations(context, flag_name, flag_value): """ Set InterproceduralOptimizations """ del flag_name, flag_value target_type = context.settings[context.current_setting]['target_type'] fat_lto_option = '' if target_type == 'StaticLibrary': fat_lto_option = ';-ffat-lto-objects' message( context, 'For unix added option -ffat-lto-objects to fix linking with -ipo.', '') flag_values = { 'ipoMultiFile': { ifort_cl_win: '-Qipo', ifort_cl_unix: '-ipo{}'.format(fat_lto_option) }, 'ipoSingleFile': { ifort_cl_win: '-Qip', ifort_cl_unix: '-ip' }, default_value: {} } return flag_values
def get_cmake_lists(context, cmake_path=None, open_type='w'): """ Create CMakeLists.txt file in wanted "cmake_path" :param context: the context of converter :type context: Context :param cmake_path: path where CMakeLists.txt should be open :type cmake_path: str :param open_type: type that CMakeLists.txt should be opened :type open_type: str :return: cmake file wrapper opened :rtype: _io.TextIOWrapper """ if not cmake_path: if open_type == 'w': message(context, 'CMakeLists will be written at current directory.', 'warn') cmake = os.path.join(os.getcwd(), 'CMakeLists.txt') else: if open_type == 'w': message(context, 'CMakeLists.txt will be written at : ' + str(cmake_path), '') if cmake_path[-1:] == '/' or cmake_path[-1:] == '\\': cmake = str(cmake_path) + 'CMakeLists.txt' else: cmake = str(cmake_path) + '/CMakeLists.txt' if not os.path.exists(cmake) and open_type == 'r': return None return open(cmake, open_type, newline='\n', encoding='utf-8')
def write_source_groups(self, context, cmake_file): """ Writes source groups of project files into CMakwLists.txt """ write_comment(cmake_file, 'Source groups') source_group_list = sorted(context.source_groups) for source_group in source_group_list: source_group_var = self.get_source_group_var(context, source_group) cmake_file.write('set({}\n'.format(source_group_var)) for src_file in context.source_groups[source_group]: fmt = ' "{}"\n' if context.file_contexts[src_file].excluded_from_build: fmt = '#' + fmt message( context, "file {} is excluded from build. Written but commented. " "No support in CMake yet.".format(src_file), 'warn4') cmake_file.write(fmt.format(src_file)) cmake_file.write(')\n') cmake_file.write('source_group("{}" FILES ${{{}}})\n\n'.format( source_group, source_group_var)) cmake_file.write('set(ALL_FILES\n') for source_group in source_group_list: cmake_file.write(' ${{{}}}\n'.format( context.files.get_source_group_var(context, source_group))) cmake_file.write(')\n\n')
def set_path_and_name_from_node(context, node_name, value, path_property, name_property): """ Common routine for evaluating path and name from node text """ file_path_value = value.strip() if not file_path_value: return file_path_value = replace_vs_vars_with_cmake_vars( context, file_path_value) file_path_value = set_native_slash(file_path_value) path, name = get_dir_name_with_vars(context, file_path_value) result_name = replace_vs_vars_with_cmake_vars(context, name) result_path = cleaning_output(context, path) result_path = check_for_relative_in_path(context, result_path) message(context, '{} directory = {}'.format(node_name, result_path), '') message(context, '{} name = {}'.format(node_name, result_name), '') context.settings[context.current_setting][path_property] = [ result_path ] context.settings[context.current_setting][name_property] = [ result_name ]
def set_target_additional_library_directories( context, flag_name, additional_library_directories, node): """ Find and set additional library directories in context """ del flag_name, node if additional_library_directories: list_depends = additional_library_directories.replace( '%(AdditionalLibraryDirectories)', '') if list_depends != '': add_lib_dirs = [] for d in list_depends.split(';'): d = d.strip() if d != '': add_lib_dirs.append( check_for_relative_in_path( context, cleaning_output(context, d))) message( context, 'Additional Library Directories = {}'.format(add_lib_dirs), '') context.settings[ context.current_setting]['target_link_dirs'] = add_lib_dirs
def get_cmake_lists(context, cmake_path=None, open_type='w'): """ Create CMakeLists.txt file in wanted "cmake_path" :param context: the context of converter :type context: Context :param cmake_path: path where CMakeLists.txt should be open :type cmake_path: str :param open_type: type that CMakeLists.txt should be opened :type open_type: str :return: cmake file wrapper opened :rtype: _io.TextIOWrapper """ if not cmake_path: cmake_path = os.getcwd() message(context, 'CMakeLists dir is current directory.', 'warn') cmake = os.path.join(cmake_path, 'CMakeLists.txt') if not os.path.exists(cmake) and open_type == 'r': return None message(context, 'CMakeLists.txt will be written to : ' + cmake, '') return open(cmake, open_type, newline='\n', encoding='utf-8')
def define_defines(self): """ DEFINES """ # PreprocessorDefinitions for setting in self.settings: define = self.tree.find( '%s/ns:ClCompile/ns:PreprocessorDefinitions' % self.definitiongroups[setting], namespaces=self.ns) if define is not None: for preproc in define.text.split(";"): if preproc != '%(PreprocessorDefinitions)' and preproc != 'WIN32': self.settings[setting][ defines] += ' -D%s \n' % preproc # Unicode unicode = self.tree.find("{0}/ns:CharacterSet".format( self.definitiongroups[setting]), namespaces=self.ns) if unicode is not None: if 'Unicode' in unicode.text: self.settings[setting][defines] += ' -DUNICODE\n' self.settings[setting][defines] += ' -D_UNICODE\n' message('PreprocessorDefinitions for {0}'.format(setting), 'ok')
def write_include_dir(self): """ Write on "CMakeLists.txt" include directories required for compilation. """ incl_dir = self.tree.find( '//ns:ItemGroup/ns:ClCompile/ns:AdditionalIncludeDirectories', namespaces=self.ns) if incl_dir is None: incl_dir = self.tree.find( '//ns:ItemDefinitionGroup/ns:ClCompile/ns:AdditionalIncludeDirectories', namespaces=self.ns) if incl_dir is not None: self.cmake.write('# Include directories \n') inc_dir = incl_dir.text.replace('$(ProjectDir)', './') for i in inc_dir.split(';'): i = i.replace('\\', '/') i = re.sub(r'\$\((.+?)\)', r'$ENV{\1}', i) self.cmake.write('include_directories(%s)\n' % i) message('Include Directories found : %s' % i, 'warn') self.cmake.write('\n') else: # pragma: no cover message('Include Directories not found for this project.', 'warn')
def __add_file_into_container(self, context, **kwargs): files_container = kwargs['files_container'] file_path = kwargs['file_path'] file_name = kwargs['file_name'] source_group = kwargs['source_group'] real_name = take_name_from_list_case_ignore(context, self.file_lists[file_path], file_name) if real_name: name_to_add = real_name else: if context.ignore_absent_sources: return None name_to_add = file_name message( context, 'Adding absent {} file into project files'.format(file_name), 'warn') files_container[file_path].append(name_to_add) file_path_name = os.path.normpath(os.path.join(file_path, name_to_add)) file_path_name = set_unix_slash(file_path_name) if source_group not in context.source_groups: context.source_groups[source_group] = [] context.source_groups[source_group].append(file_path_name) context.source_groups[source_group].sort(key=str.lower) if real_name: self.include_directive_case_check( context, file_path_name, self.file_lists_for_include_paths) context.file_contexts[file_path_name] = self.__create_file_context( context) return context.file_contexts[file_path_name]
def __get_info_from_results( project_context, results, subdirectories_set, subdirectories_to_target_name ): for directory_results in results: for sln_target_result in directory_results: subdirectory = os.path.relpath( sln_target_result['cmake'], project_context.solution_path ) if subdirectory != '.': subdirectories_set.add(subdirectory) subdirectories_to_target_name[subdirectory] = sln_target_result['target_name'] project_context.project_languages.update(sln_target_result['project_languages']) if project_context.target_windows_version and \ sln_target_result['target_windows_ver'] and \ project_context.target_windows_version !=\ sln_target_result['target_windows_ver']: message( project_context, 'CMake does not support more than 1 version of windows SDK', 'warn' ) if sln_target_result['target_windows_ver']: project_context.target_windows_version = \ sln_target_result['target_windows_ver'] project_context.warnings_count += sln_target_result['warnings_count']
def __parse_project_configuration_platforms(self, context, sln_text, sln_projects_data): projects_configurations_re = re.compile( r'GlobalSection\(ProjectConfigurationPlatforms\) = ' r'postSolution((?:.|\n)*?)EndGlobalSection' ) projects_configurations_matches = projects_configurations_re.findall(sln_text) projects_configuration_re = re.compile(r'({.+\})\.([\w -|]+)\.ActiveCfg = ([\w -|]+)') for projects_configuration_match in projects_configurations_matches: sln_config_groups = projects_configuration_re.findall(projects_configuration_match) for sln_config_group in sln_config_groups: if not self.__check_project_guid(context, sln_projects_data, sln_config_group[0]): continue p = sln_projects_data[sln_config_group[0]] sln_cmake_configuration = make_cmake_configuration(context, sln_config_group[1]) project_cmake_configuration = make_cmake_configuration(context, sln_config_group[2]) p['sln_configs_2_project_configs'][tuple(sln_cmake_configuration.split('|'))] = \ tuple(project_cmake_configuration.split('|')) message( context, ' "{}" -> "{}" for {}'.format( sln_cmake_configuration, project_cmake_configuration, p['name'] ), '' )
def __set_default_flags(self, context): message(context, '== start making default flags ==', '') if context.current_setting not in self.flags: self.flags[context.current_setting] = {} for flag_name in self.flags_handlers: self.__set_default_flag(context, flag_name) message(context, '== end making default flags ==', '')
def __parse_project_data(context, project_data_match, path): """ Parse project section at *.sln file :param context: context from input file :type context: Context :param project_data_match: :param path: :return: """ project = dict() project['name'] = project_data_match[1] message(context, ' Found project "{}" with {}'.format(path, project_data_match[3]), '') project['path'] = path project['sln_configs_2_project_configs'] = OrderedDict({(None, None): (None, None)}) if 'ProjectDependencies' in project_data_match[0]: project['sln_deps'] = [] dependencies_section = re.compile( r'ProjectSection\(ProjectDependencies\) = ' r'postProject(?:.|\n)*?EndProjectSection' ) dep_data = dependencies_section.findall(project_data_match[0]) dependencies_guids = re.compile(r'(({.*\}) = ({.*\}))') guids_deps_matches = dependencies_guids.findall(dep_data[0]) for guids_deps_match in guids_deps_matches: project['sln_deps'].append(guids_deps_match[2]) return project
def add_target_property_sheet(context, attr_name, filename, node): """ Find and set in current context property sheets :param context: :param attr_name: :param filename: :param node: :return: """ del attr_name, node if 'Microsoft' in filename: # ignore props provided by Microsoft return if filename[-8:] == '.targets': # ignore targets files return working_path = os.path.dirname(context.vcxproj_path) props_cmake_path = normalize_path(context, working_path, filename, False).replace('.props', '.cmake') props_cmake_path = cleaning_output(context, props_cmake_path) message(context, 'cmake from property sheet: {}'.format(props_cmake_path), '') context.settings[context.current_setting]['property_sheets'].append( props_cmake_path)
def collects_source_files(self): """ Collects the project source files in CMakeLists.txt file """ # Cpp Dir for cpp in self.cppfiles: if cpp.get('Include') is not None: cxx = str(cpp.get('Include')) cxx = '/'.join(cxx.split('\\')) if not cxx.rpartition('.')[-1] in self.language: self.language.append(cxx.rpartition('.')[-1]) cpp_path, cxx_file = os.path.split(cxx) if not cpp_path: # Case files are beside the VS Project cpp_path = './' if cpp_path not in self.sources: self.sources = {cpp_path: []} if cxx_file not in self.sources[cpp_path]: self.sources[cpp_path].append(cxx_file) # Headers Dir for header in self.headerfiles: h = str(header.get('Include')) h = '/'.join(h.split('\\')) header_path, header_file = os.path.split(h) if header_path not in self.headers: self.headers = {header_path: []} if header_file not in self.headers[header_path]: self.headers[header_path].append(header_file) message("C++ Extensions found: %s" % self.language, 'INFO')
def __parse_configurations_of_solution(context, sln_text, solution_data): solution_configurations_re = re.compile( r'GlobalSection\(SolutionConfigurationPlatforms\) = ' r'preSolution((?:.|\n)*?)EndGlobalSection') solution_configurations_matches = solution_configurations_re.findall( sln_text) solution_data['sln_configurations'] = [] sln_configuration_re = re.compile(r'([\w -|]+) = ([\w -|]+)') for solution_configuration_match in solution_configurations_matches: configurations = sln_configuration_re.findall( solution_configuration_match) for sln_configuration in configurations: cmake_configuration = make_cmake_configuration( context, sln_configuration[0]) solution_data['sln_configurations'].append(cmake_configuration) message( context, ' Found sln setting "{}"'.format(cmake_configuration), '') arch = cmake_configuration.split('|')[1] if arch == 'x86' and not context.is_android: message( context, 'Solution architecture is x86 and may be mapped onto Win32 at projects.' 'To avoid problems rename x86 -> Win32.', 'warn') context.supported_architectures.add(arch)
def set_cmake_lists_path(context, cmake_lists): """ Set CMakeLists.txt path in context, for given project :param context: converter context :type context: Context :param cmake_lists: path of CMakeLists related to project name :type cmake_lists: str """ context_cmake = None if cmake_lists: if os.path.exists(cmake_lists): context_cmake = cmake_lists if context_cmake is None: message( context, 'Path "{}" for CMakeLists.txt is wrong. ' 'It will be created in working directory.'.format(cmake_lists), 'warn' ) context_cmake = 'CMakeLists.txt' if context: context.cmake = context_cmake return context_cmake
def set_additional_include_directories(aid_text, setting, context): """ Return additional include directories of given context :param aid_text: path to sources :type aid_text: str :param setting: current setting (Debug|x64, Release|Win32,...) :type setting: str :param context: current context :type context: Context :return: include directories of context, separated by semicolons :rtype: str """ if not aid_text: return working_path = os.path.dirname(context.vcxproj_path) inc_dir = resolve_path_variables_of_vs(context, aid_text) inc_dir = inc_dir.replace('%(AdditionalIncludeDirectories)', '') inc_dirs = context.settings[setting]['inc_dirs'] dirs_raw = [] for i in inc_dir.split(';'): if i: dirs_raw.append(i) i = normalize_path(context, working_path, i) i = replace_vs_vars_with_cmake_vars(context, i) inc_dirs.append(i) context.settings[setting]['inc_dirs_list'].extend(dirs_raw) if inc_dirs: message( context, 'Include Directories : {0}'.format( context.settings[setting]['inc_dirs']), '')
def __set_target_build_events(context, build_event_node, value_name, event_type): """ General routine for setting build events :param context: :param build_event_node: :param value_name: :param event_type: :return: """ for command in build_event_node: if 'Command' not in command.tag: continue if command.text is None: continue for build_event in command.text.split('\n'): build_event = build_event.strip() if build_event: cmake_build_event = prepare_build_event_cmd_line_for_cmake( context, build_event) context.settings[context.current_setting][value_name] \ .append(cmake_build_event) message( context, '{} event : {}'.format(event_type, cmake_build_event), 'info')
def __set_assembler_listing_location(context, flag_name, node): """ Set AssemblerListingLocation flag: /Fa """ del flag_name, node flag_values = {default_value: {}} # /Fa breaks non Visual Studio CMake generators. # In visual studio MSBuild target MakeDirsForCl creates dir before compiling but # other generators (Ninja, NMake Makefiles) don't. # Then missing target asm directory error occurs. # if node.text: # flag_values.update( # { # node.text: { # cl_flags: '/Fa{}'.format(cleaning_output(context, node.text)) # } # } # ) message( context, '/Fa option is ignored. Too hard to handle for different CMake generators.', 'warn4') return flag_values
def run_conversion(self, subdirectory_projects_data): """ Routine that converts projects located at the same directory """ results = [] for project_data in subdirectory_projects_data: project_context = project_data['project_context'] name = project_context.project_number message(project_context, '------ Starting {} -------'.format(name), '') converted = self.convert_project( project_context, project_data['project_abs'], project_data['subdirectory'], ) message(project_context, '------ Exiting {} -------'.format(name), '') if not converted: continue project_context = project_data['project_context'] # Can't return context as a result due PicklingError results.append({ 'cmake': project_context.cmake, 'project_name': project_context.project_name, 'solution_languages': project_context.solution_languages, 'target_windows_ver': project_context.target_windows_version, 'warnings_count': project_context.warnings_count }) return results
def __check_project_guid(context, projects_data, project_guid): if project_guid not in projects_data: message( context, 'project with GUID {} is missing in solution file'.format( project_guid), 'error') return False return True
def apply_files_to_context(self, context): """ Analyzes collected set of files and initializes necessary variables """ has_headers = bool(context.headers) context.has_headers = has_headers context.has_only_headers = bool(has_headers and not context.sources) message(context, "Source files extensions found: {0}".format(self.languages), 'INFO')
def __init__(self, context, vs_project, cmake_lists_destination_path): self.init_files(context, vs_project, cmake_lists_destination_path) message( context, 'Initialization data for conversion of project {0}'.format(context.vcxproj_path), '' ) for sln_config in context.sln_configurations_map: context.configurations_to_parse.add(context.sln_configurations_map[sln_config])
def __set_additional_options(self, context, flag_name, flag_value): """ Set Additional options """ # for setting in context.settings: add_opts = flag_value if add_opts: add_opts = set_unix_slash(add_opts).split() ready_add_opts = [] for add_opt in add_opts: add_opt = add_opt.strip() if '/Qprof-dir' in add_opt: name_value = add_opt.split(':') prof_dir = normalize_path( context, os.path.dirname(context.vcxproj_path), name_value[1]) prof_dir = cleaning_output(context, prof_dir) add_opt = name_value[0] + ':' + prof_dir add_opt = '-' + add_opt[1:] ready_add_opts.append(add_opt) unix_option = add_opt.replace(':', ' ') if 'gen-interfaces' in unix_option: pass elif 'Qprec-div' in unix_option: unix_option = FortranFlags.__get_no_prefix( unix_option) + '-prec-div' elif unix_option == '-static': pass elif 'Qprof-dir' in unix_option: unix_option = unix_option.replace('Qprof-dir', 'prof-dir') elif 'Qprof-gen' in unix_option: unix_option = unix_option.replace('Qprof-gen', 'prof-gen') elif 'Qprof-use' in unix_option: unix_option = unix_option.replace('Qprof-use', 'prof-use') elif 'Qprec-sqrt' in unix_option: unix_option = FortranFlags.__get_no_prefix( unix_option) + '-prec-sqrt' elif 'Qopenmp-lib' in unix_option: unix_option = unix_option.replace('Qopenmp-lib', 'qopenmp-lib') unix_option = unix_option.replace('lib ', 'lib=') else: message( context, 'Unix ifort option "{0}" may be incorrect. ' 'Check it and set it with visual studio UI if possible.' .format(unix_option), 'warn') if ifort_cl_win not in self.flags[flag_name]: self.flags[flag_name][ifort_cl_win] = [] self.flags[flag_name][ifort_cl_win].append(add_opt) if ifort_cl_unix not in self.flags[flag_name]: self.flags[flag_name][ifort_cl_unix] = [] self.flags[flag_name][ifort_cl_unix].append(unix_option) message(context, 'Additional Options : {0}'.format(str(ready_add_opts)), '')
def create_data(self): """ Create the data and convert each part of "vcxproj" project """ # Write variables variables = ProjectVariables(self.data) variables.add_project_variables() files = ProjectFiles(self.data) files.collects_source_files() variables.add_cmake_project(files.language) variables.add_default_target() # Write Output Variables variables.add_cmake_output_directories() variables.write_project_messages() # Add includes files & directories if self.data['includecmake'] or self.data['include']: title = get_title('Includes', 'Include files and directories') self.data['cmake'].write(title) # Include ".cmake" file if self.data['includecmake']: files.add_include_cmake(self.data['includecmake']) # Write Include Directories depends = Dependencies(self.data) if self.data['include']: depends.write_include_dir() else: message('Include Directories is not set.', '') # Write Dependencies depends.write_dependencies() # Add additional code if self.data['additional'] is not None: files.add_additional_code(self.data['additional']) # Write and add Files files.write_source_files() if files.sources: files.add_target_artefact() # Write Flags all_flags = Flags(self.data) all_flags.write_flags() # Write Macro all_flags.write_defines_and_flags() # Link with other dependencies title = get_title('Link & Dependencies', 'Add project dependencies and Link to project') self.data['cmake'].write(title) depends.add_dependencies() depends.link_dependencies()
def add_include_cmake(self, filename): """ Add include directive for filename :param filename: name of file to include :type filename: str """ self.cmake.write('include("%s")\n\n' % filename) message('File "%s" is included in CMakeLists.txt' % filename, 'warn')
def set_character_set(self, context, character_set_node): """ Set defines related to selected character set """ unicode_defines = [] if 'Unicode' in character_set_node.text: unicode_defines += ['UNICODE', '_UNICODE'] if 'MultiByte' in character_set_node.text: unicode_defines += ['_MBCS'] self.unicode_defines[context.current_setting] = unicode_defines message(context, 'Unicode Definitions : {}'.format(unicode_defines), '')
def write_dependencies(self): """ Write on "CMakeLists.txt" subdirectories or link directories for external libraries. """ references = self.tree.xpath('//ns:ProjectReference', namespaces=self.ns) if references: title = get_title('Dependencies', 'Add Dependencies to project') self.cmake.write(title) self.cmake.write( '# Dependencies: disable BUILD_DEPENDS to link with libraries already built\n' ) self.cmake.write( 'option(BUILD_DEPENDS "Build other CMake project." ON)\n\n') if not self.custom_dependencies: self.cmake.write('if(BUILD_DEPENDS)\n') for ref in references: reference = str(ref.get('Include')) path_to_reference = os.path.splitext( ntpath.basename(reference))[0] if 'g3log' in path_to_reference: path_to_reference = '%sger' % path_to_reference self.cmake.write(' if(NOT TARGET %s)\n' % path_to_reference) self.cmake.write( ' add_subdirectory("${%s_DIR}" ${CMAKE_BINARY_DIR}/%s)\n' % (path_to_reference.upper(), path_to_reference)) self.cmake.write(' endif()\n') else: self.cmake.write('if(BUILD_DEPENDS)\n') d = 1 for ref in self.custom_dependencies: self.cmake.write( ' add_subdirectory(%s ${CMAKE_BINARY_DIR}/lib%s)\n' % (ref, str(d))) message( 'Add manually dependencies : %s. Will be build in "lib%s/" !' % (ref, str(d)), 'warn') d += 1 self.cmake.write('else()\n') for ref in references: reference = str(ref.get('Include')) path_to_reference = os.path.splitext( ntpath.basename(reference))[0] self.cmake.write( ' link_directories(${DEPENDENCIES_DIR}/%s)\n' % path_to_reference) self.cmake.write('endif()\n\n') else: # pragma: no cover message('No link needed.', '')
def __check_solution_version(context, sln_text): version_pattern = re.compile( r'Microsoft Visual Studio Solution File, Format Version (.*)' ) version_match = version_pattern.findall(sln_text) if not version_match or float(version_match[0]) < 9: message(context, 'Solution files with versions below 9.00 are not supported.' ' Version {} found. Upgrade you solution and try again, please' .format(version_match[0]), 'error') sys.exit(1) message(context, 'Version of solution is {}'.format(version_match[0]), '')