Remove duplicated include
[LibreOffice.git] / bin / gbuild-to-ide
blobce89a979a1d0be380c46fe62d896846e265576a9
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 ntpath
13 import os
14 import os.path
15 import shutil
16 import re
17 import sys
18 import uuid
19 import json
20 import xml.etree.ElementTree as ET
21 import xml.dom.minidom as minidom
22 import traceback
23 import subprocess
24 from sys import platform
25 import collections
26 import urllib.parse
28 class GbuildLinkTarget:
29     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target):
30         (self.name, self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.cxxflags, self.cobjects, self.objcxxobjects, self.cflags, self.linked_libs, self.link_target) = (
31             name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target)
33     def short_name(self):
34         return self.name
36     def is_empty(self):
37         return not self.include and not self.defs and not self.cxxobjects and not self.cobjects and not self.linked_libs
39     def __str__(self):
40         return '%s at %s with include path: %s, isystem includes: %s, defines: %s, objects: %s, cxxflags: %s, cobjects: %s, cflags: %s and linked libs: %s' % (
41             self.short_name(), self.location, self.include, self.include_sys, self.defs, self.cxxobjects,
42             self.cxxflags, self.cobjects, self.cflags, self.linked_libs)
45 class GbuildLib(GbuildLinkTarget):
46     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target):
47         GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target)
49     def short_name(self):
50         """Return the short name of target based on the Library_* makefile name"""
51         return 'Library %s' % self.name
53     def target_name(self):
54         return 'Library_%s' % self.name
56     def library_name(self):
57         return self.name
59 class GbuildTest(GbuildLinkTarget):
60     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target):
61         GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target)
63     def short_name(self):
64         """Return the short name of target based n the CppunitTest_* makefile names"""
65         return 'CppunitTest %s' % self.name
67     def target_name(self):
68         return 'CppunitTest_%s' % self.name
70 class GbuildExe(GbuildLinkTarget):
71     def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target):
72         GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, cobjects, objcxxobjects, cflags, linked_libs, link_target)
74     def short_name(self):
75         """Return the short name of target based on the Executable_* makefile name"""
76         return 'Executable %s' % self.name
78     def target_name(self):
79         return 'Executable_%s' % self.name
82 class GbuildParser:
83     """Main data model object.
85     Attributes:
86         target_by_path     : dict[path:string, set(target)]
87                                where target is one of the GbuildLinkTarget subclasses
88         target_by_location : dict[path:string, set(target)]
89                                where target is one of the GbuildLinkTarget subclasses
90     """
91     def __init__(self, makecmd):
92         self.makecmd = makecmd
93         self.binpath = os.path.dirname(os.environ['GPERF']) # woha, this is quite a hack
94         (self.srcdir, self.builddir, self.instdir, self.workdir) = (os.environ['SRCDIR'], os.environ['BUILDDIR'], os.environ['INSTDIR'], os.environ['WORKDIR'])
95         (self.libs, self.exes, self.tests, self.modulenamelist) = ([], [], [], [])
96         (self.target_by_path, self.target_by_location) = ({}, {})
98     includepattern = re.compile(r'-I(\S+)')
99     isystempattern = re.compile(r'-isystem\s*(\S+)')
100     warningpattern = re.compile(r'-W\S+')
101     libpattern = re.compile(r'Library_(.*)\.mk')
102     exepattern = re.compile(r'Executable_(.*)\.mk')
103     testpattern = re.compile(r'CppunitTest_(.*)\.mk')
105     @staticmethod
106     def __split_includes(includes):
107         foundisystem = GbuildParser.isystempattern.findall(includes)
108         foundincludes = [includeswitch.strip() for includeswitch in GbuildParser.includepattern.findall(includes) if
109                 len(includeswitch) > 2]
110         return (foundincludes, foundisystem)
112     @staticmethod
113     def __split_objs(objsline):
114         return [obj for obj in objsline.strip().split(' ') if len(obj) > 0 and obj != 'CXXOBJECTS' and obj != 'COBJECTS' and obj != 'OBJCXXOBJECTS' and obj != '+=']
116     @staticmethod
117     def __split_defs(defsline):
118         defs = {}
119         alldefs = [defswitch.strip() for defswitch in defsline.strip().lstrip('-D').split(' -D') if len(defswitch) > 2]
120         for d in alldefs:
121             dparts = d.split(' -U')
122             """after dparts.pop(0), dparts will contain only undefs"""
123             defparts = dparts.pop(0).strip().split('=')
124             if len(defparts) == 1:
125                 defparts.append(None)
126             defs[defparts[0]] = defparts[1]
127             """Drop undefed items (if any) from previous defs"""
128             for u in dparts:
129                 defs.pop(u.strip(), '')
130         defs["LIBO_INTERNAL_ONLY"] = None
131         return defs
133     @staticmethod
134     def __split_flags(flagsline, flagslineappend):
135         return [cxxflag.strip() for cxxflag in GbuildParser.warningpattern.sub('', '%s %s' % (flagsline, flagslineappend)).split(' ') if len(cxxflag) > 1]
137     @staticmethod
138     def __lib_from_json(json):
139         (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE'])
140         return GbuildLib(
141             GbuildParser.libpattern.match(os.path.basename(json['MAKEFILE'])).group(1),
142             os.path.dirname(json['MAKEFILE']),
143             foundincludes,
144             foundisystem,
145             GbuildParser.__split_defs(json['DEFS']),
146             GbuildParser.__split_objs(json['CXXOBJECTS']),
147             GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']),
148             GbuildParser.__split_objs(json['COBJECTS']),
149             GbuildParser.__split_objs(json['OBJCXXOBJECTS']),
150             GbuildParser.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']),
151             json['LINKED_LIBS'].strip().split(' '),
152             json['LINKTARGET'].strip())
154     @staticmethod
155     def __test_from_json(json):
156         (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE'])
157         testname_match = GbuildParser.testpattern.match(os.path.basename(json['MAKEFILE']))
159         # Workaround strange writer test makefile setup
160         if testname_match is None:
161             testname = "StrangeWriterMakefiles"
162         else:
163             testname = testname_match.group(1)
165         return GbuildTest(
166             testname,
167             os.path.dirname(json['MAKEFILE']),
168             foundincludes,
169             foundisystem,
170             GbuildParser.__split_defs(json['DEFS']),
171             GbuildParser.__split_objs(json['CXXOBJECTS']),
172             GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']),
173             GbuildParser.__split_objs(json['COBJECTS']),
174             GbuildParser.__split_objs(json['OBJCXXOBJECTS']),
175             GbuildParser.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']),
176             json['LINKED_LIBS'].strip().split(' '),
177             json['LINKTARGET'].strip())
179     @staticmethod
180     def __exe_from_json(json):
181         (foundincludes, foundisystem) = GbuildParser.__split_includes(json['INCLUDE'])
182         return GbuildExe(
183             GbuildParser.exepattern.match(os.path.basename(json['MAKEFILE'])).group(1),
184             os.path.dirname(json['MAKEFILE']),
185             foundincludes,
186             foundisystem,
187             GbuildParser.__split_defs(json['DEFS']),
188             GbuildParser.__split_objs(json['CXXOBJECTS']),
189             GbuildParser.__split_flags(json['CXXFLAGS'], json['CXXFLAGSAPPEND']),
190             GbuildParser.__split_objs(json['COBJECTS']),
191             GbuildParser.__split_objs(json['OBJCXXOBJECTS']),
192             GbuildParser.__split_flags(json['CFLAGS'], json['CFLAGSAPPEND']),
193             json['LINKED_LIBS'].strip().split(' '),
194             json['LINKTARGET'].strip())
196     def parse(self):
197         for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'Library')):
198             with open(os.path.join(self.workdir, 'GbuildToJson', 'Library', jsonfilename), 'r') as f:
199                 lib = self.__lib_from_json(json.load(f))
200                 self.libs.append(lib)
201         for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'Executable')):
202             with open(os.path.join(self.workdir, 'GbuildToJson', 'Executable', jsonfilename), 'r') as f:
203                 exe = self.__exe_from_json(json.load(f))
204                 self.exes.append(exe)
205         for jsonfilename in os.listdir(os.path.join(self.workdir, 'GbuildToJson', 'CppunitTest')):
206             with open(os.path.join(self.workdir, 'GbuildToJson', 'CppunitTest', jsonfilename), 'r') as f:
207                 test = self.__test_from_json(json.load(f))
208                 self.tests.append(test)
209         for target in set(self.libs) | set(self.exes) | set(self.tests):
210             if target.location not in self.target_by_location:
211                 self.target_by_location[target.location] = set()
212             self.target_by_location[target.location] |= set([target])
213             for cxx in target.cxxobjects:
214                 path = '/'.join(cxx.split('/')[:-1])
215                 if path not in self.target_by_path:
216                     self.target_by_path[path] = set()
217                 self.target_by_path[path] |= set([target])
218             for c in target.cobjects:
219                 path = '/'.join(c.split('/')[:-1])
220                 if path not in self.target_by_path:
221                     self.target_by_path[path] = set()
222                 self.target_by_path[path] |= set([target])
223         for location in self.target_by_location:
224             self.modulenamelist.append(os.path.split(location)[1])
225         return self
228 class IdeIntegrationGenerator:
230     def __init__(self, gbuildparser, ide):
231         self.gbuildparser = gbuildparser
232         self.ide = ide
234     def emit(self):
235         pass
237 class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator):
239     def __init__(self, gbuildparser, ide):
240         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
242     def create_include_paths(self):
243         for module in self.gbuildparser.modulenamelist:
244             modulepath = os.path.join(self.gbuildparser.builddir, module)
245             includedirfile = open(os.path.join(modulepath, '.eclipsesettingfile'), 'w')
246             modulelibs = []
247             for lib in self.gbuildparser.target_by_path.keys():
248                 if lib.startswith(module+'/'):
249                     modulelibs.append(lib)
250             include = set()
251             for lib in modulelibs:
252                 for target in self.gbuildparser.target_by_path[lib]:
253                     include |= set(target.include)
254             includedirfile.write('\n'.join(include))
255             includedirfile.close()
258     def create_macros(self):
259         for module in self.gbuildparser.modulenamelist:
260             modulepath = os.path.join(self.gbuildparser.builddir, module)
261             macrofile = open(os.path.join(modulepath, '.macros'), 'w')
262             modulelibs = []
263             for lib in self.gbuildparser.target_by_path.keys():
264                 if lib.startswith(module+'/'):
265                     modulelibs.append(lib)
266             define = []
267             defineset = set()
268             for lib in modulelibs:
269                 for target in self.gbuildparser.target_by_path[lib]:
270                     for i in target.defs.keys():
271                         tmp = str(i) +','+str(target.defs[i])
272                         if tmp not in defineset:
273                             defineset.add(tmp)
274             macrofile.write('\n'.join(defineset))
275             macrofile.close()
278     def create_settings_file(self):
280         settingsfiletemplate = """\
281 <?xml version="1.0" encoding="UTF-8"?>
282 <cdtprojectproperties>
283 <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
284 <language name="C++ Source File">
287 </language>
288 <language name="C Source File">
290 </language>
291 <language name="Object File">
293 </language>
294 <language name="Assembly Source File">
296 </language>
297 </section>
298 <section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.Macros">
299 <language name="C++ Source File">
301 </language>
302 <language name="C Source File">
304 </language>
305 <language name="Object File">
307 </language>
308 <language name="Assembly Source File">
310 </language>
311 </section>
312 </cdtprojectproperties>
315         for module in self.gbuildparser.modulenamelist:
316             tempxml = []
317             modulepath = os.path.join(self.gbuildparser.builddir, module)
319             settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'w')
320             settingsfile.write(settingsfiletemplate)
321             settingsfile.close()
323             settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'r')
324             tempxml = settingsfile.readlines()
325             tempinclude = open(os.path.join(modulepath, '.eclipsesettingfile'), 'r')
326             tempmacro = open(os.path.join(modulepath, '.macros'), 'r')
327             for includepath in tempinclude:
328                 if includepath[-1:] == "\n":
329                     includepath = includepath[:-1]
330                 templine = "<includepath>%s</includepath>\n" % includepath
331                 tempxml.insert(5, templine)
333             for line in tempmacro:
334                 macroskeyvalue = line.split(',')
335                 macrokey = macroskeyvalue[0]
336                 macrovalue = macroskeyvalue[1]
337                 if macrovalue[-1:] == "\n":
338                     macrovalue = macrovalue[:-1]
339                 templine = "<macro><name>%s</name><value>%s</value></macro>\n" %(macrokey, macrovalue)
340                 tempxml.insert(-13, templine)
341             tempxml="".join(tempxml)
342             settingsfile.close
344             settingsfile = open(os.path.join(modulepath, 'eclipsesettingfile.xml'), 'w')
345             settingsfile.write(tempxml)
346             settingsfile.close()
347             os.remove(os.path.join(modulepath, '.eclipsesettingfile'))
348             os.remove(os.path.join(modulepath, '.macros'))
350     def emit(self):
351         self.create_include_paths()
352         self.create_macros()
353         self.create_settings_file()
355 class CodeliteIntegrationGenerator(IdeIntegrationGenerator):
357     def __init__(self, gbuildparser, ide):
358         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
360     def emit(self):
361         self.create_workspace_file()
362         for module in self.gbuildparser.modulenamelist:
363             self.create_project_file(module)
364         #self.create_project_file('vcl')
366     def create_workspace_file(self):
367         root_node = ET.Element('CodeLite_Workspace', Name='libo2', Database='./libo2.tags', Version='10.0.0')
368         for module in self.gbuildparser.modulenamelist:
369             ET.SubElement(root_node, 'Project', Name=module, Path='%s/%s.project' % (module, module), Active='No')
370         build_matrix_node = ET.SubElement(root_node, 'BuildMatrix')
371         workspace_config_node = ET.SubElement(build_matrix_node, 'WorkspaceConfiguration', Name='Debug', Selected='yes')
372         ET.SubElement(workspace_config_node, 'Environment')
373         for module in self.gbuildparser.modulenamelist:
374             ET.SubElement(workspace_config_node, 'Project', Name=module, ConfigName='Debug')
375         workspace_config_node = ET.SubElement(build_matrix_node, 'WorkspaceConfiguration', Name='Release', Selected='yes')
376         ET.SubElement(workspace_config_node, 'Environment')
377         for module in self.gbuildparser.modulenamelist:
378             ET.SubElement(workspace_config_node, 'Project', Name=module, ConfigName='Release')
380         self.write_pretty_xml(root_node, os.path.join(self.gbuildparser.builddir, 'libo2.workspace'))
382     def create_project_file(self, module_name):
383         root_node = ET.Element('CodeLite_Project', Name=module_name, InternalType='')
384         ET.SubElement(root_node, 'Plugins')
386         # add CXX files
387         virtual_dirs = collections.defaultdict(set)
388         for target_path in self.gbuildparser.target_by_path.keys():
389             if target_path.startswith(module_name+'/'):
390                 for target in self.gbuildparser.target_by_path[target_path]:
391                     for file in target.cxxobjects:
392                         relative_file = '/'.join(file.split('/')[1:])
393                         path = '/'.join(file.split('/')[1:-1])
394                         virtual_dirs[path].add(relative_file + '.cxx')
395         # add HXX files
396         all_libs = set(self.gbuildparser.libs) | set(self.gbuildparser.exes)
397         for lib in all_libs:
398             if lib.name == module_name:
399                 for hdir in lib.include:
400                     # only want the module-internal ones
401                     if hdir.startswith(module_name+'/'):
402                         for hf in os.listdir(hdir):
403                             if hf.endswith(('.h', '.hxx', '.hpp', '.hrc')):
404                                 path = '/'.join(hf.split('/')[1:-1])
405                                 virtual_dirs[path].add(hf)
406         # add HXX files from the root/include/** folders
407         module_include = os.path.join(self.gbuildparser.builddir, 'include', module_name)
408         if os.path.exists(module_include):
409             for hf in os.listdir(module_include):
410                 if hf.endswith(('.h', '.hxx', '.hpp', '.hrc')):
411                     path = '../include/' + ('/'.join(hf.split('/')[1:-1]))
412                     virtual_dirs['include/' + module_name].add('../include/' + module_name + '/' + hf)
414         for vd_name in sorted(virtual_dirs.keys()):
415             vd_files = sorted(virtual_dirs[vd_name])
416             parent_node = root_node
417             for subname in vd_name.split('/'):
418                 parent_node = ET.SubElement(parent_node, 'VirtualDirectory', Name=subname)
419             for file in vd_files:
420                 ET.SubElement(parent_node, 'File', Name=file)
422         ET.SubElement(root_node, 'Description')
423         ET.SubElement(root_node, 'Dependencies')
424         ET.SubElement(root_node, 'Dependencies', Name='Debug')
425         ET.SubElement(root_node, 'Dependencies', Name='Release')
427         settingstemplate = """\
428   <Settings Type="Dynamic Library">
429     <GlobalSettings>
430       <Compiler Options="" C_Options="" Assembler="">
431         <IncludePath Value="."/>
432       </Compiler>
433       <Linker Options="">
434         <LibraryPath Value="."/>
435       </Linker>
436       <ResourceCompiler Options=""/>
437     </GlobalSettings>
438     <Configuration Name="Debug" CompilerType="clang( based on LLVM 3.5.0 )" DebuggerType="GNU gdb debugger" Type="Dynamic Library" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">
439       <Compiler Options="-g" C_Options="-g" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0">
440         <IncludePath Value="."/>
441       </Compiler>
442       <Linker Options="" Required="yes"/>
443       <ResourceCompiler Options="" Required="no"/>
444       <General OutputFile="" IntermediateDirectory="./Debug" Command="" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/>
445       <BuildSystem Name="Default"/>
446       <Environment EnvVarSetName="&lt;Use Defaults&gt;" DbgSetName="&lt;Use Defaults&gt;">
447         <![CDATA[]]>
448       </Environment>
449       <Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath="" IsExtended="no">
450         <DebuggerSearchPaths/>
451         <PostConnectCommands/>
452         <StartupCommands/>
453       </Debugger>
454       <PreBuild/>
455       <PostBuild/>
456       <CustomBuild Enabled="yes">
457         <RebuildCommand/>
458         <CleanCommand>make %s.clean</CleanCommand>
459         <BuildCommand>make %s.build</BuildCommand>
460         <PreprocessFileCommand/>
461         <SingleFileCommand/>
462         <MakefileGenerationCommand/>
463         <ThirdPartyToolName>None</ThirdPartyToolName>
464         <WorkingDirectory>$(WorkspacePath)</WorkingDirectory>
465       </CustomBuild>
466       <AdditionalRules>
467         <CustomPostBuild/>
468         <CustomPreBuild/>
469       </AdditionalRules>
470       <Completion EnableCpp11="no" EnableCpp14="no">
471         <ClangCmpFlagsC/>
472         <ClangCmpFlags/>
473         <ClangPP/>
474         <SearchPaths/>
475       </Completion>
476     </Configuration>
477     <Configuration Name="Release" CompilerType="clang( based on LLVM 3.5.0 )" DebuggerType="GNU gdb debugger" Type="Dynamic Library" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">
478       <Compiler Options="" C_Options="" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0">
479         <IncludePath Value="."/>
480       </Compiler>
481       <Linker Options="-O2" Required="yes"/>
482       <ResourceCompiler Options="" Required="no"/>
483       <General OutputFile="" IntermediateDirectory="./Release" Command="" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/>
484       <BuildSystem Name="Default"/>
485       <Environment EnvVarSetName="&lt;Use Defaults&gt;" DbgSetName="&lt;Use Defaults&gt;">
486         <![CDATA[]]>
487       </Environment>
488       <Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath="" IsExtended="no">
489         <DebuggerSearchPaths/>
490         <PostConnectCommands/>
491         <StartupCommands/>
492       </Debugger>
493       <PreBuild/>
494       <PostBuild/>
495       <CustomBuild Enabled="yes">
496         <RebuildCommand/>
497         <CleanCommand>make %s.clean</CleanCommand>
498         <BuildCommand>make %s.build</BuildCommand>
499         <PreprocessFileCommand/>
500         <SingleFileCommand/>
501         <MakefileGenerationCommand/>
502         <ThirdPartyToolName>None</ThirdPartyToolName>
503         <WorkingDirectory>$(WorkspacePath)</WorkingDirectory>
504       </CustomBuild>
505       <AdditionalRules>
506         <CustomPostBuild/>
507         <CustomPreBuild/>
508       </AdditionalRules>
509       <Completion EnableCpp11="no" EnableCpp14="no">
510         <ClangCmpFlagsC/>
511         <ClangCmpFlags/>
512         <ClangPP/>
513         <SearchPaths/>
514       </Completion>
515     </Configuration>
516   </Settings>
518         root_node.append(ET.fromstring(settingstemplate % (module_name, module_name, module_name, module_name)))
520         self.write_pretty_xml(root_node, os.path.join(self.gbuildparser.builddir, module_name, '%s.project' % module_name))
522     def write_pretty_xml(self, node, file_path):
523         xml_str = ET.tostring(node, encoding='unicode')
524         pretty_str = minidom.parseString(xml_str).toprettyxml(encoding='utf-8')
525         with open(file_path, 'w') as f:
526             f.write(pretty_str.decode())
528 class DebugIntegrationGenerator(IdeIntegrationGenerator):
530     def __init__(self, gbuildparser, ide):
531         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
533     def emit(self):
534         print(self.gbuildparser.srcdir)
535         print(self.gbuildparser.builddir)
536         for lib in self.gbuildparser.libs:
537             print(lib)
538         for exe in self.gbuildparser.exes:
539             print(exe)
540         for test in self.gbuildparser.tests:
541             print(test)
544 class VimIntegrationGenerator(IdeIntegrationGenerator):
546     def __init__(self, gbuildparser, ide):
547         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
549     def emit(self):
550         global_list = []
551         for lib in set(self.gbuildparser.libs) | set(self.gbuildparser.tests) | set(self.gbuildparser.exes):
552             entries = []
553             for file in lib.cxxobjects:
554                 filePath = os.path.join(self.gbuildparser.srcdir, file) + ".cxx"
555                 entry = {'directory': lib.location, 'file': filePath, 'command': self.generateCommand(lib, filePath)}
556                 entries.append(entry)
557             global_list.extend(entries)
558         with open(os.path.join(self.gbuildparser.builddir, 'compile_commands.json'), 'w') as export_file:
559             json.dump(global_list, export_file)
561     def generateCommand(self, lib, file):
562         command = 'clang++ -Wall'
563         for key, value in lib.defs.items():
564             command += ' -D'
565             command += key
566             if value is not None:
567                 command += '='
568                 command += value
570         for include in lib.include:
571             command += ' -I'
572             command += include
573         for isystem in lib.include_sys:
574             command += ' -isystem '
575             command += isystem
576         for cxxflag in lib.cxxflags:
577             command += ' '
578             command += cxxflag
579         command += ' -c '
580         command += file
581         return command
584 class KdevelopIntegrationGenerator(IdeIntegrationGenerator):
586     def encode_int(self, i):
587         temp = '%08x' % i
588         return '\\x%s\\x%s\\x%s\\x%s' % (temp[0:2], temp[2:4], temp[4:6], temp[6:8])
590     def encode_string(self, string):
591         result = self.encode_int(len(string) * 2)
592         for c in string.encode('utf-16-be'):
593             if c in range(32, 126):
594                 result += chr(c)
595             else:
596                 result += '\\x%02x' % c
597         return result
599     def generate_buildsystemconfigtool(self, configid, tool, args, exe, typenr):
600         return KdevelopIntegrationGenerator.buildsystemconfigtooltemplate % {'configid': configid, 'tool': tool,
601                                                                              'args': args, 'exe': exe, 'typenr': typenr}
603     buildsystemconfigtooltemplate = """
604 [CustomBuildSystem][BuildConfig%(configid)d][Tool%(tool)s]
605 Arguments=%(args)s
606 Enabled=true
607 Environment=
608 Executable=%(exe)s
609 Type=%(typenr)d
613     def generate_buildsystemconfig(self, configid, moduledir, builddir, title, buildparms=''):
614         result = KdevelopIntegrationGenerator.buildsystemconfigtemplate % {'configid': configid, 'builddir': builddir,
615                                                                            'title': title}
616         result += self.generate_buildsystemconfigtool(configid, 'Clean', 'clean %s' % buildparms,
617                                                       self.gbuildparser.makecmd, 3)
618         result += self.generate_buildsystemconfigtool(configid, 'Build', 'all %s' % buildparms,
619                                                       self.gbuildparser.makecmd, 0)
620         return result
622     buildsystemconfigtemplate = """
623 [CustomBuildSystem][BuildConfig%(configid)d]
624 BuildDir=file://%(builddir)s
625 Title=%(title)s
629     def generate_buildsystem(self, moduledir):
630         result = KdevelopIntegrationGenerator.buildsystemtemplate % {'defaultconfigid': 0}
631         result += self.generate_buildsystemconfig(0, moduledir, moduledir, 'Module Build -- Release')
632         result += self.generate_buildsystemconfig(1, moduledir, self.gbuildparser.builddir, 'Full Build -- Release')
633         result += self.generate_buildsystemconfig(2, moduledir, moduledir, 'Module Build -- Debug', 'debug=T')
634         result += self.generate_buildsystemconfig(3, moduledir, self.gbuildparser.builddir, 'Full Build -- Debug',
635                                                   'debug=T')
636         return result
638     buildsystemtemplate = """
639 [CustomBuildSystem]
640 CurrentConfiguration=BuildConfig%(defaultconfigid)d
644     def generate_launch(self, launchid, launchname, executablepath, args, workdir):
645         return KdevelopIntegrationGenerator.launchtemplate % {'launchid': launchid, 'launchname': launchname,
646                                                               'executablepath': executablepath, 'args': args,
647                                                               'workdir': workdir}
649     launchtemplate = """
650 [Launch][Launch Configuration %(launchid)d]
651 Configured Launch Modes=execute
652 Configured Launchers=nativeAppLauncher
653 Name=%(launchname)s
654 Type=Native Application
656 [Launch][Launch Configuration %(launchid)d][Data]
657 Arguments=%(args)s
658 Dependencies=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x00)
659 Dependency Action=Nothing
660 EnvironmentGroup=default
661 Executable=file://%(executablepath)s
662 External Terminal=konsole --noclose --workdir %%workdir -e %%exe
663 Project Target=
664 Use External Terminal=false
665 Working Directory=file://%(workdir)s
666 isExecutable=true
670     def generate_launches(self, moduledir):
671         launches = ','.join(['Launch Configuration %d' % i for i in range(7)])
672         result = KdevelopIntegrationGenerator.launchestemplate % {'launches': launches}
673         result += self.generate_launch(0, 'Local tests -- quick tests (unitcheck)', self.gbuildparser.makecmd,
674                                        'unitcheck', moduledir)
675         result += self.generate_launch(1, 'Local tests -- slow tests (unitcheck, slowcheck, screenshot)', self.gbuildparser.makecmd,
676                                        'unitcheck slowcheck screenshot', moduledir)
677         result += self.generate_launch(2, 'Local tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)',
678                                        self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot subsequentcheck', moduledir)
679         result += self.generate_launch(3, 'Global tests -- quick tests (unitcheck)', self.gbuildparser.makecmd,
680                                        'unitcheck', self.gbuildparser.builddir)
681         result += self.generate_launch(4, 'Global tests -- slow tests (unitcheck, slowcheck, screenshot)',
682                                        self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot', self.gbuildparser.builddir)
683         result += self.generate_launch(5, 'Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)',
684                                        self.gbuildparser.makecmd, 'unitcheck slowcheck screenshot subsequentcheck',
685                                        self.gbuildparser.builddir)
686         result += self.generate_launch(6, 'Run LibreOffice',
687                                        os.path.join(self.gbuildparser.instdir, 'program/soffice.bin'), '',
688                                        self.gbuildparser.instdir)
689         return result
691     launchestemplate = """
692 [Launch]
693 Launch Configurations=%(launches)s
697     def write_modulebeef(self, moduledir, modulename):
698         beefdir = os.path.join(moduledir, '.kdev4')
699         os.mkdir(beefdir)
700         beeffile = open(os.path.join(beefdir, 'Module_%s.kdev4' % modulename), 'w')
701         beeffile.write(self.generate_buildsystem(moduledir))
702         beeffile.write(self.generate_launches(moduledir))
703         beeffile.close()
705     def write_modulestub(self, moduledir, modulename):
706         stubfile = open(os.path.join(moduledir, 'Module_%s.kdev4' % modulename), 'w')
707         stubfile.write(KdevelopIntegrationGenerator.modulestubtemplate % {'modulename': modulename,
708                                                                           'builditem': self.encode_string(
709                                                                               'Module_%s' % modulename)})
710         stubfile.close()
712     modulestubtemplate = """
713 [Buildset]
714 BuildItems=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0b\\x00\\x00\\x00\\x00\\x01%(builditem)s)
716 [Project]
717 Name=Module_%(modulename)s
718 Manager=KDevCustomBuildSystem
719 VersionControl=kdevgit
722     def write_includepaths(self, path):
723         includedirfile = open(os.path.join(path, '.kdev_include_paths'), 'w')
724         include = set()
725         for target in self.gbuildparser.target_by_path[path]:
726             include |= set(target.include)
727         includedirfile.write('\n'.join(include))
728         includedirfile.close()
730     def __init__(self, gbuildparser, ide):
731         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
733     def emit(self):
734         for path in self.gbuildparser.target_by_path:
735             self.write_includepaths(path)
736         for location in self.gbuildparser.target_by_location:
737             for f in os.listdir(location):
738                 if f.endswith('.kdev4'):
739                     try:
740                         os.remove(os.path.join(location, f))
741                     except OSError:
742                         shutil.rmtree(os.path.join(location, f))
743         for location in self.gbuildparser.target_by_location:
744             modulename = os.path.split(location)[1]
745             self.write_modulestub(location, modulename)
746             self.write_modulebeef(location, modulename)
749 class XcodeIntegrationGenerator(IdeIntegrationGenerator):
751     def indent(self, file, level):
752         if level == 0:
753             return
754         for i in range(0, level):
755             file.write(' ')
757     def write_object(self, object, file, indent):
758         if isinstance(object, int):
759             file.write('%d' % object)
760         elif isinstance(object, str) and not re.search('[^A-Za-z0-9_]', object):
761             file.write('%s' % object)
762         elif isinstance(object, str):
763             file.write('"%s"' % object)
764         elif isinstance(object, dict):
765             self.write_dict(object, file, indent)
767     # Write a dictionary out as an "old-style (NeXT) ASCII plist"
768     def write_dict(self, dict, file, indent):
769         file.write('{')
770         file.write('\n')
771         for key in sorted(dict.keys()):
772             self.indent(file, indent + 1)
773             file.write('%s = ' % key)
774             self.write_object(dict[key], file, indent + 1)
775             file.write(';\n')
776         self.indent(file, indent)
777         file.write('}')
779     def write_dict_to_plist(self, dict, file):
780         file.write('// !$*UTF8*$!\n')
781         self.write_dict(dict, file, 0)
783     def get_product_type(self, modulename):
784         if modulename in self.gbuildparser.libs:
785             return 'com.apple.product-type.library.dynamic'
786         elif modulename in self.gbuildparser.exes:
787             return 'com.apple.product-type.something'
789     counter = 0
791     def generate_id(self):
792         XcodeIntegrationGenerator.counter = XcodeIntegrationGenerator.counter + 1
793         return str('X%07x' % XcodeIntegrationGenerator.counter)
795     def generate_build_phases(self, modulename):
796         result = [self.sourcesBuildPhaseId]
797         return result
799     def generate_root_object(self, modulename):
800         result = {'isa': 'PBXProject',
801                   'attributes': {'LastUpgradeCheck': '0500',
802                                  'ORGANIZATIONNAME': 'LibreOffice'},
803                   'buildConfigurationList': self.generate_id(),
804                   'compatibilityVersion': 'Xcode 3.2',
805                   'hasScannedForEncodings': 0,
806                   'knownRegions': ['en'],
807                   'mainGroup': self.mainGroupId,
808                   'productRefGroup': self.productRefGroupId,
809                   'projectDirPath': '',
810                   'projectRoot': '',
811                   'targets': self.targetId}
812         return result
814     def generate_target(self, modulename):
815         result = {'isa': 'PBXNativeTarget',
816                   'buildConfigurationList': self.generate_id(),
817                   'buildPhases': self.generate_build_phases(modulename),
818                   'buildRules': [],
819                   'dependencies': [],
820                   'name': modulename,
821                   'productName': modulename,
822                   'productReference': self.productReferenceId,
823                   'productType': self.get_product_type(modulename)}
824         return result
826     def generate_main_group(self, modulename):
827         result = {'isa': 'PBXGroup',
828                   'children': [self.subMainGroupId, self.productGroupId],
829                   'sourceTree': '<group>'}
830         return result
832     def generate_sub_main_children(self, modulename):
833         return {}
835     def generate_sub_main_group(self, modulename):
836         result = {'isa': 'PBXGroup',
837                   'children': self.generate_sub_main_children(modulename),
838                   'path': modulename,
839                   'sourceTree': '<group>'}
840         return result
842     def generate_product_group(self, modulename):
843         result = {'isa': 'PBXGroup',
844                   'children': [self.productReferenceId],
845                   'name': 'Products',
846                   'sourceTree': '<group>'}
847         return result
849     def build_source_list(self, module):
850         self.sourceRefList = {}
851         self.sourceList = {}
853         for i in module.cxxobjects:
854             ref = self.generate_id()
855             self.sourceList[self.generate_id()] = ref
856             self.sourceRefList[ref] = {'lastKnownFileType': 'sourcecode.cpp.cpp',
857                                        'path': i + '.cxx',
858                                        'sourceTree': '<group>'}
860     def generate_sources_build_phase(self, modulename):
861         result = {'isa': 'PBXSourcesBuildPhase',
862                   'buildActionMask': 2147483647,
863                   'files': self.sourceList.keys(),
864                   'runOnlyForDeploymentPostprocessing': 0}
865         return result
867     def generate_project(self, target):
868         self.rootObjectId = self.generate_id()
869         self.mainGroupId = self.generate_id()
870         self.subMainGroupId = self.generate_id()
871         self.productReferenceId = self.generate_id()
872         self.productRefGroupId = self.generate_id()
873         self.productGroupId = self.generate_id()
874         self.targetId = self.generate_id()
875         self.build_source_list(target)
876         self.sourcesBuildPhaseId = self.generate_id()
877         objects = {self.rootObjectId: self.generate_root_object(target),
878                    self.targetId: self.generate_target(target),
879                    self.mainGroupId: self.generate_main_group(target),
880                    self.subMainGroupId: self.generate_sub_main_group(target),
881                    self.productGroupId: self.generate_product_group(target),
882                    self.sourcesBuildPhaseId: self.generate_sources_build_phase(target)
883                    }
884         for i in self.sourceList.keys():
885             ref = self.sourceList[i]
886             objects[i] = {'isa': 'PBXBuildFile',
887                           'fileRef': ref}
888             objects[ref] = {'isa': 'PBXFileReference',
889                             'lastKnownFileType': self.sourceRefList[ref]['lastKnownFileType'],
890                             'path': self.sourceRefList[ref]['path']}
891         project = {'archiveVersion': 1,
892                    'classes': {},
893                    'objectVersion': 46,
894                    'objects': objects,
895                    'rootObject': self.rootObjectId}
896         return project
898     # For some reverse-engineered documentation on the project.pbxproj format,
899     # see http://www.monobjc.net/xcode-project-file-format.html .
900     def write_xcodeproj(self, moduledir, target):
901         xcodeprojdir = os.path.join(moduledir, '%s.xcodeproj' % target.target_name())
902         try:
903             os.mkdir(xcodeprojdir)
904         except:
905             pass
906         self.write_dict_to_plist(self.generate_project(target),
907                                  open(os.path.join(xcodeprojdir, 'project.pbxproj'), 'w'))
909     def __init__(self, gbuildparser, ide):
910         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
912     def emit(self):
913         self.rootlocation = './'
914         for location in self.gbuildparser.target_by_location:
915             # module = location.split('/')[-1]
916             # module_directory = os.path.join(self.rootlocation, module)
917             for target in self.gbuildparser.target_by_location[location]:
918                 # project_path = os.path.join(module_directory, '%s.pbxroj' % target.target_name())
919                 self.write_xcodeproj(location, target)
922 class VisualStudioIntegrationGenerator(IdeIntegrationGenerator):
924     def __init__(self, gbuildparser, ide):
925         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
926         self.toolset = os.environ['VCTOOLSET']
927         self.solution_directory = self.gbuildparser.builddir
928         self.configurations = {
929             'Build': {
930                 'build': self.module_make_command('%(target)s'),
931                 'clean': self.module_make_command('%(target)s.clean'),
932                 'rebuild': self.module_make_command('%(target)s.clean %(target)s')
933             },
934             'Unit Tests': {
935                 'build': self.module_make_command('unitcheck'),
936                 'clean': self.module_make_command('clean'),
937                 'rebuild': self.module_make_command('clean unitcheck'),
938             },
939             'Integration tests': {
940                 'build': self.module_make_command('unitcheck slowcheck screenshot subsequentcheck'),
941                 'clean': self.module_make_command('clean'),
942                 'rebuild': self.module_make_command('clean unitcheck slowcheck screenshot subsequentcheck')
943             }
944         }
946     def module_make_command(self, targets):
947         return '%(sh)s -c "PATH=\\"/bin:$PATH\\";BUILDDIR=\\"%(builddir)s\\" %(makecmd)s -rsC %(location)s ' + targets + '"'
949     class Project:
951         def __init__(self, guid, target, project_path):
952             self.guid = guid
953             self.target = target
954             self.path = project_path
956     def emit(self):
957         all_projects = []
958         for location in self.gbuildparser.target_by_location:
959             projects = []
960             module = location.split('/')[-1]
961             module_directory = os.path.join(self.solution_directory, module)
962             for target in self.gbuildparser.target_by_location[location]:
963                 project_path = os.path.join(module_directory, '%s.vcxproj' % target.target_name())
964                 project_guid = self.write_project(project_path, target)
965                 p = VisualStudioIntegrationGenerator.Project(project_guid, target, project_path)
966                 projects.append(p)
967             self.write_solution(os.path.join(module_directory, '%s.sln' % module), projects)
968             all_projects += projects
970         self.write_solution(os.path.join(self.solution_directory, 'LibreOffice.sln'), all_projects)
971         # this enables Python GDB pretty printers when debugging a WSL Linux build from VS
972         MIEngine_options_path = os.path.join(gbuildparser.srcdir, 'solenv/vs/Microsoft.MIEngine.Options.xml')
973         shutil.copy(MIEngine_options_path, self.solution_directory)
975     @staticmethod
976     def gen_guid(category, name):
977         return str(uuid.uuid5(uuid.NAMESPACE_URL, 'vnd.libreoffice.vs-ide-integration:' + category + '/' + urllib.parse.quote(name))).upper()
979     nmake_project_guid = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942'
980     nmake_folder_guid = '2150E333-8FDC-42A3-9474-1A3956D46DE8'
982     def get_dependency_libs(self, linked_libs, library_projects):
983         dependency_libs = {}
984         for linked_lib in linked_libs:
985             for library_project in library_projects:
986                 if library_project.target.library_name() == linked_lib:
987                     dependency_libs[library_project.guid] = library_project
988         return dependency_libs
990     def write_solution(self, solution_path, projects):
991         print('Solution %s:' % os.path.splitext(os.path.basename(solution_path))[0], end='')
992         library_projects = [project for project in projects if project.target in self.gbuildparser.libs]
993         test_projects = [project for project in projects if project.target in self.gbuildparser.tests]
994         executable_projects = [project for project in projects if project.target in self.gbuildparser.exes]
995         with open(solution_path, 'w') as f:
996             f.write('Microsoft Visual Studio Solution File, Format Version 12.00\n')
997             for project in projects:
998                 target = project.target
999                 print(' %s' % target.target_name(), end='')
1000                 proj_path = os.path.relpath(project.path, os.path.abspath(os.path.dirname(solution_path)))
1001                 f.write('Project("{%s}") = "%s", "%s", "{%s}"\n' %
1002                         (VisualStudioIntegrationGenerator.nmake_project_guid,
1003                          target.short_name(), proj_path, project.guid))
1004                 libs_in_solution = self.get_dependency_libs(target.linked_libs,
1005                                                             library_projects)
1006                 if libs_in_solution:
1007                     f.write('\tProjectSection(ProjectDependencies) = postProject\n')
1008                     for lib_guid in libs_in_solution.keys():
1009                         f.write('\t\t{%(guid)s} = {%(guid)s}\n' % {'guid': lib_guid})
1010                     f.write('\tEndProjectSection\n')
1011                 f.write('EndProject\n')
1012             f.write('Project("{%s}") = "Utility", "Utility", "{6778240E-8B6B-47A0-AC31-7E7A257B97E6}"\n' %
1013                     (VisualStudioIntegrationGenerator.nmake_folder_guid))
1014             f.write('\tProjectSection(SolutionItems) = preProject\n')
1015             # The natvis file gives pretty-printed variable values when debugging
1016             natvis_path = os.path.join(gbuildparser.srcdir, 'solenv/vs/LibreOffice.natvis')
1017             f.write('\t\t%(natvis)s = %(natvis)s\n' % {'natvis': natvis_path})
1018             f.write('\tEndProjectSection\n')
1019             f.write('EndProject\n')
1020             # Folders to group tests/libraries/executables
1021             nmake_tests_guid = 'CF544F7B-9D02-4D83-8370-5887851209B7'
1022             nmake_libraries_guid = 'C624F43D-616C-4627-B58F-F5C2047B7BDC'
1023             nmake_executables_guid = '1CD80999-9FA9-4BA9-B4D9-6E33035CF648'
1024             f.write('Project("{%s}") = "Tests", "Tests", "{%s}"\n' %
1025                     (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_tests_guid))
1026             f.write('EndProject\n')
1027             f.write('Project("{%s}") = "Libraries", "Libraries", "{%s}"\n' %
1028                     (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_libraries_guid))
1029             f.write('EndProject\n')
1030             f.write('Project("{%s}") = "Executables", "Executables", "{%s}"\n' %
1031                     (VisualStudioIntegrationGenerator.nmake_folder_guid, nmake_executables_guid))
1032             f.write('EndProject\n')
1033             # end Folders to group tests/libraries/executables
1034             f.write('Global\n')
1035             platform = 'Win32'
1036             f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
1037             for cfg in self.configurations:
1038                 f.write('\t\t%(cfg)s|%(platform)s = %(cfg)s|%(platform)s\n' % {'cfg': cfg, 'platform': platform})
1039             f.write('\tEndGlobalSection\n')
1040             # Group projects to folders
1041             f.write('\tGlobalSection(NestedProjects) = preSolution\n')
1042             for project in test_projects:
1043                 f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_tests_guid))
1044             for project in library_projects:
1045                 f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_libraries_guid))
1046             for project in executable_projects:
1047                 f.write('\t\t{%s} = {%s}\n' % (project.guid, nmake_executables_guid))
1048             f.write('\tEndGlobalSection\n')
1049             # end Group projects to folders
1050             f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
1051             # Specifies project configurations for solution configuration
1052             for project in projects:
1053                 for cfg in self.configurations:
1054                     params = {'guid': project.guid, 'sol_cfg': cfg, 'proj_cfg': cfg, 'platform': platform}
1055                     f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.ActiveCfg = %(proj_cfg)s|%(platform)s\n' % params)
1056                     # Build.0 is basically 'Build checkbox' in configuration manager
1057                     f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.Build.0 = %(proj_cfg)s|%(platform)s\n' % params)
1058             f.write('\tEndGlobalSection\n')
1059             f.write('EndGlobal\n')
1060         print('')
1062     @staticmethod
1063     def to_long_names(shortnames):
1064         if platform == "cygwin":
1065             return (subprocess.check_output(["cygpath", "-wal"] + shortnames).decode("utf-8", "strict").rstrip()).split("\n")
1066         else:
1067             return shortnames
1069     # Unescape the values: \"tklo.dll\" => "tklo.dll"
1070     escapepattern = re.compile(r'\\(.)')
1072     @staticmethod
1073     def defs_list(defs):
1074         defines_list = []
1075         # List defines
1076         for key, value in defs.items():
1077             define = key
1078             if value is not None:
1079                 define += '=' + VisualStudioIntegrationGenerator.escapepattern.sub(r'\1', value)
1080             defines_list.append(define)
1081         return defines_list
1083     def write_project(self, project_path, target):
1084         # See info at http://blogs.msdn.com/b/visualstudio/archive/2010/05/14/a-guide-to-vcxproj-and-props-file-structure.aspx
1085         folder = os.path.dirname(project_path)
1086         if not os.path.exists(folder):
1087             os.makedirs(folder)
1088         project_guid = self.gen_guid('project', target.short_name())
1089         cxxflags = ' '.join(target.cxxflags)
1090         ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
1091         ET.register_namespace('', ns)
1092         proj_node = ET.Element('{%s}Project' % ns, DefaultTargets='Build', ToolsVersion='4.0')
1093         proj_confs_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns, Label='ProjectConfigurations')
1094         platform = 'Win32'
1095         for configuration in self.configurations:
1096             proj_conf_node = ET.SubElement(proj_confs_node,
1097                                            '{%s}ProjectConfiguration' % ns,
1098                                            Include='%s|%s' % (configuration, platform))
1099             conf_node = ET.SubElement(proj_conf_node, '{%s}Configuration' % ns)
1100             conf_node.text = configuration
1101             platform_node = ET.SubElement(proj_conf_node, '{%s}Platform' % ns)
1102             platform_node.text = platform
1104         globals_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='Globals')
1105         proj_guid_node = ET.SubElement(globals_node, '{%s}ProjectGuid' % ns)
1106         proj_guid_node.text = '{%s}' % project_guid
1107         proj_keyword_node = ET.SubElement(globals_node, '{%s}Keyword' % ns)
1108         proj_keyword_node.text = 'MakeFileProj'
1109         proj_name_node = ET.SubElement(globals_node, '{%s}ProjectName' % ns)
1110         proj_name_node.text = target.short_name()
1112         ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.Default.props')
1113         for configuration in self.configurations:
1114             conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label="Configuration",
1115                                       Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform))
1116             # Type of project used by the MSBuild to determine build process, see Microsoft.Makefile.targets
1117             conf_type_node = ET.SubElement(conf_node, '{%s}ConfigurationType' % ns)
1118             conf_type_node.text = 'Makefile'
1119             # This defines the version of Visual Studio which can show next to project names in the Solution Explorer
1120             platform_toolset_node = ET.SubElement(conf_node, '{%s}PlatformToolset' % ns)
1121             platform_toolset_node.text = self.toolset
1123         ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.props')
1124         ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionSettings')
1125         for configuration in self.configurations:
1126             prop_sheets_node = ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='Configuration',
1127                                              Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform))
1128             ET.SubElement(prop_sheets_node, '{%s}Import' % ns,
1129                           Project='$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props',
1130                           Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')",
1131                           Label='LocalAppDataPlatform')
1133         ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='UserMacros')
1134         # VS IDE (at least "Peek definition") is allergic to paths like "C:/PROGRA~2/WI3CF2~1/10/Include/10.0.14393.0/um"; see
1135         # https://developercommunity.visualstudio.com/content/problem/139659/vc-peek-definition-fails-to-navigate-to-windows-ki.html
1136         # We need to convert to long paths here. Do this once, since it's time-consuming operation.
1137         include_path_node_text = ';'.join(self.to_long_names(target.include))
1138         for cfg_name, cfg_targets in self.configurations.items():
1139             conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns,
1140                                       Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (cfg_name, platform))
1141             nmake_params = {
1142                 'sh': os.path.join(self.gbuildparser.binpath, 'dash.exe'),
1143                 'builddir': self.gbuildparser.builddir,
1144                 'location': target.location,
1145                 'makecmd': self.gbuildparser.makecmd,
1146                 'target': target.target_name()}
1147             nmake_build_node = ET.SubElement(conf_node, '{%s}NMakeBuildCommandLine' % ns)
1148             nmake_build_node.text = cfg_targets['build'] % nmake_params
1149             nmake_clean_node = ET.SubElement(conf_node, '{%s}NMakeCleanCommandLine' % ns)
1150             nmake_clean_node.text = cfg_targets['clean'] % nmake_params
1151             nmake_rebuild_node = ET.SubElement(conf_node, '{%s}NMakeReBuildCommandLine' % ns)
1152             nmake_rebuild_node.text = cfg_targets['rebuild'] % nmake_params
1153             nmake_output_node = ET.SubElement(conf_node, '{%s}NMakeOutput' % ns)
1154             nmake_output_node.text = os.path.join(self.gbuildparser.workdir, 'LinkTarget', target.link_target)
1155             nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % ns)
1156             nmake_defs_node.text = ';'.join(self.defs_list(target.defs) + ['$(NMakePreprocessorDefinitions)'])
1157             nmake_debug_command_node = ET.SubElement(conf_node, '{%s}LocalDebuggerCommand' % ns)
1158             nmake_debug_command_node.text = os.path.join(self.gbuildparser.instdir, 'program', 'soffice.bin')
1159             nmake_debug_flavor_node = ET.SubElement(conf_node, '{%s}DebuggerFlavor' % ns)
1160             nmake_debug_flavor_node.text = 'WindowsLocalDebugger'
1161             include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % ns)
1162             include_path_node.text = include_path_node_text
1163             additional_options_node = ET.SubElement(conf_node, '{%s}AdditionalOptions' % ns)
1164             additional_options_node.text = cxxflags
1166         ET.SubElement(proj_node, '{%s}ItemDefinitionGroup' % ns)
1168         cxxobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
1169         for cxxobject in target.cxxobjects:
1170             cxxabspath = os.path.join(self.gbuildparser.srcdir, cxxobject)
1171             cxxfile = cxxabspath + '.cxx'
1172             if os.path.isfile(cxxfile):
1173                 ET.SubElement(cxxobjects_node, '{%s}ClCompile' % ns, Include=cxxfile)
1174             else:
1175                 print('Source %s in project %s does not exist' % (cxxfile, target.target_name()))
1177         cobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
1178         for cobject in target.cobjects:
1179             cabspath = os.path.join(self.gbuildparser.srcdir, cobject)
1180             cfile = cabspath + '.c'
1181             if os.path.isfile(cfile):
1182                 ET.SubElement(cobjects_node, '{%s}ClCompile' % ns, Include=cfile)
1183             else:
1184                 print('Source %s in project %s does not exist' % (cfile, target.target_name()))
1186         includes_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
1187         for cxxobject in target.cxxobjects:
1188             include_abs_path = os.path.join(self.gbuildparser.srcdir, cxxobject)
1189             hxxfile = include_abs_path + '.hxx'
1190             if os.path.isfile(hxxfile):
1191                 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hxxfile)
1192             # Few files have corresponding .h files
1193             hfile = include_abs_path + '.h'
1194             if os.path.isfile(hfile):
1195                 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hfile)
1196         for cobject in target.cobjects:
1197             include_abs_path = os.path.join(self.gbuildparser.srcdir, cobject)
1198             hfile = include_abs_path + '.h'
1199             if os.path.isfile(hfile):
1200                 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hfile)
1201         ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\\Microsoft.Cpp.targets')
1202         ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionTargets')
1203         self.write_pretty_xml(proj_node, project_path)
1204         self.write_filters(project_path + '.filters',
1205                            os.path.join(self.gbuildparser.srcdir, os.path.basename(target.location)),
1206                            [cxx_node.get('Include') for cxx_node in cxxobjects_node.findall('{%s}ClCompile' % ns)],
1207                            [c_node.get('Include') for c_node in cobjects_node.findall('{%s}ClCompile' % ns)],
1208                            [include_node.get('Include') for include_node in includes_node.findall('{%s}ClInclude' % ns)])
1209         return project_guid
1211     def get_filter(self, module_dir, proj_file):
1212         return '\\'.join(os.path.relpath(proj_file, module_dir).split('/')[:-1])
1214     def get_subfilters(self, proj_filter):
1215         parts = proj_filter.split('\\')
1216         subfilters = set([proj_filter]) if proj_filter else set()
1217         for i in range(1, len(parts)):
1218             subfilters.add('\\'.join(parts[:i]))
1219         return subfilters
1221     def write_pretty_xml(self, node, file_path):
1222         xml_str = ET.tostring(node, encoding='unicode')
1223         pretty_str = minidom.parseString(xml_str).toprettyxml(encoding='utf-8')
1224         with open(file_path, 'w') as f:
1225             f.write(pretty_str.decode())
1227     def add_nodes(self, files_node, module_dir, tag, project_files):
1228         ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
1229         filters = set()
1230         for project_file in project_files:
1231             file_node = ET.SubElement(files_node, tag, Include=project_file)
1232             if os.path.commonprefix([module_dir, project_file]) == module_dir:
1233                 project_filter = self.get_filter(module_dir, project_file)
1234                 filter_node = ET.SubElement(file_node, '{%s}Filter' % ns)
1235                 filter_node.text = project_filter
1236                 filters |= self.get_subfilters(project_filter)
1237         return filters
1239     def write_filters(self, filters_path, module_dir, cxx_files, c_files, include_files):
1240         ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
1241         ET.register_namespace('', ns)
1242         proj_node = ET.Element('{%s}Project' % ns, ToolsVersion='4.0')
1243         filters = set()
1244         compiles_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
1245         filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, cxx_files)
1246         filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, c_files)
1247         include_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
1248         filters |= self.add_nodes(include_node, module_dir, '{%s}ClInclude' % ns, include_files)
1250         filters_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
1251         for proj_filter in filters:
1252             filter_node = ET.SubElement(filters_node, '{%s}Filter' % ns, Include=proj_filter)
1253         self.write_pretty_xml(proj_node, filters_path)
1256 class QtCreatorIntegrationGenerator(IdeIntegrationGenerator):
1258     def __init__(self, gbuildparser, ide):
1259         IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
1260         self.target_by_location = {}
1261         for target in set(self.gbuildparser.libs) | set(self.gbuildparser.exes) | set(self.gbuildparser.tests):
1262             if target.location not in self.target_by_location:
1263                 self.target_by_location[target.location] = set()
1264             self.target_by_location[target.location] |= set([target])
1266         self._do_log = False  # set to 'True' to activate log of QtCreatorIntegrationGenerator
1267         if self._do_log:
1268             qtlog_path = os.path.abspath('../qtlog_.txt')
1269             self.qtlog = open(qtlog_path, 'w')
1271     def _log(self, message):
1272         if self._do_log:
1273             self.qtlog.write(message)
1275     def log_close(self):
1276         if self._do_log:
1277             self.qtlog.close()
1279     def generate_build_configs(self, lib_folder):
1280         module_folder = os.path.join(self.base_folder, lib_folder)
1281         xml = ""
1282         # In QtCreator UI, build configs are listed alphabetically,
1283         # so it can be different from the creation order.
1284         # So we prefix the names with the index.
1285         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1286             'index': '0',
1287             'base_folder': module_folder,
1288             'arg': "",
1289             'name': "1-Build %s" % lib_folder,
1290         }
1291         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1292             'index': '1',
1293             'base_folder': module_folder,
1294             'arg': "unitcheck",
1295             'name': "2-Local tests -- quick tests (unitcheck)",
1296         }
1297         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1298             'index': '2',
1299             'base_folder': module_folder,
1300             'arg': "unitcheck slowcheck screenshot",
1301             'name': "3-Local tests -- slow tests (unitcheck, slowcheck, screenshot)",
1302         }
1303         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1304             'index': '3',
1305             'base_folder': module_folder,
1306             'arg': "unitcheck slowcheck screenshot subsequentcheck",
1307             'name': "4-Local tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)",
1308         }
1309         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1310             'index': '4',
1311             'base_folder': self.base_folder,
1312             'arg': "unitcheck",
1313             'name': "5-Global tests -- quick tests (unitcheck)",
1314         }
1315         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1316             'index': '5',
1317             'base_folder': self.base_folder,
1318             'arg': "unitcheck slowcheck screenshot",
1319             'name': "6-Global tests -- slow tests (unitcheck, slowcheck, screenshot)",
1320         }
1321         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1322             'index': '6',
1323             'base_folder': self.base_folder,
1324             'arg': "unitcheck slowcheck screenshot subsequentcheck",
1325             'name': "7-Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)",
1326         }
1327         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1328             'index': '7',
1329             'base_folder': self.base_folder,
1330             'arg': "",
1331             'name': "8-Global build -- nocheck",
1332         }
1333         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1334             'index': '8',
1335             'base_folder': self.base_folder,
1336             'arg': "",
1337             'name': "9-Global build",
1338         }
1340         xml += QtCreatorIntegrationGenerator.build_configs_count_template % {
1341             'nb': '9',
1342         }
1343         return xml
1345     def generate_meta_build_configs(self):
1346         xml = ""
1347         # In QtCreator UI, build configs are listed alphabetically,
1348         # so it can be different from the creation order.
1349         # So we prefix the names with the index.
1350         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1351             'index': '0',
1352             'base_folder': self.base_folder,
1353             'arg': "",
1354             'name': "01-Global Build",
1355         }
1356         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1357             'index': '1',
1358             'base_folder': self.base_folder,
1359             'arg': "unitcheck",
1360             'name': "02-Global tests -- quick tests (unitcheck)",
1361         }
1362         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1363             'index': '2',
1364             'base_folder': self.base_folder,
1365             'arg': "unitcheck slowcheck screenshot",
1366             'name': "03-Global tests -- slow tests (unitcheck, slowcheck, screenshot)",
1367         }
1368         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1369             'index': '3',
1370             'base_folder': self.base_folder,
1371             'arg': "unitcheck slowcheck screenshot subsequentcheck",
1372             'name': "04-Global tests -- integration tests (unitcheck, slowcheck, screenshot, subsequentcheck)",
1373         }
1374         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1375             'index': '4',
1376             'base_folder': self.base_folder,
1377             'arg': "perfcheck",
1378             'name': "05-Global tests -- performance tests (perfcheck)",
1379         }
1380         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1381             'index': '5',
1382             'base_folder': self.base_folder,
1383             'arg': "check",
1384             'name': "06-Global tests -- tests (check)",
1385         }
1386         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1387             'index': '6',
1388             'base_folder': self.base_folder,
1389             'arg': "",
1390             'name': "07-Global build -- nocheck",
1391         }
1392         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1393             'index': '7',
1394             'base_folder': self.base_folder,
1395             'arg': "build-l10n-only",
1396             'name': "08-Global build -- build-l10n-only",
1397         }
1398         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1399             'index': '8',
1400             'base_folder': self.base_folder,
1401             'arg': "build-non-l10n-only",
1402             'name': "09-Global build -- build-non-l10n-only",
1403         }
1404         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1405             'index': '9',
1406             'base_folder': self.base_folder,
1407             'arg': "clean",
1408             'name': "10-Global build -- clean",
1409         }
1410         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1411             'index': '10',
1412             'base_folder': self.base_folder,
1413             'arg': "clean-build",
1414             'name': "11-Global build -- clean-build",
1415         }
1416         xml += QtCreatorIntegrationGenerator.build_configs_template % {
1417             'index': '11',
1418             'base_folder': self.base_folder,
1419             'arg': "clean-host",
1420             'name': "12-Global build -- clean-host",
1421         }
1422         xml += QtCreatorIntegrationGenerator.build_configs_count_template % {
1423             'nb': '12',
1424         }
1425         return xml
1427     # By default, QtCreator creates 2 BuildStepList : "Build" and "Clean"
1428     # but the "clean" can be empty.
1429     build_configs_template = """
1430    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.%(index)s">
1431     <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">%(base_folder)s</value>
1432     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
1433      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
1434       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
1435       <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
1436       <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1437       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
1438       <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
1439        <value type="QString">-w</value>
1440        <value type="QString">-r</value>
1441       </valuelist>
1442       <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
1443       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">%(arg)s</value>
1444       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
1445      </valuemap>
1446      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
1447      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
1448      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1449      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
1450     </valuemap>
1451     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
1452     <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
1453     <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
1454     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">%(name)s</value>
1455     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
1456     <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">%(index)s</value>
1457     <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
1458    </valuemap>
1459    """
1461     build_configs_count_template = """
1462    <!-- nb build configurations -->
1463    <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">%(nb)s</value>
1464    """
1466     def generate_deploy_configs(self, lib_folder):
1467         xml = QtCreatorIntegrationGenerator.deploy_configs_template % {}
1468         return xml
1470     deploy_configs_template = """
1471    <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
1472     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
1473      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
1474      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
1475      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1476      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
1477     </valuemap>
1478     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
1479     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
1480     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1481     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
1482    </valuemap>
1483    <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
1484     """
1486     def generate_run_configs(self, lib_folder):
1488         if platform == 'darwin':
1489             loexec = "%s/instdir/LibreOfficeDev.app/Contents/MacOS/soffice" % self.base_folder
1490         else:
1491             loexec = "%s/instdir/program/soffice.bin" % self.base_folder
1492         xml = QtCreatorIntegrationGenerator.run_configs_template % {
1493             'loexec': loexec,
1494             'workdir': self.base_folder
1495         }
1496         return xml
1498     run_configs_template = """
1499    <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
1500     <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
1501     <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
1502     <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
1503     <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
1504     <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
1505     <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
1506     <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
1507     <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
1508     <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
1509     <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
1510     <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
1511     <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
1512     <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
1513     <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
1514     <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
1515     <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
1516     <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
1517     <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
1518      <value type="int">0</value>
1519      <value type="int">1</value>
1520      <value type="int">2</value>
1521      <value type="int">3</value>
1522      <value type="int">4</value>
1523      <value type="int">5</value>
1524      <value type="int">6</value>
1525      <value type="int">7</value>
1526      <value type="int">8</value>
1527      <value type="int">9</value>
1528      <value type="int">10</value>
1529      <value type="int">11</value>
1530      <value type="int">12</value>
1531      <value type="int">13</value>
1532      <value type="int">14</value>
1533     </valuelist>
1534     <value type="int" key="PE.EnvironmentAspect.Base">2</value>
1535     <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
1537     <value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments"></value>
1538     <value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">%(loexec)s</value>
1539     <value type="bool" key="ProjectExplorer.CustomExecutableRunConfiguration.UseTerminal">false</value>
1540     <value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory">%(workdir)s</value>
1541     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run libreoffice/instdir/program/soffice</value>
1542     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
1543     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
1544     <value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
1545     <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
1546     <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
1547     <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
1548     <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
1549     <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
1551    </valuemap>
1552    <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
1553     """
1555     def generate_pro_shared_content(self, lib_folder):
1557         build_configs = self.generate_build_configs(lib_folder)
1558         deploy_configs = self.generate_deploy_configs(lib_folder)
1559         run_configs = self.generate_run_configs(lib_folder)
1561         xml = QtCreatorIntegrationGenerator.pro_shared_template % {
1562             'build_configs': build_configs,
1563             'deploy_configs': deploy_configs,
1564             'run_configs': run_configs,
1565         }
1566         return xml
1568     def generate_meta_pro_shared_content(self):
1570         build_configs = self.generate_meta_build_configs()
1571         deploy_configs = self.generate_deploy_configs("")
1572         run_configs = self.generate_run_configs("")
1574         xml = QtCreatorIntegrationGenerator.pro_shared_template % {
1575             'build_configs': build_configs,
1576             'deploy_configs': deploy_configs,
1577             'run_configs': run_configs,
1578         }
1579         return xml
1581     pro_shared_template = """<?xml version="1.0" encoding="UTF-8"?>
1582 <!DOCTYPE QtCreatorProject>
1583 <!-- Written by QtCreator 3.1.1, 2015-05-14T15:54:34. -->
1584 <qtcreator>
1585  <data>
1586   <variable>ProjectExplorer.Project.ActiveTarget</variable>
1587   <value type="int">0</value>
1588  </data>
1590  <!-- editor settings -->
1591  <data>
1592   <variable>ProjectExplorer.Project.EditorSettings</variable>
1593   <valuemap type="QVariantMap">
1594    <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
1595    <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
1596    <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
1597    <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
1598     <value type="QString" key="language">Cpp</value>
1599     <valuemap type="QVariantMap" key="value">
1600      <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
1601     </valuemap>
1602    </valuemap>
1603    <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
1604     <value type="QString" key="language">QmlJS</value>
1605     <valuemap type="QVariantMap" key="value">
1606      <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
1607     </valuemap>
1608    </valuemap>
1609    <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
1610    <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
1611    <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
1612    <value type="int" key="EditorConfiguration.IndentSize">4</value>
1613    <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
1614    <value type="int" key="EditorConfiguration.MarginColumn">80</value>
1615    <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
1616    <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
1617    <value type="int" key="EditorConfiguration.PaddingMode">1</value>
1618    <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
1619    <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
1620    <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
1621    <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
1622    <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
1623    <value type="int" key="EditorConfiguration.TabSize">8</value>
1624    <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
1625    <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
1626    <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
1627    <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
1628    <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
1629    <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
1630   </valuemap>
1631  </data>
1633  <data>
1634   <variable>ProjectExplorer.Project.PluginSettings</variable>
1635   <valuemap type="QVariantMap"/>
1636  </data>
1638  <!-- target -->
1639  <data>
1640   <variable>ProjectExplorer.Project.Target.0</variable>
1641   <valuemap type="QVariantMap">
1642    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
1643    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
1644    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{0701de51-c96e-4e4f-85c3-e70b223c5076}</value>
1645    <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
1646    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
1647    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
1649    <!-- build configurations -->
1650    %(build_configs)s
1652    <!-- deploy configurations -->
1653    %(deploy_configs)s
1655    <!-- plugin settings -->
1656    <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
1658    <!-- run configurations -->
1659    %(run_configs)s
1661   </valuemap>
1662  </data>
1663  <!-- nb targets : 1 -->
1664  <data>
1665   <variable>ProjectExplorer.Project.TargetCount</variable>
1666   <value type="int">1</value>
1667  </data>
1668  <data>
1669   <variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
1670   <value type="QByteArray">{5abcafed-86f6-49f6-b1cb-380fadd21211}</value>
1671  </data>
1672  <data>
1673   <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
1674   <value type="int">15</value>
1675  </data>
1676 </qtcreator>
1679     def get_file_path(self, src_file, ext_choices):
1680         path = os.path.join(self.gbuildparser.srcdir, src_file)
1681         for ext in ext_choices:
1682             full_path = path + ext
1683             if os.path.isfile(full_path):
1684                 return full_path
1685         return ""
1687     def get_source_path(self, src_file):
1688         return self.get_file_path(src_file, (".cxx", ".cpp", ".c", ".mm"))
1690     def get_header_path(self, src_file):
1691         return self.get_file_path(src_file, (".hxx", ".hpp", ".h"))
1693     def build_data_libs(self):
1695         self.data_libs = {}
1697         all_libs = set(self.gbuildparser.libs) | set(self.gbuildparser.exes) | set(self.gbuildparser.tests)
1698         for lib in all_libs:
1699             self._log("\nlibrary : %s, loc=%s" % (lib.short_name(), lib.location))
1700             lib_name = os.path.basename(lib.location)
1701             lib_folder = os.path.relpath(lib.location, self.base_folder)
1703             def lopath(path):
1704                 if platform == "cygwin":
1705                     # absolute paths from GbuildToJson are Windows paths,
1706                     # so convert everything to such ones
1707                     abs_path = path
1708                     if not ntpath.isabs(abs_path):
1709                         abs_path = ntpath.join(self.gbuildparser.srcdir, path)
1710                     return abs_path.replace('\\', '/')
1712                 return os.path.abspath(path)
1714             defines_list = []
1715             sources_list = []
1716             objcxx_sources_list = []
1717             includepath_list = []
1718             # The explicit headers list is not mandatory :
1719             # QtCreator just needs 'include_path_list' to find all headers files.
1720             # But files listed in 'header_list' will be shown
1721             # in a specific "Headers" folder in QtCreator's Project panel.
1722             # We will list here only headers files of current lib.
1723             headers_list = []
1724             objcxx_headers_list = []
1725             for file_ in lib.cxxobjects:
1726                 # the file has no extension : search it
1727                 # self._log("\n    file : %s" % file_)
1728                 path = self.get_source_path(file_)
1729                 if path:
1730                     sources_list.append(lopath(path))
1732                 # few cxxobject files have a header beside
1733                 path = self.get_header_path(file_)
1734                 if path:
1735                     headers_list.append(lopath(path))
1737             for file_ in lib.objcxxobjects:
1738                 # the file has no extension: search it
1739                 path = self.get_source_path(file_)
1740                 if path:
1741                     objcxx_sources_list.append(lopath(path))
1743                 # several objcxxobject files have a header beside
1744                 path = self.get_header_path(file_)
1745                 if path:
1746                     objcxx_headers_list.append(lopath(path))
1748             cxxstdversionflag = ''
1749             for cxxflag in lib.cxxflags:
1750                 # extract flag for C++ standard version
1751                 if cxxflag.startswith('-std'):
1752                     cxxstdversionflag = cxxflag
1754             # List all include paths
1755             for hdir in (lib.include + lib.include_sys):
1756                 hf_lopath = lopath(hdir)
1757                 includepath_list.append(hf_lopath)
1759             # List headers files from current lib
1760             for hdir in lib.include:
1761                 if hdir.startswith(lib.location):
1762                     for dirpath, _, files in os.walk(hdir):
1763                         for hf in files:
1764                             if hf.endswith(('.h', '.hxx', '.hpp', '.hrc')):
1765                                 hf_lopath = lopath(os.path.join(dirpath, hf))
1766                                 headers_list.append(hf_lopath)
1768             # List defines
1769             for key, value in lib.defs.items():
1770                 define = key
1771                 if value is not None:
1772                     define += '=' + value
1773                 defines_list.append(define)
1775             # All data are prepared, store them for the lib.
1776             if lib_folder in self.data_libs:
1777                 self.data_libs[lib_folder]['sources'] |= set(sources_list)
1778                 self.data_libs[lib_folder]['headers'] |= set(headers_list)
1779                 self.data_libs[lib_folder]['objcxx_sources'] |= set(objcxx_sources_list)
1780                 self.data_libs[lib_folder]['objcxx_headers'] |= set(objcxx_headers_list)
1781                 self.data_libs[lib_folder]['cxxstdversionflag'] = cxxstdversionflag
1782                 self.data_libs[lib_folder]['includepath'] |= set(includepath_list)
1783                 self.data_libs[lib_folder]['defines'] |= set(defines_list)
1784             else:
1785                 self.data_libs[lib_folder] = {
1786                     'sources': set(sources_list),
1787                     'headers': set(headers_list),
1788                     'objcxx_sources': set(objcxx_sources_list),
1789                     'objcxx_headers': set(objcxx_headers_list),
1790                     'cxxstdversionflag': cxxstdversionflag,
1791                     'includepath': set(includepath_list),
1792                     'defines': set(defines_list),
1793                     'loc': lib.location,
1794                     'name': lib_name
1795                 }
1797     def emit(self):
1799         mode = 'w+'
1800         self.base_folder = self.gbuildparser.builddir
1802         # for .pro files, we must explicitly list all files (.c, .h)
1803         # so we can't reuse directly the same method than for kde integration.
1804         self.build_data_libs()
1806         # subdirs for the meta .pro file
1807         subdirs_meta_pro = []
1808         subdirs_list = self.data_libs.keys()
1809         # Now we can create Qt files
1810         for lib_folder in subdirs_list:
1811             sources_list = sorted(self.data_libs[lib_folder]['sources'])
1812             headers_list = sorted(self.data_libs[lib_folder]['headers'])
1813             objcxx_sources_list = sorted(self.data_libs[lib_folder]['objcxx_sources'])
1814             objcxx_headers_list = sorted(self.data_libs[lib_folder]['objcxx_headers'])
1815             cxxstdversionflag = self.data_libs[lib_folder]['cxxstdversionflag']
1816             includepath_list = sorted(self.data_libs[lib_folder]['includepath'])
1817             defines_list = sorted(self.data_libs[lib_folder]['defines'])
1818             lib_loc = self.data_libs[lib_folder]['loc']
1819             lib_name = self.data_libs[lib_folder]['name']
1821             sources = " \\\n".join(sources_list)
1822             headers = " \\\n".join(headers_list)
1823             objcxx_sources = " \\\n".join(objcxx_sources_list)
1824             objcxx_headers = " \\\n".join(objcxx_headers_list)
1825             includepath = " \\\n".join(includepath_list)
1826             defines = " \\\n".join(defines_list)
1827             # strip '-std=' or '-std:' prefix
1828             assert(isinstance(cxxstdversionflag, str) and cxxstdversionflag.startswith('-std'))
1829             cxxstdversion = cxxstdversionflag[5:]
1831             # create .pro file
1832             subdirs_meta_pro.append(lib_name)
1833             qt_pro_file = os.path.join(self.base_folder, lib_name, lib_name + '.pro')
1834             try:
1835                 content = QtCreatorIntegrationGenerator.pro_template % {'sources': sources, 'headers': headers, 'cxxstdversionflag': cxxstdversionflag,
1836                                                                         'cxxstdversion': cxxstdversion, 'objcxx_sources': objcxx_sources,
1837                                                                         'objcxx_headers': objcxx_headers, 'includepath': includepath, 'defines': defines}
1838                 with open(qt_pro_file, mode) as fpro:
1839                     fpro.write(content)
1840                 self._log("created %s\n" % qt_pro_file)
1842             except Exception as e:
1843                 print("ERROR : creating pro file=" + qt_pro_file, file=sys.stderr)
1844                 print(e, file=sys.stderr)
1845                 temp = traceback.format_exc()  # .decode('utf8')
1846                 print(temp, file=sys.stderr)
1847                 print("\n\n", file=sys.stderr)
1849             # create .pro.shared file
1850             qt_pro_shared_file = os.path.join(self.base_folder, lib_name, lib_name + '.pro.shared')
1851             try:
1852                 with open(qt_pro_shared_file, mode) as fproshared:
1853                     fproshared.write(self.generate_pro_shared_content(lib_folder))
1854                 self._log("created %s\n" % qt_pro_shared_file)
1856             except Exception as e:
1857                 print("ERROR : creating pro.shared file=" + qt_pro_shared_file, file=sys.stderr)
1858                 print(e, file=sys.stderr)
1859                 temp = traceback.format_exc()
1860                 print(temp, file=sys.stderr)
1861                 print("\n\n", file=sys.stderr)
1863         # create meta .pro file (lists all sub projects)
1864         qt_meta_pro_file = os.path.join(self.base_folder, 'lo.pro')
1865         try:
1866             subdirs = " \\\n".join(sorted(subdirs_meta_pro))
1867             content = QtCreatorIntegrationGenerator.pro_meta_template % {'subdirs': subdirs}
1868             with open(qt_meta_pro_file, 'w+') as fmpro:
1869                 fmpro.write(content)
1871         except Exception as e:
1872             print("ERROR : creating lo.pro file=" + qt_meta_pro_file, file=sys.stderr)
1873             print(e, file=sys.stderr)
1874             temp = traceback.format_exc()
1875             print(temp, file=sys.stderr)
1876             print("\n\n", file=sys.stderr)
1878         # create meta .pro.shared file
1879         qt_meta_pro_shared_file = os.path.join(self.base_folder, 'lo.pro.shared')
1880         try:
1881             with open(qt_meta_pro_shared_file, mode) as fmproshared:
1882                 fmproshared.write(self.generate_meta_pro_shared_content())
1883             self._log("created %s\n" % qt_meta_pro_shared_file)
1885         except Exception as e:
1886             print("ERROR : creating lo.pro.shared file=" + qt_meta_pro_shared_file, file=sys.stderr)
1887             print(e, file=sys.stderr)
1888             temp = traceback.format_exc()
1889             print(temp, file=sys.stderr)
1890             print("\n\n", file=sys.stderr)
1892         self.log_close()
1894     pro_template = """TEMPLATE = lib
1895 CONFIG += console
1896 CONFIG -= app_bundle
1897 CONFIG -= qt
1898 CONFIG += %(cxxstdversion)s
1900 QMAKE_CXXFLAGS += %(cxxstdversionflag)s
1902 INCLUDEPATH += %(includepath)s
1904 SOURCES += %(sources)s
1906 HEADERS += %(headers)s
1908 OBJECTIVE_SOURCES += %(objcxx_sources)s
1910 OBJECTIVE_HEADERS += %(objcxx_headers)s
1912 DEFINES += %(defines)s
1915     pro_meta_template = """TEMPLATE = subdirs
1917 SUBDIRS = %(subdirs)s
1921 def get_options():
1922     parser = argparse.ArgumentParser(
1923         description='LibreOffice gbuild IDE project generator')
1924     parser.add_argument('--ide', dest='ide', required=True,
1925                         help='the IDE to generate project files for')
1926     parser.add_argument('--make', dest='makecmd', required=True,
1927                         help='the command to execute make')
1928     return parser.parse_args()
1931 if __name__ == '__main__':
1932     args = get_options()
1933     # FIXME: Hack
1934     if args.makecmd == 'make':
1935         args.makecmd = '/usr/bin/make'
1937     paths = {}
1938     generators = {
1939         'codelite': CodeliteIntegrationGenerator,
1940         'eclipsecdt': EclipseCDTIntegrationGenerator,
1941         'kdevelop': KdevelopIntegrationGenerator,
1942         'xcode': XcodeIntegrationGenerator,
1943         'vs': VisualStudioIntegrationGenerator,
1944         'vim': VimIntegrationGenerator,
1945         'debug': DebugIntegrationGenerator,
1946         'qtcreator': QtCreatorIntegrationGenerator,
1947     }
1949     if args.ide not in generators.keys():
1950         print("Invalid ide. valid values are %s" % ','.join(generators.keys()))
1951         sys.exit(1)
1953     gbuildparser = GbuildParser(args.makecmd).parse()
1955     generators[args.ide](gbuildparser, args.ide).emit()
1956     print("Successfully created the project files.")
1958 # Local Variables:
1959 # indent-tabs-mode: nil
1960 # End:
1962 # vim: set et sw=4 ts=4: