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