#! /usr/bin/env python3 # -*- Mode: python; tab-width: 4; indent-tabs-mode: t -*- # # This file is part of the LibreOffice project. # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # import argparse import os import os.path import shutil import re import sys import uuid import json import xml.etree.ElementTree as ET import xml.dom.minidom as minidom import traceback class GbuildLinkTarget: def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs): (self.name, self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.cxxflags, self.linked_libs) = ( name, location, include, include_sys, defs, cxxobjects, cxxflags, 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.linked_libs def __str__(self): return '%s at %s with include path: %s, isystem includes: %s, defines: %s, objects: %s, cxxflags: %s and linked libs: %s' % ( self.short_name(), self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.cxxflags, self.linked_libs) class GbuildLib(GbuildLinkTarget): def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs): GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs) def short_name(self): """Return the short name of target based on the Library_* makefile name""" return 'Library %s' % self.name def short_name(self): """Return the short name of target based on the Library_* makefile name""" return 'Library %s' % self.name def module(self): """Return module name """ return self.location[self.location.rindex('/')+1:] 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, linked_libs): GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs) def short_name(self): """Return the short name of target based n the CppunitTest_* makefile names""" return 'CppunitTest %s' % self.name def module(self): """Return module name """ return self.location[self.location.rindex('/')+1:] def target_name(self): return 'CppunitTest_%s' % self.name class GbuildExe(GbuildLinkTarget): def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs): GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs) def short_name(self): """Return the short name of target based on the Executable_* makefile name""" return 'Executable %s' % self.name def module(self): """Return module name """ return self.location[self.location.rindex('/')+1:] def target_name(self): return 'Executable_%s' % self.name class GbuildParser: 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('-I(\S+)') isystempattern = re.compile('-isystem\s*(\S+)') warningpattern = re.compile('-W\S+') libpattern = re.compile('Library_(.*)\.mk') exepattern = re.compile('Executable_(.*)\.mk') testpattern = re.compile('CppunitTest_(.*)\.mk') @staticmethod def __split_includes(includes): foundisystem = GbuildParser.isystempattern.findall(includes) foundincludes = [includeswitch.strip() for includeswitch in GbuildParser.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 != '+='] @staticmethod def __split_defs(defsline): defs = {} alldefs = [defswitch.strip() for defswitch in defsline.strip().lstrip('-D').split(' -D') if len(defswitch) > 2] for d in alldefs: defparts = d.split('=') if len(defparts) == 1: defparts.append(None) defs[defparts[0]] = defparts[1] defs["LIBO_INTERNAL_ONLY"] = None return defs @staticmethod def __split_flags(flagsline, flagslineappend): return [cxxflag.strip() for cxxflag in GbuildParser.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), os.path.dirname(json['MAKEFILE']), foundincludes, foundisystem, GbuildParser.__split_defs(json['DEFS']), GbuildParser.__split_objs(json['CXXOBJECTS']), GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']), json['LINKED_LIBS'].strip().split(' ')) @staticmethod def __test_from_json(json): (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE']) testname_match = GbuildParser.testpattern.match(os.path.basename(json['MAKEFILE'])) # Workaround strange writer test makefile setup if testname_match is None: testname = "StrangeWriterMakefiles" else: testname = testname_match.group(1) 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']), json['LINKED_LIBS'].strip().split(' ')) @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']), json['LINKED_LIBS'].strip().split(' ')) 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) 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) 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): if target.location not in self.target_by_location: self.target_by_location[target.location] = set() self.target_by_location[target.location] |= set([target]) for cxx in target.cxxobjects: path = '/'.join(cxx.split('/')[:-1]) if path not in self.target_by_path: self.target_by_path[path] = set() self.target_by_path[path] |= set([target]) for path in self.target_by_path: x = self.target_by_path[path] if path != '' and len(set(self.target_by_path[path])) > 1: print('fdo#70422: multiple target use dir %s: %s' % ( path, ', '.join([target.short_name() for target in set(self.target_by_path[path])]))) for location in self.target_by_location: self.modulenamelist.append(os.path.split(location)[1]) return self class IdeIntegrationGenerator: def __init__(self, gbuildparser, ide): self.gbuildparser = gbuildparser self.ide = ide def emit(self): pass class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator): def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) def create_include_paths(self): for module in self.gbuildparser.modulenamelist: modulepath = os.path.join(self.gbuildparser.builddir, module) includedirfile = open(os.path.join(modulepath, '.eclipsesettingfile'), 'w') modulelibs = [] for lib in self.gbuildparser.target_by_path.keys(): if lib.startswith(module+'/'): modulelibs.append(lib) include = set() for lib in modulelibs: for target in self.gbuildparser.target_by_path[lib]: include |= set(target.include) includedirfile.write('\n'.join(include)) includedirfile.close() def create_macros(self): for module in self.gbuildparser.modulenamelist: modulepath = os.path.join(self.gbuildparser.builddir, module) macrofile = open(os.path.join(modulepath, '.macros'), 'w') modulelibs = [] for lib in self.gbuildparser.target_by_path.keys(): if lib.startswith(module+'/'): modulelibs.append(lib) define = [] defineset = set() for lib in modulelibs: for target in self.gbuildparser.target_by_path[lib]: for i in target.defs.keys(): tmp = str(i) +','+str(target.defs[i]) if tmp not in defineset: defineset.add(tmp) macrofile.write('\n'.join(defineset)) macrofile.close() def create_settings_file(self): settingsfiletemplate = """\
""" for module in self.gbuildparser.modulenamelist: tempxml = [] modulepath = os.path.join(self.gbuildparser.builddir, module) settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'w') settingsfile.write(settingsfiletemplate) settingsfile.close() settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'r') tempxml = settingsfile.readlines() tempinclude = open(os.path.join(modulepath, '.eclipsesettingfile'), 'r') tempmacro = open(os.path.join(modulepath, '.macros'), 'r') for includepath in tempinclude: if includepath[-1:] == "\n": includepath = includepath[:-1] templine = "%s\n" % includepath tempxml.insert(5, templine) for line in tempmacro: macroskeyvalue = line.split(',') macrokey = macroskeyvalue[0] macrovalue = macroskeyvalue[1] if macrovalue[-1:] == "\n": macrovalue = macrovalue[:-1] templine = "%s%s\n" %(macrokey, macrovalue) tempxml.insert(-13, templine) tempxml="".join(tempxml) settingsfile.close settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'w') settingsfile.write(tempxml) settingsfile.close() os.remove(os.path.join(modulepath, '.eclipsesettingfile')) os.remove(os.path.join(modulepath, '.macros')) def emit(self): self.create_include_paths() self.create_macros() self.create_settings_file() class DebugIntegrationGenerator(IdeIntegrationGenerator): def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) def emit(self): print(self.gbuildparser.srcdir) print(self.gbuildparser.builddir) for lib in self.gbuildparser.libs: print(lib) for exe in self.gbuildparser.exes: print(exe) for test in self.gbuildparser.tests: print(test) class VimIntegrationGenerator(IdeIntegrationGenerator): def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) def emit(self): global_list = [] for lib in set(self.gbuildparser.libs) | set(self.gbuildparser.tests): 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) export_file = open('compile_commands.json', 'w') json.dump(global_list, export_file) def generateCommand(self, lib, file): command = 'clang++ -Wall' for key, value in lib.defs.items(): command += ' -D' command += key if value is not None: command += '=' command += value # The directory of the file is missing from lib's include list, as that # one is not the same for all source files in the lib. command += ' -I' + os.path.dirname(file) for include in lib.include: command += ' -I' command += include for isystem in lib.include_sys: command += ' -isystem ' command += isystem for cxxflag in lib.cxxflags: command += ' ' command += cxxflag command += ' -c ' command += file # Help clang when the tree is configured for gcc. for gnu in ('-std=gnu++11', '-std=gnu++1y'): command = command.replace(gnu, '-std=c++11') return command class KdevelopIntegrationGenerator(IdeIntegrationGenerator): def encode_int(self, i): temp = '%08x' % i return '\\x%s\\x%s\\x%s\\x%s' % (temp[0:2], temp[2:4], temp[4:6], temp[6:8]) def encode_string(self, string): result = self.encode_int(len(string) * 2) for c in string.encode('utf-16-be'): if c in range(32, 126): result += chr(c) else: result += '\\x%02x' % c return result def generate_buildsystemconfigtool(self, configid, tool, args, exe, typenr): return KdevelopIntegrationGenerator.buildsystemconfigtooltemplate % {'configid': configid, 'tool': tool, 'args': args, 'exe': exe, 'typenr': typenr} buildsystemconfigtooltemplate = """ [CustomBuildSystem][BuildConfig%(configid)d][Tool%(tool)s] Arguments=%(args)s Enabled=true Environment= Executable=%(exe)s Type=%(typenr)d """ def generate_buildsystemconfig(self, configid, moduledir, builddir, title, buildparms=''): result = KdevelopIntegrationGenerator.buildsystemconfigtemplate % {'configid': configid, 'builddir': builddir, 'title': title} result += self.generate_buildsystemconfigtool(configid, 'Clean', 'clean %s' % buildparms, self.gbuildparser.makecmd, 3) result += self.generate_buildsystemconfigtool(configid, 'Build', 'all %s' % buildparms, self.gbuildparser.makecmd, 0) return result buildsystemconfigtemplate = """ [CustomBuildSystem][BuildConfig%(configid)d] BuildDir=file://%(builddir)s Title=%(title)s """ def generate_buildsystem(self, moduledir): result = KdevelopIntegrationGenerator.buildsystemtemplate % {'defaultconfigid': 0} result += self.generate_buildsystemconfig(0, moduledir, moduledir, 'Module Build -- Release') result += self.generate_buildsystemconfig(1, moduledir, self.gbuildparser.builddir, 'Full Build -- Release') result += self.generate_buildsystemconfig(2, moduledir, moduledir, 'Module Build -- Debug', 'debug=T') result += self.generate_buildsystemconfig(3, moduledir, self.gbuildparser.builddir, 'Full Build -- Debug', 'debug=T') return result buildsystemtemplate = """ [CustomBuildSystem] CurrentConfiguration=BuildConfig%(defaultconfigid)d """ def generate_launch(self, launchid, launchname, executablepath, args, workdir): return KdevelopIntegrationGenerator.launchtemplate % {'launchid': launchid, 'launchname': launchname, 'executablepath': executablepath, 'args': args, 'workdir': workdir} launchtemplate = """ [Launch][Launch Configuration %(launchid)d] Configured Launch Modes=execute Configured Launchers=nativeAppLauncher Name=%(launchname)s Type=Native Application [Launch][Launch Configuration %(launchid)d][Data] Arguments=%(args)s Dependencies=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x00) Dependency Action=Nothing EnvironmentGroup=default Executable=file://%(executablepath)s External Terminal=konsole --noclose --workdir %%workdir -e %%exe Project Target= Use External Terminal=false Working Directory=file://%(workdir)s isExecutable=true """ def generate_launches(self, moduledir): launches = ','.join(['Launch Configuration %d' % i for i in range(7)]) result = KdevelopIntegrationGenerator.launchestemplate % {'launches': launches} result += self.generate_launch(0, 'Local tests -- quick tests (unitcheck)', self.gbuildparser.makecmd, 'unitcheck', moduledir) result += self.generate_launch(1, 'Local tests -- slow tests (unitcheck, slowcheck, screenshot)', self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot', moduledir) result += self.generate_launch(2, 'Local tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)', self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot subsequentcheck', moduledir) result += self.generate_launch(3, 'Global tests -- quick tests (unitcheck)', self.gbuildparser.makecmd, 'unitcheck', self.gbuildparser.builddir) result += self.generate_launch(4, 'Global tests -- slow tests (unitcheck, slowcheck, screenshot)', self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot', self.gbuildparser.builddir) result += self.generate_launch(5, 'Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)', self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot subsequentcheck', self.gbuildparser.builddir) result += self.generate_launch(6, 'Run LibreOffice', os.path.join(self.gbuildparser.instdir, 'program/soffice.bin'), '', self.gbuildparser.instdir) return result launchestemplate = """ [Launch] Launch Configurations=%(launches)s """ def write_modulebeef(self, moduledir, modulename): beefdir = os.path.join(moduledir, '.kdev4') os.mkdir(beefdir) beeffile = open(os.path.join(beefdir, 'Module_%s.kdev4' % modulename), 'w') beeffile.write(self.generate_buildsystem(moduledir)) beeffile.write(self.generate_launches(moduledir)) beeffile.close() def write_modulestub(self, moduledir, modulename): stubfile = open(os.path.join(moduledir, 'Module_%s.kdev4' % modulename), 'w') stubfile.write(KdevelopIntegrationGenerator.modulestubtemplate % {'modulename': modulename, 'builditem': self.encode_string( 'Module_%s' % modulename)}) stubfile.close() modulestubtemplate = """ [Buildset] BuildItems=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0b\\x00\\x00\\x00\\x00\\x01%(builditem)s) [Project] Name=Module_%(modulename)s Manager=KDevCustomBuildSystem VersionControl=kdevgit """ def write_includepaths(self, path): includedirfile = open(os.path.join(path, '.kdev_include_paths'), 'w') include = set() for target in self.gbuildparser.target_by_path[path]: include |= set(target.include) includedirfile.write('\n'.join(include)) includedirfile.close() def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) def emit(self): for path in self.gbuildparser.target_by_path: self.write_includepaths(path) for location in self.gbuildparser.target_by_location: for f in os.listdir(location): if f.endswith('.kdev4'): try: os.remove(os.path.join(location, f)) except OSError: shutil.rmtree(os.path.join(location, f)) for location in self.gbuildparser.target_by_location: modulename = os.path.split(location)[1] self.write_modulestub(location, modulename) self.write_modulebeef(location, modulename) class XcodeIntegrationGenerator(IdeIntegrationGenerator): def indent(self, file, level): if level == 0: return for i in range(0, level): file.write('\t') def write_object(self, object, file, indent): if isinstance(object, int): file.write('%d' % object) elif isinstance(object, str): if object == '': file.write('""') elif not re.search('[^A-Za-z0-9_]', object): file.write('%s' % object) else: file.write('"%s"' % object) elif isinstance(object, dict): self.write_dict(object, file, indent) elif isinstance(object, list): self.write_list(object, file, indent) elif isinstance(object, GbuildTest): file.write('""') elif isinstance(object, GbuildLib): file.write('""') elif isinstance(object, GbuildExe): file.write('""') # Write a dictionary out as an "old-style (NeXT) ASCII plist" def write_dict(self, dict, file, indent): file.write('{') file.write('\n') for key in sorted(dict.keys()): self.indent(file, indent + 1) file.write('%s = ' % key) self.write_object(dict[key], file, indent + 1) file.write(';\n') self.indent(file, indent) file.write('}') def write_list(self, list, file, indent): file.write('(') for key in list: self.write_object(key, file, 1) file.write(',') file.write(')') def write_dict_to_plist(self, dict, file): file.write('// !$*UTF8*$!\n') self.write_dict(dict, file, 0) def get_product_type(self, modulename): if modulename in self.gbuildparser.libs: return 'com.apple.product-type.library.dynamic' elif modulename in self.gbuildparser.exes: return 'com.apple.product-type.something' else: return 'com.apple.product-type.cppunit' counter = 0 def generate_id(self): XcodeIntegrationGenerator.counter = XcodeIntegrationGenerator.counter + 1 return str('X%07x' % XcodeIntegrationGenerator.counter) def generate_build_phases(self, modulename): result = [self.sourcesBuildPhaseId] return result def generate_root_object(self, modulename): result = {'attributes': {'LastUpgradeCheck': '0500', 'ORGANIZATIONNAME': 'LibreOffice', 'TargetAttributes' : {self.targetId : {'CreatedOnToolsVersion' : '8.2', 'ProvisioningStyle' : 'Automatic'}}}, 'compatibilityVersion': 'Xcode 3.2', 'developmentRegion': 'English', 'isa': 'PBXProject', 'hasScannedForEncodings': 0, 'knownRegions': ['en'], 'mainGroup': self.mainGroupId, 'productRefGroup': self.productGroupId, 'projectDirPath': '', 'projectRoot': '', 'buildConfigurationList': self.configurationListId, 'targets': [self.targetId]} return result def generate_ref_target(self, modulename): result = {'isa': 'PBXFileReference', 'explicitFileType': 'compiled.mach-o.executable', 'includeInIndex': 0, 'path': 'target', 'sourceTree': 'BUILT_PRODUCTS_DIR'} return result def generate_target(self, modulename): result = {'isa': 'PBXNativeTarget', 'buildConfigurationList': self.configurationListId, 'buildPhases': self.generate_build_phases(modulename), 'buildRules': [], 'dependencies': [], 'name': 'target', # modulename, 'productName': 'target', # modulename, 'productReference': self.targetRefId, 'productType': self.get_product_type(modulename)} return result def generate_configuration_debug(self, modulename): result = {'isa': 'XCBuildConfiguration', 'buildSettings': { 'ALWAYS_SEARCH_USER_PATHS': 'NO', 'CLANG_ANALYZER_NONNULL': 'YES', 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++0x', 'CLANG_CXX_LIBRARY': 'libc++', 'CLANG_ENABLE_MODULES': 'YES', 'CLANG_ENABLE_OBJC_ARC': 'YES', 'CLANG_WARN_BOOL_CONVERSION': 'YES', 'CLANG_WARN_CONSTANT_CONVERSION': 'YES', 'CLANG_WARN_DIRECT_OBJC_ISA_USAGE': 'YES_ERROR', 'CLANG_WARN_DOCUMENTATION_COMMENTS': 'YES', 'CLANG_WARN_EMPTY_BODY': 'YES', 'CLANG_WARN_ENUM_CONVERSION': 'YES', 'CLANG_WARN_INFINITE_RECURSION': 'YES', 'CLANG_WARN_INT_CONVERSION': 'YES', 'CLANG_WARN_OBJC_ROOT_CLASS': 'YES_ERROR', 'CLANG_WARN_SUSPICIOUS_MOVE': 'YES', 'CLANG_WARN_UNREACHABLE_CODE': 'YES', 'CLANG_WARN__DUPLICATE_METHOD_MATCH': 'YES', 'CODE_SIGN_IDENTITY': '-', 'COPY_PHASE_STRIP': 'NO', 'DEBUG_INFORMATION_FORMAT': 'dwarf', 'ENABLE_STRICT_OBJC_MSGSEND': 'YES', 'ENABLE_TESTABILITY': 'YES', 'GCC_C_LANGUAGE_STANDARD': 'gnu99', 'GCC_DYNAMIC_NO_PIC': 'NO', 'GCC_NO_COMMON_BLOCKS': 'YES', 'GCC_OPTIMIZATION_LEVEL': 0, 'GCC_PREPROCESSOR_DEFINITIONS': [ 'DEBUG=1', '$(inherited)'], 'GCC_WARN_64_TO_32_BIT_CONVERSION': 'YES', 'GCC_WARN_ABOUT_RETURN_TYPE': 'YES_ERROR', 'GCC_WARN_UNDECLARED_SELECTOR': 'YES', 'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE', 'GCC_WARN_UNUSED_FUNCTION': 'YES', 'GCC_WARN_UNUSED_VARIABLE': 'YES', 'MACOSX_DEPLOYMENT_TARGET': '10.12', 'MTL_ENABLE_DEBUG_INFO': 'YES', 'ONLY_ACTIVE_ARCH': 'YES', 'PRODUCT_NAME': '$(TARGET_NAME)', 'SDKROOT': 'macosx'}, 'name': 'Debug'} return result def generate_configuration_list(self, modulename): result = {'isa': 'XCConfigurationList', 'buildConfigurations': [self.configurationDebugId], 'defaultConfigurationIsVisible': 0, 'defaultConfigurationName': 'Debug'} return result def generate_main_group(self, modulename): result = {'isa': 'PBXGroup', 'children': [self.subMainGroupId, self.productGroupId], 'sourceTree': ''} return result def generate_sub_main_children(self, modulename): return list(self.sourceRefList.keys()) def generate_sub_main_group(self, modulename): try: name = modulename.module() except: name = 'target' result = {'isa': 'PBXGroup', 'children': self.generate_sub_main_children(modulename), 'name': name, 'path': modulename, 'sourceTree': ''} return result def generate_product_group(self, modulename): result = {'isa': 'PBXGroup', 'children': [self.productReferenceId], 'name': 'Products', 'sourceTree': ''} return result def build_source_list(self, module): self.sourceRefList = {} self.sourceList = {} for i in module.cxxobjects: ref = self.generate_id() self.sourceList[self.generate_id()] = ref self.sourceRefList[ref] = {'lastKnownFileType': 'sourcecode.cpp.cpp', 'path': i + '.cxx', 'sourceTree': ''} def generate_sources_build_phase(self, modulename): result = {'isa': 'PBXSourcesBuildPhase', 'buildActionMask': 2147483647, 'files': list(self.sourceList.keys()), 'runOnlyForDeploymentPostprocessing': 0} return result def generate_project(self, target): self.rootObjectId = self.generate_id() self.mainGroupId = self.generate_id() self.subMainGroupId = self.generate_id() self.productReferenceId = self.generate_id() self.productRefGroupId = self.generate_id() self.productGroupId = self.generate_id() self.targetId = self.generate_id() self.targetRefId = self.generate_id() self.build_source_list(target) self.sourcesBuildPhaseId = self.generate_id() self.configurationListId = self.generate_id() self.configurationDebugId = self.generate_id() objects = {self.rootObjectId: self.generate_root_object(target), self.targetId: self.generate_target(target), self.targetRefId: self.generate_ref_target(target), self.mainGroupId: self.generate_main_group(target), self.subMainGroupId: self.generate_sub_main_group(target), self.productGroupId: self.generate_product_group(target), self.sourcesBuildPhaseId: self.generate_sources_build_phase(target), self.configurationListId: self.generate_configuration_list(target), self.configurationDebugId: self.generate_configuration_debug(target) } for i in self.sourceList.keys(): ref = self.sourceList[i] objects[i] = {'isa': 'PBXBuildFile', 'fileRef': ref} path = self.sourceRefList[ref]['path'] try: name = path[path.rindex('/')+1:] except: name = path objects[ref] = {'isa': 'PBXFileReference', 'lastKnownFileType': self.sourceRefList[ref]['lastKnownFileType'], 'path': '../' + path, 'name': name, 'sourceTree': ''} project = {'archiveVersion': 1, 'classes': {}, 'objectVersion': 46, 'objects': objects, 'rootObject': self.rootObjectId} return project # For some reverse-engineered documentation on the project.pbxproj format, # see http://www.monobjc.net/xcode-project-file-format.html . def write_xcodeproj(self, moduledir, target): xcodeprojdir = os.path.join(moduledir, '%s.xcodeproj' % target.target_name()) try: os.mkdir(xcodeprojdir) except: pass self.write_dict_to_plist(self.generate_project(target), open(os.path.join(xcodeprojdir, 'project.pbxproj'), 'w')) def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) def emit(self): self.rootlocation = './' for location in self.gbuildparser.target_by_location: # module = location.split('/')[-1] # module_directory = os.path.join(self.rootlocation, module) for target in self.gbuildparser.target_by_location[location]: # project_path = os.path.join(module_directory, '%s.pbxroj' % target.target_name()) self.write_xcodeproj(location, target) class VisualStudioIntegrationGenerator(IdeIntegrationGenerator): def __init__(self, gbuildparser, ide): IdeIntegrationGenerator.__init__(self, gbuildparser, ide) self.toolset = self.retrieve_toolset(ide) self.solution_directory = './' self.configurations = { 'Build': { 'build': self.module_make_command('%(target)s'), 'clean': self.module_make_command('%(target)s.clean'), 'rebuild': self.module_make_command('%(target)s.clean %(target)s') }, 'Unit Tests': { 'build': self.module_make_command('unitcheck'), 'clean': self.module_make_command('clean'), 'rebuild': self.module_make_command('clean unitcheck'), }, 'Integration tests': { 'build': self.module_make_command('unitcheck slowcheck screenshot subsequentcheck'), 'clean': self.module_make_command('clean'), 'rebuild': self.module_make_command('clean unitcheck slowcheck screenshot subsequentcheck') } } def retrieve_toolset(self, ide): ide_toolset_map = {'vs2013': 'v120', 'vs2015': 'v140'} return ide_toolset_map[ide] def module_make_command(self, targets): return '%(sh)s -c "PATH=\\"/bin:$PATH\\";BUILDDIR=\\"%(builddir)s\\" %(makecmd)s -rsC %(location)s ' + targets + '"' class Project: def __init__(self, guid, target, project_path): self.guid = guid self.target = target self.path = project_path def emit(self): all_projects = [] for location in self.gbuildparser.target_by_location: projects = [] module = location.split('/')[-1] module_directory = os.path.join(self.solution_directory, module) for target in self.gbuildparser.target_by_location[location]: 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) projects.append(p) self.write_solution(os.path.join(module_directory, '%s.sln' % module), projects) all_projects += projects self.write_solution(os.path.join(self.solution_directory, 'LibreOffice.sln'), all_projects) nmake_project_guid = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942' 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: 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='') library_projects = [project for project in projects if project.target in self.gbuildparser.libs] with open(solution_path, 'w') as f: f.write('Microsoft Visual Studio Solution File, Format Version 12.00\n') for project in projects: target = project.target print(' %s' % target.target_name(), end='') proj_path = os.path.relpath(project.path, os.path.abspath(os.path.dirname(solution_path))) f.write('Project("{%s}") = "%s", "%s", "{%s}"\n' % (VisualStudioIntegrationGenerator.nmake_project_guid, target.short_name(), proj_path, project.guid)) libs_in_solution = self.get_dependency_libs(target.linked_libs, 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('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') f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') # Specifies project configurations for solution configuration for project in projects: for cfg in self.configurations: params = {'guid': project.guid, 'sol_cfg': cfg, 'proj_cfg': cfg, 'platform': platform} f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.ActiveCfg = %(proj_cfg)s|%(platform)s\n' % params) # Build.0 is basically 'Build checkbox' in configuration manager f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.Build.0 = %(proj_cfg)s|%(platform)s\n' % params) f.write('\tEndGlobalSection\n') f.write('EndGlobal\n') print('') 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() 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') platform = 'Win32' for configuration in self.configurations: proj_conf_node = ET.SubElement(proj_confs_node, '{%s}ProjectConfiguration' % ns, Include='%s|%s' % (configuration, platform)) conf_node = ET.SubElement(proj_conf_node, '{%s}Configuration' % ns) conf_node.text = configuration platform_node = ET.SubElement(proj_conf_node, '{%s}Platform' % 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) proj_guid_node.text = '{%s}' % project_guid proj_keyword_node = ET.SubElement(globals_node, '{%s}Keyword' % ns) proj_keyword_node.text = 'MakeFileProj' proj_name_node = ET.SubElement(globals_node, '{%s}ProjectName' % ns) proj_name_node.text = target.short_name() ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props') for configuration in self.configurations: conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % 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.text = 'Makefile' # VS2012: I need to have this otherwise the names of projects will contain text Visual Studio 2010 in the Solution Explorer platform_toolset_node = ET.SubElement(conf_node, '{%s}PlatformToolset' % 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') for configuration in self.configurations: prop_sheets_node = ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='Configuration', Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform)) ET.SubElement(prop_sheets_node, '{%s}Import' % 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') for cfg_name, cfg_targets in self.configurations.items(): conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (cfg_name, platform)) nmake_params = { 'sh': os.path.join(self.gbuildparser.binpath, 'dash.exe'), 'builddir': self.gbuildparser.builddir, 'location': target.location, 'makecmd': self.gbuildparser.makecmd, 'target': target.target_name()} nmake_build_node = ET.SubElement(conf_node, '{%s}NMakeBuildCommandLine' % ns) nmake_build_node.text = cfg_targets['build'] % nmake_params nmake_clean_node = ET.SubElement(conf_node, '{%s}NMakeCleanCommandLine' % ns) nmake_clean_node.text = cfg_targets['clean'] % nmake_params nmake_rebuild_node = ET.SubElement(conf_node, '{%s}NMakeReBuildCommandLine' % 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.exe') nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % ns) nmake_defs_node.text = ';'.join(list(target.defs) + ['$(NMakePreprocessorDefinitions)']) include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % ns) include_path_node.text = ';'.join(target.include + ['$(IncludePath)']) ET.SubElement(proj_node, '{%s}ItemDefinitionGroup' % 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())) 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) ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.targets') ET.SubElement(proj_node, '{%s}ImportGroup' % 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)], [include_node.get('Include') for include_node in includes_node.findall('{%s}ClInclude' % ns)]) return project_guid def get_filter(self, module_dir, proj_file): return '\\'.join(os.path.relpath(proj_file, module_dir).split('/')[:-1]) def get_subfilters(self, proj_filter): parts = proj_filter.split('\\') subfilters = set([proj_filter]) for i in range(1, len(parts)): subfilters.add('\\'.join(parts[:i])) return subfilters def write_pretty_xml(self, node, file_path): xml_str = ET.tostring(node, encoding='unicode') pretty_str = minidom.parseString(xml_str).toprettyxml(encoding='utf-8') with open(file_path, 'w') as f: 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.text = project_filter filters |= self.get_subfilters(project_filter) return filters def write_filters(self, filters_path, module_dir, compile_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') filters = set() compiles_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, compile_files) include_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns) filters |= self.add_nodes(include_node, module_dir, '{%s}ClInclude' % ns, include_files) filters_node = ET.SubElement(proj_node, '{%s}ItemGroup' % 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()) self.write_pretty_xml(proj_node, filters_path) 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): if target.location not in self.target_by_location: self.target_by_location[target.location] = set() self.target_by_location[target.location] |= set([target]) self._do_log = False # set to 'True' to activate log of QtCreatorIntegrationGenerator if self._do_log: qtlog_path = os.path.abspath('../qtlog_.txt') self.qtlog = open(qtlog_path, 'w') def _log(self, message): if self._do_log: self.qtlog.write(message) def log_close(self): if self._do_log: self.qtlog.close() def generate_build_configs(self, lib_folder): module_folder = os.path.join(self.base_folder, lib_folder) xml = "" # In QtCreator UI, build configs are listed alphabetically, # so it can be different from the creation order. # So we prefix the names with the index. xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '0', 'base_folder': module_folder, 'arg': "", 'name': "1-Build %s" % lib_folder, } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '1', 'base_folder': module_folder, 'arg': "unitcheck", 'name': "2-Local tests -- quick tests (unitcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '2', 'base_folder': module_folder, 'arg': "unitcheck slowcheck screenshot", 'name': "3-Local tests -- slow tests (unitcheck, slowcheck, screenshot)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '3', 'base_folder': module_folder, 'arg': "unitcheck slowcheck screenshot subsequentcheck", 'name': "4-Local tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '4', 'base_folder': self.base_folder, 'arg': "unitcheck", 'name': "5-Global tests -- quick tests (unitcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '5', 'base_folder': self.base_folder, 'arg': "unitcheck slowcheck screenshot", 'name': "6-Global tests -- slow tests (unitcheck, slowcheck, screenshot)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '6', 'base_folder': self.base_folder, 'arg': "unitcheck slowcheck screenshot subsequentcheck", 'name': "7-Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '7', 'base_folder': self.base_folder, 'arg': "build-nocheck", 'name': "8-Global build -- nocheck", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '8', 'base_folder': self.base_folder, 'arg': "", 'name': "9-Global build", } xml += QtCreatorIntegrationGenerator.build_configs_count_template % { 'nb': '9', } return xml def generate_meta_build_configs(self): xml = "" # In QtCreator UI, build configs are listed alphabetically, # so it can be different from the creation order. # So we prefix the names with the index. xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '0', 'base_folder': self.base_folder, 'arg': "", 'name': "01-Global Build", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '1', 'base_folder': self.base_folder, 'arg': "unitcheck", 'name': "02-Global tests -- quick tests (unitcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '2', 'base_folder': self.base_folder, 'arg': "unitcheck slowcheck screenshot", 'name': "03-Global tests -- slow tests (unitcheck, slowcheck, screenshot)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '3', 'base_folder': self.base_folder, 'arg': "unitcheck slowcheck screenshot subsequentcheck", 'name': "04-Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '4', 'base_folder': self.base_folder, 'arg': "perfcheck", 'name': "05-Global tests -- performance tests (perfcheck)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '5', 'base_folder': self.base_folder, 'arg': "check", 'name': "06-Global tests -- tests (check)", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '6', 'base_folder': self.base_folder, 'arg': "build-nocheck", 'name': "07-Global build -- nocheck", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '7', 'base_folder': self.base_folder, 'arg': "build-l10n-only", 'name': "08-Global build -- build-l10n-only", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '8', 'base_folder': self.base_folder, 'arg': "build-non-l10n-only", 'name': "09-Global build -- build-non-l10n-only", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '9', 'base_folder': self.base_folder, 'arg': "clean", 'name': "10-Global build -- clean", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '10', 'base_folder': self.base_folder, 'arg': "clean-build", 'name': "11-Global build -- clean-build", } xml += QtCreatorIntegrationGenerator.build_configs_template % { 'index': '11', 'base_folder': self.base_folder, 'arg': "clean-host", 'name': "12-Global build -- clean-host", } xml += QtCreatorIntegrationGenerator.build_configs_count_template % { 'nb': '12', } return xml # By default, QtCreator creates 2 BuildStepList : "Build" et "Clean" # but the "clean" can be empty. build_configs_template = """ %(base_folder)s true Make Qt4ProjectManager.MakeStep -w -r false %(arg)s 1 Build ProjectExplorer.BuildSteps.Build 1 false %(name)s Qt4ProjectManager.Qt4BuildConfiguration %(index)s true """ build_configs_count_template = """ %(nb)s """ def generate_deploy_configs(self, lib_folder): xml = QtCreatorIntegrationGenerator.deploy_configs_template % {} return xml deploy_configs_template = """ 0 Deploy ProjectExplorer.BuildSteps.Deploy 1 Deploy locally ProjectExplorer.DefaultDeployConfiguration 1 """ 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 xml = QtCreatorIntegrationGenerator.run_configs_template % { 'loexec': loexec, 'workdir': self.base_folder } return xml run_configs_template = """ false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 %(loexec)s false %(workdir)s Run libreoffice/instdir/program/soffice ProjectExplorer.CustomExecutableRunConfiguration 3768 false true false false true 1 """ def generate_pro_user_content(self, lib_folder): build_configs = self.generate_build_configs(lib_folder) deploy_configs = self.generate_deploy_configs(lib_folder) run_configs = self.generate_run_configs(lib_folder) xml = QtCreatorIntegrationGenerator.pro_user_template % { 'build_configs': build_configs, 'deploy_configs': deploy_configs, 'run_configs': run_configs, } return xml def generate_meta_pro_user_content(self): build_configs = self.generate_meta_build_configs() deploy_configs = self.generate_deploy_configs("") run_configs = self.generate_run_configs("") xml = QtCreatorIntegrationGenerator.pro_user_template % { 'build_configs': build_configs, 'deploy_configs': deploy_configs, 'run_configs': run_configs, } return xml pro_user_template = """ ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings true false true Cpp CppGlobal QmlJS QmlJSGlobal 2 UTF-8 false 4 false 80 true true 1 true false 1 true 0 8 true 1 true true true false ProjectExplorer.Project.PluginSettings ProjectExplorer.Project.Target.0 Desktop Desktop {0701de51-c96e-4e4f-85c3-e70b223c5076} 0 0 0 %(build_configs)s %(deploy_configs)s %(run_configs)s ProjectExplorer.Project.TargetCount 1 ProjectExplorer.Project.Updater.EnvironmentId {5abcafed-86f6-49f6-b1cb-380fadd21211} ProjectExplorer.Project.Updater.FileVersion 15 """ def remove_qt_files(self): def do_remove_file(loc, afile): try: os.remove(os.path.join(loc, afile)) self._log("removed %s\n" % afile) except OSError: self._log("unable to remove %s\n" % afile) do_remove_file(self.base_folder, "lo.pro") do_remove_file(self.base_folder, "lo.pro.user") for location in self.target_by_location: for f in os.listdir(location): if f.endswith('.pro') or f.endswith('.pro.user'): do_remove_file(location, f) def get_source_extension(self, src_file): path = os.path.join(self.base_folder, src_file) for ext in (".cxx", ".cpp", ".c", ".mm"): if os.path.isfile(path + ext): return ext return "" def get_header_extension(self, src_file): path = os.path.join(self.base_folder, src_file) for ext in (".hxx", ".hpp", ".h"): if os.path.isfile(path + ext): return ext return "" def build_data_libs(self): self.data_libs = {} all_libs = set(self.gbuildparser.libs) | set(self.gbuildparser.exes) for lib in all_libs: self._log("\nlibrary : %s, loc=%s" % (lib.short_name(), lib.location)) lib_name = os.path.basename(lib.location) lib_folder = os.path.relpath(lib.location, self.base_folder) def lopath(path): return os.path.relpath(path, lib.location) defines_list = [] sources_list = [] includepath_list = [] # The explicit headers list is not mandatory : # QtCreator just needs 'include_path_list' to find all headers files. # But files listed in 'header_list' will be shown # in a specific "Headers" folder in QtCreator's Project panel. # We will list here only headers files of current lib. headers_list = [] for file_ in lib.cxxobjects: # the file has no extension : search it # self._log("\n file : %s" % file_) ext = self.get_source_extension(file_) if ext: sources_list.append(lopath(file_ + ext)) # few cxxobject files have a header beside ext = self.get_header_extension(file_) if ext: headers_list.append(lopath(file_ + ext)) # List all include paths for hdir in lib.include: hf_lopath = lopath(hdir) includepath_list.append(hf_lopath) # List headers files from current lib for hdir in lib.include: if hdir.startswith(lib.location): for hf in os.listdir(hdir): if hf.endswith(('.h', '.hxx', '.hpp', '.hrc')): hf_lopath = lopath(os.path.join(hdir, hf)) headers_list.append(hf_lopath) # List defines for key, value in lib.defs.items(): define = key if value is not None: define += '=' + value defines_list.append(define) # All datas are prepared, store them for the lib. 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]['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), 'includepath': set(includepath_list), 'defines': set(defines_list), 'loc': lib.location, 'name': lib_name } def emit(self): self.base_folder = self.gbuildparser.builddir # we remove existing '.pro' and '.pro.user' files self.remove_qt_files() # for .pro files, we must explicitly list all files (.c, .h) # so we can't reuse directly the same method than for kde integration. self.build_data_libs() subdirs_list = self.data_libs.keys() # Now we can create Qt files for lib_folder in subdirs_list: sources_list = sorted(self.data_libs[lib_folder]['sources']) headers_list = sorted(self.data_libs[lib_folder]['headers']) 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'] lib_name = self.data_libs[lib_folder]['name'] sources = " \\\n".join(sources_list) headers = " \\\n".join(headers_list) includepath = " \\\n".join(includepath_list) defines = " \\\n".join(defines_list) # create .pro file qt_pro_file = '%s/%s.pro' % (lib_loc, lib_name) try: content = QtCreatorIntegrationGenerator.pro_template % {'sources': sources, 'headers': headers, 'includepath': includepath, 'defines': defines} mode = 'w+' with open(qt_pro_file, mode) as fpro: fpro.write(content) self._log("created %s\n" % qt_pro_file) except Exception as e: print("ERROR : creating pro file=" + qt_pro_file, file=sys.stderr) print(e, file=sys.stderr) temp = traceback.format_exc() # .decode('utf8') print(temp, file=sys.stderr) print("\n\n", file=sys.stderr) # create .pro.user file qt_pro_user_file = '%s/%s.pro.user' % (lib_loc, lib_name) try: with open(qt_pro_user_file, mode) as fprouser: fprouser.write(self.generate_pro_user_content(lib_folder)) self._log("created %s\n" % qt_pro_user_file) except Exception as e: print("ERROR : creating pro.user file=" + qt_pro_user_file, file=sys.stderr) print(e, file=sys.stderr) temp = traceback.format_exc() print(temp, file=sys.stderr) print("\n\n", file=sys.stderr) # create meta .pro file (lists all sub projects) qt_meta_pro_file = 'lo.pro' try: subdirs = " \\\n".join(subdirs_list) content = QtCreatorIntegrationGenerator.pro_meta_template % {'subdirs': subdirs} with open(qt_meta_pro_file, 'w+') as fmpro: fmpro.write(content) except Exception as e: print("ERROR : creating lo.pro file=" + qt_meta_pro_file, file=sys.stderr) print(e, file=sys.stderr) temp = traceback.format_exc() print(temp, file=sys.stderr) print("\n\n", file=sys.stderr) # create meta .pro.user file qt_meta_pro_user_file = 'lo.pro.user' try: with open(qt_meta_pro_user_file, mode) as fmprouser: fmprouser.write(self.generate_meta_pro_user_content()) self._log("created %s\n" % qt_meta_pro_user_file) except Exception as e: print("ERROR : creating lo.pro.user file=" + qt_meta_pro_user_file, file=sys.stderr) print(e, file=sys.stderr) temp = traceback.format_exc() print(temp, file=sys.stderr) print("\n\n", file=sys.stderr) self.log_close() pro_template = """TEMPLATE = app CONFIG += console CONFIG -= app_bundle CONFIG -= qt INCLUDEPATH += %(includepath)s SOURCES += %(sources)s HEADERS += %(headers)s DEFINES += %(defines)s """ pro_meta_template = """TEMPLATE = subdirs SUBDIRS = %(subdirs)s """ def get_options(): parser = argparse.ArgumentParser( description='LibreOffice gbuild IDE project generator') parser.add_argument('--ide', dest='ide', required=True, help='the IDE to generate project files for') parser.add_argument('--make', dest='makecmd', required=True, help='the command to execute make') return parser.parse_args() if __name__ == '__main__': args = get_options() # FIXME: Hack if args.makecmd == 'make': args.makecmd = '/usr/bin/make' paths = {} generators = { 'eclipsecdt': EclipseCDTIntegrationGenerator, 'kdevelop': KdevelopIntegrationGenerator, 'xcode': XcodeIntegrationGenerator, 'vs2013': VisualStudioIntegrationGenerator, 'vs2015': VisualStudioIntegrationGenerator, 'vim': VimIntegrationGenerator, 'debug': DebugIntegrationGenerator, 'qtcreator': QtCreatorIntegrationGenerator, } if args.ide not in generators.keys(): print("Invalid ide. valid values are %s" % ','.join(generators.keys())) sys.exit(1) gbuildparser = GbuildParser(args.makecmd).parse() generators[args.ide](gbuildparser, args.ide).emit() print("Successfully created the project files.") # Local Variables: # indent-tabs-mode: nil # End: # # vim: set et sw=4 ts=4: