Browse Source

Upgrade GYP to r995

v0.7.4-release
Ryan Dahl 13 years ago
parent
commit
66f77963a3
  1. 21
      tools/gyp/MANIFEST
  2. 1
      tools/gyp/OWNERS
  3. 41
      tools/gyp/PRESUBMIT.py
  4. 79
      tools/gyp/buildbot/buildbot_run.py
  5. 18
      tools/gyp/gyp
  6. 5
      tools/gyp/gyp.bat
  7. 149
      tools/gyp/pylib/gyp/MSVSProject.py
  8. 69
      tools/gyp/pylib/gyp/MSVSToolFile.py
  9. 140
      tools/gyp/pylib/gyp/MSVSUserFile.py
  10. 197
      tools/gyp/pylib/gyp/easy_xml.py
  11. 124
      tools/gyp/pylib/gyp/easy_xml_test.py
  12. 176
      tools/gyp/pylib/gyp/generator/make.py
  13. 187
      tools/gyp/pylib/gyp/generator/msvs.py
  14. 2
      tools/gyp/pylib/gyp/input.py
  15. 81
      tools/gyp/samples/samples
  16. 5
      tools/gyp/samples/samples.bat
  17. 15
      tools/gyp/tools/README
  18. 95
      tools/gyp/tools/graphviz.py
  19. 142
      tools/gyp/tools/pretty_gyp.py
  20. 167
      tools/gyp/tools/pretty_sln.py
  21. 316
      tools/gyp/tools/pretty_vcproj.py

21
tools/gyp/MANIFEST

@ -0,0 +1,21 @@
setup.py
gyp
LICENSE
AUTHORS
pylib/gyp/MSVSNew.py
pylib/gyp/MSVSProject.py
pylib/gyp/MSVSToolFile.py
pylib/gyp/MSVSUserFile.py
pylib/gyp/MSVSVersion.py
pylib/gyp/SCons.py
pylib/gyp/__init__.py
pylib/gyp/common.py
pylib/gyp/input.py
pylib/gyp/xcodeproj_file.py
pylib/gyp/generator/__init__.py
pylib/gyp/generator/gypd.py
pylib/gyp/generator/gypsh.py
pylib/gyp/generator/make.py
pylib/gyp/generator/msvs.py
pylib/gyp/generator/scons.py
pylib/gyp/generator/xcode.py

1
tools/gyp/OWNERS

@ -0,0 +1 @@
*

41
tools/gyp/PRESUBMIT.py

@ -0,0 +1,41 @@
# Copyright (c) 2011 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Top-level presubmit script for GYP.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
def CheckChangeOnUpload(input_api, output_api):
report = []
report.extend(input_api.canned_checks.PanProjectChecks(
input_api, output_api))
return report
def CheckChangeOnCommit(input_api, output_api):
report = []
license = (
r'.*? Copyright \(c\) %(year)s Google Inc\. All rights reserved\.\n'
r'.*? Use of this source code is governed by a BSD-style license that '
r'can be\n'
r'.*? found in the LICENSE file\.\n'
) % {
'year': input_api.time.strftime('%Y'),
}
report.extend(input_api.canned_checks.PanProjectChecks(
input_api, output_api, license_header=license))
report.extend(input_api.canned_checks.CheckTreeIsOpen(
input_api, output_api,
'http://gyp-status.appspot.com/status',
'http://gyp-status.appspot.com/current'))
return report
def GetPreferredTrySlaves():
return ['gyp-win32', 'gyp-win64', 'gyp-linux', 'gyp-mac']

79
tools/gyp/buildbot/buildbot_run.py

@ -0,0 +1,79 @@
#!/usr/bin/python
# Copyright (c) 2011 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Argument-less script to select what to run on the buildbots."""
import os
import shutil
import subprocess
import sys
def GypTestFormat(title, format, msvs_version=None):
"""Run the gyp tests for a given format, emitting annotator tags.
See annotator docs at:
https://sites.google.com/a/chromium.org/dev/developers/testing/chromium-build-infrastructure/buildbot-annotations
Args:
format: gyp format to test.
Returns:
0 for sucesss, 1 for failure.
"""
print '@@@BUILD_STEP ' + title + '@@@'
sys.stdout.flush()
buildbot_dir = os.path.dirname(os.path.abspath(__file__))
trunk_dir = os.path.dirname(buildbot_dir)
root_dir = os.path.dirname(trunk_dir)
env = os.environ.copy()
if msvs_version:
env['GYP_MSVS_VERSION'] = msvs_version
retcode = subprocess.call(' '.join(
[sys.executable, 'trunk/gyptest.py',
'--all',
'--passed',
'--format', format,
'--chdir', 'trunk',
'--path', '../scons']),
cwd=root_dir, env=env, shell=True)
if retcode:
# Emit failure tag, and keep going.
print '@@@STEP_FAILURE@@@'
return 1
return 0
def GypBuild():
# Dump out/ directory.
print '@@@BUILD_STEP cleanup@@@'
print 'Removing out/ ...'
shutil.rmtree('out', ignore_errors=True)
print 'Done.'
retcode = 0
if sys.platform.startswith('linux'):
retcode += GypTestFormat('scons', format='scons')
retcode += GypTestFormat('make', format='make')
elif sys.platform == 'darwin':
retcode += GypTestFormat('xcode', format='xcode')
retcode += GypTestFormat('make', format='make')
elif sys.platform == 'win32':
retcode += GypTestFormat('msvs-2008', format='msvs', msvs_version='2008')
if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64':
retcode += GypTestFormat('msvs-2010', format='msvs', msvs_version='2010')
else:
raise Exception('Unknown platform')
if retcode:
# TODO(bradnelson): once the annotator supports a postscript (section for
# after the build proper that could be used for cumulative failures),
# use that instead of this. This isolates the final return value so
# that it isn't misattributed to the last stage.
print '@@@BUILD_STEP failures@@@'
sys.exit(retcode)
if __name__ == '__main__':
GypBuild()

18
tools/gyp/gyp

@ -0,0 +1,18 @@
#!/usr/bin/env python
# Copyright (c) 2009 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
# TODO(mark): sys.path manipulation is some temporary testing stuff.
try:
import gyp
except ImportError, e:
import os.path
sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'pylib'))
import gyp
if __name__ == '__main__':
sys.exit(gyp.main(sys.argv[1:]))

5
tools/gyp/gyp.bat

@ -0,0 +1,5 @@
@rem Copyright (c) 2009 Google Inc. All rights reserved.
@rem Use of this source code is governed by a BSD-style license that can be
@rem found in the LICENSE file.
@python "%~dp0/gyp" %*

149
tools/gyp/pylib/gyp/MSVSProject.py

@ -7,8 +7,7 @@
"""Visual Studio project reader/writer.""" """Visual Studio project reader/writer."""
import common import common
import xml.dom import gyp.easy_xml as easy_xml
import xml_fix
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
@ -23,24 +22,16 @@ class Tool(object):
name: Tool name. name: Tool name.
attrs: Dict of tool attributes; may be None. attrs: Dict of tool attributes; may be None.
""" """
self.name = name self._attrs = attrs or {}
self.attrs = attrs or {} self._attrs['Name'] = name
def CreateElement(self, doc): def _GetSpecification(self):
"""Creates an element for the tool. """Creates an element for the tool.
Args:
doc: xml.dom.Document object to use for node creation.
Returns: Returns:
A new xml.dom.Element for the tool. A new xml.dom.Element for the tool.
""" """
node = doc.createElement('Tool') return ['Tool', self._attrs]
node.setAttribute('Name', self.name)
for k, v in self.attrs.items():
node.setAttribute(k, v)
return node
class Filter(object): class Filter(object):
"""Visual Studio filter - that is, a virtual folder.""" """Visual Studio filter - that is, a virtual folder."""
@ -62,24 +53,18 @@ class Filter(object):
class Writer(object): class Writer(object):
"""Visual Studio XML project writer.""" """Visual Studio XML project writer."""
def __init__(self, project_path, version): def __init__(self, project_path, version, name, guid=None, platforms=None):
"""Initializes the project. """Initializes the project.
Args: Args:
project_path: Path to the project file. project_path: Path to the project file.
version: Format version to emit. version: Format version to emit.
"""
self.project_path = project_path
self.doc = None
self.version = version
def Create(self, name, guid=None, platforms=None):
"""Creates the project document.
Args:
name: Name of the project. name: Name of the project.
guid: GUID to use for project, if not None. guid: GUID to use for project, if not None.
platforms: Array of string, the supported platforms. If null, ['Win32']
""" """
self.project_path = project_path
self.version = version
self.name = name self.name = name
self.guid = guid self.guid = guid
@ -87,46 +72,16 @@ class Writer(object):
if not platforms: if not platforms:
platforms = ['Win32'] platforms = ['Win32']
# Create XML doc # Initialize the specifications of the various sections.
xml_impl = xml.dom.getDOMImplementation() self.platform_section = ['Platforms']
self.doc = xml_impl.createDocument(None, 'VisualStudioProject', None)
# Add attributes to root element
self.n_root = self.doc.documentElement
self.n_root.setAttribute('ProjectType', 'Visual C++')
self.n_root.setAttribute('Version', self.version.ProjectVersion())
self.n_root.setAttribute('Name', self.name)
self.n_root.setAttribute('ProjectGUID', self.guid)
self.n_root.setAttribute('RootNamespace', self.name)
self.n_root.setAttribute('Keyword', 'Win32Proj')
# Add platform list
n_platform = self.doc.createElement('Platforms')
self.n_root.appendChild(n_platform)
for platform in platforms: for platform in platforms:
n = self.doc.createElement('Platform') self.platform_section.append(['Platform', {'Name': platform}])
n.setAttribute('Name', platform) self.tool_files_section = ['ToolFiles']
n_platform.appendChild(n) self.configurations_section = ['Configurations']
self.files_section = ['Files']
# Add tool files section
self.n_tool_files = self.doc.createElement('ToolFiles')
self.n_root.appendChild(self.n_tool_files)
# Add configurations section
self.n_configs = self.doc.createElement('Configurations')
self.n_root.appendChild(self.n_configs)
# Add empty References section
self.n_root.appendChild(self.doc.createElement('References'))
# Add files section
self.n_files = self.doc.createElement('Files')
self.n_root.appendChild(self.n_files)
# Keep a dict keyed on filename to speed up access. # Keep a dict keyed on filename to speed up access.
self.n_files_dict = dict() self.files_dict = dict()
# Add empty Globals section
self.n_root.appendChild(self.doc.createElement('Globals'))
def AddToolFile(self, path): def AddToolFile(self, path):
"""Adds a tool file to the project. """Adds a tool file to the project.
@ -134,20 +89,17 @@ class Writer(object):
Args: Args:
path: Relative path from project to tool file. path: Relative path from project to tool file.
""" """
n_tool = self.doc.createElement('ToolFile') self.tool_files_section.append(['ToolFile', {'RelativePath': path}])
n_tool.setAttribute('RelativePath', path)
self.n_tool_files.appendChild(n_tool)
def _AddConfigToNode(self, parent, config_type, config_name, attrs=None, def _GetSpecForConfiguration(self, config_type, config_name, attrs, tools):
tools=None): """Returns the specification for a configuration.
"""Adds a configuration to the parent node.
Args: Args:
parent: Destination node.
config_type: Type of configuration node. config_type: Type of configuration node.
config_name: Configuration name. config_name: Configuration name.
attrs: Dict of configuration attributes; may be None. attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None. tools: List of tools (strings or Tool objects); may be None.
Returns:
""" """
# Handle defaults # Handle defaults
if not attrs: if not attrs:
@ -156,19 +108,19 @@ class Writer(object):
tools = [] tools = []
# Add configuration node and its attributes # Add configuration node and its attributes
n_config = self.doc.createElement(config_type) node_attrs = attrs.copy()
n_config.setAttribute('Name', config_name) node_attrs['Name'] = config_name
for k, v in attrs.items(): specification = [config_type, node_attrs]
n_config.setAttribute(k, v)
parent.appendChild(n_config)
# Add tool nodes and their attributes # Add tool nodes and their attributes
if tools: if tools:
for t in tools: for t in tools:
if isinstance(t, Tool): if isinstance(t, Tool):
n_config.appendChild(t.CreateElement(self.doc)) specification.append(t._GetSpecification())
else: else:
n_config.appendChild(Tool(t).CreateElement(self.doc)) specification.append(Tool(t)._GetSpecification())
return specification
def AddConfig(self, name, attrs=None, tools=None): def AddConfig(self, name, attrs=None, tools=None):
"""Adds a configuration to the project. """Adds a configuration to the project.
@ -178,7 +130,8 @@ class Writer(object):
attrs: Dict of configuration attributes; may be None. attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None. tools: List of tools (strings or Tool objects); may be None.
""" """
self._AddConfigToNode(self.n_configs, 'Configuration', name, attrs, tools) spec = self._GetSpecForConfiguration('Configuration', name, attrs, tools)
self.configurations_section.append(spec)
def _AddFilesToNode(self, parent, files): def _AddFilesToNode(self, parent, files):
"""Adds files and/or filters to the parent node. """Adds files and/or filters to the parent node.
@ -191,14 +144,12 @@ class Writer(object):
""" """
for f in files: for f in files:
if isinstance(f, Filter): if isinstance(f, Filter):
node = self.doc.createElement('Filter') node = ['Filter', {'Name': f.name}]
node.setAttribute('Name', f.name)
self._AddFilesToNode(node, f.contents) self._AddFilesToNode(node, f.contents)
else: else:
node = self.doc.createElement('File') node = ['File', {'RelativePath': f}]
node.setAttribute('RelativePath', f) self.files_dict[f] = node
self.n_files_dict[f] = node parent.append(node)
parent.appendChild(node)
def AddFiles(self, files): def AddFiles(self, files):
"""Adds files to the project. """Adds files to the project.
@ -210,7 +161,7 @@ class Writer(object):
later add files to a Filter object which was passed into a previous call later add files to a Filter object which was passed into a previous call
to AddFiles(), it will not be reflected in this project. to AddFiles(), it will not be reflected in this project.
""" """
self._AddFilesToNode(self.n_files, files) self._AddFilesToNode(self.files_section, files)
# TODO(rspangler) This also doesn't handle adding files to an existing # TODO(rspangler) This also doesn't handle adding files to an existing
# filter. That is, it doesn't merge the trees. # filter. That is, it doesn't merge the trees.
@ -227,19 +178,35 @@ class Writer(object):
ValueError: Relative path does not match any file added via AddFiles(). ValueError: Relative path does not match any file added via AddFiles().
""" """
# Find the file node with the right relative path # Find the file node with the right relative path
parent = self.n_files_dict.get(path) parent = self.files_dict.get(path)
if not parent: if not parent:
raise ValueError('AddFileConfig: file "%s" not in project.' % path) raise ValueError('AddFileConfig: file "%s" not in project.' % path)
# Add the config to the file node # Add the config to the file node
self._AddConfigToNode(parent, 'FileConfiguration', config, attrs, tools) spec = self._GetSpecForConfiguration('FileConfiguration', config, attrs,
tools)
parent.append(spec)
def Write(self, writer=common.WriteOnDiff): def WriteIfChanged(self):
"""Writes the project file.""" """Writes the project file."""
f = writer(self.project_path) # First create XML content definition
fix = xml_fix.XmlFix() content = [
self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\r\n') 'VisualStudioProject',
fix.Cleanup() {'ProjectType': 'Visual C++',
f.close() 'Version': self.version.ProjectVersion(),
'Name': self.name,
'ProjectGUID': self.guid,
'RootNamespace': self.name,
'Keyword': 'Win32Proj'
},
self.platform_section,
self.tool_files_section,
self.configurations_section,
['References'], # empty section
self.files_section,
['Globals'] # empty section
]
easy_xml.WriteXmlIfChanged(content, self.project_path,
encoding="Windows-1252")
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------

69
tools/gyp/pylib/gyp/MSVSToolFile.py

@ -7,45 +7,22 @@
"""Visual Studio project reader/writer.""" """Visual Studio project reader/writer."""
import common import common
import xml.dom import gyp.easy_xml as easy_xml
import xml_fix
#------------------------------------------------------------------------------
class Writer(object): class Writer(object):
"""Visual Studio XML tool file writer.""" """Visual Studio XML tool file writer."""
def __init__(self, tool_file_path): def __init__(self, tool_file_path, name):
"""Initializes the tool file. """Initializes the tool file.
Args: Args:
tool_file_path: Path to the tool file. tool_file_path: Path to the tool file.
"""
self.tool_file_path = tool_file_path
self.doc = None
def Create(self, name):
"""Creates the tool file document.
Args:
name: Name of the tool file. name: Name of the tool file.
""" """
self.tool_file_path = tool_file_path
self.name = name self.name = name
self.rules_section = ['Rules']
# Create XML doc
xml_impl = xml.dom.getDOMImplementation()
self.doc = xml_impl.createDocument(None, 'VisualStudioToolFile', None)
# Add attributes to root element
self.n_root = self.doc.documentElement
self.n_root.setAttribute('Version', '8.00')
self.n_root.setAttribute('Name', self.name)
# Add rules section
self.n_rules = self.doc.createElement('Rules')
self.n_root.appendChild(self.n_rules)
def AddCustomBuildRule(self, name, cmd, description, def AddCustomBuildRule(self, name, cmd, description,
additional_dependencies, additional_dependencies,
@ -60,22 +37,24 @@ class Writer(object):
outputs: outputs of the rule. outputs: outputs of the rule.
extensions: extensions handled by the rule. extensions: extensions handled by the rule.
""" """
n_rule = self.doc.createElement('CustomBuildRule') rule = ['CustomBuildRule',
n_rule.setAttribute('Name', name) {'Name': name,
n_rule.setAttribute('ExecutionDescription', description) 'ExecutionDescription': description,
n_rule.setAttribute('CommandLine', cmd) 'CommandLine': cmd,
n_rule.setAttribute('Outputs', ';'.join(outputs)) 'Outputs': ';'.join(outputs),
n_rule.setAttribute('FileExtensions', ';'.join(extensions)) 'FileExtensions': ';'.join(extensions),
n_rule.setAttribute('AdditionalDependencies', 'AdditionalDependencies':
';'.join(additional_dependencies)) ';'.join(additional_dependencies)
self.n_rules.appendChild(n_rule) }]
self.rules_section.append(rule)
def Write(self, writer=common.WriteOnDiff):
def WriteIfChanged(self):
"""Writes the tool file.""" """Writes the tool file."""
f = writer(self.tool_file_path) content = ['VisualStudioToolFile',
fix = xml_fix.XmlFix() {'Version': '8.00',
self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\r\n') 'Name': self.name
fix.Cleanup() },
f.close() self.rules_section
]
#------------------------------------------------------------------------------ easy_xml.WriteXmlIfChanged(content, self.tool_file_path,
encoding="Windows-1252")

140
tools/gyp/pylib/gyp/MSVSUserFile.py

@ -10,8 +10,7 @@ import common
import os import os
import re import re
import socket # for gethostname import socket # for gethostname
import xml.dom import gyp.easy_xml as easy_xml
import xml_fix
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
@ -56,49 +55,18 @@ def _QuoteWin32CommandLineArgs(args):
class Writer(object): class Writer(object):
"""Visual Studio XML user user file writer.""" """Visual Studio XML user user file writer."""
def __init__(self, user_file_path, version): def __init__(self, user_file_path, version, name):
"""Initializes the user file. """Initializes the user file.
Args: Args:
user_file_path: Path to the user file. user_file_path: Path to the user file.
version: Version info.
name: Name of the user file.
""" """
self.user_file_path = user_file_path self.user_file_path = user_file_path
self.version = version self.version = version
self.doc = None
def Create(self, name):
"""Creates the user file document.
Args:
name: Name of the user file.
"""
self.name = name self.name = name
self.configurations = {}
# Create XML doc
xml_impl = xml.dom.getDOMImplementation()
self.doc = xml_impl.createDocument(None, 'VisualStudioUserFile', None)
# Add attributes to root element
self.n_root = self.doc.documentElement
self.n_root.setAttribute('Version', self.version.ProjectVersion())
self.n_root.setAttribute('Name', self.name)
# Add configurations section
self.n_configs = self.doc.createElement('Configurations')
self.n_root.appendChild(self.n_configs)
def _AddConfigToNode(self, parent, config_type, config_name):
"""Adds a configuration to the parent node.
Args:
parent: Destination node.
config_type: Type of configuration node.
config_name: Configuration name.
"""
# Add configuration node and its attributes
n_config = self.doc.createElement(config_type)
n_config.setAttribute('Name', config_name)
parent.appendChild(n_config)
def AddConfig(self, name): def AddConfig(self, name):
"""Adds a configuration to the project. """Adds a configuration to the project.
@ -106,8 +74,7 @@ class Writer(object):
Args: Args:
name: Configuration name. name: Configuration name.
""" """
self._AddConfigToNode(self.n_configs, 'Configuration', name) self.configurations[name] = ['Configuration', {'Name': name}]
def AddDebugSettings(self, config_name, command, environment = {}, def AddDebugSettings(self, config_name, command, environment = {},
working_directory=""): working_directory=""):
@ -121,62 +88,61 @@ class Writer(object):
""" """
command = _QuoteWin32CommandLineArgs(command) command = _QuoteWin32CommandLineArgs(command)
n_cmd = self.doc.createElement('DebugSettings')
abs_command = _FindCommandInPath(command[0]) abs_command = _FindCommandInPath(command[0])
n_cmd.setAttribute('Command', abs_command)
n_cmd.setAttribute('WorkingDirectory', working_directory)
n_cmd.setAttribute('CommandArguments', " ".join(command[1:]))
n_cmd.setAttribute('RemoteMachine', socket.gethostname())
if environment and isinstance(environment, dict): if environment and isinstance(environment, dict):
n_cmd.setAttribute('Environment', env_list = ['%s="%s"' % (key, val)
" ".join(['%s="%s"' % (key, val) for (key,val) in environment.iteritems()]
for (key,val) in environment.iteritems()])) environment = ' '.join(env_list)
else: else:
n_cmd.setAttribute('Environment', '') environment = ''
n_cmd.setAttribute('EnvironmentMerge', 'true') n_cmd = ['DebugSettings',
{'Command': abs_command,
# Currently these are all "dummy" values that we're just setting 'WorkingDirectory': working_directory,
# in the default manner that MSVS does it. We could use some of 'CommandArguments': " ".join(command[1:]),
# these to add additional capabilities, I suppose, but they might 'RemoteMachine': socket.gethostname(),
# not have parity with other platforms then. 'Environment': environment,
n_cmd.setAttribute('Attach', 'false') 'EnvironmentMerge': 'true',
n_cmd.setAttribute('DebuggerType', '3') # 'auto' debugger # Currently these are all "dummy" values that we're just setting
n_cmd.setAttribute('Remote', '1') # in the default manner that MSVS does it. We could use some of
n_cmd.setAttribute('RemoteCommand', '') # these to add additional capabilities, I suppose, but they might
n_cmd.setAttribute('HttpUrl', '') # not have parity with other platforms then.
n_cmd.setAttribute('PDBPath', '') 'Attach': 'false',
n_cmd.setAttribute('SQLDebugging', '') 'DebuggerType': '3', # 'auto' debugger
n_cmd.setAttribute('DebuggerFlavor', '0') 'Remote': '1',
n_cmd.setAttribute('MPIRunCommand', '') 'RemoteCommand': '',
n_cmd.setAttribute('MPIRunArguments', '') 'HttpUrl': '',
n_cmd.setAttribute('MPIRunWorkingDirectory', '') 'PDBPath': '',
n_cmd.setAttribute('ApplicationCommand', '') 'SQLDebugging': '',
n_cmd.setAttribute('ApplicationArguments', '') 'DebuggerFlavor': '0',
n_cmd.setAttribute('ShimCommand', '') 'MPIRunCommand': '',
n_cmd.setAttribute('MPIAcceptMode', '') 'MPIRunArguments': '',
n_cmd.setAttribute('MPIAcceptFilter', '') 'MPIRunWorkingDirectory': '',
'ApplicationCommand': '',
'ApplicationArguments': '',
'ShimCommand': '',
'MPIAcceptMode': '',
'MPIAcceptFilter': ''
}]
# Find the config, and add it if it doesn't exist. # Find the config, and add it if it doesn't exist.
found = False if config_name not in self.configurations:
for config in self.n_configs.childNodes:
if config.getAttribute("Name") == config_name:
found = True
if not found:
self.AddConfig(config_name) self.AddConfig(config_name)
# Add the DebugSettings onto the appropriate config. # Add the DebugSettings onto the appropriate config.
for config in self.n_configs.childNodes: self.configurations[config_name].append(n_cmd)
if config.getAttribute("Name") == config_name:
config.appendChild(n_cmd)
break
def Write(self, writer=common.WriteOnDiff): def WriteIfChanged(self):
"""Writes the user file.""" """Writes the user file."""
f = writer(self.user_file_path) configs = ['Configurations']
self.doc.writexml(f, encoding='Windows-1252', addindent=' ', newl='\r\n') for config, spec in sorted(self.configurations.iteritems()):
f.close() configs.append(spec)
#------------------------------------------------------------------------------ content = ['VisualStudioUserFile',
{'Version': self.version.ProjectVersion(),
'Name': self.name
},
configs]
easy_xml.WriteXmlIfChanged(content, self.user_file_path,
encoding="Windows-1252")

197
tools/gyp/pylib/gyp/easy_xml.py

@ -4,20 +4,19 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import xml.dom import re
import xml_fix
import common
class EasyXml(object):
""" Class to easily create XML files with substantial pre-defined structures.
Visual Studio files have a lot of pre-defined structures. This class makes def XmlToString(content, encoding='utf-8', pretty=False):
""" Writes the XML content to disk, touching the file only if it has changed.
Visual Studio files have a lot of pre-defined structures. This function makes
it easy to represent these structures as Python data structures, instead of it easy to represent these structures as Python data structures, instead of
having to create a lot of function calls. having to create a lot of function calls.
For this class, an XML element is represented as a list composed of: Each XML element of the content is represented as a list composed of:
1. The name of the element, a string, 1. The name of the element, a string,
2. The attributes of the element, an dictionary (optional), and 2. The attributes of the element, a dictionary (optional), and
3+. The content of the element, if any. Strings are simple text nodes and 3+. The content of the element, if any. Strings are simple text nodes and
lists are child elements. lists are child elements.
@ -37,85 +36,115 @@ class EasyXml(object):
['childtype', 'This is'], ['childtype', 'This is'],
['childtype', 'it!'], ['childtype', 'it!'],
] ]
Args:
content: The structured content to be converted.
encoding: The encoding to report on the first XML line.
pretty: True if we want pretty printing with indents and new lines.
Returns:
The XML content as a string.
""" """
# We create a huge list of all the elements of the file.
xml_parts = ['<?xml version="1.0" encoding="%s"?>' % encoding]
if pretty:
xml_parts.append('\n')
_ConstructContentList(xml_parts, content, pretty)
# Convert it to a string
return ''.join(xml_parts)
def __init__(self, name, attributes=None):
""" Constructs an object representing an XML document. def _ConstructContentList(xml_parts, specification, pretty, level=0):
""" Appends the XML parts corresponding to the specification.
Args:
name: A string, the name of the root element. Args:
attributes: A dictionary, the attributes of the root. xml_parts: A list of XML parts to be appended to.
""" specification: The specification of the element. See EasyXml docs.
xml_impl = xml.dom.getDOMImplementation() pretty: True if we want pretty printing with indents and new lines.
self.doc = xml_impl.createDocument(None, name, None) level: Indentation level.
if attributes: """
self.SetAttributes(self.doc.documentElement, attributes) # The first item in a specification is the name of the element.
if pretty:
def AppendChildren(self, parent, children_specifications): indentation = ' ' * level
""" Appends multiple children. new_line = '\n'
else:
Args: indentation = ''
parent: The node to which the children will be added. new_line = ''
children_specifications: A list of node specifications. name = specification[0]
""" if not isinstance(name, str):
for specification in children_specifications: raise Exception('The first item of an EasyXml specification should be '
'a string. Specification was ' + str(specification))
xml_parts.append(indentation + '<' + name)
# Optionally in second position is a dictionary of the attributes.
rest = specification[1:]
if rest and isinstance(rest[0], dict):
for at, val in sorted(rest[0].iteritems()):
xml_parts.append(' %s="%s"' % (at, _XmlEscape(val)))
rest = rest[1:]
if rest:
xml_parts.append('>')
all_strings = reduce(lambda x, y: x and isinstance(y, str), rest, True)
multi_line = not all_strings
if multi_line and new_line:
xml_parts.append(new_line)
for child_spec in rest:
# If it's a string, append a text node. # If it's a string, append a text node.
# Otherwise append an XML node. # Otherwise recurse over that child definition
if isinstance(specification, str): if isinstance(child_spec, str):
parent.appendChild(self.doc.createTextNode(specification)) xml_parts.append(_XmlEscape(child_spec))
else: else:
self.AppendNode(parent, specification) _ConstructContentList(xml_parts, child_spec, pretty, level + 1)
if multi_line and indentation:
def AppendNode(self, parent, specification): xml_parts.append(indentation)
""" Appends multiple children. xml_parts.append('</%s>%s' % (name, new_line))
else:
Args: xml_parts.append('/>%s' % new_line)
parent: The node to which the child will be added.
children_specifications: A list, the node specification. The first
entry is the name of the element. If the second entry is a def WriteXmlIfChanged(content, path, encoding='utf-8', pretty=False):
dictionary, it is the attributes. The remaining entries of the """ Writes the XML content to disk, touching the file only if it has changed.
list are the sub-elements.
Returns: Args:
The XML element created. content: The structured content to be written.
""" path: Location of the file.
name = specification[0] encoding: The encoding to report on the first line of the XML file.
if not isinstance(name, str): pretty: True if we want pretty printing with indents and new lines.
raise Exception('The first item of an EasyXml specification should be ' """
'a string. Specification was ' + str(specification)) xml_string = XmlToString(content, encoding, pretty)
element = self.doc.createElement(name)
parent.appendChild(element) # Get the old content
rest = specification[1:] try:
# The second element is optionally a dictionary of the attributes. f = open(path, 'r')
if rest and isinstance(rest[0], dict): existing = f.read()
self.SetAttributes(element, rest[0]) f.close()
rest = rest[1:] except:
if rest: existing = None
self.AppendChildren(element, rest)
return element # It has changed, write it
if existing != xml_string:
def SetAttributes(self, element, attribute_description): f = open(path, 'w')
""" Sets the attributes of an element. f.write(xml_string)
Args:
element: The node to which the child will be added.
attribute_description: A dictionary that maps attribute names to
attribute values.
"""
for attribute, value in attribute_description.iteritems():
element.setAttribute(attribute, value)
def Root(self):
""" Returns the root element. """
return self.doc.documentElement
def WriteIfChanged(self, path):
""" Writes the XML doc but don't touch the file if unchanged. """
f = common.WriteOnDiff(path)
fix = xml_fix.XmlFix()
self.doc.writexml(f, encoding='utf-8', addindent='', newl='')
fix.Cleanup()
f.close() f.close()
def __str__(self):
""" Converts the doc to a string. """ _xml_escape_map = {
return self.doc.toxml() '"': '&quot;',
"'": '&apos;',
'<': '&lt;',
'>': '&gt;',
'&': '&amp;',
'\n': '&#xA;',
'\r': '&#xD;',
}
_xml_escape_re = re.compile(
"(%s)" % "|".join(map(re.escape, _xml_escape_map.keys())))
def _XmlEscape(value):
""" Escape a string for inclusion in XML."""
replace = lambda m: _xml_escape_map[m.string[m.start() : m.end()]]
return _xml_escape_re.sub(replace, value)

124
tools/gyp/pylib/gyp/easy_xml_test.py

@ -17,75 +17,85 @@ class TestSequenceFunctions(unittest.TestCase):
self.stderr = StringIO.StringIO() self.stderr = StringIO.StringIO()
def test_EasyXml_simple(self): def test_EasyXml_simple(self):
xml = easy_xml.EasyXml('test') self.assertEqual(
self.assertEqual(str(xml), '<?xml version="1.0" ?><test/>') easy_xml.XmlToString(['test']),
'<?xml version="1.0" encoding="utf-8"?><test/>')
self.assertEqual(
easy_xml.XmlToString(['test'], encoding='Windows-1252'),
'<?xml version="1.0" encoding="Windows-1252"?><test/>')
def test_EasyXml_simple_with_attributes(self): def test_EasyXml_simple_with_attributes(self):
xml = easy_xml.EasyXml('test2', {'a': 'value1', 'b': 'value2'}) self.assertEqual(
self.assertEqual(str(xml), easy_xml.XmlToString(['test2', {'a': 'value1', 'b': 'value2'}]),
'<?xml version="1.0" ?><test2 a="value1" b="value2"/>') '<?xml version="1.0" encoding="utf-8"?><test2 a="value1" b="value2"/>')
def test_EasyXml_escaping(self):
original = '<test>\'"\r&\nfoo'
converted = '&lt;test&gt;&apos;&quot;&#xD;&amp;&#xA;foo'
self.assertEqual(
easy_xml.XmlToString(['test3', {'a': original}, original]),
'<?xml version="1.0" encoding="utf-8"?><test3 a="%s">%s</test3>' %
(converted, converted))
def test_EasyXml_pretty(self):
self.assertEqual(
easy_xml.XmlToString(
['test3',
['GrandParent',
['Parent1',
['Child']
],
['Parent2']
]
],
pretty=True),
'<?xml version="1.0" encoding="utf-8"?>\n'
'<test3>\n'
' <GrandParent>\n'
' <Parent1>\n'
' <Child/>\n'
' </Parent1>\n'
' <Parent2/>\n'
' </GrandParent>\n'
'</test3>\n')
def test_EasyXml_add_node(self):
# We want to create:
target = ('<?xml version="1.0" ?>'
'<test3>'
'<GrandParent>'
'<Parent1>'
'<Child/>'
'</Parent1>'
'<Parent2/>'
'</GrandParent>'
'</test3>')
# Do it the hard way first:
xml = easy_xml.EasyXml('test3')
grand_parent = xml.AppendNode(xml.Root(), ['GrandParent'])
parent1 = xml.AppendNode(grand_parent, ['Parent1'])
parent2 = xml.AppendNode(grand_parent, ['Parent2'])
xml.AppendNode(parent1, ['Child'])
self.assertEqual(str(xml), target)
# Do it the easier way:
xml = easy_xml.EasyXml('test3')
xml.AppendNode(xml.Root(),
['GrandParent',
['Parent1', ['Child']],
['Parent2']])
self.assertEqual(str(xml), target)
def test_EasyXml_complex(self): def test_EasyXml_complex(self):
# We want to create: # We want to create:
target = ('<?xml version="1.0" ?>' target = (
'<Project>' '<?xml version="1.0" encoding="utf-8"?>'
'<PropertyGroup Label="Globals">' '<Project>'
'<ProjectGuid>{D2250C20-3A94-4FB9-AF73-11BC5B73884B}</ProjectGuid>' '<PropertyGroup Label="Globals">'
'<Keyword>Win32Proj</Keyword>' '<ProjectGuid>{D2250C20-3A94-4FB9-AF73-11BC5B73884B}</ProjectGuid>'
'<RootNamespace>automated_ui_tests</RootNamespace>' '<Keyword>Win32Proj</Keyword>'
'</PropertyGroup>' '<RootNamespace>automated_ui_tests</RootNamespace>'
'<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props"/>' '</PropertyGroup>'
'<PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'' '<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props"/>'
'Debug|Win32\'" Label="Configuration">' '<PropertyGroup '
'<ConfigurationType>Application</ConfigurationType>' 'Condition="&apos;$(Configuration)|$(Platform)&apos;=='
'<CharacterSet>Unicode</CharacterSet>' '&apos;Debug|Win32&apos;" Label="Configuration">'
'</PropertyGroup>' '<ConfigurationType>Application</ConfigurationType>'
'</Project>') '<CharacterSet>Unicode</CharacterSet>'
'</PropertyGroup>'
xml = easy_xml.EasyXml('Project') '</Project>')
xml.AppendChildren(xml.Root(), [
['PropertyGroup', {'Label': 'Globals'}, xml = easy_xml.XmlToString(
['Project',
['PropertyGroup', {'Label': 'Globals'},
['ProjectGuid', '{D2250C20-3A94-4FB9-AF73-11BC5B73884B}'], ['ProjectGuid', '{D2250C20-3A94-4FB9-AF73-11BC5B73884B}'],
['Keyword', 'Win32Proj'], ['Keyword', 'Win32Proj'],
['RootNamespace', 'automated_ui_tests'] ['RootNamespace', 'automated_ui_tests']
], ],
['Import', {'Project': '$(VCTargetsPath)\\Microsoft.Cpp.props'}], ['Import', {'Project': '$(VCTargetsPath)\\Microsoft.Cpp.props'}],
['PropertyGroup', ['PropertyGroup',
{'Condition': "'$(Configuration)|$(Platform)'=='Debug|Win32'", {'Condition': "'$(Configuration)|$(Platform)'=='Debug|Win32'",
'Label': 'Configuration'}, 'Label': 'Configuration'},
['ConfigurationType', 'Application'], ['ConfigurationType', 'Application'],
['CharacterSet', 'Unicode'] ['CharacterSet', 'Unicode']
] ]
]) ])
self.assertEqual(str(xml), target) self.assertEqual(xml, target)
if __name__ == '__main__': if __name__ == '__main__':

