diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.in | 10 | ||||
-rwxr-xr-x | bin/gbuild-to-ide | 295 |
3 files changed, 307 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore index 4680de1d779d..0de9054739b4 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,8 @@ *~ .*sw? \#* +*.kdev4 +.kdev_include_paths # things below this point are targeted for elimination diff --git a/Makefile.in b/Makefile.in index 95bf5b37473f..443f7581da13 100644 --- a/Makefile.in +++ b/Makefile.in @@ -400,6 +400,16 @@ subsequentcheck :| $(if $(filter-out subsequentcheck,$(MAKECMDGOALS)),build) debugrun help slowcheck translations unitcheck : $(GNUMAKE) -j $(PARALLELISM) $(GMAKE_OPTIONS) -f $(SRCDIR)/Makefile.gbuild $@ +define GbuildToIdeIntegration +$(1)-ide-integration: + cd $(SRCDIR) && (make cmd -npf Makefile.gbuild all || true) | $(SRCDIR)/bin/gbuild-to-ide --ide $(1) + +endef + +$(foreach ide,\ + kdevelop, \ +$(eval $(call GbuildToIdeIntegration,$(ide)))) + endif # MAKE_RESTARTS # vim: set noet sw=4 ts=4: diff --git a/bin/gbuild-to-ide b/bin/gbuild-to-ide new file mode 100755 index 000000000000..d18ee84eb519 --- /dev/null +++ b/bin/gbuild-to-ide @@ -0,0 +1,295 @@ +#! /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 inspect +import os +import os.path +import shutil +import re +import sys + +class GbuildParserState: + def __init__(self): + self.include = [] + self.defs = {} + self.cxxobjects = [] + self.linked_libs = [] + +class GbuildLinkTarget: + def __init__(self, name, location, include, defs, cxxobjects, linked_libs): + (self.name, self.location, self.include, self.defs, self.cxxobjects, self.linked_libs) = (name, location, include, defs, cxxobjects, linked_libs) + def short_name(self): + return self.name + def __str__(self): + return '%s at %s with include path: %s, defines %s, objects: %s and linked libs: %s' % (self.short_name(), self.location, self.include, self.defs, self.cxxobjects, self.linked_libs) + +class GbuildLib(GbuildLinkTarget): + def __init__(self, name, location, include, defs, cxxobjects, linked_libs): + GbuildLinkTarget.__init__(self, name, location, include, defs, cxxobjects, linked_libs) + def short_name(self): + return 'Library %s' % self.name + +class GbuildExe(GbuildLinkTarget): + def __init__(self, name, location, include, defs, cxxobjects, linked_libs): + GbuildLinkTarget.__init__(self, name, location, include, defs, cxxobjects, linked_libs) + def short_name(self): + return 'Executable %s' % self.name + +class GbuildParser: + makecmdpattern = re.compile('^MAKE_COMMAND := (.*)') + srcdirpattern = re.compile('^SRCDIR = (.*)') + builddirpattern = re.compile('^BUILDDIR = (.*)') + instdirpattern = re.compile('^INSTDIR = (.*)') + libpattern = re.compile('# [a-z]+ to execute \(from `(.*)/Library_(.*)\.mk\', line [0-9]*\):') + exepattern = re.compile('# [a-z]+ to execute \(from `(.*)/Executable_(.*)\.mk\', line [0-9]*\):') + includepattern = re.compile('# INCLUDE := (.*)') + defspattern = re.compile('# DEFS := (.*)') + cxxpattern = re.compile('# CXXOBJECTS := (.*)') + linkedlibspattern = re.compile('# LINKED_LIBS := (.*)') + def __init__(self): + (self.makecmd, self.srcdir, self.builddir, self.instdir, self.libs, self.exes) = ('', '', '', '', [], []) + def parse(self, gbuildstate): + state = GbuildParserState() + for line in gbuildstate: + if not line.startswith('#'): + makecmdmatch = GbuildParser.makecmdpattern.match(line) + if makecmdmatch: + self.makecmd = makecmdmatch.group(1) + # FIXME: Hack + if self.makecmd == 'make': + self.makecmd = '/usr/bin/make' + continue + srcdirmatch = GbuildParser.srcdirpattern.match(line) + if srcdirmatch: + self.srcdir = srcdirmatch.group(1) + continue + builddirmatch = GbuildParser.builddirpattern.match(line) + if builddirmatch: + self.builddir = builddirmatch.group(1) + continue + instdirmatch = GbuildParser.instdirpattern.match(line) + if instdirmatch: + self.instdir = instdirmatch.group(1) + continue + state = GbuildParserState() + continue + libmatch = GbuildParser.libpattern.match(line) + if libmatch: + self.libs.append(GbuildLib(libmatch.group(2), libmatch.group(1), state.include, state.defs, state.cxxobjects, state.linked_libs)) + state = GbuildParserState() + continue + exematch = GbuildParser.exepattern.match(line) + if exematch: + self.exes.append(GbuildExe(exematch.group(2), exematch.group(1), state.include, state.defs, state.cxxobjects, state.linked_libs)) + state = GbuildParserState() + continue + includematch = GbuildParser.includepattern.match(line) + if includematch: + state.include = [includeswitch.strip()[2:] for includeswitch in includematch.group(1).split(' ') if len(includeswitch) > 2] + continue + defsmatch = GbuildParser.defspattern.match(line) + if defsmatch: + alldefs = [defswitch.strip()[2:] for defswitch in defsmatch.group(1).split(' ') if len(defswitch) >2] + for d in alldefs: + defparts = d.split('=') + if len(defparts) == 1: + defparts.append(None) + state.defs[defparts[0]] = defparts[1] + continue + cxxmatch = GbuildParser.cxxpattern.match(line) + if cxxmatch: + state.cxxobjects = [obj for obj in cxxmatch.group(1).split(' ') if len(obj) > 0] + continue + linkedlibsmatch = GbuildParser.linkedlibspattern.match(line) + if linkedlibsmatch: + state.linked_libs = linkedlibsmatch.group(1).strip().split(' ') + continue + #we could match a lot of other stuff here if needed for integration rpaths etc. + return self + +class IdeIntegrationGenerator: + def __init__(self, gbuildparser): + self.gbuildparser = gbuildparser + def emit(self): + pass + +class DebugIntegrationGenerator(IdeIntegrationGenerator): + def __init__(self, gbuildparser): + IdeIntegrationGenerator.__init__(self, gbuildparser) + 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) + +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 } + pathid = 0 + 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)', self.gbuildparser.makecmd, 'unitcheck slowcheck', moduledir) + result += self.generate_launch(2, 'Local tests -- integration tests (unitcheck, slowcheck, subsequentcheck)', self.gbuildparser.makecmd, 'unitcheck slowcheck 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)', self.gbuildparser.makecmd, 'unitcheck slowcheck', self.gbuildparser.builddir) + result += self.generate_launch(5, 'Global tests -- integration tests (unitcheck, slowcheck, subsequentcheck)', self.gbuildparser.makecmd, 'unitcheck slowcheck 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') + fullpath = '%s/%s' % (self.gbuildparser.srcdir, path) + include = set() + for target in self.target_by_path[path]: + include |= set(target.include) + includedirfile.write('\n'.join(include)) + includedirfile.close() + def __init__(self, gbuildparser): + IdeIntegrationGenerator.__init__(self, gbuildparser) + self.target_by_location = {} + self.target_by_path = {} + for target in set(self.gbuildparser.libs) | set(self.gbuildparser.exes): + if not target.location 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 not path 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: + if len(self.target_by_path[path]) > 1: + print('fdo#70422: multiple target use dir %s: %s' % (path, ', '.join([target.short_name() for target in self.target_by_path[path]]))) + def emit(self): + for path in self.target_by_path: + self.write_includepaths(path) + for location in self.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.target_by_location: + modulename = os.path.split(location)[1] + self.write_modulestub(location, modulename) + self.write_modulebeef(location, modulename) + +if __name__ == '__main__': + 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') + args = parser.parse_args() + paths = {} + gbuildparser = GbuildParser().parse(sys.stdin) + #DebugIntegrationGenerator(gbuildparser).emit() + if args.ide == 'kdevelop': + KdevelopIntegrationGenerator(gbuildparser).emit() + else: + parser.print_help() + sys.exit(1) + +# vim: set noet sw=4 ts=4: |