Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / bin / gbuild-to-ide
blob90a732d7c614a82e12621e04cedfcd5449e9994e
1 #! /usr/bin/env python3
2 # -*- Mode: python; tab-width: 4; indent-tabs-mode: t -*-
4 # This file is part of the LibreOffice project.
6 # This Source Code Form is subject to the terms of the Mozilla Public
7 # License, v. 2.0. If a copy of the MPL was not distributed with this
8 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 import argparse
12 import os
13 import os.path
14 import shutil
15 import re
16 import sys
17 import uuid
18 import json
19 import xml.etree.ElementTree as ET
20 import xml.dom.minidom as minidom
21 import traceback
22 import subprocess
23 from sys import platform
26 class GbuildLinkTarget:
27     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
28         (self.name, self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.cxxflags, self.linked_libs) = (
29             name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
31     def short_name(self):
32         return self.name
34     def is_empty(self):
35         return not self.include and not self.defs and not self.cxxobjects and not self.linked_libs
37     def __str__(self):
38         return '%s at %s with include path: %s, isystem includes: %s, defines: %s, objects: %s, cxxflags: %s and linked libs: %s' % (
39             self.short_name(), self.location, self.include, self.include_sys, self.defs, self.cxxobjects,
40             self.cxxflags, self.linked_libs)
43 class GbuildLib(GbuildLinkTarget):
44     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
45         GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
47     def short_name(self):
48         """Return the short name of target based on the Library_* makefile name"""
49         return 'Library %s' % self.name
51     def target_name(self):
52         return 'Library_%s' % self.name
54     def library_name(self):
55         return self.name
57 class GbuildTest(GbuildLinkTarget):
58     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
59         GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
61     def short_name(self):
62         """Return the short name of target based n the CppunitTest_* makefile names"""
63         return 'CppunitTest %s' % self.name
65     def target_name(self):
66         return 'CppunitTest_%s' % self.name
68 class GbuildExe(GbuildLinkTarget):
69     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
70         GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
72     def short_name(self):
73         """Return the short name of target based on the Executable_* makefile name"""
74         return 'Executable %s' % self.name
76     def target_name(self):
77         return 'Executable_%s' % self.name
80 class GbuildParser:
81     def __init__(self, makecmd):
82         self.makecmd = makecmd
83         self.binpath = os.path.dirname(os.environ['GPERF']) # woha, this is quite a hack
84         (self.srcdir, self.builddir, self.instdir, self.workdir) = (os.environ['SRCDIR'], os.environ['BUILDDIR'], os.environ['INSTDIR'], os.environ['WORKDIR'])
85         (self.libs, self.exes, self.tests, self.modulenamelist) = ([], [], [], [])
86         (self.target_by_path, self.target_by_location) = ({}, {})
88     includepattern = re.compile('-I(\S+)')
89     isystempattern = re.compile('-isystem\s*(\S+)')
90     warningpattern = re.compile('-W\S+')
91     libpattern = re.compile('Library_(.*)\.mk')
92     exepattern = re.compile('Executable_(.*)\.mk')
93     testpattern = re.compile('CppunitTest_(.*)\.mk')
95     @staticmethod
96     def __split_includes(includes):
97         foundisystem = GbuildParser.isystempattern.findall(includes)
98         foundincludes = [includeswitch.strip() for includeswitch in GbuildParser.includepattern.findall(includes) if
99                 len(includeswitch) > 2]
100         return (foundincludes, foundisystem)
102     @staticmethod
103     def __split_objs(objsline):
104         return [obj for obj in objsline.strip().split(' ') if len(obj) > 0 and obj != 'CXXOBJECTS' and obj != '+=']
106     @staticmethod
107     def __split_defs(defsline):
108         defs = {}
109         alldefs = [defswitch.strip() for defswitch in defsline.strip().lstrip('-D').split(' -D') if len(defswitch) > 2]
110         for d in alldefs:
111             dparts = d.split(' -U')
112             """after dparts.pop(0), dparts will contain only undefs"""
113             defparts = dparts.pop(0).strip().split('=')
114             if len(defparts) == 1:
115                 defparts.append(None)
116             defs[defparts[0]] = defparts[1]
117             """Drop undefed items (if any) from previous defs"""
118             for u in dparts:
119                 defs.pop(u.strip(), '')
120         defs["LIBO_INTERNAL_ONLY"] = None
121         return defs
123     @staticmethod
124     def __split_flags(flagsline, flagslineappend):
125         return [cxxflag.strip() for cxxflag in GbuildParser.warningpattern.sub('', '%s %s' % (flagsline, flagslineappend)).split(' ') if len(cxxflag) > 1]
127     @staticmethod
128     def __lib_from_json(json):
129         (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE'])
130         return GbuildLib(
131             GbuildParser.libpattern.match(os.path.basename(json['MAKEFILE'])).group(1),
132             os.path.dirname(json['MAKEFILE']),
133             foundincludes,
134             foundisystem,
135             GbuildParser.__split_defs(json['DEFS']),
136             GbuildParser.__split_objs(json['CXXOBJECTS']),
137             GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']),
138             json['LINKED_LIBS'].strip().split(' '))
140     @staticmethod
141     def __test_from_json(json):
142         (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE'])
143         testname_match = GbuildParser.testpattern.match(os.path.basename(json['MAKEFILE']))
145         # Workaround strange writer test makefile setup
146         if testname_match is None:
147             testname = "StrangeWriterMakefiles"
148         else:
149             testname = testname_match.group(1)
151         return GbuildTest(
152             testname,
153             os.path.dirname(json['MAKEFILE']),
154             foundincludes,
155             foundisystem,
156             GbuildParser.__split_defs(json['DEFS']),
157             GbuildParser.__split_objs(json['CXXOBJECTS']),
158             GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']),
159             json['LINKED_LIBS'].strip().split(' '))
161     @staticmethod
162     def __exe_from_json(json):
163         (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE'])
164         return GbuildExe(
165             GbuildParser.exepattern.match(os.path.basename(json['MAKEFILE'])).group(1),
166             os.path.dirname(json['MAKEFILE']),
167             foundincludes,
168             foundisystem,
169             GbuildParser.__split_defs(json['DEFS']),
170             GbuildParser.__split_objs(json['CXXOBJECTS']),
171             GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']),
172             json['LINKED_LIBS'].strip().split(' '))
174     def parse(self):
175         for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'Library')):
176             with open(os.path.join(self.workdir, 'GbuildToJson', 'Library', jsonfilename), 'r') as f:
177                 lib = self.__lib_from_json(json.load(f))
178                 self.libs.append(lib)
179         for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'Executable')):
180             with open(os.path.join(self.workdir, 'GbuildToJson', 'Executable', jsonfilename), 'r') as f:
181                 exe = self.__exe_from_json(json.load(f))
182                 self.exes.append(exe)
183         for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'CppunitTest')):
184             with open(os.path.join(self.workdir, 'GbuildToJson', 'CppunitTest', jsonfilename), 'r') as f:
185                 test = self.__test_from_json(json.load(f))
186                 self.tests.append(test)
187         for target in set(self.libs) | set(self.exes) | set(self.tests):
188             if target.location not in self.target_by_location:
189                 self.target_by_location[target.location] = set()
190             self.target_by_location[target.location] |= set([target])
191             for cxx in target.cxxobjects:
192                 path = '/'.join(cxx.split('/')[:-1])
193                 if path not in self.target_by_path:
194                     self.target_by_path[path] = set()
195                 self.target_by_path[path] |= set([target])
196         for location in self.target_by_location:
197             self.modulenamelist.append(os.path.split(location)[1])
198         return self
201 class IdeIntegrationGenerator:
203     def __init__(self, gbuildparser, ide):
204         self.gbuildparser = gbuildparser
205         self.ide = ide
207     def emit(self):
208         pass
210 class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator):
212     def __init__(self, gbuildparser, ide):
213         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
215     def create_include_paths(self):
216         for module in self.gbuildparser.modulenamelist:
217             modulepath = os.path.join(self.gbuildparser.builddir, module)
218             includedirfile = open(os.path.join(modulepath, '.eclipsesettingfile'), 'w')
219             modulelibs = []
220             for lib in self.gbuildparser.target_by_path.keys():
221                 if lib.startswith(module+'/'):
222                     modulelibs.append(lib)
223             include = set()
224             for lib in modulelibs:
225                 for target in self.gbuildparser.target_by_path[lib]:
226                     include |= set(target.include)
227             includedirfile.write('\n'.join(include))
228             includedirfile.close()
231     def create_macros(self):
232         for module in self.gbuildparser.modulenamelist:
233             modulepath = os.path.join(self.gbuildparser.builddir, module)
234             macrofile = open(os.path.join(modulepath, '.macros'), 'w')
235             modulelibs = []
236             for lib in self.gbuildparser.target_by_path.keys():
237                 if lib.startswith(module+'/'):
238                     modulelibs.append(lib)
239             define = []
240             defineset = set()
241             for lib in modulelibs:
242                 for target in self.gbuildparser.target_by_path[lib]:
243                     for i in target.defs.keys():
244                         tmp = str(i) +','+str(target.defs[i])
245                         if tmp not in defineset:
246                             defineset.add(tmp)
247             macrofile.write('\n'.join(defineset))
248             macrofile.close()
251     def create_settings_file(self):
253         settingsfiletemplate = """\
254 <?xml version="1.0" encoding="UTF-8"?>
255 <cdtprojectproperties>
256 <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
257 <language name="C++ Source File">
260 </language>
261 <language name="C Source File">
263 </language>
264 <language name="Object File">
266 </language>
267 <language name="Assembly Source File">
269 </language>
270 </section>
271 <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros">
272 <language name="C++ Source File">
274 </language>
275 <language name="C Source File">
277 </language>
278 <language name="Object File">
280 </language>
281 <language name="Assembly Source File">
283 </language>
284 </section>
285 </cdtprojectproperties>
286 """ 
288         for module in self.gbuildparser.modulenamelist:
289             tempxml = []
290             modulepath = os.path.join(self.gbuildparser.builddir, module)
292             settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'w')
293             settingsfile.write(settingsfiletemplate)
294             settingsfile.close()
296             settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'r')
297             tempxml = settingsfile.readlines()
298             tempinclude = open(os.path.join(modulepath, '.eclipsesettingfile'), 'r')
299             tempmacro = open(os.path.join(modulepath, '.macros'), 'r')
300             for includepath in tempinclude:
301                 if includepath[-1:] == "\n":
302                     includepath = includepath[:-1]
303                 templine = "<includepath>%s</includepath>\n" % includepath
304                 tempxml.insert(5, templine)
306             for line in tempmacro:
307                 macroskeyvalue = line.split(',')
308                 macrokey = macroskeyvalue[0]
309                 macrovalue = macroskeyvalue[1]
310                 if macrovalue[-1:] == "\n":
311                     macrovalue = macrovalue[:-1] 
312                 templine = "<macro><name>%s</name><value>%s</value></macro>\n" %(macrokey, macrovalue)
313                 tempxml.insert(-13, templine)
314             tempxml="".join(tempxml)
315             settingsfile.close
317             settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'w')
318             settingsfile.write(tempxml)
319             settingsfile.close()
320             os.remove(os.path.join(modulepath, '.eclipsesettingfile'))
321             os.remove(os.path.join(modulepath, '.macros'))
323     def emit(self):
324         self.create_include_paths()
325         self.create_macros()
326         self.create_settings_file() 
328 class DebugIntegrationGenerator(IdeIntegrationGenerator):
330     def __init__(self, gbuildparser, ide):
331         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
333     def emit(self):
334         print(self.gbuildparser.srcdir)
335         print(self.gbuildparser.builddir)
336         for lib in self.gbuildparser.libs:
337             print(lib)
338         for exe in self.gbuildparser.exes:
339             print(exe)
340         for test in self.gbuildparser.tests:
341             print(test)
344 class VimIntegrationGenerator(IdeIntegrationGenerator):
346     def __init__(self, gbuildparser, ide):
347         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
349     def emit(self):
350         global_list = []
351         for lib in set(self.gbuildparser.libs) | set(self.gbuildparser.tests) | set(self.gbuildparser.exes):
352             entries = []
353             for file in lib.cxxobjects:
354                 filePath = os.path.join(self.gbuildparser.srcdir, file) + ".cxx"
355                 entry = {'directory': lib.location, 'file': filePath, 'command': self.generateCommand(lib, filePath)}
356                 entries.append(entry)
357             global_list.extend(entries)
358         export_file = open('compile_commands.json', 'w')
359         json.dump(global_list, export_file)
361     def generateCommand(self, lib, file):
362         command = 'clang++ -Wall'
363         for key, value in lib.defs.items():
364             command += ' -D'
365             command += key
366             if value is not None:
367                 command += '='
368                 command += value
370         for include in lib.include:
371             command += ' -I'
372             command += include
373         for isystem in lib.include_sys:
374             command += ' -isystem '
375             command += isystem
376         for cxxflag in lib.cxxflags:
377             command += ' '
378             command += cxxflag
379         command += ' -c '
380         command += file
381         # Help clang when the tree is configured for gcc.
382         for gnu in ('-std=gnu++11', '-std=gnu++1y'):
383             command = command.replace(gnu, '-std=c++11')
384         return command
387 class KdevelopIntegrationGenerator(IdeIntegrationGenerator):
389     def encode_int(self, i):
390         temp = '%08x' % i
391         return '\\x%s\\x%s\\x%s\\x%s' % (temp[0:2], temp[2:4], temp[4:6], temp[6:8])
393     def encode_string(self, string):
394         result = self.encode_int(len(string) * 2)
395         for c in string.encode('utf-16-be'):
396             if c in range(32, 126):
397                 result += chr(c)
398             else:
399                 result += '\\x%02x' % c
400         return result
402     def generate_buildsystemconfigtool(self, configid, tool, args, exe, typenr):
403         return KdevelopIntegrationGenerator.buildsystemconfigtooltemplate % {'configid': configid, 'tool': tool,
404                                                                              'args': args, 'exe': exe, 'typenr': typenr}
406     buildsystemconfigtooltemplate = """
407 [CustomBuildSystem][BuildConfig%(configid)d][Tool%(tool)s]
408 Arguments=%(args)s
409 Enabled=true
410 Environment=
411 Executable=%(exe)s
412 Type=%(typenr)d
416     def generate_buildsystemconfig(self, configid, moduledir, builddir, title, buildparms=''):
417         result = KdevelopIntegrationGenerator.buildsystemconfigtemplate % {'configid': configid, 'builddir': builddir,
418                                                                            'title': title}
419         result += self.generate_buildsystemconfigtool(configid, 'Clean', 'clean %s' % buildparms,
420                                                       self.gbuildparser.makecmd, 3)
421         result += self.generate_buildsystemconfigtool(configid, 'Build', 'all %s' % buildparms,
422                                                       self.gbuildparser.makecmd, 0)
423         return result
425     buildsystemconfigtemplate = """
426 [CustomBuildSystem][BuildConfig%(configid)d]
427 BuildDir=file://%(builddir)s
428 Title=%(title)s
432     def generate_buildsystem(self, moduledir):
433         result = KdevelopIntegrationGenerator.buildsystemtemplate % {'defaultconfigid': 0}
434         result += self.generate_buildsystemconfig(0, moduledir, moduledir, 'Module Build -- Release')
435         result += self.generate_buildsystemconfig(1, moduledir, self.gbuildparser.builddir, 'Full Build -- Release')
436         result += self.generate_buildsystemconfig(2, moduledir, moduledir, 'Module Build -- Debug', 'debug=T')
437         result += self.generate_buildsystemconfig(3, moduledir, self.gbuildparser.builddir, 'Full Build -- Debug',
438                                                   'debug=T')
439         return result
441     buildsystemtemplate = """
442 [CustomBuildSystem]
443 CurrentConfiguration=BuildConfig%(defaultconfigid)d
447     def generate_launch(self, launchid, launchname, executablepath, args, workdir):
448         return KdevelopIntegrationGenerator.launchtemplate % {'launchid': launchid, 'launchname': launchname,
449                                                               'executablepath': executablepath, 'args': args,
450                                                               'workdir': workdir}
452     launchtemplate = """
453 [Launch][Launch Configuration %(launchid)d]
454 Configured Launch Modes=execute
455 Configured Launchers=nativeAppLauncher
456 Name=%(launchname)s
457 Type=Native Application
459 [Launch][Launch Configuration %(launchid)d][Data]
460 Arguments=%(args)s
461 Dependencies=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x00)
462 Dependency Action=Nothing
463 EnvironmentGroup=default
464 Executable=file://%(executablepath)s
465 External Terminal=konsole --noclose --workdir %%workdir -e %%exe
466 Project Target=
467 Use External Terminal=false
468 Working Directory=file://%(workdir)s
469 isExecutable=true
473     def generate_launches(self, moduledir):
474         launches = ','.join(['Launch Configuration %d' % i for i in range(7)])
475         result = KdevelopIntegrationGenerator.launchestemplate % {'launches': launches}
476         result += self.generate_launch(0, 'Local tests -- quick tests (unitcheck)', self.gbuildparser.makecmd,
477                                        'unitcheck', moduledir)
478         result += self.generate_launch(1, 'Local tests -- slow tests (unitcheck, slowcheck, screenshot)', self.gbuildparser.makecmd,
479                                        'unitcheck slowcheck screenshot', moduledir)
480         result += self.generate_launch(2, 'Local tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)',
481                                        self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot subsequentcheck', moduledir)
482         result += self.generate_launch(3, 'Global tests -- quick tests (unitcheck)', self.gbuildparser.makecmd,
483                                        'unitcheck', self.gbuildparser.builddir)
484         result += self.generate_launch(4, 'Global tests -- slow tests (unitcheck, slowcheck, screenshot)',
485                                        self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot', self.gbuildparser.builddir)
486         result += self.generate_launch(5, 'Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)',
487                                        self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot subsequentcheck',
488                                        self.gbuildparser.builddir)
489         result += self.generate_launch(6, 'Run LibreOffice',
490                                        os.path.join(self.gbuildparser.instdir, 'program/soffice.bin'), '',
491                                        self.gbuildparser.instdir)
492         return result
494     launchestemplate = """
495 [Launch]
496 Launch Configurations=%(launches)s
500     def write_modulebeef(self, moduledir, modulename):
501         beefdir = os.path.join(moduledir, '.kdev4')
502         os.mkdir(beefdir)
503         beeffile = open(os.path.join(beefdir, 'Module_%s.kdev4' % modulename), 'w')
504         beeffile.write(self.generate_buildsystem(moduledir))
505         beeffile.write(self.generate_launches(moduledir))
506         beeffile.close()
508     def write_modulestub(self, moduledir, modulename):
509         stubfile = open(os.path.join(moduledir, 'Module_%s.kdev4' % modulename), 'w')
510         stubfile.write(KdevelopIntegrationGenerator.modulestubtemplate % {'modulename': modulename,
511                                                                           'builditem': self.encode_string(
512                                                                               'Module_%s' % modulename)})
513         stubfile.close()
515     modulestubtemplate = """
516 [Buildset]
517 BuildItems=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0b\\x00\\x00\\x00\\x00\\x01%(builditem)s)
519 [Project]
520 Name=Module_%(modulename)s
521 Manager=KDevCustomBuildSystem
522 VersionControl=kdevgit
525     def write_includepaths(self, path):
526         includedirfile = open(os.path.join(path, '.kdev_include_paths'), 'w')
527         include = set()
528         for target in self.gbuildparser.target_by_path[path]:
529             include |= set(target.include)
530         includedirfile.write('\n'.join(include))
531         includedirfile.close()
533     def __init__(self, gbuildparser, ide):
534         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
536     def emit(self):
537         for path in self.gbuildparser.target_by_path:
538             self.write_includepaths(path)
539         for location in self.gbuildparser.target_by_location:
540             for f in os.listdir(location):
541                 if f.endswith('.kdev4'):
542                     try:
543                         os.remove(os.path.join(location, f))
544                     except OSError:
545                         shutil.rmtree(os.path.join(location, f))
546         for location in self.gbuildparser.target_by_location:
547             modulename = os.path.split(location)[1]
548             self.write_modulestub(location, modulename)
549             self.write_modulebeef(location, modulename)
552 class XcodeIntegrationGenerator(IdeIntegrationGenerator):
554     def indent(self, file, level):
555         if level == 0:
556             return
557         for i in range(0, level):
558             file.write(' ')
560     def write_object(self, object, file, indent):
561         if isinstance(object, int):
562             file.write('%d' % object)
563         elif isinstance(object, str) and not re.search('[^A-Za-z0-9_]', object):
564             file.write('%s' % object)
565         elif isinstance(object, str):
566             file.write('"%s"' % object)
567         elif isinstance(object, dict):
568             self.write_dict(object, file, indent)
570     # Write a dictionary out as an "old-style (NeXT) ASCII plist"
571     def write_dict(self, dict, file, indent):
572         file.write('{')
573         file.write('\n')
574         for key in sorted(dict.keys()):
575             self.indent(file, indent + 1)
576             file.write('%s = ' % key)
577             self.write_object(dict[key], file, indent + 1)
578             file.write(';\n')
579         self.indent(file, indent)
580         file.write('}')
582     def write_dict_to_plist(self, dict, file):
583         file.write('// !$*UTF8*$!\n')
584         self.write_dict(dict, file, 0)
586     def get_product_type(self, modulename):
587         if modulename in self.gbuildparser.libs:
588             return 'com.apple.product-type.library.dynamic'
589         elif modulename in self.gbuildparser.exes:
590             return 'com.apple.product-type.something'
592     counter = 0
594     def generate_id(self):
595         XcodeIntegrationGenerator.counter = XcodeIntegrationGenerator.counter + 1
596         return str('X%07x' % XcodeIntegrationGenerator.counter)
598     def generate_build_phases(self, modulename):
599         result = [self.sourcesBuildPhaseId]
600         return result
602     def generate_root_object(self, modulename):
603         result = {'isa': 'PBXProject',
604                   'attributes': {'LastUpgradeCheck': '0500',
605                                  'ORGANIZATIONNAME': 'LibreOffice'},
606                   'buildConfigurationList': self.generate_id(),
607                   'compatibilityVersion': 'Xcode 3.2',
608                   'hasScannedForEncodings': 0,
609                   'knownRegions': ['en'],
610                   'mainGroup': self.mainGroupId,
611                   'productRefGroup': self.productRefGroupId,
612                   'projectDirPath': '',
613                   'projectRoot': '',
614                   'targets': self.targetId}
615         return result
617     def generate_target(self, modulename):
618         result = {'isa': 'PBXNativeTarget',
619                   'buildConfigurationList': self.generate_id(),
620                   'buildPhases': self.generate_build_phases(modulename),
621                   'buildRules': [],
622                   'dependencies': [],
623                   'name': modulename,
624                   'productName': modulename,
625                   'productReference': self.productReferenceId,
626                   'productType': self.get_product_type(modulename)}
627         return result
629     def generate_main_group(self, modulename):
630         result = {'isa': 'PBXGroup',
631                   'children': [self.subMainGroupId, self.productGroupId],
632                   'sourceTree': '<group>'}
633         return result
635     def generate_sub_main_children(self, modulename):
636         return {}
638     def generate_sub_main_group(self, modulename):
639         result = {'isa': 'PBXGroup',
640                   'children': self.generate_sub_main_children(modulename),
641                   'path': modulename,
642                   'sourceTree': '<group>'}
643         return result
645     def generate_product_group(self, modulename):
646         result = {'isa': 'PBXGroup',
647                   'children': [self.productReferenceId],
648                   'name': 'Products',
649                   'sourceTree': '<group>'}
650         return result
652     def build_source_list(self, module):
653         self.sourceRefList = {}
654         self.sourceList = {}
656         for i in module.cxxobjects:
657             ref = self.generate_id()
658             self.sourceList[self.generate_id()] = ref
659             self.sourceRefList[ref] = {'lastKnownFileType': 'sourcecode.cpp.cpp',
660                                        'path': i + '.cxx',
661                                        'sourceTree': '<group>'}
663     def generate_sources_build_phase(self, modulename):
664         result = {'isa': 'PBXSourcesBuildPhase',
665                   'buildActionMask': 2147483647,
666                   'files': self.sourceList.keys(),
667                   'runOnlyForDeploymentPostprocessing': 0}
668         return result
670     def generate_project(self, target):
671         self.rootObjectId = self.generate_id()
672         self.mainGroupId = self.generate_id()
673         self.subMainGroupId = self.generate_id()
674         self.productReferenceId = self.generate_id()
675         self.productRefGroupId = self.generate_id()
676         self.productGroupId = self.generate_id()
677         self.targetId = self.generate_id()
678         self.build_source_list(target)
679         self.sourcesBuildPhaseId = self.generate_id()
680         objects = {self.rootObjectId: self.generate_root_object(target),
681                    self.targetId: self.generate_target(target),
682                    self.mainGroupId: self.generate_main_group(target),
683                    self.subMainGroupId: self.generate_sub_main_group(target),
684                    self.productGroupId: self.generate_product_group(target),
685                    self.sourcesBuildPhaseId: self.generate_sources_build_phase(target)
686                    }
687         for i in self.sourceList.keys():
688             ref = self.sourceList[i]
689             objects[i] = {'isa': 'PBXBuildFile',
690                           'fileRef': ref}
691             objects[ref] = {'isa': 'PBXFileReference',
692                             'lastKnownFileType': self.sourceRefList[ref]['lastKnownFileType'],
693                             'path': self.sourceRefList[ref]['path']}
694         project = {'archiveVersion': 1,
695                    'classes': {},
696                    'objectVersion': 46,
697                    'objects': objects,
698                    'rootObject': self.rootObjectId}
699         return project
701     # For some reverse-engineered documentation on the project.pbxproj format,
702     # see http://www.monobjc.net/xcode-project-file-format.html .
703     def write_xcodeproj(self, moduledir, target):
704         xcodeprojdir = os.path.join(moduledir, '%s.xcodeproj' % target.target_name())
705         try:
706             os.mkdir(xcodeprojdir)
707         except:
708             pass
709         self.write_dict_to_plist(self.generate_project(target),
710                                  open(os.path.join(xcodeprojdir, 'project.pbxproj'), 'w'))
712     def __init__(self, gbuildparser, ide):
713         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
715     def emit(self):
716         self.rootlocation = './'
717         for location in self.gbuildparser.target_by_location:
718             # module = location.split('/')[-1]
719             # module_directory = os.path.join(self.rootlocation, module)
720             for target in self.gbuildparser.target_by_location[location]:
721                 # project_path = os.path.join(module_directory, '%s.pbxroj' % target.target_name())
722                 self.write_xcodeproj(location, target)
725 class VisualStudioIntegrationGenerator(IdeIntegrationGenerator):
727     def __init__(self, gbuildparser, ide):
728         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
729         self.toolset = self.retrieve_toolset(ide)
730         self.solution_directory = './'
731         self.configurations = {
732             'Build': {
733                 'build': self.module_make_command('%(target)s'),
734                 'clean': self.module_make_command('%(target)s.clean'),
735                 'rebuild': self.module_make_command('%(target)s.clean %(target)s')
736             },
737             'Unit Tests': {
738                 'build': self.module_make_command('unitcheck'),
739                 'clean': self.module_make_command('clean'),
740                 'rebuild': self.module_make_command('clean unitcheck'),
741             },
742             'Integration tests': {
743                 'build': self.module_make_command('unitcheck slowcheck screenshot subsequentcheck'),
744                 'clean': self.module_make_command('clean'),
745                 'rebuild': self.module_make_command('clean unitcheck slowcheck screenshot subsequentcheck')
746             }
747         }
749     def retrieve_toolset(self, ide):
750         ide_toolset_map = {'vs2015': 'v140'}
751         return ide_toolset_map[ide]
753     def module_make_command(self, targets):
754         return '%(sh)s -c "PATH=\\"/bin:$PATH\\";BUILDDIR=\\"%(builddir)s\\" %(makecmd)s -rsC %(location)s ' + targets + '"'
756     class Project:
758         def __init__(self, guid, target, project_path):
759             self.guid = guid
760             self.target = target
761             self.path = project_path
763     def emit(self):
764         all_projects = []
765         for location in self.gbuildparser.target_by_location:
766             projects = []
767             module = location.split('/')[-1]
768             module_directory = os.path.join(self.solution_directory, module)
769             for target in self.gbuildparser.target_by_location[location]:
770                 project_path = os.path.join(module_directory, '%s.vcxproj' % target.target_name())
771                 project_guid = self.write_project(project_path, target)
772                 p = VisualStudioIntegrationGenerator.Project(project_guid, target, project_path)
773                 projects.append(p)
774             self.write_solution(os.path.join(module_directory, '%s.sln' % module), projects)
775             all_projects += projects
777         self.write_solution(os.path.join(self.solution_directory, 'LibreOffice.sln'), all_projects)
779     nmake_project_guid = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942'
781     def get_dependency_libs(self, linked_libs, library_projects):
782         dependency_libs = {}
783         for linked_lib in linked_libs:
784             for library_project in library_projects:
785                 if library_project.target.library_name() == linked_lib:
786                     dependency_libs[library_project.guid] = library_project
787         return dependency_libs
789     def write_solution(self, solution_path, projects):
790         print('Solution %s:' % os.path.splitext(os.path.basename(solution_path))[0], end='')
791         library_projects = [project for project in projects if project.target in self.gbuildparser.libs]
792         with open(solution_path, 'w') as f:
793             f.write('Microsoft Visual Studio Solution File, Format Version 12.00\n')
794             for project in projects:
795                 target = project.target
796                 print(' %s' % target.target_name(), end='')
797                 proj_path = os.path.relpath(project.path, os.path.abspath(os.path.dirname(solution_path)))
798                 f.write('Project("{%s}") = "%s", "%s", "{%s}"\n' %
799                         (VisualStudioIntegrationGenerator.nmake_project_guid,
800                          target.short_name(), proj_path, project.guid))
801                 libs_in_solution = self.get_dependency_libs(target.linked_libs,
802                                                             library_projects)
803                 if libs_in_solution:
804                     f.write('\tProjectSection(ProjectDependencies) = postProject\n')
805                     for lib_guid in libs_in_solution.keys():
806                         f.write('\t\t{%(guid)s} = {%(guid)s}\n' % {'guid': lib_guid})
807                     f.write('\tEndProjectSection\n')
808                 f.write('EndProject\n')
809             f.write('Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B9292527-A979-4D13-A598-C75A33222174}"\n')
810             f.write('\tProjectSection(SolutionItems) = preProject\n')
811             f.write('\t\tsolenv/vs/LibreOffice.natvis = solenv/vs/LibreOffice.natvis\n')
812             f.write('\tEndProjectSection\n')
813             f.write('EndProject\n')
814             f.write('Global\n')
815             platform = 'Win32'
816             f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
817             for cfg in self.configurations:
818                 f.write('\t\t%(cfg)s|%(platform)s = %(cfg)s|%(platform)s\n' % {'cfg': cfg, 'platform': platform})
819             f.write('\tEndGlobalSection\n')
820             f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
821             # Specifies project configurations for solution configuration
822             for project in projects:
823                 for cfg in self.configurations:
824                     params = {'guid': project.guid, 'sol_cfg': cfg, 'proj_cfg': cfg, 'platform': platform}
825                     f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.ActiveCfg = %(proj_cfg)s|%(platform)s\n' % params)
826                     # Build.0 is basically 'Build checkbox' in configuration manager
827                     f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.Build.0 = %(proj_cfg)s|%(platform)s\n' % params)
828             f.write('\tEndGlobalSection\n')
829             f.write('EndGlobal\n')
830         print('')
832     @staticmethod
833     def to_long_names(shortnames):
834         if platform == "cygwin":
835             return (subprocess.check_output(["cygpath", "-wal"] + shortnames).decode("utf-8", "strict").rstrip()).split("\n")
836         else:
837             return shortnames
839     @staticmethod
840     def defs_list(defs):
841         defines_list = []
842         # List defines
843         for key, value in defs.items():
844             define = key
845             if value is not None:
846                 define += '=' + value
847             defines_list.append(define)
848         return defines_list
850     def write_project(self, project_path, target):
851         # See info at http://blogs.msdn.com/b/visualstudio/archive/2010/05/14/a-guide-to-vcxproj-and-props-file-structure.aspx
852         folder = os.path.dirname(project_path)
853         if not os.path.exists(folder):
854             os.makedirs(folder)
855         project_guid = str(uuid.uuid4()).upper()
856         ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
857         ET.register_namespace('', ns)
858         proj_node = ET.Element('{%s}Project' % ns, DefaultTargets='Build', ToolsVersion='4.0')
859         proj_confs_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns, Label='ProjectConfigurations')
860         platform = 'Win32'
861         for configuration in self.configurations:
862             proj_conf_node = ET.SubElement(proj_confs_node,
863                                            '{%s}ProjectConfiguration' % ns,
864                                            Include='%s|%s' % (configuration, platform))
865             conf_node = ET.SubElement(proj_conf_node, '{%s}Configuration' % ns)
866             conf_node.text = configuration
867             platform_node = ET.SubElement(proj_conf_node, '{%s}Platform' % ns)
868             platform_node.text = platform
870         globals_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='Globals')
871         proj_guid_node = ET.SubElement(globals_node, '{%s}ProjectGuid' % ns)
872         proj_guid_node.text = '{%s}' % project_guid
873         proj_keyword_node = ET.SubElement(globals_node, '{%s}Keyword' % ns)
874         proj_keyword_node.text = 'MakeFileProj'
875         proj_name_node = ET.SubElement(globals_node, '{%s}ProjectName' % ns)
876         proj_name_node.text = target.short_name()
878         ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
879         for configuration in self.configurations:
880             conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label="Configuration",
881                                       Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform))
882             # Type of project used by the MSBuild to determine build process, see Microsoft.Makefile.targets
883             conf_type_node = ET.SubElement(conf_node, '{%s}ConfigurationType' % ns)
884             conf_type_node.text = 'Makefile'
885             # VS2012: I need to have this otherwise the names of projects will contain text Visual Studio 2010 in the Solution Explorer
886             platform_toolset_node = ET.SubElement(conf_node, '{%s}PlatformToolset' % ns)
887             platform_toolset_node.text = self.toolset
889         ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.props')
890         ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionSettings')
891         for configuration in self.configurations:
892             prop_sheets_node = ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='Configuration',
893                                              Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform))
894             ET.SubElement(prop_sheets_node, '{%s}Import' % ns,
895                           Project='$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props',
896                           Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')",
897                           Label='LocalAppDataPlatform')
899         ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='UserMacros')
900         # VS IDE (at least "Peek definition") is allergic to paths like "C:/PROGRA~2/WI3CF2~1/10/Include/10.0.14393.0/um"; see
901         # https://developercommunity.visualstudio.com/content/problem/139659/vc-peek-definition-fails-to-navigate-to-windows-ki.html
902         # We need to convert to long paths here. Do this once, since it's time-consuming operation.
903         include_path_node_text = ';'.join(self.to_long_names(target.include))
904         for cfg_name, cfg_targets in self.configurations.items():
905             conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns,
906                                       Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (cfg_name, platform))
907             nmake_params = {
908                 'sh': os.path.join(self.gbuildparser.binpath, 'dash.exe'),
909                 'builddir': self.gbuildparser.builddir,
910                 'location': target.location,
911                 'makecmd': self.gbuildparser.makecmd,
912                 'target': target.target_name()}
913             nmake_build_node = ET.SubElement(conf_node, '{%s}NMakeBuildCommandLine' % ns)
914             nmake_build_node.text = cfg_targets['build'] % nmake_params
915             nmake_clean_node = ET.SubElement(conf_node, '{%s}NMakeCleanCommandLine' % ns)
916             nmake_clean_node.text = cfg_targets['clean'] % nmake_params
917             nmake_rebuild_node = ET.SubElement(conf_node, '{%s}NMakeReBuildCommandLine' % ns)
918             nmake_rebuild_node.text = cfg_targets['rebuild'] % nmake_params
919             nmake_output_node = ET.SubElement(conf_node, '{%s}NMakeOutput' % ns)
920             nmake_output_node.text = os.path.join(self.gbuildparser.instdir, 'program', 'soffice.bin')
921             nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % ns)
922             nmake_defs_node.text = ';'.join(self.defs_list(target.defs) + ['$(NMakePreprocessorDefinitions)'])
923             include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % ns)
924             include_path_node.text = include_path_node_text
926         ET.SubElement(proj_node, '{%s}ItemDefinitionGroup' % ns)
928         cxxobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
929         for cxxobject in target.cxxobjects:
930             cxxabspath = os.path.join(self.gbuildparser.srcdir, cxxobject)
931             cxxfile = cxxabspath + '.cxx'
932             if os.path.isfile(cxxfile):
933                 ET.SubElement(cxxobjects_node, '{%s}ClCompile' % ns, Include=cxxfile)
934             else:
935                 print('Source %s in project %s does not exist' % (cxxfile, target.target_name()))
937         includes_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
938         for cxxobject in target.cxxobjects:
939             include_abs_path = os.path.join(self.gbuildparser.srcdir, cxxobject)
940             hxxfile = include_abs_path + '.hxx'
941             if os.path.isfile(hxxfile):
942                 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hxxfile)
943             # Few files have corresponding .h files
944             hfile = include_abs_path + '.h'
945             if os.path.isfile(hfile):
946                 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hfile)
947         ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
948         ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionTargets')
949         self.write_pretty_xml(proj_node, project_path)
950         self.write_filters(project_path + '.filters',
951                            os.path.join(self.gbuildparser.srcdir, os.path.basename(target.location)),
952                            [cxx_node.get('Include') for cxx_node in cxxobjects_node.findall('{%s}ClCompile' % ns)],
953                            [include_node.get('Include') for include_node in includes_node.findall('{%s}ClInclude' % ns)])
954         return project_guid
956     def get_filter(self, module_dir, proj_file):
957         return '\\'.join(os.path.relpath(proj_file, module_dir).split('/')[:-1])
959     def get_subfilters(self, proj_filter):
960         parts = proj_filter.split('\\')
961         subfilters = set([proj_filter]) if proj_filter else set()
962         for i in range(1, len(parts)):
963             subfilters.add('\\'.join(parts[:i]))
964         return subfilters
966     def write_pretty_xml(self, node, file_path):
967         xml_str = ET.tostring(node, encoding='unicode')
968         pretty_str = minidom.parseString(xml_str).toprettyxml(encoding='utf-8')
969         with open(file_path, 'w') as f:
970             f.write(pretty_str.decode())
972     def add_nodes(self, files_node, module_dir, tag, project_files):
973         ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
974         filters = set()
975         for project_file in project_files:
976             file_node = ET.SubElement(files_node, tag, Include=project_file)
977             if os.path.commonprefix([module_dir, project_file]) == module_dir:
978                 project_filter = self.get_filter(module_dir, project_file)
979                 filter_node = ET.SubElement(file_node, '{%s}Filter' % ns)
980                 filter_node.text = project_filter
981                 filters |= self.get_subfilters(project_filter)
982         return filters
984     def write_filters(self, filters_path, module_dir, compile_files, include_files):
985         ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
986         ET.register_namespace('', ns)
987         proj_node = ET.Element('{%s}Project' % ns, ToolsVersion='4.0')
988         filters = set()
989         compiles_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
990         filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, compile_files)
991         include_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
992         filters |= self.add_nodes(include_node, module_dir, '{%s}ClInclude' % ns, include_files)
994         filters_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
995         for proj_filter in filters:
996             filter_node = ET.SubElement(filters_node, '{%s}Filter' % ns, Include=proj_filter)
997             filter_id_node = ET.SubElement(filter_node, '{%s}UniqueIdentifier' % ns)
998             filter_id_node.text = '{%s}' % str(uuid.uuid4())
999         self.write_pretty_xml(proj_node, filters_path)
1002 class QtCreatorIntegrationGenerator(IdeIntegrationGenerator):
1004     def __init__(self, gbuildparser, ide):
1005         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
1006         self.target_by_location = {}
1007         for target in set(self.gbuildparser.libs) | set(self.gbuildparser.exes):
1008             if target.location not in self.target_by_location:
1009                 self.target_by_location[target.location] = set()
1010             self.target_by_location[target.location] |= set([target])
1012         self._do_log = False  # set to 'True' to activate log of QtCreatorIntegrationGenerator
1013         if self._do_log:
1014             qtlog_path = os.path.abspath('../qtlog_.txt')
1015             self.qtlog = open(qtlog_path, 'w')
1017     def _log(self, message):
1018         if self._do_log:
1019             self.qtlog.write(message)
1021     def log_close(self):
1022         if self._do_log:
1023             self.qtlog.close()
1025     def generate_build_configs(self, lib_folder):
1026         module_folder = os.path.join(self.base_folder, lib_folder)
1027         xml = ""
1028         # In QtCreator UI, build configs are listed alphabetically,
1029         # so it can be different from the creation order.
1030         # So we prefix the names with the index.
1031         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1032             'index': '0',
1033             'base_folder': module_folder,
1034             'arg': "",
1035             'name': "1-Build %s" % lib_folder,
1036         }
1037         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1038             'index': '1',
1039             'base_folder': module_folder,
1040             'arg': "unitcheck",
1041             'name': "2-Local tests -- quick tests (unitcheck)",
1042         }
1043         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1044             'index': '2',
1045             'base_folder': module_folder,
1046             'arg': "unitcheck slowcheck screenshot",
1047             'name': "3-Local tests -- slow tests (unitcheck, slowcheck, screenshot)",
1048         }
1049         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1050             'index': '3',
1051             'base_folder': module_folder,
1052             'arg': "unitcheck slowcheck screenshot subsequentcheck",
1053             'name': "4-Local tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)",
1054         }
1055         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1056             'index': '4',
1057             'base_folder': self.base_folder,
1058             'arg': "unitcheck",
1059             'name': "5-Global tests -- quick tests (unitcheck)",
1060         }
1061         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1062             'index': '5',
1063             'base_folder': self.base_folder,
1064             'arg': "unitcheck slowcheck screenshot",
1065             'name': "6-Global tests -- slow tests (unitcheck, slowcheck, screenshot)",
1066         }
1067         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1068             'index': '6',
1069             'base_folder': self.base_folder,
1070             'arg': "unitcheck slowcheck screenshot subsequentcheck",
1071             'name': "7-Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)",
1072         }
1073         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1074             'index': '7',
1075             'base_folder': self.base_folder,
1076             'arg': "build-nocheck",
1077             'name': "8-Global build -- nocheck",
1078         }
1079         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1080             'index': '8',
1081             'base_folder': self.base_folder,
1082             'arg': "",
1083             'name': "9-Global build",
1084         }
1086         xml += QtCreatorIntegrationGenerator.build_configs_count_template % {
1087             'nb': '9',
1088         }
1089         return xml
1091     def generate_meta_build_configs(self):
1092         xml = ""
1093         # In QtCreator UI, build configs are listed alphabetically,
1094         # so it can be different from the creation order.
1095         # So we prefix the names with the index.
1096         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1097             'index': '0',
1098             'base_folder': self.base_folder,
1099             'arg': "",
1100             'name': "01-Global Build",
1101         }
1102         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1103             'index': '1',
1104             'base_folder': self.base_folder,
1105             'arg': "unitcheck",
1106             'name': "02-Global tests -- quick tests (unitcheck)",
1107         }
1108         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1109             'index': '2',
1110             'base_folder': self.base_folder,
1111             'arg': "unitcheck slowcheck screenshot",
1112             'name': "03-Global tests -- slow tests (unitcheck, slowcheck, screenshot)",
1113         }
1114         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1115             'index': '3',
1116             'base_folder': self.base_folder,
1117             'arg': "unitcheck slowcheck screenshot subsequentcheck",
1118             'name': "04-Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)",
1119         }
1120         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1121             'index': '4',
1122             'base_folder': self.base_folder,
1123             'arg': "perfcheck",
1124             'name': "05-Global tests -- performance tests (perfcheck)",
1125         }
1126         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1127             'index': '5',
1128             'base_folder': self.base_folder,
1129             'arg': "check",
1130             'name': "06-Global tests -- tests (check)",
1131         }
1132         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1133             'index': '6',
1134             'base_folder': self.base_folder,
1135             'arg': "build-nocheck",
1136             'name': "07-Global build -- nocheck",
1137         }
1138         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1139             'index': '7',
1140             'base_folder': self.base_folder,
1141             'arg': "build-l10n-only",
1142             'name': "08-Global build -- build-l10n-only",
1143         }
1144         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1145             'index': '8',
1146             'base_folder': self.base_folder,
1147             'arg': "build-non-l10n-only",
1148             'name': "09-Global build -- build-non-l10n-only",
1149         }
1150         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1151             'index': '9',
1152             'base_folder': self.base_folder,
1153             'arg': "clean",
1154             'name': "10-Global build -- clean",
1155         }
1156         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1157             'index': '10',
1158             'base_folder': self.base_folder,
1159             'arg': "clean-build",
1160             'name': "11-Global build -- clean-build",
1161         }
1162         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1163             'index': '11',
1164             'base_folder': self.base_folder,
1165             'arg': "clean-host",
1166             'name': "12-Global build -- clean-host",
1167         }
1168         xml += QtCreatorIntegrationGenerator.build_configs_count_template % {
1169             'nb': '12',
1170         }
1171         return xml
1173     # By default, QtCreator creates 2 BuildStepList : "Build" et "Clean"
1174     # but the "clean" can be empty.
1175     build_configs_template = """
1176     <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.%(index)s">
1177     <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">%(base_folder)s</value>
1179     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
1181      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
1182       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
1183       <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
1184       <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1185       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
1186       <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
1187        <value type="QString">-w</value>
1188        <value type="QString">-r</value>
1189       </valuelist>
1190       <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
1191       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">%(arg)s</value>
1192       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
1193      </valuemap>
1195      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
1196      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
1197      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1198      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
1199     </valuemap>
1201     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
1202     <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
1203     <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
1204     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">%(name)s</value>
1205     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1206     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
1207     <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">%(index)s</value>
1208     <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
1209    </valuemap>
1210    """
1212     build_configs_count_template = """
1213    <!-- nb build configurations -->
1214    <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">%(nb)s</value>
1215    """
1217     def generate_deploy_configs(self, lib_folder):
1218         xml = QtCreatorIntegrationGenerator.deploy_configs_template % {}
1219         return xml
1221     deploy_configs_template = """
1222    <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
1223     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
1224      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
1225      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
1226      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1227      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
1228     </valuemap>
1229     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
1230     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
1231     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1232     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
1233    </valuemap>
1234    <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
1235     """
1237     def generate_run_configs(self, lib_folder):
1239         # If we use 'soffice', it's ok only for "Run", not for "Debug".
1240         # So we put "soffice.bin" that is ok for both.
1241         loexec = "%s/instdir/program/soffice.bin" % self.base_folder
1242         xml = QtCreatorIntegrationGenerator.run_configs_template % {
1243             'loexec': loexec,
1244             'workdir': self.base_folder
1245         }
1246         return xml
1248     run_configs_template = """
1249    <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
1250     <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
1251     <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
1252     <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
1253     <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
1254     <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
1255     <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
1256     <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
1257     <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
1258     <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
1259     <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
1260     <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
1261     <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
1262     <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
1263     <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
1264     <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
1265     <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
1266     <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
1267     <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
1268      <value type="int">0</value>
1269      <value type="int">1</value>
1270      <value type="int">2</value>
1271      <value type="int">3</value>
1272      <value type="int">4</value>
1273      <value type="int">5</value>
1274      <value type="int">6</value>
1275      <value type="int">7</value>
1276      <value type="int">8</value>
1277      <value type="int">9</value>
1278      <value type="int">10</value>
1279      <value type="int">11</value>
1280      <value type="int">12</value>
1281      <value type="int">13</value>
1282      <value type="int">14</value>
1283     </valuelist>
1284     <value type="int" key="PE.EnvironmentAspect.Base">2</value>
1285     <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
1287     <value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments"></value>
1288     <value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%(loexec)s</value>
1289     <value type="bool" key="ProjectExplorer.CustomExecutableRunConfiguration.UseTerminal">false</value>
1290     <value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory">%(workdir)s</value>
1291     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run libreoffice/instdir/program/soffice</value>
1292     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1293     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
1294     <value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
1295     <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
1296     <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
1297     <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
1298     <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
1299     <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
1301    </valuemap>
1302    <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
1303     """
1305     def generate_pro_user_content(self, lib_folder):
1307         build_configs = self.generate_build_configs(lib_folder)
1308         deploy_configs = self.generate_deploy_configs(lib_folder)
1309         run_configs = self.generate_run_configs(lib_folder)
1311         xml = QtCreatorIntegrationGenerator.pro_user_template % {
1312             'build_configs': build_configs,
1313             'deploy_configs': deploy_configs,
1314             'run_configs': run_configs,
1315         }
1316         return xml
1318     def generate_meta_pro_user_content(self):
1320         build_configs = self.generate_meta_build_configs()
1321         deploy_configs = self.generate_deploy_configs("")
1322         run_configs = self.generate_run_configs("")
1324         xml = QtCreatorIntegrationGenerator.pro_user_template % {
1325             'build_configs': build_configs,
1326             'deploy_configs': deploy_configs,
1327             'run_configs': run_configs,
1328         }
1329         return xml
1331     pro_user_template = """<?xml version="1.0" encoding="UTF-8"?>
1332 <!DOCTYPE QtCreatorProject>
1333 <!-- Written by QtCreator 3.1.1, 2015-05-14T15:54:34. -->
1334 <qtcreator>
1335  <data>
1336   <variable>ProjectExplorer.Project.ActiveTarget</variable>
1337   <value type="int">0</value>
1338  </data>
1340  <!-- editor settings -->
1341  <data>
1342   <variable>ProjectExplorer.Project.EditorSettings</variable>
1343   <valuemap type="QVariantMap">
1344    <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
1345    <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
1346    <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
1347    <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
1348     <value type="QString" key="language">Cpp</value>
1349     <valuemap type="QVariantMap" key="value">
1350      <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
1351     </valuemap>
1352    </valuemap>
1353    <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
1354     <value type="QString" key="language">QmlJS</value>
1355     <valuemap type="QVariantMap" key="value">
1356      <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
1357     </valuemap>
1358    </valuemap>
1359    <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
1360    <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
1361    <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
1362    <value type="int" key="EditorConfiguration.IndentSize">4</value>
1363    <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
1364    <value type="int" key="EditorConfiguration.MarginColumn">80</value>
1365    <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
1366    <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
1367    <value type="int" key="EditorConfiguration.PaddingMode">1</value>
1368    <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
1369    <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
1370    <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
1371    <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
1372    <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
1373    <value type="int" key="EditorConfiguration.TabSize">8</value>
1374    <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
1375    <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
1376    <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
1377    <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
1378    <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
1379    <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
1380   </valuemap>
1381  </data>
1383  <data>
1384   <variable>ProjectExplorer.Project.PluginSettings</variable>
1385   <valuemap type="QVariantMap"/>
1386  </data>
1388  <!-- target -->
1389  <data>
1390   <variable>ProjectExplorer.Project.Target.0</variable>
1391   <valuemap type="QVariantMap">
1392    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
1393    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
1394    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{0701de51-c96e-4e4f-85c3-e70b223c5076}</value>
1395    <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
1396    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
1397    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
1399    <!-- build configurations -->
1400    %(build_configs)s
1402    <!-- deploy configurations -->
1403    %(deploy_configs)s
1405    <!-- plugin settings -->
1406    <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
1408    <!-- run configurations -->
1409    %(run_configs)s
1411   </valuemap>
1412  </data>
1413  <!-- nb targets : 1 -->
1414  <data>
1415   <variable>ProjectExplorer.Project.TargetCount</variable>
1416   <value type="int">1</value>
1417  </data>
1418  <data>
1419   <variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
1420   <value type="QByteArray">{5abcafed-86f6-49f6-b1cb-380fadd21211}</value>
1421  </data>
1422  <data>
1423   <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
1424   <value type="int">15</value>
1425  </data>
1426 </qtcreator>
1429     def remove_qt_files(self):
1431         def do_remove_file(loc, afile):
1432             try:
1433                 os.remove(os.path.join(loc, afile))
1434                 self._log("removed %s\n" % afile)
1435             except OSError:
1436                 self._log("unable to remove %s\n" % afile)
1438         do_remove_file(self.base_folder, "lo.pro")
1439         do_remove_file(self.base_folder, "lo.pro.user")
1440         for location in self.target_by_location:
1441             for f in os.listdir(location):
1442                 if f.endswith('.pro') or f.endswith('.pro.user'):
1443                     do_remove_file(location, f)
1445     def get_source_extension(self, src_file):
1446         path = os.path.join(self.base_folder, src_file)
1447         for ext in (".cxx", ".cpp", ".c", ".mm"):
1448             if os.path.isfile(path + ext):
1449                 return ext
1450         return ""
1452     def get_header_extension(self, src_file):
1453         path = os.path.join(self.base_folder, src_file)
1454         for ext in (".hxx", ".hpp", ".h"):
1455             if os.path.isfile(path + ext):
1456                 return ext
1457         return ""
1459     def build_data_libs(self):
1461         self.data_libs = {}
1463         all_libs = set(self.gbuildparser.libs) | set(self.gbuildparser.exes)
1464         for lib in all_libs:
1465             self._log("\nlibrary : %s, loc=%s" % (lib.short_name(), lib.location))
1466             lib_name = os.path.basename(lib.location)
1467             lib_folder = os.path.relpath(lib.location, self.base_folder)
1469             def lopath(path):
1470                 return os.path.relpath(path, lib.location)
1472             defines_list = []
1473             sources_list = []
1474             includepath_list = []
1475             # The explicit headers list is not mandatory :
1476             # QtCreator just needs 'include_path_list' to find all headers files.
1477             # But files listed in 'header_list' will be shown
1478             # in a specific "Headers" folder in QtCreator's Project panel.
1479             # We will list here only headers files of current lib.
1480             headers_list = []
1481             for file_ in lib.cxxobjects:
1482                 # the file has no extension : search it
1483                 # self._log("\n    file : %s" % file_)
1484                 ext = self.get_source_extension(file_)
1485                 if ext:
1486                     sources_list.append(lopath(file_ + ext))
1488                 # few cxxobject files have a header beside
1489                 ext = self.get_header_extension(file_)
1490                 if ext:
1491                     headers_list.append(lopath(file_ + ext))
1493             # List all include paths
1494             for hdir in lib.include:
1495                 hf_lopath = lopath(hdir)
1496                 includepath_list.append(hf_lopath)
1498             # List headers files from current lib
1499             for hdir in lib.include:
1500                 if hdir.startswith(lib.location):
1501                     for hf in os.listdir(hdir):
1502                         if hf.endswith(('.h', '.hxx', '.hpp', '.hrc')):
1503                             hf_lopath = lopath(os.path.join(hdir, hf))
1504                             headers_list.append(hf_lopath)
1506             # List defines
1507             for key, value in lib.defs.items():
1508                 define = key
1509                 if value is not None:
1510                     define += '=' + value
1511                 defines_list.append(define)
1513             # All datas are prepared, store them for the lib.
1514             if lib_folder in self.data_libs:
1515                 self.data_libs[lib_folder]['sources'] |= set(sources_list)
1516                 self.data_libs[lib_folder]['headers'] |= set(headers_list)
1517                 self.data_libs[lib_folder]['includepath'] |= set(includepath_list)
1518                 self.data_libs[lib_folder]['defines'] |= set(defines_list)
1519             else:
1520                 self.data_libs[lib_folder] = {
1521                     'sources': set(sources_list),
1522                     'headers': set(headers_list),
1523                     'includepath': set(includepath_list),
1524                     'defines': set(defines_list),
1525                     'loc': lib.location,
1526                     'name': lib_name
1527                 }
1529     def emit(self):
1531         self.base_folder = self.gbuildparser.builddir
1533         # we remove existing '.pro' and '.pro.user' files
1534         self.remove_qt_files()
1536         # for .pro files, we must explicitly list all files (.c, .h)
1537         # so we can't reuse directly the same method than for kde integration.
1538         self.build_data_libs()
1540         subdirs_list = self.data_libs.keys()
1541         # Now we can create Qt files
1542         for lib_folder in subdirs_list:
1543             sources_list = sorted(self.data_libs[lib_folder]['sources'])
1544             headers_list = sorted(self.data_libs[lib_folder]['headers'])
1545             includepath_list = sorted(self.data_libs[lib_folder]['includepath'])
1546             defines_list = sorted(self.data_libs[lib_folder]['defines'])
1547             lib_loc = self.data_libs[lib_folder]['loc']
1548             lib_name = self.data_libs[lib_folder]['name']
1550             sources = " \\\n".join(sources_list)
1551             headers = " \\\n".join(headers_list)
1552             includepath = " \\\n".join(includepath_list)
1553             defines = " \\\n".join(defines_list)
1555             # create .pro file
1556             qt_pro_file = '%s/%s.pro' % (lib_loc, lib_name)
1557             try:
1558                 content = QtCreatorIntegrationGenerator.pro_template % {'sources': sources, 'headers': headers, 'includepath': includepath, 'defines': defines}
1559                 mode = 'w+'
1560                 with open(qt_pro_file, mode) as fpro:
1561                     fpro.write(content)
1562                 self._log("created %s\n" % qt_pro_file)
1564             except Exception as e:
1565                 print("ERROR : creating pro file=" + qt_pro_file, file=sys.stderr)
1566                 print(e, file=sys.stderr)
1567                 temp = traceback.format_exc()  # .decode('utf8')
1568                 print(temp, file=sys.stderr)
1569                 print("\n\n", file=sys.stderr)
1571             # create .pro.user file
1572             qt_pro_user_file = '%s/%s.pro.user' % (lib_loc, lib_name)
1573             try:
1574                 with open(qt_pro_user_file, mode) as fprouser:
1575                     fprouser.write(self.generate_pro_user_content(lib_folder))
1576                 self._log("created %s\n" % qt_pro_user_file)
1578             except Exception as e:
1579                 print("ERROR : creating pro.user file=" + qt_pro_user_file, file=sys.stderr)
1580                 print(e, file=sys.stderr)
1581                 temp = traceback.format_exc()
1582                 print(temp, file=sys.stderr)
1583                 print("\n\n", file=sys.stderr)
1585         # create meta .pro file (lists all sub projects)
1586         qt_meta_pro_file = 'lo.pro'
1587         try:
1588             subdirs = " \\\n".join(subdirs_list)
1589             content = QtCreatorIntegrationGenerator.pro_meta_template % {'subdirs': subdirs}
1590             with open(qt_meta_pro_file, 'w+') as fmpro:
1591                 fmpro.write(content)
1593         except Exception as e:
1594             print("ERROR : creating lo.pro file=" + qt_meta_pro_file, file=sys.stderr)
1595             print(e, file=sys.stderr)
1596             temp = traceback.format_exc()
1597             print(temp, file=sys.stderr)
1598             print("\n\n", file=sys.stderr)
1600         # create meta .pro.user file
1601         qt_meta_pro_user_file = 'lo.pro.user'
1602         try:
1603             with open(qt_meta_pro_user_file, mode) as fmprouser:
1604                 fmprouser.write(self.generate_meta_pro_user_content())
1605             self._log("created %s\n" % qt_meta_pro_user_file)
1607         except Exception as e:
1608             print("ERROR : creating lo.pro.user file=" + qt_meta_pro_user_file, file=sys.stderr)
1609             print(e, file=sys.stderr)
1610             temp = traceback.format_exc()
1611             print(temp, file=sys.stderr)
1612             print("\n\n", file=sys.stderr)
1614         self.log_close()
1616     pro_template = """TEMPLATE = app
1617 CONFIG += console
1618 CONFIG -= app_bundle
1619 CONFIG -= qt
1621 INCLUDEPATH += %(includepath)s
1623 SOURCES += %(sources)s
1625 HEADERS += %(headers)s
1627 DEFINES += %(defines)s
1630     pro_meta_template = """TEMPLATE = subdirs
1632 SUBDIRS = %(subdirs)s
1636 def get_options():
1637     parser = argparse.ArgumentParser(
1638         description='LibreOffice gbuild IDE project generator')
1639     parser.add_argument('--ide', dest='ide', required=True,
1640                         help='the IDE to generate project files for')
1641     parser.add_argument('--make', dest='makecmd', required=True,
1642                         help='the command to execute make')
1643     return parser.parse_args()
1646 if __name__ == '__main__':
1647     args = get_options()
1648     # FIXME: Hack
1649     if args.makecmd == 'make':
1650         args.makecmd = '/usr/bin/make'
1652     paths = {}
1653     generators = {
1654         'eclipsecdt': EclipseCDTIntegrationGenerator,
1655         'kdevelop': KdevelopIntegrationGenerator,
1656         'xcode': XcodeIntegrationGenerator,
1657         'vs2015': VisualStudioIntegrationGenerator,
1658         'vim': VimIntegrationGenerator,
1659         'debug': DebugIntegrationGenerator,
1660         'qtcreator': QtCreatorIntegrationGenerator,
1661     }
1663     if args.ide not in generators.keys():
1664         print("Invalid ide. valid values are %s" % ','.join(generators.keys()))
1665         sys.exit(1)
1667     gbuildparser = GbuildParser(args.makecmd).parse()
1669     generators[args.ide](gbuildparser, args.ide).emit()
1670     print("Successfully created the project files.")
1672 # Local Variables:
1673 # indent-tabs-mode: nil
1674 # End:
1676 # vim: set et sw=4 ts=4: