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/.
19 import xml.etree.ElementTree as ET
20 import xml.dom.minidom as minidom
23 class GbuildParserState:
36 class GbuildLinkTarget:
38 def __init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
39 (self.name, self.location, self.include, self.include_sys, self.defs, self.cxxobjects, self.cxxflags, self.linked_libs) = (
40 name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
46 return not self.include and not self.defs and not self.cxxobjects and not self.linked_libs
49 return '%s at %s with include path: %s, isystem includes: %s, defines: %s, objects: %s, cxxflags: %s and linked libs: %s' % (
50 self.short_name(), self.location, self.include, self.include_sys, self.defs, self.cxxobjects,
51 self.cxxflags, self.linked_libs)
54 class GbuildLib(GbuildLinkTarget):
56 def __init__(self, name, library, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
57 GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
58 self.library = library
61 """Return the short name of target based on the Library_* makefile name"""
62 return 'Library %s' % self.name
64 def target_name(self):
65 return 'Library_%s' % self.library
67 def library_name(self):
71 class GbuildExe(GbuildLinkTarget):
73 def __init__(self, name, executable, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs):
74 GbuildLinkTarget.__init__(self, name, location, include, include_sys, defs, cxxobjects, cxxflags, linked_libs)
75 self.executable = executable
78 """Return the short name of target based on the Executable_* makefile name"""
79 return 'Executable %s' % self.name
81 def target_name(self):
82 return 'Executable_%s' % self.executable
86 makecmdpattern = re.compile('^MAKE_COMMAND := (.*)')
87 srcdirpattern = re.compile('^SRCDIR = (.*)')
88 builddirpattern = re.compile('^BUILDDIR = (.*)')
89 instdirpattern = re.compile('^INSTDIR = (.*)')
90 binpathpattern = re.compile('^GPERF = (.*)gperf(.exe)?')
91 libnamespattern = re.compile('^gb_Library_ILIBFILENAMES := (.*)')
92 exenamepattern = re.compile('^gb_Executable_FILENAMES_FOR_BUILD := (.*)')
93 rulepattern = re.compile('^(.+?):( .*)?$')
94 libpattern = re.compile('# [a-z]+ to execute \(from [\'`](.*)/Library_(.*)\.mk\', line [0-9]*\):')
95 exepattern = re.compile('# [a-z]+ to execute \(from [\'`](.*)/Executable_(.*)\.mk\', line [0-9]*\):')
96 includepattern = re.compile('-I(\S+)')
97 isystempattern = re.compile('-isystem\s*(\S+)')
98 defspattern = re.compile('# DEFS := (.*)')
99 cxxpattern = re.compile('# CXXOBJECTS := (.*)')
100 linkedlibspattern = re.compile('# LINKED_LIBS := (.*)')
101 ilibpattern = re.compile('# ILIBTARGET := (.*)')
102 warningpattern = re.compile('-W\S+')
105 (self.makecmd, self.srcdir, self.builddir, self.instdir, self.libs,
106 self.exes, self.libnames, self.exenames, self.target_by_path, self.target_by_location) = ('', '', '', '', [], [], {}, {}, {}, {})
108 def __mapping_to_dict(self, mapping):
110 for item in mapping.split(' '):
111 library, target = item.split(':')
112 mapping_dict[target] = library
115 def _parse_hash(self, line, state):
116 libmatch = GbuildParser.libpattern.match(line)
118 libname = self.libnames.get(state.ilib, None)
120 GbuildLib(libmatch.group(2), libname, libmatch.group(1),
121 state.include, state.include_sys, state.defs, state.cxxobjects,
122 state.cxxflags, state.linked_libs))
123 state = GbuildParserState()
125 exematch = GbuildParser.exepattern.match(line)
127 exename = self.exenames.get(state.target, None)
129 GbuildExe(exematch.group(2), exename, exematch.group(1),
130 state.include, state.include_sys, state.defs, state.cxxobjects,
131 state.cxxflags, state.linked_libs))
132 state = GbuildParserState()
134 if line.find('# INCLUDE :=') == 0:
135 isystemmatch = GbuildParser.isystempattern.findall(line)
137 state.include_sys = isystemmatch
138 state.include = [includeswitch.strip() for includeswitch in GbuildParser.includepattern.findall(line) if
139 len(includeswitch) > 2]
141 defsmatch = GbuildParser.defspattern.match(line)
143 alldefs = [defswitch.strip()[2:] for defswitch in defsmatch.group(1).split(' ') if len(defswitch) > 2]
145 defparts = d.split('=')
146 if len(defparts) == 1:
147 defparts.append(None)
148 state.defs[defparts[0]] = defparts[1]
149 state.defs["LIBO_INTERNAL_ONLY"] = None
151 cxxmatch = GbuildParser.cxxpattern.match(line)
153 state.cxxobjects = [obj for obj in cxxmatch.group(1).split(' ') if len(obj) > 0]
155 linkedlibsmatch = GbuildParser.linkedlibspattern.match(line)
157 state.linked_libs = linkedlibsmatch.group(1).strip().split(' ')
159 ilibmatch = GbuildParser.ilibpattern.match(line)
161 state.ilib = os.path.basename(ilibmatch.group(1))
163 if line.find('# T_CXXFLAGS :=') == 0:
164 state.cxxflags = [cxxflag.strip() for cxxflag in GbuildParser.warningpattern.sub('', line.replace('# T_CXXFLAGS :=', '')).split(' ') if len(cxxflag) > 1]
166 # we could match a lot of other stuff here if needed for integration rpaths etc.
169 def parse(self, gbuildstate):
170 state = GbuildParserState()
171 for line in gbuildstate:
172 line = line.rstrip('\r\n')
173 if line.startswith('#'):
174 state = self._parse_hash(line, state)
176 makecmdmatch = GbuildParser.makecmdpattern.match(line)
178 self.makecmd = makecmdmatch.group(1)
180 if self.makecmd == 'make':
181 self.makecmd = '/usr/bin/make'
183 srcdirmatch = GbuildParser.srcdirpattern.match(line)
185 self.srcdir = srcdirmatch.group(1)
187 builddirmatch = GbuildParser.builddirpattern.match(line)
189 self.builddir = builddirmatch.group(1)
191 instdirmatch = GbuildParser.instdirpattern.match(line)
193 self.instdir = instdirmatch.group(1)
195 binpathmatch = GbuildParser.binpathpattern.match(line)
197 self.binpath = binpathmatch.group(1)
199 rulematch = self.rulepattern.match(line)
201 if len(rulematch.groups()) == 2 \
202 and rulematch.group(2) is not None \
203 and ':=' in rulematch.group(2):
204 # Hack to make GNU make >= 4.x happy
205 state = self._parse_hash('#' + rulematch.group(2), state)
207 state.target = os.path.basename(rulematch.group(1))
209 libnamesmatch = GbuildParser.libnamespattern.match(line)
211 self.libnames = self.__mapping_to_dict(libnamesmatch.group(1))
213 exenamesmatch = GbuildParser.exenamepattern.match(line)
215 self.exenames = self.__mapping_to_dict(exenamesmatch.group(1))
217 state = GbuildParserState()
219 for target in set(self.libs) | set(self.exes):
220 if target.location not in self.target_by_location:
221 self.target_by_location[target.location] = set()
222 self.target_by_location[target.location] |= set([target])
223 for cxx in target.cxxobjects:
224 path = '/'.join(cxx.split('/')[:-1])
225 if path not in self.target_by_path:
226 self.target_by_path[path] = set()
227 self.target_by_path[path] |= set([target])
228 for path in self.target_by_path:
229 if len(self.target_by_path[path]) > 1:
230 print('fdo#70422: multiple target use dir %s: %s' % (
231 path, ', '.join([target.short_name() for target in self.target_by_path[path]])))
236 class IdeIntegrationGenerator:
238 def __init__(self, gbuildparser, ide):
239 self.gbuildparser = gbuildparser
245 class EclipseCDTIntegrationGenerator(IdeIntegrationGenerator):
246 def __init__(self, gbuildparser, ide):
247 IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
248 self.oe_cdt = 'org.eclipse.cdt'
249 self.cdt_mb = self.oe_cdt + '.managebuilder.core'
250 self.cdt_core = self.oe_cdt + '.core'
252 def generate_project_file(self, name, comment, xmlversion, encoding):
254 projectfiletemplate = """
255 <?xml version="%(xmlversion)s" encoding="%(encoding)s"?>
257 <name>%(name)s</name>
258 <comment>%(comment)s</comment>
263 <name>"""+ self.cdt_mb +""".genmakebuilder</name>
264 <triggers>clean,full,incremental,</triggers>
269 <name>"""+ self.cdt_mb +""".ScannerConfigBuilder</name>
270 <triggers>full,incremental,</triggers>
276 <nature>""" + self.cdt_core + """.cnature</nature>
277 <nature>""" + self.cdt_core + """.ccnature</nature>
278 <nature>""" + self.cdt_mb + """.managedBuildNature</nature>
279 <nature>""" + self.cdt_mb + """.ScannerConfigNature</nature>
281 </projectDescription>
284 return projectfiletemplate % {'name': name, 'comment': comment, 'xmlversion': xmlversion, 'encoding':encoding}
286 class DebugIntegrationGenerator(IdeIntegrationGenerator):
288 def __init__(self, gbuildparser, ide):
289 IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
292 print(self.gbuildparser.srcdir)
293 print(self.gbuildparser.builddir)
294 for lib in self.gbuildparser.libs:
296 for exe in self.gbuildparser.exes:
300 class VimIntegrationGenerator(IdeIntegrationGenerator):
302 def __init__(self, gbuildparser, ide):
303 IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
307 for lib in self.gbuildparser.libs:
309 for file in lib.cxxobjects:
310 filePath = os.path.join(self.gbuildparser.srcdir, file) + ".cxx"
311 entry = {'directory': lib.location, 'file': filePath, 'command': self.generateCommand(lib, filePath)}
312 entries.append(entry)
313 global_list.extend(entries)
314 export_file = open('compile_commands.json', 'w')
315 json.dump(global_list, export_file)
317 def generateCommand(self, lib, file):
319 for key, value in lib.defs.items():
322 if value is not None:
325 for include in lib.include:
328 for isystem in lib.include_sys:
329 command += ' -isystem '
331 for cxxflag in lib.cxxflags:
336 return command.replace('-std=gnu++11', '-std=c++11')
339 class KdevelopIntegrationGenerator(IdeIntegrationGenerator):
341 def encode_int(self, i):
343 return '\\x%s\\x%s\\x%s\\x%s' % (temp[0:2], temp[2:4], temp[4:6], temp[6:8])
345 def encode_string(self, string):
346 result = self.encode_int(len(string) * 2)
347 for c in string.encode('utf-16-be'):
348 if c in range(32, 126):
351 result += '\\x%02x' % c
354 def generate_buildsystemconfigtool(self, configid, tool, args, exe, typenr):
355 return KdevelopIntegrationGenerator.buildsystemconfigtooltemplate % {'configid': configid, 'tool': tool,
356 'args': args, 'exe': exe, 'typenr': typenr}
358 buildsystemconfigtooltemplate = """
359 [CustomBuildSystem][BuildConfig%(configid)d][Tool%(tool)s]
368 def generate_buildsystemconfig(self, configid, moduledir, builddir, title, buildparms=''):
369 result = KdevelopIntegrationGenerator.buildsystemconfigtemplate % {'configid': configid, 'builddir': builddir,
371 result += self.generate_buildsystemconfigtool(configid, 'Clean', 'clean %s' % buildparms,
372 self.gbuildparser.makecmd, 3)
373 result += self.generate_buildsystemconfigtool(configid, 'Build', 'all %s' % buildparms,
374 self.gbuildparser.makecmd, 0)
377 buildsystemconfigtemplate = """
378 [CustomBuildSystem][BuildConfig%(configid)d]
379 BuildDir=file://%(builddir)s
384 def generate_buildsystem(self, moduledir):
385 result = KdevelopIntegrationGenerator.buildsystemtemplate % {'defaultconfigid': 0}
386 result += self.generate_buildsystemconfig(0, moduledir, moduledir, 'Module Build -- Release')
387 result += self.generate_buildsystemconfig(1, moduledir, self.gbuildparser.builddir, 'Full Build -- Release')
388 result += self.generate_buildsystemconfig(2, moduledir, moduledir, 'Module Build -- Debug', 'debug=T')
389 result += self.generate_buildsystemconfig(3, moduledir, self.gbuildparser.builddir, 'Full Build -- Debug',
393 buildsystemtemplate = """
395 CurrentConfiguration=BuildConfig%(defaultconfigid)d
399 def generate_launch(self, launchid, launchname, executablepath, args, workdir):
400 return KdevelopIntegrationGenerator.launchtemplate % {'launchid': launchid, 'launchname': launchname,
401 'executablepath': executablepath, 'args': args,
405 [Launch][Launch Configuration %(launchid)d]
406 Configured Launch Modes=execute
407 Configured Launchers=nativeAppLauncher
409 Type=Native Application
411 [Launch][Launch Configuration %(launchid)d][Data]
413 Dependencies=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x00)
414 Dependency Action=Nothing
415 EnvironmentGroup=default
416 Executable=file://%(executablepath)s
417 External Terminal=konsole --noclose --workdir %%workdir -e %%exe
419 Use External Terminal=false
420 Working Directory=file://%(workdir)s
425 def generate_launches(self, moduledir):
426 launches = ','.join(['Launch Configuration %d' % i for i in range(7)])
427 result = KdevelopIntegrationGenerator.launchestemplate % {'launches': launches}
428 result += self.generate_launch(0, 'Local tests -- quick tests (unitcheck)', self.gbuildparser.makecmd,
429 'unitcheck', moduledir)
430 result += self.generate_launch(1, 'Local tests -- slow tests (unitcheck, slowcheck)', self.gbuildparser.makecmd,
431 'unitcheck slowcheck', moduledir)
432 result += self.generate_launch(2, 'Local tests -- integration tests (unitcheck, slowcheck, subsequentcheck)',
433 self.gbuildparser.makecmd, 'unitcheck slowcheck subsequentcheck', moduledir)
434 result += self.generate_launch(3, 'Global tests -- quick tests (unitcheck)', self.gbuildparser.makecmd,
435 'unitcheck', self.gbuildparser.builddir)
436 result += self.generate_launch(4, 'Global tests -- slow tests (unitcheck, slowcheck)',
437 self.gbuildparser.makecmd, 'unitcheck slowcheck', self.gbuildparser.builddir)
438 result += self.generate_launch(5, 'Global tests -- integration tests (unitcheck, slowcheck, subsequentcheck)',
439 self.gbuildparser.makecmd, 'unitcheck slowcheck subsequentcheck',
440 self.gbuildparser.builddir)
441 result += self.generate_launch(6, 'Run LibreOffice',
442 os.path.join(self.gbuildparser.instdir, 'program/soffice.bin'), '',
443 self.gbuildparser.instdir)
446 launchestemplate = """
448 Launch Configurations=%(launches)s
452 def write_modulebeef(self, moduledir, modulename):
453 beefdir = os.path.join(moduledir, '.kdev4')
455 beeffile = open(os.path.join(beefdir, 'Module_%s.kdev4' % modulename), 'w')
456 beeffile.write(self.generate_buildsystem(moduledir))
457 beeffile.write(self.generate_launches(moduledir))
460 def write_modulestub(self, moduledir, modulename):
461 stubfile = open(os.path.join(moduledir, 'Module_%s.kdev4' % modulename), 'w')
462 stubfile.write(KdevelopIntegrationGenerator.modulestubtemplate % {'modulename': modulename,
463 'builditem': self.encode_string(
464 'Module_%s' % modulename)})
467 modulestubtemplate = """
469 BuildItems=@Variant(\\x00\\x00\\x00\\t\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0b\\x00\\x00\\x00\\x00\\x01%(builditem)s)
472 Name=Module_%(modulename)s
473 Manager=KDevCustomBuildSystem
474 VersionControl=kdevgit
477 def write_includepaths(self, path):
478 includedirfile = open(os.path.join(path, '.kdev_include_paths'), 'w')
480 for target in self.gbuildparser.target_by_path[path]:
481 include |= set(target.include)
482 includedirfile.write('\n'.join(include))
483 includedirfile.close()
485 def __init__(self, gbuildparser, ide):
486 IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
490 for path in self.gbuildparser.target_by_path:
491 self.write_includepaths(path)
492 for location in self.gbuildparser.target_by_location:
493 for f in os.listdir(location):
494 if f.endswith('.kdev4'):
496 os.remove(os.path.join(location, f))
498 shutil.rmtree(os.path.join(location, f))
499 for location in self.gbuildparser.target_by_location:
500 modulename = os.path.split(location)[1]
501 self.write_modulestub(location, modulename)
502 self.write_modulebeef(location, modulename)
505 class XcodeIntegrationGenerator(IdeIntegrationGenerator):
507 def indent(self, file, level):
510 for i in range(0, level):
513 def write_object(self, object, file, indent):
514 if isinstance(object, int):
515 file.write('%d' % object)
516 elif isinstance(object, str) and not re.search('[^A-Za-z0-9_]', object):
517 file.write('%s' % object)
518 elif isinstance(object, str):
519 file.write('"%s"' % object)
520 elif isinstance(object, dict):
521 self.write_dict(object, file, indent)
523 # Write a dictionary out as an "old-style (NeXT) ASCII plist"
524 def write_dict(self, dict, file, indent):
527 for key in sorted(dict.keys()):
528 self.indent(file, indent + 1)
529 file.write('%s = ' % key)
530 self.write_object(dict[key], file, indent + 1)
532 self.indent(file, indent)
535 def write_dict_to_plist(self, dict, file):
536 file.write('// !$*UTF8*$!\n')
537 self.write_dict(dict, file, 0)
539 def get_product_type(self, modulename):
540 if modulename in self.gbuildparser.libs:
541 return 'com.apple.product-type.library.dynamic'
542 elif modulename in self.gbuildparser.exes:
543 return 'com.apple.product-type.something'
547 def generate_id(self):
548 XcodeIntegrationGenerator.counter = XcodeIntegrationGenerator.counter + 1
549 return str('X%07x' % XcodeIntegrationGenerator.counter)
551 def generate_build_phases(self, modulename):
552 result = [self.sourcesBuildPhaseId]
555 def generate_root_object(self, modulename):
556 result = {'isa': 'PBXProject',
557 'attributes': {'LastUpgradeCheck': '0500',
558 'ORGANIZATIONNAME': 'LibreOffice'},
559 'buildConfigurationList': self.generate_id(),
560 'compatibilityVersion': 'Xcode 3.2',
561 'hasScannedForEncodings': 0,
562 'knownRegions': ['en'],
563 'mainGroup': self.mainGroupId,
564 'productRefGroup': self.productRefGroupId,
565 'projectDirPath': '',
567 'targets': self.targetId}
570 def generate_target(self, modulename):
571 result = {'isa': 'PBXNativeTarget',
572 'buildConfigurationList': self.generate_id(),
573 'buildPhases': self.generate_build_phases(modulename),
577 'productName': modulename,
578 'productReference': self.productReferenceId,
579 'productType': self.get_product_type(modulename)}
582 def generate_main_group(self, modulename):
583 result = {'isa': 'PBXGroup',
584 'children': [self.subMainGroupId, self.productGroupId],
585 'sourceTree': '<group>'}
588 def generate_sub_main_children(self, modulename):
591 def generate_sub_main_group(self, modulename):
592 result = {'isa': 'PBXGroup',
593 'children': self.generate_sub_main_children(modulename),
595 'sourceTree': '<group>'}
598 def generate_product_group(self, modulename):
599 result = {'isa': 'PBXGroup',
600 'children': [self.productReferenceId],
602 'sourceTree': '<group>'}
605 def build_source_list(self, module):
606 self.sourceRefList = {}
609 for i in module.cxxobjects:
610 ref = self.generate_id()
611 self.sourceList[self.generate_id()] = ref
612 self.sourceRefList[ref] = {'lastKnownFileType': 'sourcecode.cpp.cpp',
614 'sourceTree': '<group>'}
616 def generate_sources_build_phase(self, modulename):
617 result = {'isa': 'PBXSourcesBuildPhase',
618 'buildActionMask': 2147483647,
619 'files': self.sourceList.keys(),
620 'runOnlyForDeploymentPostprocessing': 0}
623 def generate_project(self, target):
624 self.rootObjectId = self.generate_id()
625 self.mainGroupId = self.generate_id()
626 self.subMainGroupId = self.generate_id()
627 self.productReferenceId = self.generate_id()
628 self.productRefGroupId = self.generate_id()
629 self.productGroupId = self.generate_id()
630 self.targetId = self.generate_id()
631 self.build_source_list(target)
632 self.sourcesBuildPhaseId = self.generate_id()
633 objects = {self.rootObjectId: self.generate_root_object(target),
634 self.targetId: self.generate_target(target),
635 self.mainGroupId: self.generate_main_group(target),
636 self.subMainGroupId: self.generate_sub_main_group(target),
637 self.productGroupId: self.generate_product_group(target),
638 self.sourcesBuildPhaseId: self.generate_sources_build_phase(target)
640 for i in self.sourceList.keys():
641 ref = self.sourceList[i]
642 objects[i] = {'isa': 'PBXBuildFile',
644 objects[ref] = {'isa': 'PBXFileReference',
645 'lastKnownFileType': self.sourceRefList[ref]['lastKnownFileType'],
646 'path': self.sourceRefList[ref]['path']}
647 project = {'archiveVersion': 1,
651 'rootObject': self.rootObjectId}
654 # For some reverse-engineered documentation on the project.pbxproj format,
655 # see http://www.monobjc.net/xcode-project-file-format.html .
656 def write_xcodeproj(self, moduledir, target):
657 xcodeprojdir = os.path.join(moduledir, '%s.xcodeproj' % target.name)
659 os.mkdir(xcodeprojdir)
662 self.write_dict_to_plist(self.generate_project(target),
663 open(os.path.join(xcodeprojdir, 'project.pbxproj'), 'w'))
665 def __init__(self, gbuildparser, ide):
666 IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
669 self.rootlocation = './'
670 for location in self.gbuildparser.target_by_location:
671 module = location.split('/')[-1]
672 module_directory = os.path.join(self.rootlocation, module)
673 for target in self.gbuildparser.target_by_location[location]:
674 # project_path = os.path.join(module_directory, '%s.pbxroj' % target.name)
675 self.write_xcodeproj(location, target)
677 class VisualStudioIntegrationGenerator(IdeIntegrationGenerator):
679 def __init__(self, gbuildparser, ide):
680 IdeIntegrationGenerator.__init__(self, gbuildparser, ide)
681 self.toolset = self.retrieve_toolset(ide)
682 self.solution_directory = './'
683 self.configurations = {
685 'build': self.module_make_command('%(target)s'),
686 'clean': self.module_make_command('%(target)s.clean'),
687 'rebuild': self.module_make_command('%(target)s.clean %(target)s')
690 'build': self.module_make_command('unitcheck'),
691 'clean': self.module_make_command('clean'),
692 'rebuild': self.module_make_command('clean unitcheck'),
694 'Integration tests': {
695 'build': self.module_make_command('unitcheck slowcheck subsequentcheck'),
696 'clean': self.module_make_command('clean'),
697 'rebuild': self.module_make_command('clean unitcheck slowcheck subsequentcheck')
701 def retrieve_toolset(self, ide):
702 ide_toolset_map = {'vs2012': 'v110', 'vs2013': 'v120'}
703 return ide_toolset_map[ide]
705 def module_make_command(self, targets):
706 return '%(sh)s -c "PATH=\\"/bin:$PATH\\";BUILDDIR=\\"%(builddir)s\\" %(makecmd)s -rsC %(location)s ' + targets + '"'
710 def __init__(self, guid, target, project_path):
713 self.path = project_path
717 for location in self.gbuildparser.target_by_location:
719 module = location.split('/')[-1]
720 module_directory = os.path.join(self.solution_directory, module)
721 for target in self.gbuildparser.target_by_location[location]:
722 project_path = os.path.join(module_directory, '%s.vcxproj' % target.name)
723 project_guid = self.write_project(project_path, target)
724 p = VisualStudioIntegrationGenerator.Project(project_guid, target, project_path)
726 self.write_solution(os.path.join(module_directory, '%s.sln' % module), projects)
727 all_projects += projects
729 self.write_solution(os.path.join(self.solution_directory, 'LibreOffice.sln'), all_projects)
731 nmake_project_guid = '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942'
733 def get_dependency_libs(self, linked_libs, library_projects):
735 for linked_lib in linked_libs:
736 for library_project in library_projects:
737 if library_project.target.library_name() == linked_lib:
738 dependency_libs[library_project.guid] = library_project
739 return dependency_libs
741 def write_solution(self, solution_path, projects):
742 print('Solution %s:' % os.path.splitext(os.path.basename(solution_path))[0], end='')
743 library_projects = [project for project in projects if project.target in self.gbuildparser.libs]
744 with open(solution_path, 'w') as f:
745 f.write('Microsoft Visual Studio Solution File, Format Version 12.00\n')
746 for project in projects:
747 target = project.target
748 print(' %s' % target.name, end='')
749 proj_path = os.path.relpath(project.path, os.path.abspath(os.path.dirname(solution_path)))
750 f.write('Project("{%s}") = "%s", "%s", "{%s}"\n' %
751 (VisualStudioIntegrationGenerator.nmake_project_guid,
752 target.short_name(), proj_path, project.guid))
753 libs_in_solution = self.get_dependency_libs(target.linked_libs,
756 f.write('\tProjectSection(ProjectDependencies) = postProject\n')
757 for lib_guid in libs_in_solution.keys():
758 f.write('\t\t{%(guid)s} = {%(guid)s}\n' % {'guid': lib_guid})
759 f.write('\tEndProjectSection\n')
760 f.write('EndProject\n')
763 f.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
764 for cfg in self.configurations:
765 f.write('\t\t%(cfg)s|%(platform)s = %(cfg)s|%(platform)s\n' % {'cfg': cfg, 'platform': platform})
766 f.write('\tEndGlobalSection\n')
767 f.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
768 # Specifies project configurations for solution configuration
769 for project in projects:
770 for cfg in self.configurations:
771 params = {'guid': project.guid, 'sol_cfg': cfg, 'proj_cfg': cfg, 'platform': platform}
772 f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.ActiveCfg = %(proj_cfg)s|%(platform)s\n' % params)
773 # Build.0 is basically 'Build checkbox' in configuration manager
774 f.write('\t\t{%(guid)s}.%(sol_cfg)s|%(platform)s.Build.0 = %(proj_cfg)s|%(platform)s\n' % params)
775 f.write('\tEndGlobalSection\n')
776 f.write('EndGlobal\n')
779 def write_project(self, project_path, target):
780 # See info at http://blogs.msdn.com/b/visualstudio/archive/2010/05/14/a-guide-to-vcxproj-and-props-file-structure.aspx
781 folder = os.path.dirname(project_path)
782 if not os.path.exists(folder):
784 project_guid = str(uuid.uuid4()).upper()
785 ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
786 ET.register_namespace('', ns)
787 proj_node = ET.Element('{%s}Project' % ns, DefaultTargets='Build', ToolsVersion='4.0')
788 proj_confs_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns, Label='ProjectConfigurations')
790 for configuration in self.configurations:
791 proj_conf_node = ET.SubElement(proj_confs_node,
792 '{%s}ProjectConfiguration' % ns,
793 Include='%s|%s' % (configuration, platform))
794 conf_node = ET.SubElement(proj_conf_node, '{%s}Configuration' % ns)
795 conf_node.text = configuration
796 platform_node = ET.SubElement(proj_conf_node, '{%s}Platform' % ns)
797 platform_node.text = platform
799 globals_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='Globals')
800 proj_guid_node = ET.SubElement(globals_node, '{%s}ProjectGuid' % ns)
801 proj_guid_node.text = '{%s}' % project_guid
802 proj_keyword_node = ET.SubElement(globals_node, '{%s}Keyword' % ns)
803 proj_keyword_node.text = 'MakeFileProj'
804 proj_name_node = ET.SubElement(globals_node, '{%s}ProjectName' % ns)
805 proj_name_node.text = target.short_name()
807 ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
808 for configuration in self.configurations:
809 conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label="Configuration",
810 Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform))
811 # Type of project used by the MSBuild to determine build process, see Microsoft.Makefile.targets
812 conf_type_node = ET.SubElement(conf_node, '{%s}ConfigurationType' % ns)
813 conf_type_node.text = 'Makefile'
814 # VS2012: I need to have this otherwise the names of projects will contain text Visual Studio 2010 in the Solution Explorer
815 platform_toolset_node = ET.SubElement(conf_node, '{%s}PlatformToolset' % ns)
816 platform_toolset_node.text = self.toolset
818 ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.props')
819 ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionSettings')
820 for configuration in self.configurations:
821 prop_sheets_node = ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='Configuration',
822 Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (configuration, platform))
823 ET.SubElement(prop_sheets_node, '{%s}Import' % ns,
824 Project='$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props',
825 Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')",
826 Label='LocalAppDataPlatform')
828 ET.SubElement(proj_node, '{%s}PropertyGroup' % ns, Label='UserMacros')
829 for cfg_name, cfg_targets in self.configurations.items():
830 conf_node = ET.SubElement(proj_node, '{%s}PropertyGroup' % ns,
831 Condition="'$(Configuration)|$(Platform)'=='%s|%s'" % (cfg_name, platform))
833 'sh': os.path.join(self.gbuildparser.binpath, 'dash.exe'),
834 'builddir': self.gbuildparser.builddir,
835 'location': target.location,
836 'makecmd': self.gbuildparser.makecmd,
837 'target': target.target_name()}
838 nmake_build_node = ET.SubElement(conf_node, '{%s}NMakeBuildCommandLine' % ns)
839 nmake_build_node.text = cfg_targets['build'] % nmake_params
840 nmake_clean_node = ET.SubElement(conf_node, '{%s}NMakeCleanCommandLine' % ns)
841 nmake_clean_node.text = cfg_targets['clean'] % nmake_params
842 nmake_rebuild_node = ET.SubElement(conf_node, '{%s}NMakeReBuildCommandLine' % ns)
843 nmake_rebuild_node.text = cfg_targets['rebuild'] % nmake_params
844 nmake_output_node = ET.SubElement(conf_node, '{%s}NMakeOutput' % ns)
845 nmake_output_node.text = os.path.join(self.gbuildparser.instdir, 'program', 'soffice.exe')
846 nmake_defs_node = ET.SubElement(conf_node, '{%s}NMakePreprocessorDefinitions' % ns)
847 nmake_defs_node.text = ';'.join(list(target.defs) + ['$(NMakePreprocessorDefinitions)'])
848 include_path_node = ET.SubElement(conf_node, '{%s}IncludePath' % ns)
849 include_path_node.text = ';'.join(target.include + ['$(IncludePath)'])
851 ET.SubElement(proj_node, '{%s}ItemDefinitionGroup' % ns)
853 cxxobjects_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
854 for cxxobject in target.cxxobjects:
855 cxxabspath = os.path.join(self.gbuildparser.srcdir, cxxobject)
856 cxxfile = cxxabspath + '.cxx'
857 if os.path.isfile(cxxfile):
858 ET.SubElement(cxxobjects_node, '{%s}ClCompile' % ns, Include=cxxfile)
860 print('Source %s in project %s does not exist' % (cxxfile, target.name))
862 includes_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
863 for cxxobject in target.cxxobjects:
864 include_abs_path = os.path.join(self.gbuildparser.srcdir, cxxobject)
865 hxxfile = include_abs_path + '.hxx'
866 if os.path.isfile(hxxfile):
867 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hxxfile)
868 # Few files have corresponding .h files
869 hfile = include_abs_path + '.h'
870 if os.path.isfile(hfile):
871 ET.SubElement(includes_node, '{%s}ClInclude' % ns, Include=hfile)
872 ET.SubElement(proj_node, '{%s}Import' % ns, Project='$(VCTargetsPath)\Microsoft.Cpp.targets')
873 ET.SubElement(proj_node, '{%s}ImportGroup' % ns, Label='ExtensionTargets')
874 self.write_pretty_xml(proj_node, project_path)
875 self.write_filters(project_path + '.filters',
876 os.path.join(self.gbuildparser.srcdir, os.path.basename(target.location)),
877 [cxx_node.get('Include') for cxx_node in cxxobjects_node.findall('{%s}ClCompile' % ns)],
878 [include_node.get('Include') for include_node in includes_node.findall('{%s}ClInclude' % ns)])
881 def get_filter(self, module_dir, proj_file):
882 return '\\'.join(os.path.relpath(proj_file, module_dir).split('/')[:-1])
884 def get_subfilters(self, proj_filter):
885 parts = proj_filter.split('\\')
886 subfilters = set([proj_filter])
887 for i in range(1, len(parts)):
888 subfilters.add('\\'.join(parts[:i]))
891 def write_pretty_xml(self, node, file_path):
892 xml_str = ET.tostring(node, encoding='unicode')
893 pretty_str = minidom.parseString(xml_str).toprettyxml(encoding='utf-8')
894 with open(file_path, 'w') as f:
895 f.write(pretty_str.decode())
897 def add_nodes(self, files_node, module_dir, tag, project_files):
898 ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
900 for project_file in project_files:
901 file_node = ET.SubElement(files_node, tag, Include=project_file)
902 if os.path.commonprefix([module_dir, project_file]) == module_dir:
903 project_filter = self.get_filter(module_dir, project_file)
904 filter_node = ET.SubElement(file_node, '{%s}Filter' % ns)
905 filter_node.text = project_filter
906 filters |= self.get_subfilters(project_filter)
909 def write_filters(self, filters_path, module_dir, compile_files, include_files):
910 ns = 'http://schemas.microsoft.com/developer/msbuild/2003'
911 ET.register_namespace('', ns)
912 proj_node = ET.Element('{%s}Project' % ns, ToolsVersion='4.0')
914 compiles_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
915 filters |= self.add_nodes(compiles_node, module_dir, '{%s}ClCompile' % ns, compile_files)
916 include_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
917 filters |= self.add_nodes(include_node, module_dir, '{%s}ClInclude' % ns, include_files)
919 filters_node = ET.SubElement(proj_node, '{%s}ItemGroup' % ns)
920 for proj_filter in filters:
921 filter_node = ET.SubElement(filters_node, '{%s}Filter' % ns, Include=proj_filter)
922 filter_id_node = ET.SubElement(filter_node, '{%s}UniqueIdentifier' % ns)
923 filter_id_node.text = '{%s}' % str(uuid.uuid4())
924 self.write_pretty_xml(proj_node, filters_path)
927 if __name__ == '__main__':
928 parser = argparse.ArgumentParser(
929 description='LibreOffice gbuild IDE project generator')
930 parser.add_argument('--ide', dest='ide', required=True,
931 help='the IDE to generate project files for')
932 parser.add_argument('--input', dest='input', required=False,
933 help='the input file, not normally used, for debugging this script')
934 args = parser.parse_args()
937 'eclipsecdt': EclipseCDTIntegrationGenerator,
938 'kdevelop': KdevelopIntegrationGenerator,
939 'xcode': XcodeIntegrationGenerator,
940 'vs2012': VisualStudioIntegrationGenerator,
941 'vs2013': VisualStudioIntegrationGenerator,
942 'vim': VimIntegrationGenerator,
943 'debug': DebugIntegrationGenerator}
945 if args.ide not in generators.keys():
946 print("Invalid ide. valid values are %s" % ','.join(generators.keys()))
950 gbuildparser = GbuildParser().parse(open(args.input, 'r'))
952 gbuildparser = GbuildParser().parse(sys.stdin)
954 generators[args.ide](gbuildparser, args.ide).emit()
957 # indent-tabs-mode: nil
960 # vim: set et sw=4 ts=4: