#! /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: