related fdo#70414 gbuild to ide: kdevelop
This provides kdevelop integration and generates one project file for each old-style module (top level dir). This project file has: - has four build configurations: - build the module of the project or build all of LibreOffice - for each of the above a debug and a nondebug build - has seven launch targets: - running the unitchecks, the slowchecks and subsequentchecks - for each of the above once for the module and once for all - running LibreOffice interactively - has custom include paths and thus provides full autocompletion Change-Id: I6dd51133147d019fc403e3bd814bc6103df94cac Reviewed-on: https://gerrit.libreoffice.org/6694 Reviewed-by: Björn Michaelsen <bjoern.michaelsen@canonical.com> Tested-by: Björn Michaelsen <bjoern.michaelsen@canonical.com>
This commit is contained in:
committed by
Björn Michaelsen
parent
3773201d59
commit
ba99e29607
2
.gitignore
vendored
2
.gitignore
vendored
@@ -58,6 +58,8 @@
|
|||||||
*~
|
*~
|
||||||
.*sw?
|
.*sw?
|
||||||
\#*
|
\#*
|
||||||
|
*.kdev4
|
||||||
|
.kdev_include_paths
|
||||||
|
|
||||||
# things below this point are targeted for elimination
|
# things below this point are targeted for elimination
|
||||||
|
|
||||||
|
10
Makefile.in
10
Makefile.in
@@ -400,6 +400,16 @@ subsequentcheck :| $(if $(filter-out subsequentcheck,$(MAKECMDGOALS)),build)
|
|||||||
debugrun help slowcheck translations unitcheck :
|
debugrun help slowcheck translations unitcheck :
|
||||||
$(GNUMAKE) -j $(PARALLELISM) $(GMAKE_OPTIONS) -f $(SRCDIR)/Makefile.gbuild $@
|
$(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
|
endif # MAKE_RESTARTS
|
||||||
|
|
||||||
# vim: set noet sw=4 ts=4:
|
# vim: set noet sw=4 ts=4:
|
||||||
|
295
bin/gbuild-to-ide
Executable file
295
bin/gbuild-to-ide
Executable file
@@ -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:
|
Reference in New Issue
Block a user