176
tools/gyp/pylib/gyp/generator/make.py

@ -368,7 +368,7 @@ command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\\
# so we can check their command lines. # so we can check their command lines.
# $? -- new prerequisites # $? -- new prerequisites
# $| -- order-only dependencies # $| -- order-only dependencies
prereq_changed = $(filter-out $|,$?) prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))
# do_cmd: run a command via the above cmd_foo names, if necessary. # do_cmd: run a command via the above cmd_foo names, if necessary.
# Should always run for a given target to handle command-line changes. # Should always run for a given target to handle command-line changes.
@ -454,90 +454,11 @@ SHARED_HEADER_SUFFIX_RULES_COMMENT1 = ("""\
# Suffix rules, putting all outputs into $(obj). # Suffix rules, putting all outputs into $(obj).
""") """)
SHARED_HEADER_SUFFIX_RULES_SRCDIR = {
'.c': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
"""),
'.s': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.s FORCE_DO_CMD
@$(call do_cmd,cc,1)
"""),
'.S': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.S FORCE_DO_CMD
@$(call do_cmd,cc,1)
"""),
'.cpp': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.cc': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.cxx': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.m': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.m FORCE_DO_CMD
@$(call do_cmd,objc,1)
"""),
'.mm': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD
@$(call do_cmd,objcxx,1)
"""),
}
SHARED_HEADER_SUFFIX_RULES_COMMENT2 = ("""\ SHARED_HEADER_SUFFIX_RULES_COMMENT2 = ("""\
# Try building from generated source, too. # Try building from generated source, too.
""") """)
SHARED_HEADER_SUFFIX_RULES_OBJDIR1 = {
'.c': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
"""),
'.cc': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.cpp': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.m': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD
@$(call do_cmd,objc,1)
"""),
'.mm': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD
@$(call do_cmd,objcxx,1)
"""),
}
SHARED_HEADER_SUFFIX_RULES_OBJDIR2 = {
'.c': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD
@$(call do_cmd,cc,1)
"""),
'.cc': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.cpp': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cpp FORCE_DO_CMD
@$(call do_cmd,cxx,1)
"""),
'.m': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.m FORCE_DO_CMD
@$(call do_cmd,objc,1)
"""),
'.mm': ("""\
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.mm FORCE_DO_CMD
@$(call do_cmd,objcxx,1)
"""),
}
SHARED_FOOTER = """\ SHARED_FOOTER = """\
# "all" is a concatenation of the "all" targets from all the included # "all" is a concatenation of the "all" targets from all the included
@ -700,7 +621,7 @@ class XcodeSettings(object):
assert self._IsBundle() assert self._IsBundle()
return self.GetPerTargetSetting('FRAMEWORK_VERSION', default='A') return self.GetPerTargetSetting('FRAMEWORK_VERSION', default='A')
def _GetBundleExtension(self): def GetWrapperExtension(self):
"""Returns the bundle extension (.app, .framework, .plugin, etc). Only """Returns the bundle extension (.app, .framework, .plugin, etc). Only
valid for bundles.""" valid for bundles."""
assert self._IsBundle() assert self._IsBundle()
@ -714,12 +635,15 @@ class XcodeSettings(object):
assert False, "Don't know extension for '%s', target '%s'" % ( assert False, "Don't know extension for '%s', target '%s'" % (
self.spec['type'], self.spec['target_name']) self.spec['type'], self.spec['target_name'])
def GetBundleName(self): def GetProductName(self):
"""Returns PRODUCT_NAME."""
return self.spec.get('product_name', self.spec['target_name'])
def GetWrapperName(self):
"""Returns the directory name of the bundle represented by this target. """Returns the directory name of the bundle represented by this target.
Only valid for bundles.""" Only valid for bundles."""
assert self._IsBundle() assert self._IsBundle()
return self.spec.get('product_name', return self.GetProductName() + self.GetWrapperExtension()
self.spec['target_name']) + self._GetBundleExtension()
def GetBundleContentsFolderPath(self): def GetBundleContentsFolderPath(self):
"""Returns the qualified path to the bundle's contents folder. E.g. """Returns the qualified path to the bundle's contents folder. E.g.
@ -727,10 +651,10 @@ class XcodeSettings(object):
assert self._IsBundle() assert self._IsBundle()
if self.spec['type'] == 'shared_library': if self.spec['type'] == 'shared_library':
return os.path.join( return os.path.join(
self.GetBundleName(), 'Versions', self.GetFrameworkVersion()) self.GetWrapperName(), 'Versions', self.GetFrameworkVersion())
else: else:
# loadable_modules have a 'Contents' folder like executables. # loadable_modules have a 'Contents' folder like executables.
return os.path.join(self.GetBundleName(), 'Contents') return os.path.join(self.GetWrapperName(), 'Contents')
def GetBundleResourceFolder(self): def GetBundleResourceFolder(self):
"""Returns the qualified path to the bundle's resource folder. E.g. """Returns the qualified path to the bundle's resource folder. E.g.
@ -826,10 +750,8 @@ class XcodeSettings(object):
self._WarnUnimplemented('ARCHS') self._WarnUnimplemented('ARCHS')
self._WarnUnimplemented('COPY_PHASE_STRIP') self._WarnUnimplemented('COPY_PHASE_STRIP')
self._WarnUnimplemented('DEPLOYMENT_POSTPROCESSING') self._WarnUnimplemented('DEPLOYMENT_POSTPROCESSING')
self._WarnUnimplemented('DYLIB_INSTALL_NAME_BASE')
self._WarnUnimplemented('INFOPLIST_PREPROCESS') self._WarnUnimplemented('INFOPLIST_PREPROCESS')
self._WarnUnimplemented('INFOPLIST_PREPROCESSOR_DEFINITIONS') self._WarnUnimplemented('INFOPLIST_PREPROCESSOR_DEFINITIONS')
self._WarnUnimplemented('LD_DYLIB_INSTALL_NAME')
self._WarnUnimplemented('STRIPFLAGS') self._WarnUnimplemented('STRIPFLAGS')
self._WarnUnimplemented('STRIP_INSTALLED_PRODUCT') self._WarnUnimplemented('STRIP_INSTALLED_PRODUCT')
@ -931,6 +853,39 @@ class XcodeSettings(object):
ldflags.append('-L' + generator_default_variables['LIB_DIR']) ldflags.append('-L' + generator_default_variables['LIB_DIR'])
ldflags.append('-L' + generator_default_variables['PRODUCT_DIR']) ldflags.append('-L' + generator_default_variables['PRODUCT_DIR'])
install_name = self.GetPerTargetSetting('LD_DYLIB_INSTALL_NAME')
if install_name:
# Hardcode support for the variables used in chromium for now, to unblock
# people using the make build.
if '$' in install_name:
assert install_name == ('$(DYLIB_INSTALL_NAME_BASE:standardizepath)/'
'$(WRAPPER_NAME)/$(PRODUCT_NAME)'), (
'Variables in LD_DYLIB_INSTALL_NAME are not generally supported yet'
' in target \'%s\' (got \'%s\')' %
(self.spec['target_name'], install_name))
install_base = self.GetPerTargetSetting('DYLIB_INSTALL_NAME_BASE')
# I'm not quite sure what :standardizepath does. Just call normpath(),
# but don't let @executable_path/../foo collapse to foo
prefix, rest = '', install_base
if install_base.startswith('@'):
prefix, rest = install_base.split('/', 1)
rest = os.path.normpath(rest) # :standardizepath
install_base = os.path.join(prefix, rest)
install_name = install_name.replace(
'$(DYLIB_INSTALL_NAME_BASE:standardizepath)', install_base)
install_name = install_name.replace(
'$(WRAPPER_NAME)', self.GetWrapperName())
install_name = install_name.replace(
'$(PRODUCT_NAME)', self.GetProductName())
install_name = QuoteSpaces(install_name)
ldflags.append('-install_name ' + install_name)
elif self.GetPerTargetSetting('DYLIB_INSTALL_NAME_BASE'):
# LD_DYLIB_INSTALL_NAME defaults to
# $(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH).
print 'Warning: DYLIB_INSTALL_NAME_BASE is not fully implemented.'
self.configname = None self.configname = None
return ldflags return ldflags
@ -946,7 +901,7 @@ class XcodeSettings(object):
else: else:
assert result == self.xcode_settings[configname].get(setting, None), ( assert result == self.xcode_settings[configname].get(setting, None), (
"Expected per-target setting for '%s', got per-config setting " "Expected per-target setting for '%s', got per-config setting "
"(target %s" % (setting, spec['target_name'])) "(target %s)" % (setting, spec['target_name']))
if result is None: if result is None:
return default return default
return result return result
@ -1057,6 +1012,28 @@ class MakefileWriter:
# Keep track of the total number of outputs for this makefile. # Keep track of the total number of outputs for this makefile.
self._num_outputs = 0 self._num_outputs = 0
self.suffix_rules_srcdir = {}
self.suffix_rules_objdir1 = {}
self.suffix_rules_objdir2 = {}
# Generate suffix rules for all compilable extensions.
for ext in COMPILABLE_EXTENSIONS.keys():
# Suffix rules for source folder.
self.suffix_rules_srcdir.update({ext: ("""\
$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD
@$(call do_cmd,%s,1)
""" % (ext, COMPILABLE_EXTENSIONS[ext]))})
# Suffix rules for generated source files.
self.suffix_rules_objdir1.update({ext: ("""\
$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD
@$(call do_cmd,%s,1)
""" % (ext, COMPILABLE_EXTENSIONS[ext]))})
self.suffix_rules_objdir2.update({ext: ("""\
$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
@$(call do_cmd,%s,1)
""" % (ext, COMPILABLE_EXTENSIONS[ext]))})
def NumOutputs(self): def NumOutputs(self):
return self._num_outputs return self._num_outputs
@ -1165,15 +1142,15 @@ class MakefileWriter:
self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1) self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1)
extensions = set([os.path.splitext(s)[1] for s in sources]) extensions = set([os.path.splitext(s)[1] for s in sources])
for ext in extensions: for ext in extensions:
if ext in SHARED_HEADER_SUFFIX_RULES_SRCDIR: if ext in self.suffix_rules_srcdir:
self.WriteLn(SHARED_HEADER_SUFFIX_RULES_SRCDIR[ext]) self.WriteLn(self.suffix_rules_srcdir[ext])
self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT2) self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT2)
for ext in extensions: for ext in extensions:
if ext in SHARED_HEADER_SUFFIX_RULES_OBJDIR1: if ext in self.suffix_rules_objdir1:
self.WriteLn(SHARED_HEADER_SUFFIX_RULES_OBJDIR1[ext]) self.WriteLn(self.suffix_rules_objdir1[ext])
for ext in extensions: for ext in extensions:
if ext in SHARED_HEADER_SUFFIX_RULES_OBJDIR2: if ext in self.suffix_rules_objdir2:
self.WriteLn(SHARED_HEADER_SUFFIX_RULES_OBJDIR2[ext]) self.WriteLn(self.suffix_rules_objdir2[ext])
self.WriteLn('# End of this set of suffix rules') self.WriteLn('# End of this set of suffix rules')
# Add dependency from bundle to bundle binary. # Add dependency from bundle to bundle binary.
@ -1672,7 +1649,7 @@ class MakefileWriter:
"""Return the 'output' (full output path) to a bundle output directory.""" """Return the 'output' (full output path) to a bundle output directory."""
assert self.is_mac_bundle assert self.is_mac_bundle
path = generator_default_variables['PRODUCT_DIR'] path = generator_default_variables['PRODUCT_DIR']
return os.path.join(path, self.xcode_settings.GetBundleName()) return os.path.join(path, self.xcode_settings.GetWrapperName())
def ComputeMacBundleBinaryOutput(self, spec): def ComputeMacBundleBinaryOutput(self, spec):
@ -1735,7 +1712,12 @@ class MakefileWriter:
if self.flavor == 'mac': if self.flavor == 'mac':
ldflags = self.xcode_settings.GetLdflags(self, configname) ldflags = self.xcode_settings.GetLdflags(self, configname)
else: else:
ldflags = config.get('ldflags') ldflags = config.get('ldflags', [])
# Compute an rpath for this output if needed.
if any(dep.endswith('.so') for dep in deps):
# We want to get the literal string "$ORIGIN" into the link command,
# so we need lots of escaping.
ldflags.append(r'-Wl,-rpath=\$$ORIGIN/lib.%s/' % self.toolset)
self.WriteList(ldflags, 'LDFLAGS_%s' % configname) self.WriteList(ldflags, 'LDFLAGS_%s' % configname)
libraries = spec.get('libraries') libraries = spec.get('libraries')
if libraries: if libraries:

