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