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