187
tools/gyp/pylib/gyp/generator/msvs.py

@ -56,7 +56,7 @@ generator_default_variables = {
# of the warnings. # of the warnings.
# TODO(jeanluc) I had: 'LIB_DIR': '$(OutDir)lib', # TODO(jeanluc) I had: 'LIB_DIR': '$(OutDir)lib',
#'LIB_DIR': '$(OutDir)/lib', 'LIB_DIR': '$(OutDir)/lib',
'RULE_INPUT_ROOT': '$(InputName)', 'RULE_INPUT_ROOT': '$(InputName)',
'RULE_INPUT_EXT': '$(InputExt)', 'RULE_INPUT_EXT': '$(InputExt)',
'RULE_INPUT_NAME': '$(InputFileName)', 'RULE_INPUT_NAME': '$(InputFileName)',
@ -480,8 +480,8 @@ def _GenerateNativeRulesForMSVS(p, rules, output_dir, spec, options):
""" """
rules_filename = '%s%s.rules' % (spec['target_name'], rules_filename = '%s%s.rules' % (spec['target_name'],
options.suffix) options.suffix)
rules_file = MSVSToolFile.Writer(os.path.join(output_dir, rules_filename)) rules_file = MSVSToolFile.Writer(os.path.join(output_dir, rules_filename),
rules_file.Create(spec['target_name']) spec['target_name'])
# Add each rule. # Add each rule.
for r in rules: for r in rules:
rule_name = r['rule_name'] rule_name = r['rule_name']
@ -496,7 +496,7 @@ def _GenerateNativeRulesForMSVS(p, rules, output_dir, spec, options):
outputs=outputs, outputs=outputs,
cmd=cmd) cmd=cmd)
# Write out rules file. # Write out rules file.
rules_file.Write() rules_file.WriteIfChanged()
# Add rules file to project. # Add rules file to project.
p.AddToolFile(rules_filename) p.AddToolFile(rules_filename)
@ -575,18 +575,7 @@ def _GenerateExternalRules(rules, output_dir, spec,
'IntDir=$(IntDir)', 'IntDir=$(IntDir)',
'-j', '${NUMBER_OF_PROCESSORS_PLUS_1}', '-j', '${NUMBER_OF_PROCESSORS_PLUS_1}',
'-f', filename] '-f', filename]
cmd = _BuildCommandLineForRuleRaw(spec, cmd, True, False, True)
# Currently this weird argument munging is used to duplicate the way a
# python script would need to be run as part of the chrome tree.
# Eventually we should add some sort of rule_default option to set this
# per project. For now the behavior chrome needs is the default.
mcs = rule.get('msvs_cygwin_shell')
if mcs is None:
mcs = int(spec.get('msvs_cygwin_shell', 1))
elif isinstance(mcs, str):
mcs = int(mcs)
quote_cmd = int(rule.get('msvs_quote_cmd', 1))
cmd = _BuildCommandLineForRuleRaw(spec, cmd, mcs, False, quote_cmd)
# Insert makefile as 0'th input, so it gets the action attached there, # Insert makefile as 0'th input, so it gets the action attached there,
# as this is easier to understand from in the IDE. # as this is easier to understand from in the IDE.
all_inputs = list(all_inputs) all_inputs = list(all_inputs)
@ -862,8 +851,8 @@ def _GenerateMSVSProject(project, options, version):
os.makedirs(vcproj_dir) os.makedirs(vcproj_dir)
platforms = _GetUniquePlatforms(spec) platforms = _GetUniquePlatforms(spec)
p = MSVSProject.Writer(project.path, version=version) p = MSVSProject.Writer(project.path, version, spec['target_name'],
p.Create(spec['target_name'], guid=project.guid, platforms=platforms) project.guid, platforms)
# Get directory project file is in. # Get directory project file is in.
gyp_dir = os.path.split(project.path)[0] gyp_dir = os.path.split(project.path)[0]
@ -889,6 +878,7 @@ def _GenerateMSVSProject(project, options, version):
spec, options, gyp_dir, sources, excluded_sources)) spec, options, gyp_dir, sources, excluded_sources))
# Add in files. # Add in files.
# _VerifySourcesExist(sources, gyp_dir)
p.AddFiles(sources) p.AddFiles(sources)
_AddToolFilesToMSVS(p, spec) _AddToolFilesToMSVS(p, spec)
@ -905,7 +895,7 @@ def _GenerateMSVSProject(project, options, version):
_AddAccumulatedActionsToMSVS(p, spec, actions_to_add) _AddAccumulatedActionsToMSVS(p, spec, actions_to_add)
# Write it out. # Write it out.
p.Write() p.WriteIfChanged()
def _GetUniquePlatforms(spec): def _GetUniquePlatforms(spec):
@ -937,8 +927,8 @@ def _CreateMSVSUserFile(proj_path, version, spec):
""" """
(domain, username) = _GetDomainAndUserName() (domain, username) = _GetDomainAndUserName()
vcuser_filename = '.'.join([proj_path, domain, username, 'user']) vcuser_filename = '.'.join([proj_path, domain, username, 'user'])
user_file = MSVSUserFile.Writer(vcuser_filename, version=version) user_file = MSVSUserFile.Writer(vcuser_filename, version,
user_file.Create(spec['target_name']) spec['target_name'])
return user_file return user_file
@ -1106,7 +1096,7 @@ def _GetOutputFilePathAndTool(spec):
# TODO(jeanluc) If we want to avoid the MSB8012 warnings in # TODO(jeanluc) If we want to avoid the MSB8012 warnings in
# VisualStudio 2010, we will have to change the value of $(OutDir) # VisualStudio 2010, we will have to change the value of $(OutDir)
# to contain the \lib suffix, rather than doing it as below. # to contain the \lib suffix, rather than doing it as below.
'static_library': ('VCLibrarianTool', 'Lib', '$(OutDir)\\', '.lib'), 'static_library': ('VCLibrarianTool', 'Lib', '$(OutDir)\\lib\\', '.lib'),
'dummy_executable': ('VCLinkerTool', 'Link', '$(IntDir)\\', '.junk'), 'dummy_executable': ('VCLinkerTool', 'Link', '$(IntDir)\\', '.junk'),
} }
output_file_props = output_file_map.get(spec['type']) output_file_props = output_file_map.get(spec['type'])
@ -1429,7 +1419,7 @@ def _WriteMSVSUserFile(project_path, version, spec):
for config_name, c_data in spec['configurations'].iteritems(): for config_name, c_data in spec['configurations'].iteritems():
user_file.AddDebugSettings(_ConfigFullName(config_name, c_data), user_file.AddDebugSettings(_ConfigFullName(config_name, c_data),
action, environment, working_directory) action, environment, working_directory)
user_file.Write() user_file.WriteIfChanged()
def _AddCopies(actions_to_add, spec): def _AddCopies(actions_to_add, spec):
@ -1538,11 +1528,8 @@ def _GetPathOfProject(qualified_target, spec, options, msvs_version):
if options.generator_output: if options.generator_output:
project_dir_path = os.path.dirname(os.path.abspath(proj_path)) project_dir_path = os.path.dirname(os.path.abspath(proj_path))
proj_path = os.path.join(options.generator_output, proj_path) proj_path = os.path.join(options.generator_output, proj_path)
if options.msvs_abspath_output: fix_prefix = gyp.common.RelativePath(project_dir_path,
fix_prefix = project_dir_path os.path.dirname(proj_path))
else:
fix_prefix = gyp.common.RelativePath(project_dir_path,
os.path.dirname(proj_path))
return proj_path, fix_prefix return proj_path, fix_prefix
@ -1613,14 +1600,6 @@ def CalculateVariables(default_variables, params):
# Stash msvs_version for later (so we don't have to probe the system twice). # Stash msvs_version for later (so we don't have to probe the system twice).
params['msvs_version'] = msvs_version params['msvs_version'] = msvs_version
# The generation of Visual Studio vcproj files currently calculates the
# relative path of some files more than once, which can cause errors depending
# on the directory within which gyp is run. With this option, we output
# these as absolute paths instead and are thus immune from that problem.
# See http://code.google.com/p/gyp/issues/detail?id=201
params['msvs_abspath_output'] = generator_flags.get(
'msvs_abspath_output', False)
# Set a variable so conditions can be based on msvs_version. # Set a variable so conditions can be based on msvs_version.
default_variables['MSVS_VERSION'] = msvs_version.ShortName() default_variables['MSVS_VERSION'] = msvs_version.ShortName()
@ -1720,7 +1699,6 @@ def GenerateOutput(target_list, target_dicts, data, params):
# Get the project file format version back out of where we stashed it in # Get the project file format version back out of where we stashed it in
# GeneratorCalculatedVariables. # GeneratorCalculatedVariables.
msvs_version = params['msvs_version'] msvs_version = params['msvs_version']
options.msvs_abspath_output = params['msvs_abspath_output']
# Optionally shard targets marked with 'msvs_shard': SHARD_COUNT. # Optionally shard targets marked with 'msvs_shard': SHARD_COUNT.
(target_list, target_dicts) = _ShardTargets(target_list, target_dicts) (target_list, target_dicts) = _ShardTargets(target_list, target_dicts)
@ -1782,15 +1760,14 @@ def _GenerateMSBuildFiltersFile(filters_path, source_files,
_AppendFiltersForMSBuild('', source_files, extension_to_rule_name, _AppendFiltersForMSBuild('', source_files, extension_to_rule_name,
filter_group, source_group) filter_group, source_group)
if filter_group: if filter_group:
doc = easy_xml.EasyXml( content = ['Project',
'Project', {'ToolsVersion': '4.0',
{'ToolsVersion': '4.0', 'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'
'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) },
root = doc.Root() ['ItemGroup'] + filter_group,
doc.AppendChildren(root, [ ['ItemGroup'] + source_group
['ItemGroup'] + filter_group, ]
['ItemGroup'] + source_group]) easy_xml.WriteXmlIfChanged(content, filters_path)
doc.WriteIfChanged(filters_path)
elif os.path.exists(filters_path): elif os.path.exists(filters_path):
# We don't need this filter anymore. Delete the old filter file. # We don't need this filter anymore. Delete the old filter file.
os.unlink(filters_path) os.unlink(filters_path)
@ -1958,12 +1935,10 @@ class MSBuildRule(object):
def _GenerateMSBuildRulePropsFile(props_path, msbuild_rules): def _GenerateMSBuildRulePropsFile(props_path, msbuild_rules):
"""Generate the .props file.""" """Generate the .props file."""
doc = easy_xml.EasyXml( content = ['Project',
'Project', {'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}]
{'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'})
root = doc.Root()
for rule in msbuild_rules: for rule in msbuild_rules:
doc.AppendChildren(root, [ content.extend([
['PropertyGroup', ['PropertyGroup',
{'Condition': "'$(%s)' == '' and '$(%s)' == '' and " {'Condition': "'$(%s)' == '' and '$(%s)' == '' and "
"'$(ConfigurationType)' != 'Makefile'" % (rule.before_targets, "'$(ConfigurationType)' != 'Makefile'" % (rule.before_targets,
@ -1987,32 +1962,31 @@ def _GenerateMSBuildRulePropsFile(props_path, msbuild_rules):
], ],
] ]
]) ])
doc.WriteIfChanged(props_path) easy_xml.WriteXmlIfChanged(content, props_path)
def _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules): def _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules):
"""Generate the .targets file.""" """Generate the .targets file."""
doc = easy_xml.EasyXml( content = ['Project',
'Project', {'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'
{'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003'}) }
root = doc.Root() ]
item_group = doc.AppendNode( item_group = [
root, 'ItemGroup',
['ItemGroup', ['PropertyPageSchema',
['PropertyPageSchema', {'Include': '$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml'}
{'Include': '$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml'} ]
], ]
])
for rule in msbuild_rules: for rule in msbuild_rules:
doc.AppendNode( item_group.append(
item_group,
['AvailableItemName', ['AvailableItemName',
{'Include': rule.rule_name}, {'Include': rule.rule_name},
['Targets', rule.target_name], ['Targets', rule.target_name],
]) ])
content.append(item_group)
for rule in msbuild_rules: for rule in msbuild_rules:
doc.AppendNode( content.append(
root,
['UsingTask', ['UsingTask',
{'TaskName': rule.rule_name, {'TaskName': rule.rule_name,
'TaskFactory': 'XamlTaskFactory', 'TaskFactory': 'XamlTaskFactory',
@ -2074,7 +2048,7 @@ def _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules):
'Inputs': rule_inputs 'Inputs': rule_inputs
} }
] ]
doc.AppendChildren(root, [ content.extend([
['Target', ['Target',
{'Name': rule.target_name, {'Name': rule.target_name,
'BeforeTargets': '$(%s)' % rule.before_targets, 'BeforeTargets': '$(%s)' % rule.before_targets,
@ -2135,22 +2109,23 @@ def _GenerateMSBuildRuleTargetsFile(targets_path, msbuild_rules):
] ]
], ],
]) ])
doc.WriteIfChanged(targets_path) easy_xml.WriteXmlIfChanged(content, targets_path)
def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules): def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules):
# Generate the .xml file # Generate the .xml file
doc = easy_xml.EasyXml( content = [
'ProjectSchemaDefinitions', 'ProjectSchemaDefinitions',
{'xmlns': ('clr-namespace:Microsoft.Build.Framework.XamlTypes;' {'xmlns': ('clr-namespace:Microsoft.Build.Framework.XamlTypes;'
'assembly=Microsoft.Build.Framework'), 'assembly=Microsoft.Build.Framework'),
'xmlns:x': 'http://schemas.microsoft.com/winfx/2006/xaml', 'xmlns:x': 'http://schemas.microsoft.com/winfx/2006/xaml',
'xmlns:sys': 'clr-namespace:System;assembly=mscorlib', 'xmlns:sys': 'clr-namespace:System;assembly=mscorlib',
'xmlns:transformCallback': 'xmlns:transformCallback':
'Microsoft.Cpp.Dev10.ConvertPropertyCallback'}) 'Microsoft.Cpp.Dev10.ConvertPropertyCallback'
root = doc.Root() }
]
for rule in msbuild_rules: for rule in msbuild_rules:
doc.AppendChildren(root, [ content.extend([
['Rule', ['Rule',
{'Name': rule.rule_name, {'Name': rule.rule_name,
'PageTemplate': 'tool', 'PageTemplate': 'tool',
@ -2312,7 +2287,7 @@ def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules):
} }
] ]
]) ])
doc.WriteIfChanged(xml_path) easy_xml.WriteXmlIfChanged(content, xml_path)
def _GetConfigurationAndPlatform(name, settings): def _GetConfigurationAndPlatform(name, settings):
@ -2597,9 +2572,6 @@ def _GetValueFormattedForMSBuild(tool_name, name, value):
'DisableSpecificWarnings', 'DisableSpecificWarnings',
'PreprocessorDefinitions']: 'PreprocessorDefinitions']:
value.append('%%(%s)' % name) value.append('%%(%s)' % name)
# TODO(jeanluc) Not all of them need to be fixed, why?
if name in ['AdditionalIncludeDirectories', 'AdditionalLibraryDirectories']:
value = _FixPaths(value)
# For most tools, entries in a list should be separated with ';' but some # For most tools, entries in a list should be separated with ';' but some
# settings use a space. Check for those first. # settings use a space. Check for those first.
exceptions = { exceptions = {
@ -2617,15 +2589,35 @@ def _GetValueFormattedForMSBuild(tool_name, name, value):
return formatted_value return formatted_value
def _GetMSBuildSources(spec, root_dir, sources, exclusions, def _VerifySourcesExist(sources, root_dir):
extension_to_rule_name, actions_spec, """Verifies that all source files exist on disk.
sources_handled_by_action):
Checks that all regular source files, i.e. not created at run time,
exist on disk. Missing files cause needless recompilation but no otherwise
visible errors.
Arguments:
sources: A recursive list of Filter/file names.
root_dir: The root directory for the relative path names.
"""
for source in sources:
if isinstance(source, MSVSProject.Filter):
_VerifySourcesExist(source.contents, root_dir)
else:
if '$' not in source:
full_path = os.path.join(root_dir, source)
if not os.path.exists(full_path):
print 'Error: Missing input file ' + full_path
def _GetMSBuildSources(spec, sources, exclusions, extension_to_rule_name,
actions_spec, sources_handled_by_action):
groups = ['none', 'midl', 'include', 'compile', 'resource', 'rule'] groups = ['none', 'midl', 'include', 'compile', 'resource', 'rule']
grouped_sources = {} grouped_sources = {}
for g in groups: for g in groups:
grouped_sources[g] = [] grouped_sources[g] = []
_AddSources2(spec, root_dir, sources, exclusions, grouped_sources, _AddSources2(spec, sources, exclusions, grouped_sources,
extension_to_rule_name, sources_handled_by_action) extension_to_rule_name, sources_handled_by_action)
sources = [] sources = []
for g in groups: for g in groups:
@ -2636,20 +2628,13 @@ def _GetMSBuildSources(spec, root_dir, sources, exclusions,
return sources return sources
def _AddSources2(spec, root_dir, sources, exclusions, grouped_sources, def _AddSources2(spec, sources, exclusions, grouped_sources,
extension_to_rule_name, sources_handled_by_action): extension_to_rule_name, sources_handled_by_action):
for source in sources: for source in sources:
if isinstance(source, MSVSProject.Filter): if isinstance(source, MSVSProject.Filter):
_AddSources2(spec, root_dir, source.contents, exclusions, grouped_sources, _AddSources2(spec, source.contents, exclusions, grouped_sources,
extension_to_rule_name, sources_handled_by_action) extension_to_rule_name, sources_handled_by_action)
else: else:
# If it is a regular source file, i.e. not created at run time,
# warn if it does not exists. Missing header files will cause needless
# recompilation but no otherwise visible errors.
if '$' not in source:
full_path = os.path.join(root_dir, source)
if not os.path.exists(full_path):
print 'Warning: Missing input file ' + full_path
if not source in sources_handled_by_action: if not source in sources_handled_by_action:
detail = [] detail = []
excluded_configurations = exclusions.get(source, []) excluded_configurations = exclusions.get(source, [])
@ -2736,19 +2721,13 @@ def _GenerateMSBuildProject(project, options, version):
_GenerateMSBuildFiltersFile(project.path + '.filters', sources, _GenerateMSBuildFiltersFile(project.path + '.filters', sources,
extension_to_rule_name) extension_to_rule_name)
# _VerifySourcesExist(sources, gyp_dir)
for (_, configuration) in configurations.iteritems(): for (_, configuration) in configurations.iteritems():
_FinalizeMSBuildSettings(spec, configuration) _FinalizeMSBuildSettings(spec, configuration)
# Add attributes to root element # Add attributes to root element
doc = easy_xml.EasyXml(
'Project',
{'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003',
'ToolsVersion': version.ProjectVersion(),
'DefaultTargets': 'Build'
})
import_default_section = [ import_default_section = [
['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.Default.props'}]] ['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.Default.props'}]]
import_cpp_props_section = [ import_cpp_props_section = [
@ -2757,7 +2736,14 @@ def _GenerateMSBuildProject(project, options, version):
['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.targets'}]] ['Import', {'Project': r'$(VCTargetsPath)\Microsoft.Cpp.targets'}]]
macro_section = [['PropertyGroup', {'Label': 'UserMacros'}]] macro_section = [['PropertyGroup', {'Label': 'UserMacros'}]]
content = _GetMSBuildProjectConfigurations(configurations) content = [
'Project',
{'xmlns': 'http://schemas.microsoft.com/developer/msbuild/2003',
'ToolsVersion': version.ProjectVersion(),
'DefaultTargets': 'Build'
}]
content += _GetMSBuildProjectConfigurations(configurations)
content += _GetMSBuildGlobalProperties(spec, project.guid, gyp_file_name) content += _GetMSBuildGlobalProperties(spec, project.guid, gyp_file_name)
content += import_default_section content += import_default_section
content += _GetMSBuildConfigurationDetails(spec, project.build_file) content += _GetMSBuildConfigurationDetails(spec, project.build_file)
@ -2769,7 +2755,7 @@ def _GenerateMSBuildProject(project, options, version):
project.build_file) project.build_file)
content += _GetMSBuildToolSettingsSections(spec, configurations) content += _GetMSBuildToolSettingsSections(spec, configurations)
content += _GetMSBuildSources( content += _GetMSBuildSources(
spec, gyp_dir, sources, exclusions, extension_to_rule_name, actions_spec, spec, sources, exclusions, extension_to_rule_name, actions_spec,
sources_handled_by_action) sources_handled_by_action)
content += _GetMSBuildProjectReferences(project) content += _GetMSBuildProjectReferences(project)
content += import_cpp_targets_section content += import_cpp_targets_section
@ -2778,8 +2764,7 @@ def _GenerateMSBuildProject(project, options, version):
# TODO(jeanluc) File a bug to get rid of runas. We had in MSVS: # TODO(jeanluc) File a bug to get rid of runas. We had in MSVS:
# has_run_as = _WriteMSVSUserFile(project.path, version, spec) # has_run_as = _WriteMSVSUserFile(project.path, version, spec)
doc.AppendChildren(doc.Root(), content) easy_xml.WriteXmlIfChanged(content, project.path)
doc.WriteIfChanged(project.path)
def _GetMSBuildExtensions(props_files_of_rules): def _GetMSBuildExtensions(props_files_of_rules):

2
tools/gyp/pylib/gyp/input.py

@ -413,7 +413,7 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes,
index = index + 1 index = index + 1
else: else:
raise Exception, \ raise Exception, \
"Unable to find targets in build file %s" % os.path.abspath(build_file_path) "Unable to find targets in build file %s" % build_file_path
# No longer needed. # No longer needed.
del build_file_data['target_defaults'] del build_file_data['target_defaults']

81
tools/gyp/samples/samples

@ -0,0 +1,81 @@
#!/usr/bin/python
# Copyright (c) 2009 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os.path
import shutil
import sys
gyps = [
'app/app.gyp',
'base/base.gyp',
'build/temp_gyp/googleurl.gyp',
'build/all.gyp',
'build/common.gypi',
'build/external_code.gypi',
'chrome/test/security_tests/security_tests.gyp',
'chrome/third_party/hunspell/hunspell.gyp',
'chrome/chrome.gyp',
'media/media.gyp',
'net/net.gyp',
'printing/printing.gyp',
'sdch/sdch.gyp',
'skia/skia.gyp',
'testing/gmock.gyp',
'testing/gtest.gyp',
'third_party/bzip2/bzip2.gyp',
'third_party/icu38/icu38.gyp',
'third_party/libevent/libevent.gyp',
'third_party/libjpeg/libjpeg.gyp',
'third_party/libpng/libpng.gyp',
'third_party/libxml/libxml.gyp',
'third_party/libxslt/libxslt.gyp',
'third_party/lzma_sdk/lzma_sdk.gyp',
'third_party/modp_b64/modp_b64.gyp',
'third_party/npapi/npapi.gyp',
'third_party/sqlite/sqlite.gyp',
'third_party/zlib/zlib.gyp',
'v8/tools/gyp/v8.gyp',
'webkit/activex_shim/activex_shim.gyp',
'webkit/activex_shim_dll/activex_shim_dll.gyp',
'webkit/build/action_csspropertynames.py',
'webkit/build/action_cssvaluekeywords.py',
'webkit/build/action_jsconfig.py',
'webkit/build/action_makenames.py',
'webkit/build/action_maketokenizer.py',
'webkit/build/action_useragentstylesheets.py',
'webkit/build/rule_binding.py',
'webkit/build/rule_bison.py',
'webkit/build/rule_gperf.py',
'webkit/tools/test_shell/test_shell.gyp',
'webkit/webkit.gyp',
]
def Main(argv):
if len(argv) != 3 or argv[1] not in ['push', 'pull']:
print 'Usage: %s push/pull PATH_TO_CHROME' % argv[0]
return 1
path_to_chrome = argv[2]
for g in gyps:
chrome_file = os.path.join(path_to_chrome, g)
local_file = os.path.join(os.path.dirname(argv[0]), os.path.split(g)[1])
if argv[1] == 'push':
print 'Copying %s to %s' % (local_file, chrome_file)
shutil.copyfile(local_file, chrome_file)
elif argv[1] == 'pull':
print 'Copying %s to %s' % (chrome_file, local_file)
shutil.copyfile(chrome_file, local_file)
else:
assert False
return 0
if __name__ == '__main__':
sys.exit(Main(sys.argv))

5
tools/gyp/samples/samples.bat

@ -0,0 +1,5 @@
@rem Copyright (c) 2009 Google Inc. All rights reserved.
@rem Use of this source code is governed by a BSD-style license that can be
@rem found in the LICENSE file.
@python %~dp0/samples %*

15
tools/gyp/tools/README

@ -0,0 +1,15 @@
pretty_vcproj:
Usage: pretty_vcproj.py "c:\path\to\vcproj.vcproj" [key1=value1] [key2=value2]
They key/value pair are used to resolve vsprops name.
For example, if I want to diff the base.vcproj project:
pretty_vcproj.py z:\dev\src-chrome\src\base\build\base.vcproj "$(SolutionDir)=z:\dev\src-chrome\src\chrome\\" "$(CHROMIUM_BUILD)=" "$(CHROME_BUILD_TYPE)=" > orignal.txt
pretty_vcproj.py z:\dev\src-chrome\src\base\base_gyp.vcproj "$(SolutionDir)=z:\dev\src-chrome\src\chrome\\" "$(CHROMIUM_BUILD)=" "$(CHROME_BUILD_TYPE)=" > gyp.txt
And you can use your favorite diff tool to see the changes.
Note: In the case of base.vcproj, the original vcproj is one level up the generated one.
I suggest you do a search and replace for '"..\' and replace it with '"' in original.txt
before you perform the diff.

95
tools/gyp/tools/graphviz.py

@ -0,0 +1,95 @@
#!/usr/bin/python
# Copyright (c) 2011 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Using the JSON dumped by the dump-dependency-json generator,
generate input suitable for graphviz to render a dependency graph of
targets."""
import collections
import json
import sys
def ParseTarget(target):
target, _, suffix = target.partition('#')
filename, _, target = target.partition(':')
return filename, target, suffix
def LoadEdges(filename, targets):
"""Load the edges map from the dump file, and filter it to only
show targets in |targets| and their depedendents."""
file = open('dump.json')
edges = json.load(file)
file.close()
# Copy out only the edges we're interested in from the full edge list.
target_edges = {}
to_visit = targets[:]
while to_visit:
src = to_visit.pop()
if src in target_edges:
continue
target_edges[src] = edges[src]
to_visit.extend(edges[src])
return target_edges
def WriteGraph(edges):
"""Print a graphviz graph to stdout.
|edges| is a map of target to a list of other targets it depends on."""
# Bucket targets by file.
files = collections.defaultdict(list)
for src, dst in edges.items():
build_file, target_name, toolset = ParseTarget(src)
files[build_file].append(src)
print 'digraph D {'
print ' fontsize=8' # Used by subgraphs.
print ' node [fontsize=8]'
# Output nodes by file. We must first write out each node within
# its file grouping before writing out any edges that may refer
# to those nodes.
for filename, targets in files.items():
if len(targets) == 1:
# If there's only one node for this file, simplify
# the display by making it a box without an internal node.
target = targets[0]
build_file, target_name, toolset = ParseTarget(target)
print ' "%s" [shape=box, label="%s\\n%s"]' % (target, filename,
target_name)
else:
# Group multiple nodes together in a subgraph.
print ' subgraph "cluster_%s" {' % filename
print ' label = "%s"' % filename
for target in targets:
build_file, target_name, toolset = ParseTarget(target)
print ' "%s" [label="%s"]' % (target, target_name)
print ' }'
# Now that we've placed all the nodes within subgraphs, output all
# the edges between nodes.
for src, dsts in edges.items():
for dst in dsts:
print ' "%s" -> "%s"' % (src, dst)
print '}'
if __name__ == '__main__':
if len(sys.argv) < 2:
print >>sys.stderr, __doc__
print >>sys.stderr
print >>sys.stderr, 'usage: %s target1 target2...' % (sys.argv[0])
sys.exit(1)
edges = LoadEdges('dump.json', sys.argv[1:])
WriteGraph(edges)

142
tools/gyp/tools/pretty_gyp.py

@ -0,0 +1,142 @@
#!/usr/bin/env python
# Copyright (c) 2009 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This file pretty-prints the contents of a GYP file.
import sys
import re
input = []
if len(sys.argv) > 1:
input_file = open(sys.argv[1])
input = input_file.read().splitlines()
input_file.close()
else:
input = sys.stdin.read().splitlines()
# This is used to remove comments when we're counting braces.
comment_re = re.compile(r'\s*#.*')
# This is used to remove quoted strings when we're counting braces.
# It takes into account quoted quotes, and makes sure that the quotes
# match.
# NOTE: It does not handle quotes that span more than one line, or
# cases where an escaped quote is preceeded by an escaped backslash.
quote_re_str = r'(?P<q>[\'"])(.*?)(?<![^\\][\\])(?P=q)'
quote_re = re.compile(quote_re_str)
def comment_replace(matchobj):
return matchobj.group(1) + matchobj.group(2) + '#' * len(matchobj.group(3))
def mask_comments(input):
# This is used to mask the quoted strings so we skip braces inside
# quoted strings.
search_re = re.compile(r'(.*?)(#)(.*)')
return [search_re.sub(comment_replace, line) for line in input]
def quote_replace(matchobj):
return "%s%s%s%s" % (matchobj.group(1),
matchobj.group(2),
'x'*len(matchobj.group(3)),
matchobj.group(2))
def mask_quotes(input):
# This is used to mask the quoted strings so we skip braces inside
# quoted strings.
search_re = re.compile(r'(.*?)' + quote_re_str)
return [search_re.sub(quote_replace, line) for line in input]
def do_split(input, masked_input, search_re):
output = []
mask_output = []
for (line, masked_line) in zip(input, masked_input):
m = search_re.match(masked_line)
while m:
split = len(m.group(1))
line = line[:split] + r'\n' + line[split:]
masked_line = masked_line[:split] + r'\n' + masked_line[split:]
m = search_re.match(masked_line)
output.extend(line.split(r'\n'))
mask_output.extend(masked_line.split(r'\n'))
return (output, mask_output)
# This masks out the quotes and comments, and then splits appropriate
# lines (lines that matche the double_*_brace re's above) before
# indenting them below.
def split_double_braces(input):
# These are used to split lines which have multiple braces on them, so
# that the indentation looks prettier when all laid out (e.g. closing
# braces make a nice diagonal line).
double_open_brace_re = re.compile(r'(.*?[\[\{\(,])(\s*)([\[\{\(])')
double_close_brace_re = re.compile(r'(.*?[\]\}\)],?)(\s*)([\]\}\)])')
masked_input = mask_quotes(input)
masked_input = mask_comments(masked_input)
(output, mask_output) = do_split(input, masked_input, double_open_brace_re)
(output, mask_output) = do_split(output, mask_output, double_close_brace_re)
return output
# This keeps track of the number of braces on a given line and returns
# the result. It starts at zero and subtracts for closed braces, and
# adds for open braces.
def count_braces(line):
open_braces = ['[', '(', '{']
close_braces = [']', ')', '}']
closing_prefix_re = re.compile(r'(.*?[^\s\]\}\)]+.*?)([\]\}\)],?)\s*$')
cnt = 0
stripline = comment_re.sub(r'', line)
stripline = quote_re.sub(r"''", stripline)
for char in stripline:
for brace in open_braces:
if char == brace:
cnt += 1
for brace in close_braces:
if char == brace:
cnt -= 1
after = False
if cnt > 0:
after = True
# This catches the special case of a closing brace having something
# other than just whitespace ahead of it -- we don't want to
# unindent that until after this line is printed so it stays with
# the previous indentation level.
if cnt < 0 and closing_prefix_re.match(stripline):
after = True
return (cnt, after)
# This does the main work of indenting the input based on the brace counts.
def prettyprint_input(lines):
indent = 0
basic_offset = 2
last_line = ""
for line in lines:
if comment_re.match(line):
print line
else:
line = line.strip('\r\n\t ') # Otherwise doesn't strip \r on Unix.
if len(line) > 0:
(brace_diff, after) = count_braces(line)
if brace_diff != 0:
if after:
print " " * (basic_offset * indent) + line
indent += brace_diff
else:
indent += brace_diff
print " " * (basic_offset * indent) + line
else:
print " " * (basic_offset * indent) + line
else:
print ""
last_line = line
# Split up the double braces.
lines = split_double_braces(input)
# Indent and print the output.
prettyprint_input(lines)

167
tools/gyp/tools/pretty_sln.py

@ -0,0 +1,167 @@
#!/usr/bin/python2.5
# Copyright (c) 2009 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Prints the information in a sln file in a diffable way.
It first outputs each projects in alphabetical order with their
dependencies.
Then it outputs a possible build order.
"""
__author__ = 'nsylvain (Nicolas Sylvain)'
import os
import re
import sys
import pretty_vcproj
def BuildProject(project, built, projects, deps):
# if all dependencies are done, we can build it, otherwise we try to build the
# dependency.
# This is not infinite-recursion proof.
for dep in deps[project]:
if dep not in built:
BuildProject(dep, built, projects, deps)
print project
built.append(project)
def ParseSolution(solution_file):
# All projects, their clsid and paths.
projects = dict()
# A list of dependencies associated with a project.
dependencies = dict()
# Regular expressions that matches the SLN format.
# The first line of a project definition.
begin_project = re.compile(('^Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942'
'}"\) = "(.*)", "(.*)", "(.*)"$'))
# The last line of a project definition.
end_project = re.compile('^EndProject$')
# The first line of a dependency list.
begin_dep = re.compile('ProjectSection\(ProjectDependencies\) = postProject$')
# The last line of a dependency list.
end_dep = re.compile('EndProjectSection$')
# A line describing a dependency.
dep_line = re.compile(' *({.*}) = ({.*})$')
in_deps = False
solution = open(solution_file)
for line in solution:
results = begin_project.search(line)
if results:
# Hack to remove icu because the diff is too different.
if results.group(1).find('icu') != -1:
continue
# We remove "_gyp" from the names because it helps to diff them.
current_project = results.group(1).replace('_gyp', '')
projects[current_project] = [results.group(2).replace('_gyp', ''),
results.group(3),
results.group(2)]
dependencies[current_project] = []
continue
results = end_project.search(line)
if results:
current_project = None
continue
results = begin_dep.search(line)
if results:
in_deps = True
continue
results = end_dep.search(line)
if results:
in_deps = False
continue
results = dep_line.search(line)
if results and in_deps and current_project:
dependencies[current_project].append(results.group(1))
continue
# Change all dependencies clsid to name instead.
for project in dependencies:
# For each dependencies in this project
new_dep_array = []
for dep in dependencies[project]:
# Look for the project name matching this cldis
for project_info in projects:
if projects[project_info][1] == dep:
new_dep_array.append(project_info)
dependencies[project] = sorted(new_dep_array)
return (projects, dependencies)
def PrintDependencies(projects, deps):
print "---------------------------------------"
print "Dependencies for all projects"
print "---------------------------------------"
print "-- --"
for (project, dep_list) in sorted(deps.items()):
print "Project : %s" % project
print "Path : %s" % projects[project][0]
if dep_list:
for dep in dep_list:
print " - %s" % dep
print ""
print "-- --"
def PrintBuildOrder(projects, deps):
print "---------------------------------------"
print "Build order "
print "---------------------------------------"
print "-- --"
built = []
for (project, dep_list) in sorted(deps.items()):
if project not in built:
BuildProject(project, built, projects, deps)
print "-- --"
def PrintVCProj(projects):
for project in projects:
print "-------------------------------------"
print "-------------------------------------"
print project
print project
print project
print "-------------------------------------"
print "-------------------------------------"
project_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[1]),
projects[project][2]))
pretty = pretty_vcproj
argv = [ '',
project_path,
'$(SolutionDir)=%s\\' % os.path.dirname(sys.argv[1]),
]
argv.extend(sys.argv[3:])
pretty.main(argv)
def main():
# check if we have exactly 1 parameter.
if len(sys.argv) < 2:
print 'Usage: %s "c:\\path\\to\\project.sln"' % sys.argv[0]
return
(projects, deps) = ParseSolution(sys.argv[1])
PrintDependencies(projects, deps)
PrintBuildOrder(projects, deps)
if '--recursive' in sys.argv:
PrintVCProj(projects)
if __name__ == '__main__':
main()

