ea1f50894a
It turns out that VS IDE's "Peek definition" (and other functions that navigate to sources) fail if the short ("DOS") path to the file is given in project's includes: see issue at https://developercommunity.visualstudio.com/content/problem/139659/vc-peek-definition-fails-to-navigate-to-windows-ki.html This patch converts the include paths to full Windows paths, to avoid the problem. Also, since IDE starts working correctly with this change, this patch removes inclusion of "inherited" paths "$(IncludePath)", which are the paths added by Visual Studio itself. Since we do specify all include paths explicitly, that is not required, and avoids confusion. Change-Id: Ide2d948f8c7b050b02f550342144fede4fcafb82 Reviewed-on: https://gerrit.libreoffice.org/53731 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
1676 lines
74 KiB
Python
Executable file
1676 lines
74 KiB
Python
Executable file
#! /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
|
|
import subprocess
|
|
from sys import platform
|
|
|
|
|
|
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 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 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 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:
|
|
dparts = d.split(' -U')
|
|
"""after dparts.pop(0), dparts will contain only undefs"""
|
|
defparts = dparts.pop(0).strip().split('=')
|
|
if len(defparts) == 1:
|
|
defparts.append(None)
|
|
defs[defparts[0]] = defparts[1]
|
|
"""Drop undefed items (if any) from previous defs"""
|
|
for u in dparts:
|
|
defs.pop(u.strip(), '')
|
|
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 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 = """\
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<cdtprojectproperties>
|
|
<section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
|
|
<language name="C++ Source File">
|
|
|
|
|
|
</language>
|
|
<language name="C Source File">
|
|
|
|
</language>
|
|
<language name="Object File">
|
|
|
|
</language>
|
|
<language name="Assembly Source File">
|
|
|
|
</language>
|
|
</section>
|
|
<section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros">
|
|
<language name="C++ Source File">
|
|
|
|
</language>
|
|
<language name="C Source File">
|
|
|
|
</language>
|
|
<language name="Object File">
|
|
|
|
</language>
|
|
<language name="Assembly Source File">
|
|
|
|
</language>
|
|
</section>
|
|
</cdtprojectproperties>
|
|
"""
|
|
|
|
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 = "<includepath>%s</includepath>\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 = "<macro><name>%s</name><value>%s</value></macro>\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) | set(self.gbuildparser.exes):
|
|
entries = []
|
|
for file in lib.cxxobjects:
|
|
filePath = os.path.join(self.gbuildparser.srcdir, file) + ".cxx"
|
|
entry = {'directory': lib.location, 'file': filePath, 'command': self.generateCommand(lib, filePath)}
|
|
entries.append(entry)
|
|
global_list.extend(entries)
|
|
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
|
|
|
|
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(' ')
|
|
|
|
def write_object(self, object, file, indent):
|
|
if isinstance(object, int):
|
|
file.write('%d' % object)
|
|
elif isinstance(object, str) and not re.search('[^A-Za-z0-9_]', object):
|
|
file.write('%s' % object)
|
|
elif isinstance(object, str):
|
|
file.write('"%s"' % object)
|
|
elif isinstance(object, dict):
|
|
self.write_dict(object, file, indent)
|
|
|
|
# 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_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'
|
|
|
|
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 = {'isa': 'PBXProject',
|
|
'attributes': {'LastUpgradeCheck': '0500',
|
|
'ORGANIZATIONNAME': 'LibreOffice'},
|
|
'buildConfigurationList': self.generate_id(),
|
|
'compatibilityVersion': 'Xcode 3.2',
|
|
'hasScannedForEncodings': 0,
|
|
'knownRegions': ['en'],
|
|
'mainGroup': self.mainGroupId,
|
|
'productRefGroup': self.productRefGroupId,
|
|
'projectDirPath': '',
|
|
'projectRoot': '',
|
|
'targets': self.targetId}
|
|
return result
|
|
|
|
def generate_target(self, modulename):
|
|
result = {'isa': 'PBXNativeTarget',
|
|
'buildConfigurationList': self.generate_id(),
|
|
'buildPhases': self.generate_build_phases(modulename),
|
|
'buildRules': [],
|
|
'dependencies': [],
|
|
'name': modulename,
|
|
'productName': modulename,
|
|
'productReference': self.productReferenceId,
|
|
'productType': self.get_product_type(modulename)}
|
|
return result
|
|
|
|
def generate_main_group(self, modulename):
|
|
result = {'isa': 'PBXGroup',
|
|
'children': [self.subMainGroupId, self.productGroupId],
|
|
'sourceTree': '<group>'}
|
|
return result
|
|
|
|
def generate_sub_main_children(self, modulename):
|
|
return {}
|
|
|
|
def generate_sub_main_group(self, modulename):
|
|
result = {'isa': 'PBXGroup',
|
|
'children': self.generate_sub_main_children(modulename),
|
|
'path': modulename,
|
|
'sourceTree': '<group>'}
|
|
return result
|
|
|
|
def generate_product_group(self, modulename):
|
|
result = {'isa': 'PBXGroup',
|
|
'children': [self.productReferenceId],
|
|
'name': 'Products',
|
|
'sourceTree': '<group>'}
|
|
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': '<group>'}
|
|
|
|
def generate_sources_build_phase(self, modulename):
|
|
result = {'isa': 'PBXSourcesBuildPhase',
|
|
'buildActionMask': 2147483647,
|
|
'files': 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.build_source_list(target)
|
|
self.sourcesBuildPhaseId = self.generate_id()
|
|
objects = {self.rootObjectId: self.generate_root_object(target),
|
|
self.targetId: self.generate_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)
|
|
}
|
|
for i in self.sourceList.keys():
|
|
ref = self.sourceList[i]
|
|
objects[i] = {'isa': 'PBXBuildFile',
|
|
'fileRef': ref}
|
|
objects[ref] = {'isa': 'PBXFileReference',
|
|
'lastKnownFileType': self.sourceRefList[ref]['lastKnownFileType'],
|
|
'path': self.sourceRefList[ref]['path']}
|
|
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 = {'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('Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B9292527-A979-4D13-A598-C75A33222174}"\n')
|
|
f.write('\tProjectSection(SolutionItems) = preProject\n')
|
|
f.write('\t\tsolenv/vs/LibreOffice.natvis = solenv/vs/LibreOffice.natvis\n')
|
|
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('')
|
|
|
|
@staticmethod
|
|
def to_long_names(shortnames):
|
|
if platform == "cygwin":
|
|
return (subprocess.check_output(["cygpath", "-wal"] + shortnames).decode("utf-8", "strict").rstrip()).split("\n")
|
|
else:
|
|
return shortnames
|
|
|
|
@staticmethod
|
|
def defs_list(defs):
|
|
defines_list = []
|
|
# List defines
|
|
for key, value in defs.items():
|
|
define = key
|
|
if value is not None:
|
|
define += '=' + value
|
|
defines_list.append(define)
|
|
return defines_list
|
|
|
|
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')
|
|
# VS IDE (at least "Peek definition") is allergic to paths like "C:/PROGRA~2/WI3CF2~1/10/Include/10.0.14393.0/um"; see
|
|
# https://developercommunity.visualstudio.com/content/problem/139659/vc-peek-definition-fails-to-navigate-to-windows-ki.html
|
|
# We need to convert to long paths here. Do this once, since it's time-consuming operation.
|
|
include_path_node_text = ';'.join(self.to_long_names(target.include))
|
|
for cfg_name, cfg_targets in self.configurations.items():
|
|
conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns,
|
|
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.bin')
|
|
nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % ns)
|
|
nmake_defs_node.text = ';'.join(self.defs_list(target.defs) + ['$(NMakePreprocessorDefinitions)'])
|
|
include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % ns)
|
|
include_path_node.text = include_path_node_text
|
|
|
|
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]) if proj_filter else set()
|
|
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 = """
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.%(index)s">
|
|
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">%(base_folder)s</value>
|
|
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
|
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
|
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
|
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
|
|
<value type="QString">-w</value>
|
|
<value type="QString">-r</value>
|
|
</valuelist>
|
|
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
|
|
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">%(arg)s</value>
|
|
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
|
|
</valuemap>
|
|
|
|
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
|
</valuemap>
|
|
|
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
|
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
|
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">%(name)s</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
|
|
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">%(index)s</value>
|
|
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
|
|
</valuemap>
|
|
"""
|
|
|
|
build_configs_count_template = """
|
|
<!-- nb build configurations -->
|
|
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">%(nb)s</value>
|
|
"""
|
|
|
|
def generate_deploy_configs(self, lib_folder):
|
|
xml = QtCreatorIntegrationGenerator.deploy_configs_template % {}
|
|
return xml
|
|
|
|
deploy_configs_template = """
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
|
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
|
</valuemap>
|
|
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
|
</valuemap>
|
|
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
|
"""
|
|
|
|
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 = """
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
|
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
|
|
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
|
|
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
|
|
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
|
|
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
|
|
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
|
|
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
|
|
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
|
|
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
|
|
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
|
|
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
|
|
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
|
|
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
|
|
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
|
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
|
|
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
|
|
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
|
|
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
|
<value type="int">0</value>
|
|
<value type="int">1</value>
|
|
<value type="int">2</value>
|
|
<value type="int">3</value>
|
|
<value type="int">4</value>
|
|
<value type="int">5</value>
|
|
<value type="int">6</value>
|
|
<value type="int">7</value>
|
|
<value type="int">8</value>
|
|
<value type="int">9</value>
|
|
<value type="int">10</value>
|
|
<value type="int">11</value>
|
|
<value type="int">12</value>
|
|
<value type="int">13</value>
|
|
<value type="int">14</value>
|
|
</valuelist>
|
|
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
|
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
|
|
|
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments"></value>
|
|
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%(loexec)s</value>
|
|
<value type="bool" key="ProjectExplorer.CustomExecutableRunConfiguration.UseTerminal">false</value>
|
|
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory">%(workdir)s</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run libreoffice/instdir/program/soffice</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
|
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
|
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
|
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
|
|
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
|
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
|
|
|
</valuemap>
|
|
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
|
"""
|
|
|
|
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 = """<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE QtCreatorProject>
|
|
<!-- Written by QtCreator 3.1.1, 2015-05-14T15:54:34. -->
|
|
<qtcreator>
|
|
<data>
|
|
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
|
<value type="int">0</value>
|
|
</data>
|
|
|
|
<!-- editor settings -->
|
|
<data>
|
|
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
|
<valuemap type="QVariantMap">
|
|
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
|
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
|
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
|
<value type="QString" key="language">Cpp</value>
|
|
<valuemap type="QVariantMap" key="value">
|
|
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
|
</valuemap>
|
|
</valuemap>
|
|
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
|
<value type="QString" key="language">QmlJS</value>
|
|
<valuemap type="QVariantMap" key="value">
|
|
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
|
</valuemap>
|
|
</valuemap>
|
|
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
|
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
|
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
|
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
|
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
|
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
|
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
|
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
|
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
|
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
|
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
|
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
|
|
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
|
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
|
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
|
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
|
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
|
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
|
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
|
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
|
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
|
</valuemap>
|
|
</data>
|
|
|
|
<data>
|
|
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
|
<valuemap type="QVariantMap"/>
|
|
</data>
|
|
|
|
<!-- target -->
|
|
<data>
|
|
<variable>ProjectExplorer.Project.Target.0</variable>
|
|
<valuemap type="QVariantMap">
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
|
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{0701de51-c96e-4e4f-85c3-e70b223c5076}</value>
|
|
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
|
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
|
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
|
|
|
<!-- build configurations -->
|
|
%(build_configs)s
|
|
|
|
<!-- deploy configurations -->
|
|
%(deploy_configs)s
|
|
|
|
<!-- plugin settings -->
|
|
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
|
|
|
|
<!-- run configurations -->
|
|
%(run_configs)s
|
|
|
|
</valuemap>
|
|
</data>
|
|
<!-- nb targets : 1 -->
|
|
<data>
|
|
<variable>ProjectExplorer.Project.TargetCount</variable>
|
|
<value type="int">1</value>
|
|
</data>
|
|
<data>
|
|
<variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
|
|
<value type="QByteArray">{5abcafed-86f6-49f6-b1cb-380fadd21211}</value>
|
|
</data>
|
|
<data>
|
|
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
|
<value type="int">15</value>
|
|
</data>
|
|
</qtcreator>
|
|
"""
|
|
|
|
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,
|
|
'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:
|