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