316
tools/gyp/tools/pretty_vcproj.py

@ -0,0 +1,316 @@
#!/usr/bin/python2.5
# Copyright (c) 2009 Google Inc. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Make the format of a vcproj really pretty.
This script normalize and sort an xml. It also fetches all the properties
inside linked vsprops and include them explicitly in the vcproj.
It outputs the resulting xml to stdout.
"""
__author__ = 'nsylvain (Nicolas Sylvain)'
import os
import sys
from xml.dom.minidom import parse
from xml.dom.minidom import Node
REPLACEMENTS = dict()
ARGUMENTS = None
class CmpTuple:
"""Compare function between 2 tuple."""
def __call__(self, x, y):
(key1, value1) = x
(key2, value2) = y
return cmp(key1, key2)
class CmpNode:
"""Compare function between 2 xml nodes."""
def get_string(self, node):
node_string = "node"
node_string += node.nodeName
if node.nodeValue:
node_string += node.nodeValue
if node.attributes:
# We first sort by name, if present.
node_string += node.getAttribute("Name")
all_nodes = []
for (name, value) in node.attributes.items():
all_nodes.append((name, value))
all_nodes.sort(CmpTuple())
for (name, value) in all_nodes:
node_string += name
node_string += value
return node_string
def __call__(self, x, y):
return cmp(self.get_string(x), self.get_string(y))
def PrettyPrintNode(node, indent=0):
if node.nodeType == Node.TEXT_NODE:
if node.data.strip():
print '%s%s' % (' '*indent, node.data.strip())
return
if node.childNodes:
node.normalize()
# Get the number of attributes
attr_count = 0
if node.attributes:
attr_count = node.attributes.length
# Print the main tag
if attr_count == 0:
print '%s<%s>' % (' '*indent, node.nodeName)
else:
print '%s<%s' % (' '*indent, node.nodeName)
all_attributes = []
for (name, value) in node.attributes.items():
all_attributes.append((name, value))
all_attributes.sort(CmpTuple())
for (name, value) in all_attributes:
print '%s %s="%s"' % (' '*indent, name, value)
print '%s>' % (' '*indent)
if node.nodeValue:
print '%s %s' % (' '*indent, node.nodeValue)
for sub_node in node.childNodes:
PrettyPrintNode(sub_node, indent=indent+2)
print '%s</%s>' % (' '*indent, node.nodeName)
def FlattenFilter(node):
"""Returns a list of all the node and sub nodes."""
node_list = []
if (node.attributes and
node.getAttribute('Name') == '_excluded_files'):
# We don't add the "_excluded_files" filter.
return []
for current in node.childNodes:
if current.nodeName == 'Filter':
node_list.extend(FlattenFilter(current))
else:
node_list.append(current)
return node_list
def FixFilenames(filenames, current_directory):
new_list = []
for filename in filenames:
if filename:
for key in REPLACEMENTS:
filename = filename.replace(key, REPLACEMENTS[key])
os.chdir(current_directory)
filename = filename.strip('"\' ')
if filename.startswith('$'):
new_list.append(filename)
else:
new_list.append(os.path.abspath(filename))
return new_list
def AbsoluteNode(node):
# Make all the properties we know about in this node absolute.
if node.attributes:
for (name, value) in node.attributes.items():
if name in ['InheritedPropertySheets', 'RelativePath',
'AdditionalIncludeDirectories',
'IntermediateDirectory', 'OutputDirectory',
'AdditionalLibraryDirectories']:
# We want to fix up these paths
path_list = value.split(';')
new_list = FixFilenames(path_list, os.path.dirname(ARGUMENTS[1]))
node.setAttribute(name, ';'.join(new_list))
if not value:
node.removeAttribute(name)
def CleanupVcproj(node):
# For each sub node, we call recursively this function.
for sub_node in node.childNodes:
AbsoluteNode(sub_node)
CleanupVcproj(sub_node)
# Normalize the node, and remove all extranous whitespaces.
for sub_node in node.childNodes:
if sub_node.nodeType == Node.TEXT_NODE:
sub_node.data = sub_node.data.replace("\r", "")
sub_node.data = sub_node.data.replace("\n", "")
sub_node.data = sub_node.data.rstrip()
# Fix all the semicolon separated attributes to be sorted, and we also
# remove the dups.
if node.attributes:
for (name, value) in node.attributes.items():
sorted_list = sorted(value.split(';'))
unique_list = []
[unique_list.append(i) for i in sorted_list if not unique_list.count(i)]
node.setAttribute(name, ';'.join(unique_list))
if not value:
node.removeAttribute(name)
if node.childNodes:
node.normalize()
# For each node, take a copy, and remove it from the list.
node_array = []
while node.childNodes and node.childNodes[0]:
# Take a copy of the node and remove it from the list.
current = node.childNodes[0]
node.removeChild(current)
# If the child is a filter, we want to append all its children
# to this same list.
if current.nodeName == 'Filter':
node_array.extend(FlattenFilter(current))
else:
node_array.append(current)
# Sort the list.
node_array.sort(CmpNode())
# Insert the nodes in the correct order.
for new_node in node_array:
# But don't append empty tool node.
if new_node.nodeName == 'Tool':
if new_node.attributes and new_node.attributes.length == 1:
# This one was empty.
continue
if new_node.nodeName == 'UserMacro':
continue
node.appendChild(new_node)
def GetConfiguationNodes(vcproj):
#TODO(nsylvain): Find a better way to navigate the xml.
nodes = []
for node in vcproj.childNodes:
if node.nodeName == "Configurations":
for sub_node in node.childNodes:
if sub_node.nodeName == "Configuration":
nodes.append(sub_node)
return nodes
def GetChildrenVsprops(filename):
dom = parse(filename)
if dom.documentElement.attributes:
vsprops = dom.documentElement.getAttribute('InheritedPropertySheets')
return FixFilenames(vsprops.split(';'), os.path.dirname(filename))
return []
def SeekToNode(node1, child2):
# A text node does not have properties.
if child2.nodeType == Node.TEXT_NODE:
return None
# Get the name of the current node.
current_name = child2.getAttribute("Name")
if not current_name:
# There is no name. We don't know how to merge.
return None
# Look through all the nodes to find a match.
for sub_node in node1.childNodes:
if sub_node.nodeName == child2.nodeName:
name = sub_node.getAttribute("Name")
if name == current_name:
return sub_node
# No match. We give up.
return None
def MergeAttributes(node1, node2):
# No attributes to merge?
if not node2.attributes:
return
for (name, value2) in node2.attributes.items():
# Don't merge the 'Name' attribute.
if name == 'Name':
continue
value1 = node1.getAttribute(name)
if value1:
# The attribute exist in the main node. If it's equal, we leave it
# untouched, otherwise we concatenate it.
if value1 != value2:
node1.setAttribute(name, ';'.join([value1, value2]))
else:
# The attribute does nto exist in the main node. We append this one.
node1.setAttribute(name, value2)
# If the attribute was a property sheet attributes, we remove it, since
# they are useless.
if name == 'InheritedPropertySheets':
node1.removeAttribute(name)
def MergeProperties(node1, node2):
MergeAttributes(node1, node2)
for child2 in node2.childNodes:
child1 = SeekToNode(node1, child2)
if child1:
MergeProperties(child1, child2)
else:
node1.appendChild(child2.cloneNode(True))
def main(argv):
global REPLACEMENTS
global ARGUMENTS
ARGUMENTS = argv
"""Main function of this vcproj prettifier."""
# check if we have exactly 1 parameter.
if len(argv) < 2:
print ('Usage: %s "c:\\path\\to\\vcproj.vcproj" [key1=value1] '
'[key2=value2]' % argv[0])
return
# Parse the keys
for i in range(2, len(argv)):
(key, value) = argv[i].split('=')
REPLACEMENTS[key] = value
# Open the vcproj and parse the xml.
dom = parse(argv[1])
# First thing we need to do is find the Configuration Node and merge them
# with the vsprops they include.
for configuration_node in GetConfiguationNodes(dom.documentElement):
# Get the property sheets associated with this configuration.
vsprops = configuration_node.getAttribute('InheritedPropertySheets')
# Fix the filenames to be absolute.
vsprops_list = FixFilenames(vsprops.strip().split(';'),
os.path.dirname(argv[1]))
# Extend the list of vsprops with all vsprops contained in the current
# vsprops.
for current_vsprops in vsprops_list:
vsprops_list.extend(GetChildrenVsprops(current_vsprops))
# Now that we have all the vsprops, we need to merge them.
for current_vsprops in vsprops_list:
MergeProperties(configuration_node,
parse(current_vsprops).documentElement)
# Now that everything is merged, we need to cleanup the xml.
CleanupVcproj(dom.documentElement)
# Finally, we use the prett xml function to print the vcproj back to the
# user.
#print dom.toprettyxml(newl="\n")
PrettyPrintNode(dom.documentElement)
if __name__ == '__main__':
main(sys.argv)
Loading…
Cancel
Save