diff options
Diffstat (limited to 'bin/gbuild-to-ide')
-rwxr-xr-x | bin/gbuild-to-ide | 516 |
1 files changed, 270 insertions, 246 deletions
diff --git a/bin/gbuild-to-ide b/bin/gbuild-to-ide index 430e4c96cbcc..ef1e27dd6748 100755 --- a/bin/gbuild-to-ide +++ b/bin/gbuild-to-ide @@ -23,94 +23,23 @@ import traceback import subprocess from sys import platform import collections +import urllib.parse class GbuildLinkTarget: - def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs): - (self.name, self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.cxxflags, self.cobjects, self.cflags, self.linked_libs) = ( - name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs) - - def short_name(self): - return self.name - - def is_empty(self): - return not self.include and not self.defs and not self.cxxobjects and not self.cobjects and not self.linked_libs - - def __str__(self): - return '%s at %s with include path: %s, isystem includes: %s, defines: %s, objects: %s, cxxflags: %s, cobjects: %s, cflags: %s and linked libs: %s' % ( - self.short_name(), self.location, self.include, self.include_sys, self.defs, self.cxxobjects, - self.cxxflags, self.cobjects, self.cflags, self.linked_libs) - - -class GbuildLib(GbuildLinkTarget): - def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs): - GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs) - - def short_name(self): - """Return the short name of target based on the Library_* makefile name""" - return 'Library %s' % self.name - - def target_name(self): - return 'Library_%s' % self.name - - def library_name(self): - return self.name - -class GbuildTest(GbuildLinkTarget): - def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs): - GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs) - - def short_name(self): - """Return the short name of target based n the CppunitTest_* makefile names""" - return 'CppunitTest %s' % self.name - - def target_name(self): - return 'CppunitTest_%s' % self.name - -class GbuildExe(GbuildLinkTarget): - def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs): - GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, cflags, linked_libs) - - def short_name(self): - """Return the short name of target based on the Executable_* makefile name""" - return 'Executable %s' % self.name - - def target_name(self): - return 'Executable_%s' % self.name - - -class GbuildParser: - """Main data model object. - - Attributes: - target_by_path : dict[path:string, set(target)] - where target is one of the GbuildLinkTarget subclasses - target_by_location : dict[path:string, set(target)] - where target is one of the GbuildLinkTarget subclasses - """ - def __init__(self, makecmd): - self.makecmd = makecmd - self.binpath = os.path.dirname(os.environ['GPERF']) # woha, this is quite a hack - (self.srcdir, self.builddir, self.instdir, self.workdir) = (os.environ['SRCDIR'], os.environ['BUILDDIR'], os.environ['INSTDIR'], os.environ['WORKDIR']) - (self.libs, self.exes, self.tests, self.modulenamelist) = ([], [], [], []) - (self.target_by_path, self.target_by_location) = ({}, {}) - includepattern = re.compile(r'-I(\S+)') isystempattern = re.compile(r'-isystem\s*(\S+)') warningpattern = re.compile(r'-W\S+') - libpattern = re.compile(r'Library_(.*)\.mk') - exepattern = re.compile(r'Executable_(.*)\.mk') - testpattern = re.compile(r'CppunitTest_(.*)\.mk') @staticmethod def __split_includes(includes): - foundisystem = GbuildParser.isystempattern.findall(includes) - foundincludes = [includeswitch.strip() for includeswitch in GbuildParser.includepattern.findall(includes) if + foundisystem = GbuildLinkTarget.isystempattern.findall(includes) + foundincludes = [includeswitch.strip() for includeswitch in GbuildLinkTarget.includepattern.findall(includes) if len(includeswitch) > 2] return (foundincludes, foundisystem) @staticmethod def __split_objs(objsline): - return [obj for obj in objsline.strip().split(' ') if len(obj) > 0 and obj != 'CXXOBJECTS' and obj != 'COBJECTS' and obj != '+='] + return [obj for obj in objsline.strip().split(' ') if obj not in { '', 'CXXOBJECTS', 'COBJECTS', 'OBJCXXOBJECTS', 'CXXCLROBJECTS', '+=' }] @staticmethod def __split_defs(defsline): @@ -131,75 +60,91 @@ class GbuildParser: @staticmethod def __split_flags(flagsline, flagslineappend): - return [cxxflag.strip() for cxxflag in GbuildParser.warningpattern.sub('', '%s %s' % (flagsline, flagslineappend)).split(' ') if len(cxxflag) > 1] + return [cxxflag.strip() for cxxflag in GbuildLinkTarget.warningpattern.sub('', '%s %s' % (flagsline, flagslineappend)).split(' ') if len(cxxflag) > 1] - @staticmethod - def __lib_from_json(json): - (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE']) - return GbuildLib( - GbuildParser.libpattern.match(os.path.basename(json['MAKEFILE'])).group(1), + def __init__(self, json): + (foundincludes, foundisystem) = GbuildLinkTarget.__split_includes(json['INCLUDE']) + (self.name, self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.objcxxobjects, self.cxxflags, self.cobjects, self.cflags, self.cxxclrobjects, self.cxxclrflags, self.linked_libs, self.linked_static_libs, self.link_target) = ( + type(self).targetpattern.match(os.path.basename(json['MAKEFILE'])).group(1), os.path.dirname(json['MAKEFILE']), foundincludes, foundisystem, - GbuildParser.__split_defs(json['DEFS']), - GbuildParser.__split_objs(json['CXXOBJECTS']), - GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']), - GbuildParser.__split_objs(json['COBJECTS']), - GbuildParser.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']), - json['LINKED_LIBS'].strip().split(' ')) + GbuildLinkTarget.__split_defs(json['DEFS']), + GbuildLinkTarget.__split_objs(json['CXXOBJECTS']), + GbuildLinkTarget.__split_objs(json['OBJCXXOBJECTS']), + GbuildLinkTarget.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']), + GbuildLinkTarget.__split_objs(json['COBJECTS']), + GbuildLinkTarget.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']), + GbuildLinkTarget.__split_objs(json['CXXCLROBJECTS']), + GbuildLinkTarget.__split_flags(json['CXXCLRFLAGS'], json['CXXCLRFLAGSAPPEND']), + json['LINKED_LIBS'].strip().split(' '), + json['LINKED_STATIC_LIBS'].strip().split(' '), + json['LINKTARGET'].strip()) - @staticmethod - def __test_from_json(json): - (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE']) - testname_match = GbuildParser.testpattern.match(os.path.basename(json['MAKEFILE'])) + def short_name(self): + """Return the short name of target based on the Foo_* makefile name""" + return '%s %s' % (type(self).targetprefix, self.name) - # Workaround strange writer test makefile setup - if testname_match is None: - testname = "StrangeWriterMakefiles" - else: - testname = testname_match.group(1) + def target_name(self): + return '%s_%s' % (type(self).targetprefix, self.name) - return GbuildTest( - testname, - os.path.dirname(json['MAKEFILE']), - foundincludes, - foundisystem, - GbuildParser.__split_defs(json['DEFS']), - GbuildParser.__split_objs(json['CXXOBJECTS']), - GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']), - GbuildParser.__split_objs(json['COBJECTS']), - GbuildParser.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']), - json['LINKED_LIBS'].strip().split(' ')) + def __str__(self): + return '%s at %s with include path: %s, isystem includes: %s, defines: %s, objects: %s, cxxflags: %s, cobjects: %s, cflags: %s, linked libs: %s and linked static libs: %s' % ( + self.short_name(), self.location, self.include, self.include_sys, self.defs, self.cxxobjects, + self.cxxflags, self.cobjects, self.cflags, self.linked_libs, self.linked_static_libs) - @staticmethod - def __exe_from_json(json): - (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE']) - return GbuildExe( - GbuildParser.exepattern.match(os.path.basename(json['MAKEFILE'])).group(1), - os.path.dirname(json['MAKEFILE']), - foundincludes, - foundisystem, - GbuildParser.__split_defs(json['DEFS']), - GbuildParser.__split_objs(json['CXXOBJECTS']), - GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']), - GbuildParser.__split_objs(json['COBJECTS']), - GbuildParser.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']), - json['LINKED_LIBS'].strip().split(' ')) + +class GbuildLib(GbuildLinkTarget): + targetpattern = re.compile(r'Library_(.*)\.mk') + targetprefix = "Library" + +class GbuildStaticLib(GbuildLinkTarget): + targetpattern = re.compile(r'StaticLibrary_(.*)\.mk') + targetprefix = "StaticLibrary" + +class GbuildTest(GbuildLinkTarget): + targetpattern = re.compile(r'CppunitTest_(.*)\.mk') + targetprefix = "CppunitTest" + +class GbuildExe(GbuildLinkTarget): + targetpattern = re.compile(r'Executable_(.*)\.mk') + targetprefix = "Executable" + + +class GbuildParser: + """Main data model object. + + Attributes: + target_by_path : dict[path:string, set(target)] + where target is one of the GbuildLinkTarget subclasses + target_by_location : dict[path:string, set(target)] + where target is one of the GbuildLinkTarget subclasses + """ + def __init__(self, makecmd): + self.makecmd = makecmd + self.binpath = os.path.dirname(os.environ['GPERF']) # woha, this is quite a hack + (self.srcdir, self.builddir, self.instdir, self.workdir) = (os.environ['SRCDIR'], os.environ['BUILDDIR'], os.environ['INSTDIR'], os.environ['WORKDIR']) + (self.libs, self.static_libs, self.exes, self.tests, self.modulenamelist) = (set(), set(), set(), set(), []) + (self.target_by_path, self.target_by_location) = ({}, {}) def parse(self): for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'Library')): with open(os.path.join(self.workdir, 'GbuildToJson', 'Library', jsonfilename), 'r') as f: - lib = self.__lib_from_json(json.load(f)) - self.libs.append(lib) + lib = GbuildLib(json.load(f)) + self.libs.add(lib) + for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'StaticLibrary')): + with open(os.path.join(self.workdir, 'GbuildToJson', 'StaticLibrary', jsonfilename), 'r') as f: + static_lib = GbuildStaticLib(json.load(f)) + self.static_libs.add(static_lib) for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'Executable')): with open(os.path.join(self.workdir, 'GbuildToJson', 'Executable', jsonfilename), 'r') as f: - exe = self.__exe_from_json(json.load(f)) - self.exes.append(exe) + exe = GbuildExe(json.load(f)) + self.exes.add(exe) for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'CppunitTest')): with open(os.path.join(self.workdir, 'GbuildToJson', 'CppunitTest', jsonfilename), 'r') as f: - test = self.__test_from_json(json.load(f)) - self.tests.append(test) - for target in set(self.libs) | set(self.exes) | set(self.tests): + test = GbuildTest(json.load(f)) + self.tests.add(test) + for target in self.libs | self.static_libs | self.exes | self.tests: if target.location not in self.target_by_location: self.target_by_location[target.location] = set() self.target_by_location[target.location] |= set([target]) @@ -303,7 +248,7 @@ class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator): </language> </section> </cdtprojectproperties> -""" +""" for module in self.gbuildparser.modulenamelist: tempxml = [] @@ -328,7 +273,7 @@ class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator): macrokey = macroskeyvalue[0] macrovalue = macroskeyvalue[1] if macrovalue[-1:] == "\n": - macrovalue = macrovalue[:-1] + macrovalue = macrovalue[:-1] templine = "<macro><name>%s</name><value>%s</value></macro>\n" %(macrokey, macrovalue) tempxml.insert(-13, templine) tempxml="".join(tempxml) @@ -343,7 +288,7 @@ class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator): def emit(self): self.create_include_paths() self.create_macros() - self.create_settings_file() + self.create_settings_file() class CodeliteIntegrationGenerator(IdeIntegrationGenerator): @@ -353,22 +298,26 @@ class CodeliteIntegrationGenerator(IdeIntegrationGenerator): def emit(self): self.create_workspace_file() for module in self.gbuildparser.modulenamelist: - self.create_project_file(module) + if os.path.exists(module): # ignore external modules + self.create_project_file(module) #self.create_project_file('vcl') def create_workspace_file(self): root_node = ET.Element('CodeLite_Workspace', Name='libo2', Database='./libo2.tags', Version='10.0.0') for module in self.gbuildparser.modulenamelist: - ET.SubElement(root_node, 'Project', Name=module, Path='%s/%s.project' % (module, module), Active='No') + if os.path.exists(module): # ignore external modules + ET.SubElement(root_node, 'Project', Name=module, Path='%s/%s.project' % (module, module), Active='No') build_matrix_node = ET.SubElement(root_node, 'BuildMatrix') workspace_config_node = ET.SubElement(build_matrix_node, 'WorkspaceConfiguration', Name='Debug', Selected='yes') ET.SubElement(workspace_config_node, 'Environment') for module in self.gbuildparser.modulenamelist: - ET.SubElement(workspace_config_node, 'Project', Name=module, ConfigName='Debug') + if os.path.exists(module): # ignore external modules + ET.SubElement(workspace_config_node, 'Project', Name=module, ConfigName='Debug') workspace_config_node = ET.SubElement(build_matrix_node, 'WorkspaceConfiguration', Name='Release', Selected='yes') ET.SubElement(workspace_config_node, 'Environment') for module in self.gbuildparser.modulenamelist: - ET.SubElement(workspace_config_node, 'Project', Name=module, ConfigName='Release') + if os.path.exists(module): # ignore external modules + ET.SubElement(workspace_config_node, 'Project', Name=module, ConfigName='Release') self.write_pretty_xml(root_node, os.path.join(self.gbuildparser.builddir, 'libo2.workspace')) @@ -386,7 +335,7 @@ class CodeliteIntegrationGenerator(IdeIntegrationGenerator): path = '/'.join(file.split('/')[1:-1]) virtual_dirs[path].add(relative_file + '.cxx') # add HXX files - all_libs = set(self.gbuildparser.libs) | set(self.gbuildparser.exes) + all_libs = self.gbuildparser.libs | self.gbuildparser.exes for lib in all_libs: if lib.name == module_name: for hdir in lib.include: @@ -541,14 +490,14 @@ class VimIntegrationGenerator(IdeIntegrationGenerator): def emit(self): global_list = [] - for lib in set(self.gbuildparser.libs) | set(self.gbuildparser.tests) | set(self.gbuildparser.exes): + for lib in self.gbuildparser.libs | self.gbuildparser.tests | self.gbuildparser.exes: entries = [] for file in lib.cxxobjects: filePath = os.path.join(self.gbuildparser.srcdir, file) + ".cxx" entry = {'directory': lib.location, 'file': filePath, 'command': self.generateCommand(lib, filePath)} entries.append(entry) global_list.extend(entries) - with open('compile_commands.json', 'w') as export_file: + with open(os.path.join(self.gbuildparser.builddir, 'compile_commands.json'), 'w') as export_file: json.dump(global_list, export_file) def generateCommand(self, lib, file): @@ -946,13 +895,21 @@ class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): self.target = target self.path = project_path + # Check if the target is empty (no source files would be added to project file) + @staticmethod + def should_skip(target): + return not target.cxxobjects and not target.cxxclrobjects and not target.cobjects + def emit(self): all_projects = [] - for location in self.gbuildparser.target_by_location: + for location, targets in self.gbuildparser.target_by_location.items(): projects = [] module = location.split('/')[-1] module_directory = os.path.join(self.solution_directory, module) - for target in self.gbuildparser.target_by_location[location]: + for target in targets: + if self.should_skip(target): + print(' %s: no files to add, skipping' % target.target_name()) + continue project_path = os.path.join(module_directory, '%s.vcxproj' % target.target_name()) project_guid = self.write_project(project_path, target) p = VisualStudioIntegrationGenerator.Project(project_guid, target, project_path) @@ -961,20 +918,34 @@ class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): all_projects += projects self.write_solution(os.path.join(self.solution_directory, 'LibreOffice.sln'), all_projects) + # this enables Python GDB pretty printers when debugging a WSL Linux build from VS + MIEngine_options_path = os.path.join(gbuildparser.srcdir, 'solenv/vs/Microsoft.MIEngine.Options.xml') + shutil.copy(MIEngine_options_path, self.solution_directory) + + @staticmethod + def gen_guid(category, name): + return str(uuid.uuid5(uuid.NAMESPACE_URL, 'vnd.libreoffice.vs-ide-integration:' + category + '/' + urllib.parse.quote(name))).upper() nmake_project_guid = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942' + nmake_folder_guid = '2150E333-8FDC-42A3-9474-1A3956D46DE8' def get_dependency_libs(self, linked_libs, library_projects): dependency_libs = {} for linked_lib in linked_libs: for library_project in library_projects: - if library_project.target.library_name() == linked_lib: + if library_project.target.name == linked_lib: dependency_libs[library_project.guid] = library_project return dependency_libs def write_solution(self, solution_path, projects): print('Solution %s:' % os.path.splitext(os.path.basename(solution_path))[0], end='') + if not projects: + print(' no projects, skipping') + return library_projects = [project for project in projects if project.target in self.gbuildparser.libs] + static_library_projects = [project for project in projects if project.target in self.gbuildparser.static_libs] + test_projects = [project for project in projects if project.target in self.gbuildparser.tests] + executable_projects = [project for project in projects if project.target in self.gbuildparser.exes] with open(solution_path, 'w') as f: f.write('Microsoft Visual Studio Solution File, Format Version 12.00\n') for project in projects: @@ -986,25 +957,61 @@ class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): target.short_name(), proj_path, project.guid)) libs_in_solution = self.get_dependency_libs(target.linked_libs, library_projects) + libs_in_solution |= self.get_dependency_libs(target.linked_static_libs, + static_library_projects) if libs_in_solution: f.write('\tProjectSection(ProjectDependencies) = postProject\n') for lib_guid in libs_in_solution.keys(): f.write('\t\t{%(guid)s} = {%(guid)s}\n' % {'guid': lib_guid}) f.write('\tEndProjectSection\n') f.write('EndProject\n') - f.write('Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B9292527-A979-4D13-A598-C75A33222174}"\n') + f.write('Project("{%s}") = "Utility", "Utility", "{6778240E-8B6B-47A0-AC31-7E7A257B97E6}"\n' % + (VisualStudioIntegrationGenerator.nmake_folder_guid)) f.write('\tProjectSection(SolutionItems) = preProject\n') # The natvis file gives pretty-printed variable values when debugging natvis_path = os.path.join(gbuildparser.srcdir, 'solenv/vs/LibreOffice.natvis') f.write('\t\t%(natvis)s = %(natvis)s\n' % {'natvis': natvis_path}) + # The natstepfilter file allows to skip specific functions when stepping into in debugging + natstepfilter_path = os.path.join(gbuildparser.srcdir, 'solenv/vs/LibreOffice.natstepfilter') + f.write('\t\t%(natstepfilter)s = %(natstepfilter)s\n' % {'natstepfilter': natstepfilter_path}) f.write('\tEndProjectSection\n') f.write('EndProject\n') + # Folders to group tests/libraries/executables + nmake_tests_guid = 'CF544F7B-9D02-4D83-8370-5887851209B7' + nmake_libraries_guid = 'C624F43D-616C-4627-B58F-F5C2047B7BDC' + nmake_static_libraries_guid = 'EB2CD1D4-7C29-4232-9B1E-9FB1FE62D61C' + nmake_executables_guid = '1CD80999-9FA9-4BA9-B4D9-6E33035CF648' + f.write('Project("{%s}") = "Tests", "Tests", "{%s}"\n' % + (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_tests_guid)) + f.write('EndProject\n') + f.write('Project("{%s}") = "Libraries", "Libraries", "{%s}"\n' % + (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_libraries_guid)) + f.write('EndProject\n') + f.write('Project("{%s}") = "StaticLibraries", "StaticLibraries", "{%s}"\n' % + (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_static_libraries_guid)) + f.write('EndProject\n') + f.write('Project("{%s}") = "Executables", "Executables", "{%s}"\n' % + (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_executables_guid)) + f.write('EndProject\n') + # end Folders to group tests/libraries/executables f.write('Global\n') platform = 'Win32' f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') for cfg in self.configurations: f.write('\t\t%(cfg)s|%(platform)s = %(cfg)s|%(platform)s\n' % {'cfg': cfg, 'platform': platform}) f.write('\tEndGlobalSection\n') + # Group projects to folders + f.write('\tGlobalSection(NestedProjects) = preSolution\n') + for project in test_projects: + f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_tests_guid)) + for project in library_projects: + f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_libraries_guid)) + for project in static_library_projects: + f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_static_libraries_guid)) + for project in executable_projects: + f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_executables_guid)) + f.write('\tEndGlobalSection\n') + # end Group projects to folders f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') # Specifies project configurations for solution configuration for project in projects: @@ -1034,67 +1041,82 @@ class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): for key, value in defs.items(): define = key if value is not None: - define += '=' + VisualStudioIntegrationGenerator.escapepattern.sub(r'\1', value) + define += '=' + VisualStudioIntegrationGenerator.escapepattern.sub(r'\1', value).replace('""', '"') defines_list.append(define) return defines_list + ns = 'http://schemas.microsoft.com/developer/msbuild/2003' + + @classmethod + def add_objects(cls, objects, root_path, ext, target, parent_node, flags = ''): + for obj in objects: + objfile = os.path.join(root_path, obj) + ext + if os.path.isfile(objfile): + obj_node = ET.SubElement(parent_node, '{%s}ClCompile' % cls.ns, Include=objfile) + if flags: + obj_additional_options_node = ET.SubElement(obj_node, '{%s}AdditionalOptions' % cls.ns) + obj_additional_options_node.text = flags + else: + print('Source %s in project %s does not exist' % (objfile, target.target_name())) + def write_project(self, project_path, target): # See info at http://blogs.msdn.com/b/visualstudio/archive/2010/05/14/a-guide-to-vcxproj-and-props-file-structure.aspx folder = os.path.dirname(project_path) if not os.path.exists(folder): os.makedirs(folder) - project_guid = str(uuid.uuid4()).upper() + project_guid = self.gen_guid('project', target.short_name()) cxxflags = ' '.join(target.cxxflags) - ns = 'http://schemas.microsoft.com/developer/msbuild/2003' - ET.register_namespace('', ns) - proj_node = ET.Element('{%s}Project' % ns, DefaultTargets='Build', ToolsVersion='4.0') - proj_confs_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns, Label='ProjectConfigurations') + cxxclrflags = ' '.join(target.cxxclrflags) + cflags = ' '.join(target.cflags) + ET.register_namespace('', self.ns) + proj_node = ET.Element('{%s}Project' % self.ns, DefaultTargets='Build', ToolsVersion='4.0') + proj_confs_node = ET.SubElement(proj_node, '{%s}ItemGroup' % self.ns, Label='ProjectConfigurations') platform = 'Win32' for configuration in self.configurations: proj_conf_node = ET.SubElement(proj_confs_node, - '{%s}ProjectConfiguration' % ns, + '{%s}ProjectConfiguration' % self.ns, Include='%s|%s' % (configuration, platform)) - conf_node = ET.SubElement(proj_conf_node, '{%s}Configuration' % ns) + conf_node = ET.SubElement(proj_conf_node, '{%s}Configuration' % self.ns) conf_node.text = configuration - platform_node = ET.SubElement(proj_conf_node, '{%s}Platform' % ns) + platform_node = ET.SubElement(proj_conf_node, '{%s}Platform' % self.ns) platform_node.text = platform - globals_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='Globals') - proj_guid_node = ET.SubElement(globals_node, '{%s}ProjectGuid' % ns) + globals_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % self.ns, Label='Globals') + proj_guid_node = ET.SubElement(globals_node, '{%s}ProjectGuid' % self.ns) proj_guid_node.text = '{%s}' % project_guid - proj_keyword_node = ET.SubElement(globals_node, '{%s}Keyword' % ns) + proj_keyword_node = ET.SubElement(globals_node, '{%s}Keyword' % self.ns) proj_keyword_node.text = 'MakeFileProj' - proj_name_node = ET.SubElement(globals_node, '{%s}ProjectName' % ns) + proj_name_node = ET.SubElement(globals_node, '{%s}ProjectName' % self.ns) proj_name_node.text = target.short_name() - ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.Default.props') + ET.SubElement(proj_node, '{%s}Import' % self.ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.Default.props') for configuration in self.configurations: - conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label="Configuration", + conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % self.ns, Label="Configuration", Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform)) # Type of project used by the MSBuild to determine build process, see Microsoft.Makefile.targets - conf_type_node = ET.SubElement(conf_node, '{%s}ConfigurationType' % ns) + conf_type_node = ET.SubElement(conf_node, '{%s}ConfigurationType' % self.ns) conf_type_node.text = 'Makefile' # This defines the version of Visual Studio which can show next to project names in the Solution Explorer - platform_toolset_node = ET.SubElement(conf_node, '{%s}PlatformToolset' % ns) + platform_toolset_node = ET.SubElement(conf_node, '{%s}PlatformToolset' % self.ns) platform_toolset_node.text = self.toolset - ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.props') - ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionSettings') + ET.SubElement(proj_node, '{%s}Import' % self.ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.props') + ET.SubElement(proj_node, '{%s}ImportGroup' % self.ns, Label='ExtensionSettings') for configuration in self.configurations: - prop_sheets_node = ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='Configuration', + prop_sheets_node = ET.SubElement(proj_node, '{%s}ImportGroup' % self.ns, Label='Configuration', Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform)) - ET.SubElement(prop_sheets_node, '{%s}Import' % ns, + ET.SubElement(prop_sheets_node, '{%s}Import' % self.ns, Project='$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props', Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')", Label='LocalAppDataPlatform') - ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='UserMacros') + ET.SubElement(proj_node, '{%s}PropertyGroup' % self.ns, Label='UserMacros') # VS IDE (at least "Peek definition") is allergic to paths like "C:/PROGRA~2/WI3CF2~1/10/Include/10.0.14393.0/um"; see # https://developercommunity.visualstudio.com/content/problem/139659/vc-peek-definition-fails-to-navigate-to-windows-ki.html # We need to convert to long paths here. Do this once, since it's time-consuming operation. include_path_node_text = ';'.join(self.to_long_names(target.include)) for cfg_name, cfg_targets in self.configurations.items(): - conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, + conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % self.ns, Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (cfg_name, platform)) nmake_params = { 'sh': os.path.join(self.gbuildparser.binpath, 'dash.exe'), @@ -1102,64 +1124,41 @@ class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): 'location': target.location, 'makecmd': self.gbuildparser.makecmd, 'target': target.target_name()} - nmake_build_node = ET.SubElement(conf_node, '{%s}NMakeBuildCommandLine' % ns) + nmake_build_node = ET.SubElement(conf_node, '{%s}NMakeBuildCommandLine' % self.ns) nmake_build_node.text = cfg_targets['build'] % nmake_params - nmake_clean_node = ET.SubElement(conf_node, '{%s}NMakeCleanCommandLine' % ns) + nmake_clean_node = ET.SubElement(conf_node, '{%s}NMakeCleanCommandLine' % self.ns) nmake_clean_node.text = cfg_targets['clean'] % nmake_params - nmake_rebuild_node = ET.SubElement(conf_node, '{%s}NMakeReBuildCommandLine' % ns) + nmake_rebuild_node = ET.SubElement(conf_node, '{%s}NMakeReBuildCommandLine' % self.ns) nmake_rebuild_node.text = cfg_targets['rebuild'] % nmake_params - nmake_output_node = ET.SubElement(conf_node, '{%s}NMakeOutput' % ns) - nmake_output_node.text = os.path.join(self.gbuildparser.instdir, 'program', 'soffice.bin') - nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % ns) + nmake_output_node = ET.SubElement(conf_node, '{%s}NMakeOutput' % self.ns) + nmake_output_node.text = os.path.join(self.gbuildparser.workdir, 'LinkTarget', target.link_target) + nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % self.ns) nmake_defs_node.text = ';'.join(self.defs_list(target.defs) + ['$(NMakePreprocessorDefinitions)']) - include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % ns) + nmake_debug_command_node = ET.SubElement(conf_node, '{%s}LocalDebuggerCommand' % self.ns) + nmake_debug_command_node.text = os.path.join(self.gbuildparser.instdir, 'program', 'soffice.bin') + nmake_debug_flavor_node = ET.SubElement(conf_node, '{%s}DebuggerFlavor' % self.ns) + nmake_debug_flavor_node.text = 'WindowsLocalDebugger' + include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % self.ns) include_path_node.text = include_path_node_text - additional_options_node = ET.SubElement(conf_node, '{%s}AdditionalOptions' % ns) + additional_options_node = ET.SubElement(conf_node, '{%s}AdditionalOptions' % self.ns) additional_options_node.text = cxxflags - ET.SubElement(proj_node, '{%s}ItemDefinitionGroup' % ns) + ET.SubElement(proj_node, '{%s}ItemDefinitionGroup' % self.ns) - cxxobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) - for cxxobject in target.cxxobjects: - cxxabspath = os.path.join(self.gbuildparser.srcdir, cxxobject) - cxxfile = cxxabspath + '.cxx' - if os.path.isfile(cxxfile): - ET.SubElement(cxxobjects_node, '{%s}ClCompile' % ns, Include=cxxfile) - else: - print('Source %s in project %s does not exist' % (cxxfile, target.target_name())) - - cobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) - for cobject in target.cobjects: - cabspath = os.path.join(self.gbuildparser.srcdir, cobject) - cfile = cabspath + '.c' - if os.path.isfile(cfile): - ET.SubElement(cobjects_node, '{%s}ClCompile' % ns, Include=cfile) - else: - print('Source %s in project %s does not exist' % (cfile, target.target_name())) - - includes_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) - for cxxobject in target.cxxobjects: - include_abs_path = os.path.join(self.gbuildparser.srcdir, cxxobject) - hxxfile = include_abs_path + '.hxx' - if os.path.isfile(hxxfile): - ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hxxfile) - # Few files have corresponding .h files - hfile = include_abs_path + '.h' - if os.path.isfile(hfile): - ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hfile) - for cobject in target.cobjects: - include_abs_path = os.path.join(self.gbuildparser.srcdir, cobject) - hfile = include_abs_path + '.h' - if os.path.isfile(hfile): - ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hfile) - ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.targets') - ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionTargets') + cxxobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % self.ns) + self.add_objects(target.cxxobjects, self.gbuildparser.srcdir, '.cxx', target, cxxobjects_node) + self.add_objects(target.cxxclrobjects, self.gbuildparser.srcdir, '.cxx', target, cxxobjects_node, cxxclrflags) + + cobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % self.ns) + self.add_objects(target.cobjects, self.gbuildparser.srcdir, '.c', target, cobjects_node, cflags) + + ET.SubElement(proj_node, '{%s}Import' % self.ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.targets') + ET.SubElement(proj_node, '{%s}ImportGroup' % self.ns, Label='ExtensionTargets') self.write_pretty_xml(proj_node, project_path) self.write_filters(project_path + '.filters', os.path.join(self.gbuildparser.srcdir, os.path.basename(target.location)), - [cxx_node.get('Include') for cxx_node in cxxobjects_node.findall('{%s}ClCompile' % ns)], - [c_node.get('Include') for c_node in cobjects_node.findall('{%s}ClCompile' % ns)], - [include_node.get('Include') for include_node in includes_node.findall('{%s}ClInclude' % ns)]) + [cxx_node.get('Include') for cxx_node in cxxobjects_node.findall('{%s}ClCompile' % self.ns)], + [c_node.get('Include') for c_node in cobjects_node.findall('{%s}ClCompile' % self.ns)]) return project_guid def get_filter(self, module_dir, proj_file): @@ -1179,33 +1178,27 @@ class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): f.write(pretty_str.decode()) def add_nodes(self, files_node, module_dir, tag, project_files): - ns = 'http://schemas.microsoft.com/developer/msbuild/2003' filters = set() for project_file in project_files: file_node = ET.SubElement(files_node, tag, Include=project_file) if os.path.commonprefix([module_dir, project_file]) == module_dir: project_filter = self.get_filter(module_dir, project_file) - filter_node = ET.SubElement(file_node, '{%s}Filter' % ns) + filter_node = ET.SubElement(file_node, '{%s}Filter' % self.ns) filter_node.text = project_filter filters |= self.get_subfilters(project_filter) return filters - def write_filters(self, filters_path, module_dir, cxx_files, c_files, include_files): - ns = 'http://schemas.microsoft.com/developer/msbuild/2003' - ET.register_namespace('', ns) - proj_node = ET.Element('{%s}Project' % ns, ToolsVersion='4.0') + def write_filters(self, filters_path, module_dir, cxx_files, c_files): + ET.register_namespace('', self.ns) + proj_node = ET.Element('{%s}Project' % self.ns, ToolsVersion='4.0') filters = set() - compiles_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) - filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, cxx_files) - filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, c_files) - include_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) - filters |= self.add_nodes(include_node, module_dir, '{%s}ClInclude' % ns, include_files) + compiles_node = ET.SubElement(proj_node, '{%s}ItemGroup' % self.ns) + filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % self.ns, cxx_files) + filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % self.ns, c_files) - filters_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) + filters_node = ET.SubElement(proj_node, '{%s}ItemGroup' % self.ns) for proj_filter in filters: - filter_node = ET.SubElement(filters_node, '{%s}Filter' % ns, Include=proj_filter) - filter_id_node = ET.SubElement(filter_node, '{%s}UniqueIdentifier' % ns) - filter_id_node.text = '{%s}' % str(uuid.uuid4()) + filter_node = ET.SubElement(filters_node, '{%s}Filter' % self.ns, Include=proj_filter) self.write_pretty_xml(proj_node, filters_path) @@ -1214,7 +1207,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) self.target_by_location = {} - for target in set(self.gbuildparser.libs) | set(self.gbuildparser.exes) | set(self.gbuildparser.tests): + for target in self.gbuildparser.libs | self.gbuildparser.exes | self.gbuildparser.tests: if target.location not in self.target_by_location: self.target_by_location[target.location] = set() self.target_by_location[target.location] |= set([target]) @@ -1283,7 +1276,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '7', 'base_folder': self.base_folder, - 'arg': "build-nocheck", + 'arg': "", 'name': "8-Global build -- nocheck", } xml += QtCreatorIntegrationGenerator.build_configs_template % { @@ -1342,7 +1335,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '6', 'base_folder': self.base_folder, - 'arg': "build-nocheck", + 'arg': "", 'name': "07-Global build -- nocheck", } xml += QtCreatorIntegrationGenerator.build_configs_template % { @@ -1441,9 +1434,10 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): def generate_run_configs(self, lib_folder): - # If we use 'soffice', it's ok only for "Run", not for "Debug". - # So we put "soffice.bin" that is ok for both. - loexec = "%s/instdir/program/soffice.bin" % self.base_folder + if platform == 'darwin': + loexec = "%s/instdir/LibreOfficeDev.app/Contents/MacOS/soffice" % self.base_folder + else: + loexec = "%s/instdir/program/soffice.bin" % self.base_folder xml = QtCreatorIntegrationGenerator.run_configs_template % { 'loexec': loexec, 'workdir': self.base_folder @@ -1649,7 +1643,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): self.data_libs = {} - all_libs = set(self.gbuildparser.libs) | set(self.gbuildparser.exes) | set(self.gbuildparser.tests) + all_libs = self.gbuildparser.libs | self.gbuildparser.exes | self.gbuildparser.tests for lib in all_libs: self._log("\nlibrary : %s, loc=%s" % (lib.short_name(), lib.location)) lib_name = os.path.basename(lib.location) @@ -1668,6 +1662,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): defines_list = [] sources_list = [] + objcxx_sources_list = [] includepath_list = [] # The explicit headers list is not mandatory : # QtCreator just needs 'include_path_list' to find all headers files. @@ -1675,6 +1670,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): # in a specific "Headers" folder in QtCreator's Project panel. # We will list here only headers files of current lib. headers_list = [] + objcxx_headers_list = [] for file_ in lib.cxxobjects: # the file has no extension : search it # self._log("\n file : %s" % file_) @@ -1687,11 +1683,22 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): if path: headers_list.append(lopath(path)) - cxxflags_list = [] + for file_ in lib.objcxxobjects: + # the file has no extension: search it + path = self.get_source_path(file_) + if path: + objcxx_sources_list.append(lopath(path)) + + # several objcxxobject files have a header beside + path = self.get_header_path(file_) + if path: + objcxx_headers_list.append(lopath(path)) + + cxxstdversionflag = '' for cxxflag in lib.cxxflags: # extract flag for C++ standard version if cxxflag.startswith('-std'): - cxxflags_list.append(cxxflag) + cxxstdversionflag = cxxflag # List all include paths for hdir in (lib.include + lib.include_sys): @@ -1718,14 +1725,18 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): if lib_folder in self.data_libs: self.data_libs[lib_folder]['sources'] |= set(sources_list) self.data_libs[lib_folder]['headers'] |= set(headers_list) - self.data_libs[lib_folder]['cxxflags'] |= set(cxxflags_list) + self.data_libs[lib_folder]['objcxx_sources'] |= set(objcxx_sources_list) + self.data_libs[lib_folder]['objcxx_headers'] |= set(objcxx_headers_list) + self.data_libs[lib_folder]['cxxstdversionflag'] = cxxstdversionflag self.data_libs[lib_folder]['includepath'] |= set(includepath_list) self.data_libs[lib_folder]['defines'] |= set(defines_list) else: self.data_libs[lib_folder] = { 'sources': set(sources_list), 'headers': set(headers_list), - 'cxxflags': set(cxxflags_list), + 'objcxx_sources': set(objcxx_sources_list), + 'objcxx_headers': set(objcxx_headers_list), + 'cxxstdversionflag': cxxstdversionflag, 'includepath': set(includepath_list), 'defines': set(defines_list), 'loc': lib.location, @@ -1748,7 +1759,9 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): for lib_folder in subdirs_list: sources_list = sorted(self.data_libs[lib_folder]['sources']) headers_list = sorted(self.data_libs[lib_folder]['headers']) - cxxflags_list = sorted(self.data_libs[lib_folder]['cxxflags']) + objcxx_sources_list = sorted(self.data_libs[lib_folder]['objcxx_sources']) + objcxx_headers_list = sorted(self.data_libs[lib_folder]['objcxx_headers']) + cxxstdversionflag = self.data_libs[lib_folder]['cxxstdversionflag'] includepath_list = sorted(self.data_libs[lib_folder]['includepath']) defines_list = sorted(self.data_libs[lib_folder]['defines']) lib_loc = self.data_libs[lib_folder]['loc'] @@ -1756,16 +1769,22 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): sources = " \\\n".join(sources_list) headers = " \\\n".join(headers_list) - cxxflags = " \\\n".join(cxxflags_list) + objcxx_sources = " \\\n".join(objcxx_sources_list) + objcxx_headers = " \\\n".join(objcxx_headers_list) includepath = " \\\n".join(includepath_list) defines = " \\\n".join(defines_list) + # strip '-std=' or '-std:' prefix + assert(isinstance(cxxstdversionflag, str) and cxxstdversionflag.startswith('-std')) + cxxstdversion = cxxstdversionflag[5:] # create .pro file - subdirs_meta_pro.append(lib_name) - qt_pro_file = os.path.join(self.base_folder, lib_name, lib_name + '.pro') + relative_subdir_path = os.path.relpath(lib_loc, self.gbuildparser.srcdir) + subdirs_meta_pro.append(relative_subdir_path) + qt_pro_file = os.path.join(self.base_folder, relative_subdir_path, lib_name + '.pro') try: - content = QtCreatorIntegrationGenerator.pro_template % {'sources': sources, 'headers': headers, - 'cxxflags': cxxflags, 'includepath': includepath, 'defines': defines} + content = QtCreatorIntegrationGenerator.pro_template % {'sources': sources, 'headers': headers, 'cxxstdversionflag': cxxstdversionflag, + 'cxxstdversion': cxxstdversion, 'objcxx_sources': objcxx_sources, + 'objcxx_headers': objcxx_headers, 'includepath': includepath, 'defines': defines} with open(qt_pro_file, mode) as fpro: fpro.write(content) self._log("created %s\n" % qt_pro_file) @@ -1778,7 +1797,7 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): print("\n\n", file=sys.stderr) # create .pro.shared file - qt_pro_shared_file = os.path.join(self.base_folder, lib_name, lib_name + '.pro.shared') + qt_pro_shared_file = os.path.join(self.base_folder, relative_subdir_path, lib_name + '.pro.shared') try: with open(qt_pro_shared_file, mode) as fproshared: fproshared.write(self.generate_pro_shared_content(lib_folder)) @@ -1826,8 +1845,9 @@ class QtCreatorIntegrationGenerator(IdeIntegrationGenerator): CONFIG += console CONFIG -= app_bundle CONFIG -= qt +CONFIG += %(cxxstdversion)s -QMAKE_CXXFLAGS += %(cxxflags)s +QMAKE_CXXFLAGS += %(cxxstdversionflag)s INCLUDEPATH += %(includepath)s @@ -1835,6 +1855,10 @@ SOURCES += %(sources)s HEADERS += %(headers)s +OBJECTIVE_SOURCES += %(objcxx_sources)s + +OBJECTIVE_HEADERS += %(objcxx_headers)s + DEFINES += %(defines)s """ |