tools: update gyp-next to 0.20.4

PR-URL: https://github.com/nodejs/node/pull/59690
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
Node.js GitHub Bot 2025-09-02 02:08:00 +01:00 committed by GitHub
parent 255dd7b62c
commit ff533758df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 1977 additions and 1997 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## [0.20.4](https://github.com/nodejs/gyp-next/compare/v0.20.3...v0.20.4) (2025-08-25)
### Bug Fixes
* **cli:** remove duplicate usage ([#308](https://github.com/nodejs/gyp-next/issues/308)) ([0996f60](https://github.com/nodejs/gyp-next/commit/0996f60e9bc83ec9d7b31e39bebd23f8dc990130))
* **docs:** Add running gyp via uv ([#306](https://github.com/nodejs/gyp-next/issues/306)) ([0e43f61](https://github.com/nodejs/gyp-next/commit/0e43f61da8154f9b460ccba9ce4c0a25d2383ac4))
## [0.20.3](https://github.com/nodejs/gyp-next/compare/v0.20.2...v0.20.3) (2025-08-20)

View File

@ -6,8 +6,9 @@ Documents are available at [`./docs`](./docs).
__gyp-next__ is [released](https://github.com/nodejs/gyp-next/releases) to the [__Python Packaging Index__](https://pypi.org/project/gyp-next) (PyPI) and can be installed with the command:
* `python3 -m pip install gyp-next`
When used as a command line utility, __gyp-next__ can also be installed with [pipx](https://pypa.github.io/pipx):
* `pipx install gyp-next`
When used as a command line utility, __gyp-next__ can also be installed with [pipx](https://pypa.github.io/pipx) or [uv](https://docs.astral.sh/uv):
* `pipx install gyp-next ` # --or--
* `uv tool install gyp-next`
```
Installing to a new venv 'gyp-next'
installed package gyp-next 0.13.0, installed using Python 3.10.6
@ -17,10 +18,11 @@ done! ✨ 🌟 ✨
```
Or to run __gyp-next__ directly without installing it:
* `pipx run gyp-next --help`
* `pipx run gyp-next --help ` # --or--
* `uvx --from=gyp-next gyp --help`
```
NOTE: running app 'gyp' from 'gyp-next'
usage: usage: gyp [options ...] [build_file ...]
usage: gyp [options ...] [build_file ...]
options:
-h, --help show this help message and exit

View File

@ -32,18 +32,18 @@ ENTRY_TYPE_GUIDS = {
def MakeGuid(name, seed="msvs_new"):
"""Returns a GUID for the specified target name.
Args:
name: Target name.
seed: Seed for MD5 hash.
Returns:
A GUID-line string calculated from the name and seed.
Args:
name: Target name.
seed: Seed for MD5 hash.
Returns:
A GUID-line string calculated from the name and seed.
This generates something which looks like a GUID, but depends only on the
name and seed. This means the same name/seed will always generate the same
GUID, so that projects and solutions which refer to each other can explicitly
determine the GUID to refer to explicitly. It also means that the GUID will
not change when the project for a target is rebuilt.
"""
This generates something which looks like a GUID, but depends only on the
name and seed. This means the same name/seed will always generate the same
GUID, so that projects and solutions which refer to each other can explicitly
determine the GUID to refer to explicitly. It also means that the GUID will
not change when the project for a target is rebuilt.
"""
# Calculate a MD5 signature for the seed and name.
d = hashlib.md5((str(seed) + str(name)).encode("utf-8")).hexdigest().upper()
# Convert most of the signature to GUID form (discard the rest)
@ -78,15 +78,15 @@ class MSVSFolder(MSVSSolutionEntry):
def __init__(self, path, name=None, entries=None, guid=None, items=None):
"""Initializes the folder.
Args:
path: Full path to the folder.
name: Name of the folder.
entries: List of folder entries to nest inside this folder. May contain
Folder or Project objects. May be None, if the folder is empty.
guid: GUID to use for folder, if not None.
items: List of solution items to include in the folder project. May be
None, if the folder does not directly contain items.
"""
Args:
path: Full path to the folder.
name: Name of the folder.
entries: List of folder entries to nest inside this folder. May contain
Folder or Project objects. May be None, if the folder is empty.
guid: GUID to use for folder, if not None.
items: List of solution items to include in the folder project. May be
None, if the folder does not directly contain items.
"""
if name:
self.name = name
else:
@ -128,19 +128,19 @@ class MSVSProject(MSVSSolutionEntry):
):
"""Initializes the project.
Args:
path: Absolute path to the project file.
name: Name of project. If None, the name will be the same as the base
name of the project file.
dependencies: List of other Project objects this project is dependent
upon, if not None.
guid: GUID to use for project, if not None.
spec: Dictionary specifying how to build this project.
build_file: Filename of the .gyp file that the vcproj file comes from.
config_platform_overrides: optional dict of configuration platforms to
used in place of the default for this target.
fixpath_prefix: the path used to adjust the behavior of _fixpath
"""
Args:
path: Absolute path to the project file.
name: Name of project. If None, the name will be the same as the base
name of the project file.
dependencies: List of other Project objects this project is dependent
upon, if not None.
guid: GUID to use for project, if not None.
spec: Dictionary specifying how to build this project.
build_file: Filename of the .gyp file that the vcproj file comes from.
config_platform_overrides: optional dict of configuration platforms to
used in place of the default for this target.
fixpath_prefix: the path used to adjust the behavior of _fixpath
"""
self.path = path
self.guid = guid
self.spec = spec
@ -195,16 +195,16 @@ class MSVSSolution:
):
"""Initializes the solution.
Args:
path: Path to solution file.
version: Format version to emit.
entries: List of entries in solution. May contain Folder or Project
objects. May be None, if the folder is empty.
variants: List of build variant strings. If none, a default list will
be used.
websiteProperties: Flag to decide if the website properties section
is generated.
"""
Args:
path: Path to solution file.
version: Format version to emit.
entries: List of entries in solution. May contain Folder or Project
objects. May be None, if the folder is empty.
variants: List of build variant strings. If none, a default list will
be used.
websiteProperties: Flag to decide if the website properties section
is generated.
"""
self.path = path
self.websiteProperties = websiteProperties
self.version = version
@ -230,9 +230,9 @@ class MSVSSolution:
def Write(self, writer=gyp.common.WriteOnDiff):
"""Writes the solution file to disk.
Raises:
IndexError: An entry appears multiple times.
"""
Raises:
IndexError: An entry appears multiple times.
"""
# Walk the entry tree and collect all the folders and projects.
all_entries = set()
entries_to_check = self.entries[:]

View File

@ -15,19 +15,19 @@ class Tool:
def __init__(self, name, attrs=None):
"""Initializes the tool.
Args:
name: Tool name.
attrs: Dict of tool attributes; may be None.
"""
Args:
name: Tool name.
attrs: Dict of tool attributes; may be None.
"""
self._attrs = attrs or {}
self._attrs["Name"] = name
def _GetSpecification(self):
"""Creates an element for the tool.
Returns:
A new xml.dom.Element for the tool.
"""
Returns:
A new xml.dom.Element for the tool.
"""
return ["Tool", self._attrs]
@ -37,10 +37,10 @@ class Filter:
def __init__(self, name, contents=None):
"""Initializes the folder.
Args:
name: Filter (folder) name.
contents: List of filenames and/or Filter objects contained.
"""
Args:
name: Filter (folder) name.
contents: List of filenames and/or Filter objects contained.
"""
self.name = name
self.contents = list(contents or [])
@ -54,13 +54,13 @@ class Writer:
def __init__(self, project_path, version, name, guid=None, platforms=None):
"""Initializes the project.
Args:
project_path: Path to the project file.
version: Format version to emit.
name: Name of the project.
guid: GUID to use for project, if not None.
platforms: Array of string, the supported platforms. If null, ['Win32']
"""
Args:
project_path: Path to the project file.
version: Format version to emit.
name: Name of the project.
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
@ -84,21 +84,21 @@ class Writer:
def AddToolFile(self, path):
"""Adds a tool file to the project.
Args:
path: Relative path from project to tool file.
"""
Args:
path: Relative path from project to tool file.
"""
self.tool_files_section.append(["ToolFile", {"RelativePath": path}])
def _GetSpecForConfiguration(self, config_type, config_name, attrs, tools):
"""Returns the specification for a configuration.
Args:
config_type: Type of configuration node.
config_name: Configuration name.
attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None.
Returns:
"""
Args:
config_type: Type of configuration node.
config_name: Configuration name.
attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None.
Returns:
"""
# Handle defaults
if not attrs:
attrs = {}
@ -122,23 +122,23 @@ class Writer:
def AddConfig(self, name, attrs=None, tools=None):
"""Adds a configuration to the project.
Args:
name: Configuration name.
attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None.
"""
Args:
name: Configuration name.
attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None.
"""
spec = self._GetSpecForConfiguration("Configuration", name, attrs, tools)
self.configurations_section.append(spec)
def _AddFilesToNode(self, parent, files):
"""Adds files and/or filters to the parent node.
Args:
parent: Destination node
files: A list of Filter objects and/or relative paths to files.
Args:
parent: Destination node
files: A list of Filter objects and/or relative paths to files.
Will call itself recursively, if the files list contains Filter objects.
"""
Will call itself recursively, if the files list contains Filter objects.
"""
for f in files:
if isinstance(f, Filter):
node = ["Filter", {"Name": f.name}]
@ -151,13 +151,13 @@ class Writer:
def AddFiles(self, files):
"""Adds files to the project.
Args:
files: A list of Filter objects and/or relative paths to files.
Args:
files: A list of Filter objects and/or relative paths to files.
This makes a copy of the file/filter tree at the time of this call. If you
later add files to a Filter object which was passed into a previous call
to AddFiles(), it will not be reflected in this project.
"""
This makes a copy of the file/filter tree at the time of this call. If you
later add files to a Filter object which was passed into a previous call
to AddFiles(), it will not be reflected in this project.
"""
self._AddFilesToNode(self.files_section, files)
# TODO(rspangler) This also doesn't handle adding files to an existing
# filter. That is, it doesn't merge the trees.
@ -165,15 +165,15 @@ class Writer:
def AddFileConfig(self, path, config, attrs=None, tools=None):
"""Adds a configuration to a file.
Args:
path: Relative path to the file.
config: Name of configuration to add.
attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None.
Args:
path: Relative path to the file.
config: Name of configuration to add.
attrs: Dict of configuration attributes; may be None.
tools: List of tools (strings or Tool objects); may be None.
Raises:
ValueError: Relative path does not match any file added via AddFiles().
"""
Raises:
ValueError: Relative path does not match any file added via AddFiles().
"""
# Find the file node with the right relative path
parent = self.files_dict.get(path)
if not parent:

View File

@ -35,10 +35,10 @@ _msbuild_name_of_tool = {}
class _Tool:
"""Represents a tool used by MSVS or MSBuild.
Attributes:
msvs_name: The name of the tool in MSVS.
msbuild_name: The name of the tool in MSBuild.
"""
Attributes:
msvs_name: The name of the tool in MSVS.
msbuild_name: The name of the tool in MSBuild.
"""
def __init__(self, msvs_name, msbuild_name):
self.msvs_name = msvs_name
@ -48,11 +48,11 @@ class _Tool:
def _AddTool(tool):
"""Adds a tool to the four dictionaries used to process settings.
This only defines the tool. Each setting also needs to be added.
This only defines the tool. Each setting also needs to be added.
Args:
tool: The _Tool object to be added.
"""
Args:
tool: The _Tool object to be added.
"""
_msvs_validators[tool.msvs_name] = {}
_msbuild_validators[tool.msbuild_name] = {}
_msvs_to_msbuild_converters[tool.msvs_name] = {}
@ -70,35 +70,35 @@ class _Type:
def ValidateMSVS(self, value):
"""Verifies that the value is legal for MSVS.
Args:
value: the value to check for this type.
Args:
value: the value to check for this type.
Raises:
ValueError if value is not valid for MSVS.
"""
Raises:
ValueError if value is not valid for MSVS.
"""
def ValidateMSBuild(self, value):
"""Verifies that the value is legal for MSBuild.
Args:
value: the value to check for this type.
Args:
value: the value to check for this type.
Raises:
ValueError if value is not valid for MSBuild.
"""
Raises:
ValueError if value is not valid for MSBuild.
"""
def ConvertToMSBuild(self, value):
"""Returns the MSBuild equivalent of the MSVS value given.
Args:
value: the MSVS value to convert.
Args:
value: the MSVS value to convert.
Returns:
the MSBuild equivalent.
Returns:
the MSBuild equivalent.
Raises:
ValueError if value is not valid.
"""
Raises:
ValueError if value is not valid.
"""
return value
@ -178,15 +178,15 @@ class _Integer(_Type):
class _Enumeration(_Type):
"""Type of settings that is an enumeration.
In MSVS, the values are indexes like '0', '1', and '2'.
MSBuild uses text labels that are more representative, like 'Win32'.
In MSVS, the values are indexes like '0', '1', and '2'.
MSBuild uses text labels that are more representative, like 'Win32'.
Constructor args:
label_list: an array of MSBuild labels that correspond to the MSVS index.
In the rare cases where MSVS has skipped an index value, None is
used in the array to indicate the unused spot.
new: an array of labels that are new to MSBuild.
"""
Constructor args:
label_list: an array of MSBuild labels that correspond to the MSVS index.
In the rare cases where MSVS has skipped an index value, None is
used in the array to indicate the unused spot.
new: an array of labels that are new to MSBuild.
"""
def __init__(self, label_list, new=None):
_Type.__init__(self)
@ -234,23 +234,23 @@ _newly_boolean = _Enumeration(["", "false", "true"])
def _Same(tool, name, setting_type):
"""Defines a setting that has the same name in MSVS and MSBuild.
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
name: the name of the setting.
setting_type: the type of this setting.
"""
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
name: the name of the setting.
setting_type: the type of this setting.
"""
_Renamed(tool, name, name, setting_type)
def _Renamed(tool, msvs_name, msbuild_name, setting_type):
"""Defines a setting for which the name has changed.
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
msvs_name: the name of the MSVS setting.
msbuild_name: the name of the MSBuild setting.
setting_type: the type of this setting.
"""
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
msvs_name: the name of the MSVS setting.
msbuild_name: the name of the MSBuild setting.
setting_type: the type of this setting.
"""
def _Translate(value, msbuild_settings):
msbuild_tool_settings = _GetMSBuildToolSettings(msbuild_settings, tool)
@ -272,13 +272,13 @@ def _MovedAndRenamed(
):
"""Defines a setting that may have moved to a new section.
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
msvs_settings_name: the MSVS name of the setting.
msbuild_tool_name: the name of the MSBuild tool to place the setting under.
msbuild_settings_name: the MSBuild name of the setting.
setting_type: the type of this setting.
"""
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
msvs_settings_name: the MSVS name of the setting.
msbuild_tool_name: the name of the MSBuild tool to place the setting under.
msbuild_settings_name: the MSBuild name of the setting.
setting_type: the type of this setting.
"""
def _Translate(value, msbuild_settings):
tool_settings = msbuild_settings.setdefault(msbuild_tool_name, {})
@ -293,11 +293,11 @@ def _MovedAndRenamed(
def _MSVSOnly(tool, name, setting_type):
"""Defines a setting that is only found in MSVS.
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
name: the name of the setting.
setting_type: the type of this setting.
"""
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
name: the name of the setting.
setting_type: the type of this setting.
"""
def _Translate(unused_value, unused_msbuild_settings):
# Since this is for MSVS only settings, no translation will happen.
@ -310,11 +310,11 @@ def _MSVSOnly(tool, name, setting_type):
def _MSBuildOnly(tool, name, setting_type):
"""Defines a setting that is only found in MSBuild.
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
name: the name of the setting.
setting_type: the type of this setting.
"""
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
name: the name of the setting.
setting_type: the type of this setting.
"""
def _Translate(value, msbuild_settings):
# Let msbuild-only properties get translated as-is from msvs_settings.
@ -328,11 +328,11 @@ def _MSBuildOnly(tool, name, setting_type):
def _ConvertedToAdditionalOption(tool, msvs_name, flag):
"""Defines a setting that's handled via a command line option in MSBuild.
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
msvs_name: the name of the MSVS setting that if 'true' becomes a flag
flag: the flag to insert at the end of the AdditionalOptions
"""
Args:
tool: a dictionary that gives the names of the tool for MSVS and MSBuild.
msvs_name: the name of the MSVS setting that if 'true' becomes a flag
flag: the flag to insert at the end of the AdditionalOptions
"""
def _Translate(value, msbuild_settings):
if value == "true":
@ -384,15 +384,15 @@ _EXCLUDED_SUFFIX_RE = re.compile("^(.*)_excluded$")
def _ValidateExclusionSetting(setting, settings, error_msg, stderr=sys.stderr):
"""Verify that 'setting' is valid if it is generated from an exclusion list.
If the setting appears to be generated from an exclusion list, the root name
is checked.
If the setting appears to be generated from an exclusion list, the root name
is checked.
Args:
setting: A string that is the setting name to validate
settings: A dictionary where the keys are valid settings
error_msg: The message to emit in the event of error
stderr: The stream receiving the error messages.
"""
Args:
setting: A string that is the setting name to validate
settings: A dictionary where the keys are valid settings
error_msg: The message to emit in the event of error
stderr: The stream receiving the error messages.
"""
# This may be unrecognized because it's an exclusion list. If the
# setting name has the _excluded suffix, then check the root name.
unrecognized = True
@ -408,11 +408,11 @@ def _ValidateExclusionSetting(setting, settings, error_msg, stderr=sys.stderr):
def FixVCMacroSlashes(s):
"""Replace macros which have excessive following slashes.
These macros are known to have a built-in trailing slash. Furthermore, many
scripts hiccup on processing paths with extra slashes in the middle.
These macros are known to have a built-in trailing slash. Furthermore, many
scripts hiccup on processing paths with extra slashes in the middle.
This list is probably not exhaustive. Add as needed.
"""
This list is probably not exhaustive. Add as needed.
"""
if "$" in s:
s = fix_vc_macro_slashes_regex.sub(r"\1", s)
return s
@ -421,8 +421,8 @@ def FixVCMacroSlashes(s):
def ConvertVCMacrosToMSBuild(s):
"""Convert the MSVS macros found in the string to the MSBuild equivalent.
This list is probably not exhaustive. Add as needed.
"""
This list is probably not exhaustive. Add as needed.
"""
if "$" in s:
replace_map = {
"$(ConfigurationName)": "$(Configuration)",
@ -444,16 +444,16 @@ def ConvertVCMacrosToMSBuild(s):
def ConvertToMSBuildSettings(msvs_settings, stderr=sys.stderr):
"""Converts MSVS settings (VS2008 and earlier) to MSBuild settings (VS2010+).
Args:
msvs_settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
Args:
msvs_settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
Returns:
A dictionary of MSBuild settings. The key is either the MSBuild tool name
or the empty string (for the global settings). The values are themselves
dictionaries of settings and their values.
"""
Returns:
A dictionary of MSBuild settings. The key is either the MSBuild tool name
or the empty string (for the global settings). The values are themselves
dictionaries of settings and their values.
"""
msbuild_settings = {}
for msvs_tool_name, msvs_tool_settings in msvs_settings.items():
if msvs_tool_name in _msvs_to_msbuild_converters:
@ -492,36 +492,36 @@ def ConvertToMSBuildSettings(msvs_settings, stderr=sys.stderr):
def ValidateMSVSSettings(settings, stderr=sys.stderr):
"""Validates that the names of the settings are valid for MSVS.
Args:
settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
"""
Args:
settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
"""
_ValidateSettings(_msvs_validators, settings, stderr)
def ValidateMSBuildSettings(settings, stderr=sys.stderr):
"""Validates that the names of the settings are valid for MSBuild.
Args:
settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
"""
Args:
settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
"""
_ValidateSettings(_msbuild_validators, settings, stderr)
def _ValidateSettings(validators, settings, stderr):
"""Validates that the settings are valid for MSBuild or MSVS.
We currently only validate the names of the settings, not their values.
We currently only validate the names of the settings, not their values.
Args:
validators: A dictionary of tools and their validators.
settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
"""
Args:
validators: A dictionary of tools and their validators.
settings: A dictionary. The key is the tool name. The values are
themselves dictionaries of settings and their values.
stderr: The stream receiving the error messages.
"""
for tool_name in settings:
if tool_name in validators:
tool_validators = validators[tool_name]
@ -637,7 +637,9 @@ _Same(
),
) # /RTC1
_Same(
_compile, "BrowseInformation", _Enumeration(["false", "true", "true"]) # /FR
_compile,
"BrowseInformation",
_Enumeration(["false", "true", "true"]), # /FR
) # /Fr
_Same(
_compile,
@ -695,7 +697,9 @@ _Same(
_Enumeration(["false", "Sync", "Async"], new=["SyncCThrow"]), # /EHsc # /EHa
) # /EHs
_Same(
_compile, "FavorSizeOrSpeed", _Enumeration(["Neither", "Speed", "Size"]) # /Ot
_compile,
"FavorSizeOrSpeed",
_Enumeration(["Neither", "Speed", "Size"]), # /Ot
) # /Os
_Same(
_compile,
@ -908,7 +912,9 @@ _target_machine_enumeration = _Enumeration(
) # /MACHINE:X64
_Same(
_link, "AssemblyDebug", _Enumeration(["", "true", "false"]) # /ASSEMBLYDEBUG
_link,
"AssemblyDebug",
_Enumeration(["", "true", "false"]), # /ASSEMBLYDEBUG
) # /ASSEMBLYDEBUG:DISABLE
_Same(
_link,
@ -1158,17 +1164,23 @@ _Renamed(_midl, "ValidateParameters", "ValidateAllParameters", _boolean) # /rob
_MSBuildOnly(_midl, "ApplicationConfigurationMode", _boolean) # /app_config
_MSBuildOnly(_midl, "ClientStubFile", _file_name) # /cstub
_MSBuildOnly(
_midl, "GenerateClientFiles", _Enumeration([], new=["Stub", "None"]) # /client stub
_midl,
"GenerateClientFiles",
_Enumeration([], new=["Stub", "None"]), # /client stub
) # /client none
_MSBuildOnly(
_midl, "GenerateServerFiles", _Enumeration([], new=["Stub", "None"]) # /client stub
_midl,
"GenerateServerFiles",
_Enumeration([], new=["Stub", "None"]), # /client stub
) # /client none
_MSBuildOnly(_midl, "LocaleID", _integer) # /lcid DECIMAL
_MSBuildOnly(_midl, "ServerStubFile", _file_name) # /sstub
_MSBuildOnly(_midl, "SuppressCompilerWarnings", _boolean) # /no_warn
_MSBuildOnly(_midl, "TrackerLogDirectory", _folder_name)
_MSBuildOnly(
_midl, "TypeLibFormat", _Enumeration([], new=["NewFormat", "OldFormat"]) # /newtlb
_midl,
"TypeLibFormat",
_Enumeration([], new=["NewFormat", "OldFormat"]), # /newtlb
) # /oldtlb

View File

@ -1143,47 +1143,47 @@ class TestSequenceFunctions(unittest.TestCase):
def testConvertToMSBuildSettings_actual(self):
"""Tests the conversion of an actual project.
A VS2008 project with most of the options defined was created through the
VS2008 IDE. It was then converted to VS2010. The tool settings found in
the .vcproj and .vcxproj files were converted to the two dictionaries
msvs_settings and expected_msbuild_settings.
A VS2008 project with most of the options defined was created through the
VS2008 IDE. It was then converted to VS2010. The tool settings found in
the .vcproj and .vcxproj files were converted to the two dictionaries
msvs_settings and expected_msbuild_settings.
Note that for many settings, the VS2010 converter adds macros like
%(AdditionalIncludeDirectories) to make sure than inherited values are
included. Since the Gyp projects we generate do not use inheritance,
we removed these macros. They were:
ClCompile:
AdditionalIncludeDirectories: ';%(AdditionalIncludeDirectories)'
AdditionalOptions: ' %(AdditionalOptions)'
AdditionalUsingDirectories: ';%(AdditionalUsingDirectories)'
DisableSpecificWarnings: ';%(DisableSpecificWarnings)',
ForcedIncludeFiles: ';%(ForcedIncludeFiles)',
ForcedUsingFiles: ';%(ForcedUsingFiles)',
PreprocessorDefinitions: ';%(PreprocessorDefinitions)',
UndefinePreprocessorDefinitions:
';%(UndefinePreprocessorDefinitions)',
Link:
AdditionalDependencies: ';%(AdditionalDependencies)',
AdditionalLibraryDirectories: ';%(AdditionalLibraryDirectories)',
AdditionalManifestDependencies:
';%(AdditionalManifestDependencies)',
AdditionalOptions: ' %(AdditionalOptions)',
AddModuleNamesToAssembly: ';%(AddModuleNamesToAssembly)',
AssemblyLinkResource: ';%(AssemblyLinkResource)',
DelayLoadDLLs: ';%(DelayLoadDLLs)',
EmbedManagedResourceFile: ';%(EmbedManagedResourceFile)',
ForceSymbolReferences: ';%(ForceSymbolReferences)',
IgnoreSpecificDefaultLibraries:
';%(IgnoreSpecificDefaultLibraries)',
ResourceCompile:
AdditionalIncludeDirectories: ';%(AdditionalIncludeDirectories)',
AdditionalOptions: ' %(AdditionalOptions)',
PreprocessorDefinitions: ';%(PreprocessorDefinitions)',
Manifest:
AdditionalManifestFiles: ';%(AdditionalManifestFiles)',
AdditionalOptions: ' %(AdditionalOptions)',
InputResourceManifests: ';%(InputResourceManifests)',
"""
Note that for many settings, the VS2010 converter adds macros like
%(AdditionalIncludeDirectories) to make sure than inherited values are
included. Since the Gyp projects we generate do not use inheritance,
we removed these macros. They were:
ClCompile:
AdditionalIncludeDirectories: ';%(AdditionalIncludeDirectories)'
AdditionalOptions: ' %(AdditionalOptions)'
AdditionalUsingDirectories: ';%(AdditionalUsingDirectories)'
DisableSpecificWarnings: ';%(DisableSpecificWarnings)',
ForcedIncludeFiles: ';%(ForcedIncludeFiles)',
ForcedUsingFiles: ';%(ForcedUsingFiles)',
PreprocessorDefinitions: ';%(PreprocessorDefinitions)',
UndefinePreprocessorDefinitions:
';%(UndefinePreprocessorDefinitions)',
Link:
AdditionalDependencies: ';%(AdditionalDependencies)',
AdditionalLibraryDirectories: ';%(AdditionalLibraryDirectories)',
AdditionalManifestDependencies:
';%(AdditionalManifestDependencies)',
AdditionalOptions: ' %(AdditionalOptions)',
AddModuleNamesToAssembly: ';%(AddModuleNamesToAssembly)',
AssemblyLinkResource: ';%(AssemblyLinkResource)',
DelayLoadDLLs: ';%(DelayLoadDLLs)',
EmbedManagedResourceFile: ';%(EmbedManagedResourceFile)',
ForceSymbolReferences: ';%(ForceSymbolReferences)',
IgnoreSpecificDefaultLibraries:
';%(IgnoreSpecificDefaultLibraries)',
ResourceCompile:
AdditionalIncludeDirectories: ';%(AdditionalIncludeDirectories)',
AdditionalOptions: ' %(AdditionalOptions)',
PreprocessorDefinitions: ';%(PreprocessorDefinitions)',
Manifest:
AdditionalManifestFiles: ';%(AdditionalManifestFiles)',
AdditionalOptions: ' %(AdditionalOptions)',
InputResourceManifests: ';%(InputResourceManifests)',
"""
msvs_settings = {
"VCCLCompilerTool": {
"AdditionalIncludeDirectories": "dir1",
@ -1346,8 +1346,7 @@ class TestSequenceFunctions(unittest.TestCase):
"EmbedManifest": "false",
"GenerateCatalogFiles": "true",
"InputResourceManifests": "asfsfdafs",
"ManifestResourceFile":
"$(IntDir)\\$(TargetFileName).embed.manifest.resfdsf",
"ManifestResourceFile": "$(IntDir)\\$(TargetFileName).embed.manifest.resfdsf", # noqa: E501
"OutputManifestFile": "$(TargetPath).manifestdfs",
"RegistrarScriptFile": "sdfsfd",
"ReplacementsFile": "sdffsd",
@ -1531,8 +1530,7 @@ class TestSequenceFunctions(unittest.TestCase):
"LinkIncremental": "",
},
"ManifestResourceCompile": {
"ResourceOutputFileName":
"$(IntDir)$(TargetFileName).embed.manifest.resfdsf"
"ResourceOutputFileName": "$(IntDir)$(TargetFileName).embed.manifest.resfdsf" # noqa: E501
},
}
self.maxDiff = 9999 # on failure display a long diff

View File

@ -13,10 +13,10 @@ class Writer:
def __init__(self, tool_file_path, name):
"""Initializes the tool file.
Args:
tool_file_path: Path to the tool file.
name: Name of the tool file.
"""
Args:
tool_file_path: Path to the tool file.
name: Name of the tool file.
"""
self.tool_file_path = tool_file_path
self.name = name
self.rules_section = ["Rules"]
@ -26,14 +26,14 @@ class Writer:
):
"""Adds a rule to the tool file.
Args:
name: Name of the rule.
description: Description of the rule.
cmd: Command line of the rule.
additional_dependencies: other files which may trigger the rule.
outputs: outputs of the rule.
extensions: extensions handled by the rule.
"""
Args:
name: Name of the rule.
description: Description of the rule.
cmd: Command line of the rule.
additional_dependencies: other files which may trigger the rule.
outputs: outputs of the rule.
extensions: extensions handled by the rule.
"""
rule = [
"CustomBuildRule",
{

View File

@ -15,11 +15,11 @@ from gyp import easy_xml
def _FindCommandInPath(command):
"""If there are no slashes in the command given, this function
searches the PATH env to find the given command, and converts it
to an absolute path. We have to do this because MSVS is looking
for an actual file to launch a debugger on, not just a command
line. Note that this happens at GYP time, so anything needing to
be built needs to have a full path."""
searches the PATH env to find the given command, and converts it
to an absolute path. We have to do this because MSVS is looking
for an actual file to launch a debugger on, not just a command
line. Note that this happens at GYP time, so anything needing to
be built needs to have a full path."""
if "/" in command or "\\" in command:
# If the command already has path elements (either relative or
# absolute), then assume it is constructed properly.
@ -58,11 +58,11 @@ class Writer:
def __init__(self, user_file_path, version, name):
"""Initializes the user file.
Args:
user_file_path: Path to the user file.
version: Version info.
name: Name of the user file.
"""
Args:
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.version = version
self.name = name
@ -71,9 +71,9 @@ class Writer:
def AddConfig(self, name):
"""Adds a configuration to the project.
Args:
name: Configuration name.
"""
Args:
name: Configuration name.
"""
self.configurations[name] = ["Configuration", {"Name": name}]
def AddDebugSettings(
@ -81,12 +81,12 @@ class Writer:
):
"""Adds a DebugSettings node to the user file for a particular config.
Args:
command: command line to run. First element in the list is the
executable. All elements of the command will be quoted if
necessary.
working_directory: other files which may trigger the rule. (optional)
"""
Args:
command: command line to run. First element in the list is the
executable. All elements of the command will be quoted if
necessary.
working_directory: other files which may trigger the rule. (optional)
"""
command = _QuoteWin32CommandLineArgs(command)
abs_command = _FindCommandInPath(command[0])

View File

@ -29,13 +29,13 @@ def _GetLargePdbShimCcPath():
def _DeepCopySomeKeys(in_dict, keys):
"""Performs a partial deep-copy on |in_dict|, only copying the keys in |keys|.
Arguments:
in_dict: The dictionary to copy.
keys: The keys to be copied. If a key is in this list and doesn't exist in
|in_dict| this is not an error.
Returns:
The partially deep-copied dictionary.
"""
Arguments:
in_dict: The dictionary to copy.
keys: The keys to be copied. If a key is in this list and doesn't exist in
|in_dict| this is not an error.
Returns:
The partially deep-copied dictionary.
"""
d = {}
for key in keys:
if key not in in_dict:
@ -47,12 +47,12 @@ def _DeepCopySomeKeys(in_dict, keys):
def _SuffixName(name, suffix):
"""Add a suffix to the end of a target.
Arguments:
name: name of the target (foo#target)
suffix: the suffix to be added
Returns:
Target name with suffix added (foo_suffix#target)
"""
Arguments:
name: name of the target (foo#target)
suffix: the suffix to be added
Returns:
Target name with suffix added (foo_suffix#target)
"""
parts = name.rsplit("#", 1)
parts[0] = f"{parts[0]}_{suffix}"
return "#".join(parts)
@ -61,24 +61,24 @@ def _SuffixName(name, suffix):
def _ShardName(name, number):
"""Add a shard number to the end of a target.
Arguments:
name: name of the target (foo#target)
number: shard number
Returns:
Target name with shard added (foo_1#target)
"""
Arguments:
name: name of the target (foo#target)
number: shard number
Returns:
Target name with shard added (foo_1#target)
"""
return _SuffixName(name, str(number))
def ShardTargets(target_list, target_dicts):
"""Shard some targets apart to work around the linkers limits.
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
Returns:
Tuple of the new sharded versions of the inputs.
"""
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
Returns:
Tuple of the new sharded versions of the inputs.
"""
# Gather the targets to shard, and how many pieces.
targets_to_shard = {}
for t in target_dicts:
@ -128,22 +128,22 @@ def ShardTargets(target_list, target_dicts):
def _GetPdbPath(target_dict, config_name, vars):
"""Returns the path to the PDB file that will be generated by a given
configuration.
configuration.
The lookup proceeds as follows:
- Look for an explicit path in the VCLinkerTool configuration block.
- Look for an 'msvs_large_pdb_path' variable.
- Use '<(PRODUCT_DIR)/<(product_name).(exe|dll).pdb' if 'product_name' is
specified.
- Use '<(PRODUCT_DIR)/<(target_name).(exe|dll).pdb'.
The lookup proceeds as follows:
- Look for an explicit path in the VCLinkerTool configuration block.
- Look for an 'msvs_large_pdb_path' variable.
- Use '<(PRODUCT_DIR)/<(product_name).(exe|dll).pdb' if 'product_name' is
specified.
- Use '<(PRODUCT_DIR)/<(target_name).(exe|dll).pdb'.
Arguments:
target_dict: The target dictionary to be searched.
config_name: The name of the configuration of interest.
vars: A dictionary of common GYP variables with generator-specific values.
Returns:
The path of the corresponding PDB file.
"""
Arguments:
target_dict: The target dictionary to be searched.
config_name: The name of the configuration of interest.
vars: A dictionary of common GYP variables with generator-specific values.
Returns:
The path of the corresponding PDB file.
"""
config = target_dict["configurations"][config_name]
msvs = config.setdefault("msvs_settings", {})
@ -168,16 +168,16 @@ def _GetPdbPath(target_dict, config_name, vars):
def InsertLargePdbShims(target_list, target_dicts, vars):
"""Insert a shim target that forces the linker to use 4KB pagesize PDBs.
This is a workaround for targets with PDBs greater than 1GB in size, the
limit for the 1KB pagesize PDBs created by the linker by default.
This is a workaround for targets with PDBs greater than 1GB in size, the
limit for the 1KB pagesize PDBs created by the linker by default.
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
vars: A dictionary of common GYP variables with generator-specific values.
Returns:
Tuple of the shimmed version of the inputs.
"""
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
vars: A dictionary of common GYP variables with generator-specific values.
Returns:
Tuple of the shimmed version of the inputs.
"""
# Determine which targets need shimming.
targets_to_shim = []
for t in target_dicts:

View File

@ -76,17 +76,17 @@ class VisualStudioVersion:
return self.path
def ToolPath(self, tool):
"""Returns the path to a given compiler tool. """
"""Returns the path to a given compiler tool."""
return os.path.normpath(os.path.join(self.path, "VC/bin", tool))
def DefaultToolset(self):
"""Returns the msbuild toolset version that will be used in the absence
of a user override."""
of a user override."""
return self.default_toolset
def _SetupScriptInternal(self, target_arch):
"""Returns a command (with arguments) to be used to set up the
environment."""
environment."""
assert target_arch in ("x86", "x64"), "target_arch not supported"
# If WindowsSDKDir is set and SetEnv.Cmd exists then we are using the
# depot_tools build tools and should run SetEnv.Cmd to set up the
@ -154,16 +154,16 @@ class VisualStudioVersion:
def _RegistryQueryBase(sysdir, key, value):
"""Use reg.exe to read a particular key.
While ideally we might use the win32 module, we would like gyp to be
python neutral, so for instance cygwin python lacks this module.
While ideally we might use the win32 module, we would like gyp to be
python neutral, so for instance cygwin python lacks this module.
Arguments:
sysdir: The system subdirectory to attempt to launch reg.exe from.
key: The registry key to read from.
value: The particular value to read.
Return:
stdout from reg.exe, or None for failure.
"""
Arguments:
sysdir: The system subdirectory to attempt to launch reg.exe from.
key: The registry key to read from.
value: The particular value to read.
Return:
stdout from reg.exe, or None for failure.
"""
# Skip if not on Windows or Python Win32 setup issue
if sys.platform not in ("win32", "cygwin"):
return None
@ -184,20 +184,20 @@ def _RegistryQueryBase(sysdir, key, value):
def _RegistryQuery(key, value=None):
r"""Use reg.exe to read a particular key through _RegistryQueryBase.
First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
that fails, it falls back to System32. Sysnative is available on Vista and
up and available on Windows Server 2003 and XP through KB patch 942589. Note
that Sysnative will always fail if using 64-bit python due to it being a
virtual directory and System32 will work correctly in the first place.
First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
that fails, it falls back to System32. Sysnative is available on Vista and
up and available on Windows Server 2003 and XP through KB patch 942589. Note
that Sysnative will always fail if using 64-bit python due to it being a
virtual directory and System32 will work correctly in the first place.
KB 942589 - http://support.microsoft.com/kb/942589/en-us.
KB 942589 - http://support.microsoft.com/kb/942589/en-us.
Arguments:
key: The registry key.
value: The particular registry value to read (optional).
Return:
stdout from reg.exe, or None for failure.
"""
Arguments:
key: The registry key.
value: The particular registry value to read (optional).
Return:
stdout from reg.exe, or None for failure.
"""
text = None
try:
text = _RegistryQueryBase("Sysnative", key, value)
@ -212,14 +212,15 @@ def _RegistryQuery(key, value=None):
def _RegistryGetValueUsingWinReg(key, value):
"""Use the _winreg module to obtain the value of a registry key.
Args:
key: The registry key.
value: The particular registry value to read.
Return:
contents of the registry key's value, or None on failure. Throws
ImportError if winreg is unavailable.
"""
Args:
key: The registry key.
value: The particular registry value to read.
Return:
contents of the registry key's value, or None on failure. Throws
ImportError if winreg is unavailable.
"""
from winreg import HKEY_LOCAL_MACHINE, OpenKey, QueryValueEx # noqa: PLC0415
try:
root, subkey = key.split("\\", 1)
assert root == "HKLM" # Only need HKLM for now.
@ -232,17 +233,17 @@ def _RegistryGetValueUsingWinReg(key, value):
def _RegistryGetValue(key, value):
"""Use _winreg or reg.exe to obtain the value of a registry key.
Using _winreg is preferable because it solves an issue on some corporate
environments where access to reg.exe is locked down. However, we still need
to fallback to reg.exe for the case where the _winreg module is not available
(for example in cygwin python).
Using _winreg is preferable because it solves an issue on some corporate
environments where access to reg.exe is locked down. However, we still need
to fallback to reg.exe for the case where the _winreg module is not available
(for example in cygwin python).
Args:
key: The registry key.
value: The particular registry value to read.
Return:
contents of the registry key's value, or None on failure.
"""
Args:
key: The registry key.
value: The particular registry value to read.
Return:
contents of the registry key's value, or None on failure.
"""
try:
return _RegistryGetValueUsingWinReg(key, value)
except ImportError:
@ -262,10 +263,10 @@ def _RegistryGetValue(key, value):
def _CreateVersion(name, path, sdk_based=False):
"""Sets up MSVS project generation.
Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
passed in that doesn't match a value in versions python will throw a error.
"""
Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
passed in that doesn't match a value in versions python will throw a error.
"""
if path:
path = os.path.normpath(path)
versions = {
@ -435,22 +436,22 @@ def _ConvertToCygpath(path):
def _DetectVisualStudioVersions(versions_to_check, force_express):
"""Collect the list of installed visual studio versions.
Returns:
A list of visual studio versions installed in descending order of
usage preference.
Base this on the registry and a quick check if devenv.exe exists.
Possibilities are:
2005(e) - Visual Studio 2005 (8)
2008(e) - Visual Studio 2008 (9)
2010(e) - Visual Studio 2010 (10)
2012(e) - Visual Studio 2012 (11)
2013(e) - Visual Studio 2013 (12)
2015 - Visual Studio 2015 (14)
2017 - Visual Studio 2017 (15)
2019 - Visual Studio 2019 (16)
2022 - Visual Studio 2022 (17)
Where (e) is e for express editions of MSVS and blank otherwise.
"""
Returns:
A list of visual studio versions installed in descending order of
usage preference.
Base this on the registry and a quick check if devenv.exe exists.
Possibilities are:
2005(e) - Visual Studio 2005 (8)
2008(e) - Visual Studio 2008 (9)
2010(e) - Visual Studio 2010 (10)
2012(e) - Visual Studio 2012 (11)
2013(e) - Visual Studio 2013 (12)
2015 - Visual Studio 2015 (14)
2017 - Visual Studio 2017 (15)
2019 - Visual Studio 2019 (16)
2022 - Visual Studio 2022 (17)
Where (e) is e for express editions of MSVS and blank otherwise.
"""
version_to_year = {
"8.0": "2005",
"9.0": "2008",
@ -527,11 +528,11 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
def SelectVisualStudioVersion(version="auto", allow_fallback=True):
"""Select which version of Visual Studio projects to generate.
Arguments:
version: Hook to allow caller to force a particular version (vs auto).
Returns:
An object representing a visual studio project format version.
"""
Arguments:
version: Hook to allow caller to force a particular version (vs auto).
Returns:
An object representing a visual studio project format version.
"""
# In auto mode, check environment variable for override.
if version == "auto":
version = os.environ.get("GYP_MSVS_VERSION", "auto")

View File

@ -25,19 +25,21 @@ DEBUG_GENERAL = "general"
DEBUG_VARIABLES = "variables"
DEBUG_INCLUDES = "includes"
def EscapeForCString(string: bytes | str) -> str:
if isinstance(string, str):
string = string.encode(encoding='utf8')
string = string.encode(encoding="utf8")
backslash_or_double_quote = {ord('\\'), ord('"')}
result = ''
backslash_or_double_quote = {ord("\\"), ord('"')}
result = ""
for char in string:
if char in backslash_or_double_quote or not 32 <= char < 127:
result += '\\%03o' % char
result += "\\%03o" % char
else:
result += chr(char)
return result
def DebugOutput(mode, message, *args):
if "all" in gyp.debug or mode in gyp.debug:
ctx = ("unknown", 0, "unknown")
@ -76,11 +78,11 @@ def Load(
circular_check=True,
):
"""
Loads one or more specified build files.
default_variables and includes will be copied before use.
Returns the generator for the specified format and the
data returned by loading the specified build files.
"""
Loads one or more specified build files.
default_variables and includes will be copied before use.
Returns the generator for the specified format and the
data returned by loading the specified build files.
"""
if params is None:
params = {}
@ -114,7 +116,7 @@ def Load(
# These parameters are passed in order (as opposed to by key)
# because ActivePython cannot handle key parameters to __import__.
generator = __import__(generator_name, globals(), locals(), generator_name)
for (key, val) in generator.generator_default_variables.items():
for key, val in generator.generator_default_variables.items():
default_variables.setdefault(key, val)
output_dir = params["options"].generator_output or params["options"].toplevel_dir
@ -184,10 +186,10 @@ def Load(
def NameValueListToDict(name_value_list):
"""
Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
of the pairs. If a string is simply NAME, then the value in the dictionary
is set to True. If VALUE can be converted to an integer, it is.
"""
Takes an array of strings of the form 'NAME=VALUE' and creates a dictionary
of the pairs. If a string is simply NAME, then the value in the dictionary
is set to True. If VALUE can be converted to an integer, it is.
"""
result = {}
for item in name_value_list:
tokens = item.split("=", 1)
@ -220,13 +222,13 @@ def FormatOpt(opt, value):
def RegenerateAppendFlag(flag, values, predicate, env_name, options):
"""Regenerate a list of command line flags, for an option of action='append'.
The |env_name|, if given, is checked in the environment and used to generate
an initial list of options, then the options that were specified on the
command line (given in |values|) are appended. This matches the handling of
environment variables and command line flags where command line flags override
the environment, while not requiring the environment to be set when the flags
are used again.
"""
The |env_name|, if given, is checked in the environment and used to generate
an initial list of options, then the options that were specified on the
command line (given in |values|) are appended. This matches the handling of
environment variables and command line flags where command line flags override
the environment, while not requiring the environment to be set when the flags
are used again.
"""
flags = []
if options.use_environment and env_name:
for flag_value in ShlexEnv(env_name):
@ -242,14 +244,14 @@ def RegenerateAppendFlag(flag, values, predicate, env_name, options):
def RegenerateFlags(options):
"""Given a parsed options object, and taking the environment variables into
account, returns a list of flags that should regenerate an equivalent options
object (even in the absence of the environment variables.)
account, returns a list of flags that should regenerate an equivalent options
object (even in the absence of the environment variables.)
Any path options will be normalized relative to depth.
Any path options will be normalized relative to depth.
The format flag is not included, as it is assumed the calling generator will
set that as appropriate.
"""
The format flag is not included, as it is assumed the calling generator will
set that as appropriate.
"""
def FixPath(path):
path = gyp.common.FixIfRelativePath(path, options.depth)
@ -307,15 +309,15 @@ class RegeneratableOptionParser(argparse.ArgumentParser):
def add_argument(self, *args, **kw):
"""Add an option to the parser.
This accepts the same arguments as ArgumentParser.add_argument, plus the
following:
regenerate: can be set to False to prevent this option from being included
in regeneration.
env_name: name of environment variable that additional values for this
option come from.
type: adds type='path', to tell the regenerator that the values of
this option need to be made relative to options.depth
"""
This accepts the same arguments as ArgumentParser.add_argument, plus the
following:
regenerate: can be set to False to prevent this option from being included
in regeneration.
env_name: name of environment variable that additional values for this
option come from.
type: adds type='path', to tell the regenerator that the values of
this option need to be made relative to options.depth
"""
env_name = kw.pop("env_name", None)
if "dest" in kw and kw.pop("regenerate", True):
dest = kw["dest"]
@ -343,7 +345,7 @@ class RegeneratableOptionParser(argparse.ArgumentParser):
def gyp_main(args):
my_name = os.path.basename(sys.argv[0])
usage = "usage: %(prog)s [options ...] [build_file ...]"
usage = "%(prog)s [options ...] [build_file ...]"
parser = RegeneratableOptionParser(usage=usage.replace("%s", "%(prog)s"))
parser.add_argument(
@ -490,6 +492,7 @@ def gyp_main(args):
options, build_files_arg = parser.parse_args(args)
if options.version:
import pkg_resources # noqa: PLC0415
print(f"v{pkg_resources.get_distribution('gyp-next').version}")
return 0
build_files = build_files_arg

View File

@ -31,9 +31,8 @@ class memoize:
class GypError(Exception):
"""Error class representing an error, which is to be presented
to the user. The main entry point will catch and display this.
"""
to the user. The main entry point will catch and display this.
"""
def ExceptionAppend(e, msg):
@ -48,9 +47,9 @@ def ExceptionAppend(e, msg):
def FindQualifiedTargets(target, qualified_list):
"""
Given a list of qualified targets, return the qualified targets for the
specified |target|.
"""
Given a list of qualified targets, return the qualified targets for the
specified |target|.
"""
return [t for t in qualified_list if ParseQualifiedTarget(t)[1] == target]
@ -115,7 +114,7 @@ def BuildFile(fully_qualified_target):
def GetEnvironFallback(var_list, default):
"""Look up a key in the environment, with fallback to secondary keys
and finally falling back to a default value."""
and finally falling back to a default value."""
for var in var_list:
if var in os.environ:
return os.environ[var]
@ -178,11 +177,11 @@ def RelativePath(path, relative_to, follow_path_symlink=True):
@memoize
def InvertRelativePath(path, toplevel_dir=None):
"""Given a path like foo/bar that is relative to toplevel_dir, return
the inverse relative path back to the toplevel_dir.
the inverse relative path back to the toplevel_dir.
E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
should always produce the empty string, unless the path contains symlinks.
"""
E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path)))
should always produce the empty string, unless the path contains symlinks.
"""
if not path:
return path
toplevel_dir = "." if toplevel_dir is None else toplevel_dir
@ -262,12 +261,12 @@ _escape = re.compile(r'(["\\`])')
def EncodePOSIXShellArgument(argument):
"""Encodes |argument| suitably for consumption by POSIX shells.
argument may be quoted and escaped as necessary to ensure that POSIX shells
treat the returned value as a literal representing the argument passed to
this function. Parameter (variable) expansions beginning with $ are allowed
to remain intact without escaping the $, to allow the argument to contain
references to variables to be expanded by the shell.
"""
argument may be quoted and escaped as necessary to ensure that POSIX shells
treat the returned value as a literal representing the argument passed to
this function. Parameter (variable) expansions beginning with $ are allowed
to remain intact without escaping the $, to allow the argument to contain
references to variables to be expanded by the shell.
"""
if not isinstance(argument, str):
argument = str(argument)
@ -282,9 +281,9 @@ def EncodePOSIXShellArgument(argument):
def EncodePOSIXShellList(list):
"""Encodes |list| suitably for consumption by POSIX shells.
Returns EncodePOSIXShellArgument for each item in list, and joins them
together using the space character as an argument separator.
"""
Returns EncodePOSIXShellArgument for each item in list, and joins them
together using the space character as an argument separator.
"""
encoded_arguments = []
for argument in list:
@ -312,14 +311,12 @@ def DeepDependencyTargets(target_dicts, roots):
def BuildFileTargets(target_list, build_file):
"""From a target_list, returns the subset from the specified build_file.
"""
"""From a target_list, returns the subset from the specified build_file."""
return [p for p in target_list if BuildFile(p) == build_file]
def AllTargets(target_list, target_dicts, build_file):
"""Returns all targets (direct and dependencies) for the specified build_file.
"""
"""Returns all targets (direct and dependencies) for the specified build_file."""
bftargets = BuildFileTargets(target_list, build_file)
deptargets = DeepDependencyTargets(target_dicts, bftargets)
return bftargets + deptargets
@ -328,12 +325,12 @@ def AllTargets(target_list, target_dicts, build_file):
def WriteOnDiff(filename):
"""Write to a file only if the new contents differ.
Arguments:
filename: name of the file to potentially write to.
Returns:
A file like object which will write to temporary file and only overwrite
the target if it differs (on close).
"""
Arguments:
filename: name of the file to potentially write to.
Returns:
A file like object which will write to temporary file and only overwrite
the target if it differs (on close).
"""
class Writer:
"""Wrapper around file which only covers the target if it differs."""
@ -421,6 +418,7 @@ def EnsureDirExists(path):
except OSError:
pass
def GetCompilerPredefines(): # -> dict
cmd = []
defines = {}
@ -448,15 +446,14 @@ def GetCompilerPredefines(): # -> dict
try:
os.close(fd)
stdout = subprocess.run(
real_cmd, shell=True,
capture_output=True, check=True
real_cmd, shell=True, capture_output=True, check=True
).stdout
except subprocess.CalledProcessError as e:
print(
"Warning: failed to get compiler predefines\n"
"cmd: %s\n"
"status: %d" % (e.cmd, e.returncode),
file=sys.stderr
file=sys.stderr,
)
return defines
finally:
@ -466,15 +463,14 @@ def GetCompilerPredefines(): # -> dict
real_cmd = [*cmd, "-dM", "-E", "-x", "c", input]
try:
stdout = subprocess.run(
real_cmd, shell=False,
capture_output=True, check=True
real_cmd, shell=False, capture_output=True, check=True
).stdout
except subprocess.CalledProcessError as e:
print(
"Warning: failed to get compiler predefines\n"
"cmd: %s\n"
"status: %d" % (e.cmd, e.returncode),
file=sys.stderr
file=sys.stderr,
)
return defines
@ -485,6 +481,7 @@ def GetCompilerPredefines(): # -> dict
defines[key] = " ".join(value)
return defines
def GetFlavorByPlatform():
"""Returns |params.flavor| if it's set, the system's default flavor else."""
flavors = {
@ -512,6 +509,7 @@ def GetFlavorByPlatform():
return "linux"
def GetFlavor(params):
if "flavor" in params:
return params["flavor"]
@ -527,7 +525,7 @@ def GetFlavor(params):
def CopyTool(flavor, out_path, generator_flags={}):
"""Finds (flock|mac|win)_tool.gyp in the gyp directory and copies it
to |out_path|."""
to |out_path|."""
# aix and solaris just need flock emulation. mac and win use more complicated
# support scripts.
prefix = {
@ -662,24 +660,24 @@ class CycleError(Exception):
def TopologicallySorted(graph, get_edges):
r"""Topologically sort based on a user provided edge definition.
Args:
graph: A list of node names.
get_edges: A function mapping from node name to a hashable collection
of node names which this node has outgoing edges to.
Returns:
A list containing all of the node in graph in topological order.
It is assumed that calling get_edges once for each node and caching is
cheaper than repeatedly calling get_edges.
Raises:
CycleError in the event of a cycle.
Example:
graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'}
def GetEdges(node):
return re.findall(r'\$\(([^))]\)', graph[node])
print TopologicallySorted(graph.keys(), GetEdges)
==>
['a', 'c', b']
"""
Args:
graph: A list of node names.
get_edges: A function mapping from node name to a hashable collection
of node names which this node has outgoing edges to.
Returns:
A list containing all of the node in graph in topological order.
It is assumed that calling get_edges once for each node and caching is
cheaper than repeatedly calling get_edges.
Raises:
CycleError in the event of a cycle.
Example:
graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'}
def GetEdges(node):
return re.findall(r'\$\(([^))]\)', graph[node])
print TopologicallySorted(graph.keys(), GetEdges)
==>
['a', 'c', b']
"""
get_edges = memoize(get_edges)
visited = set()
visiting = set()

View File

@ -28,8 +28,12 @@ class TestTopologicallySorted(unittest.TestCase):
def GetEdge(node):
return tuple(graph[node])
assert gyp.common.TopologicallySorted(
graph.keys(), GetEdge) == ["a", "c", "d", "b"]
assert gyp.common.TopologicallySorted(graph.keys(), GetEdge) == [
"a",
"c",
"d",
"b",
]
def test_Cycle(self):
"""Test that an exception is thrown on a cyclic graph."""
@ -97,10 +101,7 @@ class TestGetFlavor(unittest.TestCase):
if throws:
mock_run.side_effect = subprocess.CalledProcessError(
returncode=1,
cmd=[
*expected_cmd,
"-dM", "-E", "-x", "c", expected_input
]
cmd=[*expected_cmd, "-dM", "-E", "-x", "c", expected_input],
)
else:
mock_process = MagicMock()
@ -115,75 +116,71 @@ class TestGetFlavor(unittest.TestCase):
flavor = gyp.common.GetFlavor({})
if env.get("CC_target") or env.get("CC"):
mock_run.assert_called_with(
[
*expected_cmd,
"-dM", "-E", "-x", "c", expected_input
],
[*expected_cmd, "-dM", "-E", "-x", "c", expected_input],
shell=sys.platform == "win32",
capture_output=True, check=True)
capture_output=True,
check=True,
)
return [defines, flavor]
[defines0, _] = mock_run({ "CC": "cl.exe" }, "", ["cl.exe"], True)
[defines0, _] = mock_run({"CC": "cl.exe"}, "", ["cl.exe"], True)
assert defines0 == {}
[defines1, _] = mock_run({}, "", [])
assert defines1 == {}
[defines2, flavor2] = mock_run(
{ "CC_target": "/opt/wasi-sdk/bin/clang" },
{"CC_target": "/opt/wasi-sdk/bin/clang"},
"#define __wasm__ 1\n#define __wasi__ 1\n",
["/opt/wasi-sdk/bin/clang"]
["/opt/wasi-sdk/bin/clang"],
)
assert defines2 == { "__wasm__": "1", "__wasi__": "1" }
assert defines2 == {"__wasm__": "1", "__wasi__": "1"}
assert flavor2 == "wasi"
[defines3, flavor3] = mock_run(
{ "CC_target": "/opt/wasi-sdk/bin/clang --target=wasm32" },
{"CC_target": "/opt/wasi-sdk/bin/clang --target=wasm32"},
"#define __wasm__ 1\n",
["/opt/wasi-sdk/bin/clang", "--target=wasm32"]
["/opt/wasi-sdk/bin/clang", "--target=wasm32"],
)
assert defines3 == { "__wasm__": "1" }
assert defines3 == {"__wasm__": "1"}
assert flavor3 == "wasm"
[defines4, flavor4] = mock_run(
{ "CC_target": "/emsdk/upstream/emscripten/emcc" },
{"CC_target": "/emsdk/upstream/emscripten/emcc"},
"#define __EMSCRIPTEN__ 1\n",
["/emsdk/upstream/emscripten/emcc"]
["/emsdk/upstream/emscripten/emcc"],
)
assert defines4 == { "__EMSCRIPTEN__": "1" }
assert defines4 == {"__EMSCRIPTEN__": "1"}
assert flavor4 == "emscripten"
# Test path which include white space
[defines5, flavor5] = mock_run(
{
"CC_target": "\"/Users/Toyo Li/wasi-sdk/bin/clang\" -O3",
"CFLAGS": "--target=wasm32-wasi-threads -pthread"
"CC_target": '"/Users/Toyo Li/wasi-sdk/bin/clang" -O3',
"CFLAGS": "--target=wasm32-wasi-threads -pthread",
},
"#define __wasm__ 1\n#define __wasi__ 1\n#define _REENTRANT 1\n",
[
"/Users/Toyo Li/wasi-sdk/bin/clang",
"-O3",
"--target=wasm32-wasi-threads",
"-pthread"
]
"-pthread",
],
)
assert defines5 == {
"__wasm__": "1",
"__wasi__": "1",
"_REENTRANT": "1"
}
assert defines5 == {"__wasm__": "1", "__wasi__": "1", "_REENTRANT": "1"}
assert flavor5 == "wasi"
original_sep = os.sep
os.sep = "\\"
[defines6, flavor6] = mock_run(
{ "CC_target": "\"C:\\Program Files\\wasi-sdk\\clang.exe\"" },
{"CC_target": '"C:\\Program Files\\wasi-sdk\\clang.exe"'},
"#define __wasm__ 1\n#define __wasi__ 1\n",
["C:/Program Files/wasi-sdk/clang.exe"]
["C:/Program Files/wasi-sdk/clang.exe"],
)
os.sep = original_sep
assert defines6 == { "__wasm__": "1", "__wasi__": "1" }
assert defines6 == {"__wasm__": "1", "__wasi__": "1"}
assert flavor6 == "wasi"
if __name__ == "__main__":
unittest.main()

View File

@ -10,43 +10,43 @@ from functools import reduce
def XmlToString(content, encoding="utf-8", pretty=False):
""" Writes the XML content to disk, touching the file only if it has changed.
"""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
having to create a lot of function calls.
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
having to create a lot of function calls.
Each XML element of the content is represented as a list composed of:
1. The name of the element, a string,
2. The attributes of the element, a dictionary (optional), and
3+. The content of the element, if any. Strings are simple text nodes and
lists are child elements.
Each XML element of the content is represented as a list composed of:
1. The name of the element, a string,
2. The attributes of the element, a dictionary (optional), and
3+. The content of the element, if any. Strings are simple text nodes and
lists are child elements.
Example 1:
<test/>
becomes
['test']
Example 1:
<test/>
becomes
['test']
Example 2:
<myelement a='value1' b='value2'>
<childtype>This is</childtype>
<childtype>it!</childtype>
</myelement>
Example 2:
<myelement a='value1' b='value2'>
<childtype>This is</childtype>
<childtype>it!</childtype>
</myelement>
becomes
['myelement', {'a':'value1', 'b':'value2'},
['childtype', 'This is'],
['childtype', 'it!'],
]
becomes
['myelement', {'a':'value1', 'b':'value2'},
['childtype', 'This is'],
['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.
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.
"""
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:
@ -58,14 +58,14 @@ def XmlToString(content, encoding="utf-8", pretty=False):
def _ConstructContentList(xml_parts, specification, pretty, level=0):
""" Appends the XML parts corresponding to the specification.
"""Appends the XML parts corresponding to the specification.
Args:
xml_parts: A list of XML parts to be appended to.
specification: The specification of the element. See EasyXml docs.
pretty: True if we want pretty printing with indents and new lines.
level: Indentation level.
"""
Args:
xml_parts: A list of XML parts to be appended to.
specification: The specification of the element. See EasyXml docs.
pretty: True if we want pretty printing with indents and new lines.
level: Indentation level.
"""
# The first item in a specification is the name of the element.
if pretty:
indentation = " " * level
@ -107,16 +107,17 @@ def _ConstructContentList(xml_parts, specification, pretty, level=0):
xml_parts.append("/>%s" % new_line)
def WriteXmlIfChanged(content, path, encoding="utf-8", pretty=False,
win32=(sys.platform == "win32")):
""" Writes the XML content to disk, touching the file only if it has changed.
def WriteXmlIfChanged(
content, path, encoding="utf-8", pretty=False, win32=(sys.platform == "win32")
):
"""Writes the XML content to disk, touching the file only if it has changed.
Args:
content: The structured content to be written.
path: Location of the file.
encoding: The encoding to report on the first line of the XML file.
pretty: True if we want pretty printing with indents and new lines.
"""
Args:
content: The structured content to be written.
path: Location of the file.
encoding: The encoding to report on the first line of the XML file.
pretty: True if we want pretty printing with indents and new lines.
"""
xml_string = XmlToString(content, encoding, pretty)
if win32 and os.linesep != "\r\n":
xml_string = xml_string.replace("\n", "\r\n")
@ -157,7 +158,7 @@ _xml_escape_re = re.compile("(%s)" % "|".join(map(re.escape, _xml_escape_map.key
def _XmlEscape(value, attr=False):
""" Escape a string for inclusion in XML."""
"""Escape a string for inclusion in XML."""
def replace(match):
m = match.string[match.start() : match.end()]

View File

@ -4,7 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Unit tests for the easy_xml.py file. """
"""Unit tests for the easy_xml.py file."""
import unittest
from io import StringIO

View File

@ -62,7 +62,6 @@ directly supplied to gyp. OTOH if both "a.gyp" and "b.gyp" are supplied to gyp
then the "all" target includes "b1" and "b2".
"""
import json
import os
import posixpath
@ -130,8 +129,8 @@ def _ToGypPath(path):
def _ResolveParent(path, base_path_components):
"""Resolves |path|, which starts with at least one '../'. Returns an empty
string if the path shouldn't be considered. See _AddSources() for a
description of |base_path_components|."""
string if the path shouldn't be considered. See _AddSources() for a
description of |base_path_components|."""
depth = 0
while path.startswith("../"):
depth += 1
@ -151,11 +150,11 @@ def _ResolveParent(path, base_path_components):
def _AddSources(sources, base_path, base_path_components, result):
"""Extracts valid sources from |sources| and adds them to |result|. Each
source file is relative to |base_path|, but may contain '..'. To make
resolving '..' easier |base_path_components| contains each of the
directories in |base_path|. Additionally each source may contain variables.
Such sources are ignored as it is assumed dependencies on them are expressed
and tracked in some other means."""
source file is relative to |base_path|, but may contain '..'. To make
resolving '..' easier |base_path_components| contains each of the
directories in |base_path|. Additionally each source may contain variables.
Such sources are ignored as it is assumed dependencies on them are expressed
and tracked in some other means."""
# NOTE: gyp paths are always posix style.
for source in sources:
if not len(source) or source.startswith(("!!!", "$")):
@ -218,23 +217,23 @@ def _ExtractSources(target, target_dict, toplevel_dir):
class Target:
"""Holds information about a particular target:
deps: set of Targets this Target depends upon. This is not recursive, only the
direct dependent Targets.
match_status: one of the MatchStatus values.
back_deps: set of Targets that have a dependency on this Target.
visited: used during iteration to indicate whether we've visited this target.
This is used for two iterations, once in building the set of Targets and
again in _GetBuildTargets().
name: fully qualified name of the target.
requires_build: True if the target type is such that it needs to be built.
See _DoesTargetTypeRequireBuild for details.
added_to_compile_targets: used when determining if the target was added to the
set of targets that needs to be built.
in_roots: true if this target is a descendant of one of the root nodes.
is_executable: true if the type of target is executable.
is_static_library: true if the type of target is static_library.
is_or_has_linked_ancestor: true if the target does a link (eg executable), or
if there is a target in back_deps that does a link."""
deps: set of Targets this Target depends upon. This is not recursive, only the
direct dependent Targets.
match_status: one of the MatchStatus values.
back_deps: set of Targets that have a dependency on this Target.
visited: used during iteration to indicate whether we've visited this target.
This is used for two iterations, once in building the set of Targets and
again in _GetBuildTargets().
name: fully qualified name of the target.
requires_build: True if the target type is such that it needs to be built.
See _DoesTargetTypeRequireBuild for details.
added_to_compile_targets: used when determining if the target was added to the
set of targets that needs to be built.
in_roots: true if this target is a descendant of one of the root nodes.
is_executable: true if the type of target is executable.
is_static_library: true if the type of target is static_library.
is_or_has_linked_ancestor: true if the target does a link (eg executable), or
if there is a target in back_deps that does a link."""
def __init__(self, name):
self.deps = set()
@ -254,8 +253,8 @@ class Target:
class Config:
"""Details what we're looking for
files: set of files to search for
targets: see file description for details."""
files: set of files to search for
targets: see file description for details."""
def __init__(self):
self.files = []
@ -265,7 +264,7 @@ class Config:
def Init(self, params):
"""Initializes Config. This is a separate method as it raises an exception
if there is a parse error."""
if there is a parse error."""
generator_flags = params.get("generator_flags", {})
config_path = generator_flags.get("config_path", None)
if not config_path:
@ -289,8 +288,8 @@ class Config:
def _WasBuildFileModified(build_file, data, files, toplevel_dir):
"""Returns true if the build file |build_file| is either in |files| or
one of the files included by |build_file| is in |files|. |toplevel_dir| is
the root of the source tree."""
one of the files included by |build_file| is in |files|. |toplevel_dir| is
the root of the source tree."""
if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files:
if debug:
print("gyp file modified", build_file)
@ -319,8 +318,8 @@ def _WasBuildFileModified(build_file, data, files, toplevel_dir):
def _GetOrCreateTargetByName(targets, target_name):
"""Creates or returns the Target at targets[target_name]. If there is no
Target for |target_name| one is created. Returns a tuple of whether a new
Target was created and the Target."""
Target for |target_name| one is created. Returns a tuple of whether a new
Target was created and the Target."""
if target_name in targets:
return False, targets[target_name]
target = Target(target_name)
@ -340,13 +339,13 @@ def _DoesTargetTypeRequireBuild(target_dict):
def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, build_files):
"""Returns a tuple of the following:
. A dictionary mapping from fully qualified name to Target.
. A list of the targets that have a source file in |files|.
. Targets that constitute the 'all' target. See description at top of file
for details on the 'all' target.
This sets the |match_status| of the targets that contain any of the source
files in |files| to MATCH_STATUS_MATCHES.
|toplevel_dir| is the root of the source tree."""
. A dictionary mapping from fully qualified name to Target.
. A list of the targets that have a source file in |files|.
. Targets that constitute the 'all' target. See description at top of file
for details on the 'all' target.
This sets the |match_status| of the targets that contain any of the source
files in |files| to MATCH_STATUS_MATCHES.
|toplevel_dir| is the root of the source tree."""
# Maps from target name to Target.
name_to_target = {}
@ -379,9 +378,10 @@ def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, build
target_type = target_dicts[target_name]["type"]
target.is_executable = target_type == "executable"
target.is_static_library = target_type == "static_library"
target.is_or_has_linked_ancestor = (
target_type in {"executable", "shared_library"}
)
target.is_or_has_linked_ancestor = target_type in {
"executable",
"shared_library",
}
build_file = gyp.common.ParseQualifiedTarget(target_name)[0]
if build_file not in build_file_in_files:
@ -427,9 +427,9 @@ def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, build
def _GetUnqualifiedToTargetMapping(all_targets, to_find):
"""Returns a tuple of the following:
. mapping (dictionary) from unqualified name to Target for all the
Targets in |to_find|.
. any target names not found. If this is empty all targets were found."""
. mapping (dictionary) from unqualified name to Target for all the
Targets in |to_find|.
. any target names not found. If this is empty all targets were found."""
result = {}
if not to_find:
return {}, []
@ -446,15 +446,15 @@ def _GetUnqualifiedToTargetMapping(all_targets, to_find):
def _DoesTargetDependOnMatchingTargets(target):
"""Returns true if |target| or any of its dependencies is one of the
targets containing the files supplied as input to analyzer. This updates
|matches| of the Targets as it recurses.
target: the Target to look for."""
targets containing the files supplied as input to analyzer. This updates
|matches| of the Targets as it recurses.
target: the Target to look for."""
if target.match_status == MATCH_STATUS_DOESNT_MATCH:
return False
if (
target.match_status in {MATCH_STATUS_MATCHES,
MATCH_STATUS_MATCHES_BY_DEPENDENCY}
):
if target.match_status in {
MATCH_STATUS_MATCHES,
MATCH_STATUS_MATCHES_BY_DEPENDENCY,
}:
return True
for dep in target.deps:
if _DoesTargetDependOnMatchingTargets(dep):
@ -467,9 +467,9 @@ def _DoesTargetDependOnMatchingTargets(target):
def _GetTargetsDependingOnMatchingTargets(possible_targets):
"""Returns the list of Targets in |possible_targets| that depend (either
directly on indirectly) on at least one of the targets containing the files
supplied as input to analyzer.
possible_targets: targets to search from."""
directly on indirectly) on at least one of the targets containing the files
supplied as input to analyzer.
possible_targets: targets to search from."""
found = []
print("Targets that matched by dependency:")
for target in possible_targets:
@ -480,11 +480,11 @@ def _GetTargetsDependingOnMatchingTargets(possible_targets):
def _AddCompileTargets(target, roots, add_if_no_ancestor, result):
"""Recurses through all targets that depend on |target|, adding all targets
that need to be built (and are in |roots|) to |result|.
roots: set of root targets.
add_if_no_ancestor: If true and there are no ancestors of |target| then add
|target| to |result|. |target| must still be in |roots|.
result: targets that need to be built are added here."""
that need to be built (and are in |roots|) to |result|.
roots: set of root targets.
add_if_no_ancestor: If true and there are no ancestors of |target| then add
|target| to |result|. |target| must still be in |roots|.
result: targets that need to be built are added here."""
if target.visited:
return
@ -537,8 +537,8 @@ def _AddCompileTargets(target, roots, add_if_no_ancestor, result):
def _GetCompileTargets(matching_targets, supplied_targets):
"""Returns the set of Targets that require a build.
matching_targets: targets that changed and need to be built.
supplied_targets: set of targets supplied to analyzer to search from."""
matching_targets: targets that changed and need to be built.
supplied_targets: set of targets supplied to analyzer to search from."""
result = set()
for target in matching_targets:
print("finding compile targets for match", target.name)
@ -592,7 +592,7 @@ def _WriteOutput(params, **values):
def _WasGypIncludeFileModified(params, files):
"""Returns true if one of the files in |files| is in the set of included
files."""
files."""
if params["options"].includes:
for include in params["options"].includes:
if _ToGypPath(os.path.normpath(include)) in files:
@ -608,7 +608,7 @@ def _NamesNotIn(names, mapping):
def _LookupTargets(names, mapping):
"""Returns a list of the mapping[name] for each value in |names| that is in
|mapping|."""
|mapping|."""
return [mapping[name] for name in names if name in mapping]

View File

@ -177,9 +177,7 @@ class AndroidMkWriter:
self.WriteLn("LOCAL_IS_HOST_MODULE := true")
self.WriteLn("LOCAL_MULTILIB := $(GYP_HOST_MULTILIB)")
elif sdk_version > 0:
self.WriteLn(
"LOCAL_MODULE_TARGET_ARCH := $(TARGET_$(GYP_VAR_PREFIX)ARCH)"
)
self.WriteLn("LOCAL_MODULE_TARGET_ARCH := $(TARGET_$(GYP_VAR_PREFIX)ARCH)")
self.WriteLn("LOCAL_SDK_VERSION := %s" % sdk_version)
# Grab output directories; needed for Actions and Rules.
@ -588,7 +586,8 @@ class AndroidMkWriter:
local_files = []
for source in sources:
(root, ext) = os.path.splitext(source)
if ("$(gyp_shared_intermediate_dir)" in source
if (
"$(gyp_shared_intermediate_dir)" in source
or "$(gyp_intermediate_dir)" in source
or (IsCPPExtension(ext) and ext != local_cpp_extension)
):
@ -734,8 +733,7 @@ class AndroidMkWriter:
elif self.toolset == "host":
path = (
"$(call intermediates-dir-for,%s,%s,true,,"
"$(GYP_HOST_VAR_PREFIX))"
% (self.android_class, self.android_module)
"$(GYP_HOST_VAR_PREFIX))" % (self.android_class, self.android_module)
)
else:
path = (
@ -1001,9 +999,9 @@ class AndroidMkWriter:
# - i.e. that the resulting path is still inside the project tree. The
# path may legitimately have ended up containing just $(LOCAL_PATH), though,
# so we don't look for a slash.
assert local_path.startswith(
"$(LOCAL_PATH)"
), f"Path {path} attempts to escape from gyp path {self.path} !)"
assert local_path.startswith("$(LOCAL_PATH)"), (
f"Path {path} attempts to escape from gyp path {self.path} !)"
)
return local_path
def ExpandInputRoot(self, template, expansion, dirname):
@ -1045,9 +1043,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
base_path = gyp.common.RelativePath(os.path.dirname(build_file), options.depth)
# We write the file in the base_path directory.
output_file = os.path.join(options.depth, base_path, base_name)
assert (
not options.generator_output
), "The Android backend does not support options.generator_output."
assert not options.generator_output, (
"The Android backend does not support options.generator_output."
)
base_path = gyp.common.RelativePath(
os.path.dirname(build_file), options.toplevel_dir
)
@ -1067,9 +1065,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
makefile_name = "GypAndroid" + options.suffix + ".mk"
makefile_path = os.path.join(options.toplevel_dir, makefile_name)
assert (
not options.generator_output
), "The Android backend does not support options.generator_output."
assert not options.generator_output, (
"The Android backend does not support options.generator_output."
)
gyp.common.EnsureDirExists(makefile_path)
root_makefile = open(makefile_path, "w")

View File

@ -28,7 +28,6 @@ not be able to find the header file directories described in the generated
CMakeLists.txt file.
"""
import multiprocessing
import os
import signal
@ -97,11 +96,11 @@ def Linkable(filename):
def NormjoinPathForceCMakeSource(base_path, rel_path):
"""Resolves rel_path against base_path and returns the result.
If rel_path is an absolute path it is returned unchanged.
Otherwise it is resolved against base_path and normalized.
If the result is a relative path, it is forced to be relative to the
CMakeLists.txt.
"""
If rel_path is an absolute path it is returned unchanged.
Otherwise it is resolved against base_path and normalized.
If the result is a relative path, it is forced to be relative to the
CMakeLists.txt.
"""
if os.path.isabs(rel_path):
return rel_path
if any(rel_path.startswith(var) for var in FULL_PATH_VARS):
@ -114,10 +113,10 @@ def NormjoinPathForceCMakeSource(base_path, rel_path):
def NormjoinPath(base_path, rel_path):
"""Resolves rel_path against base_path and returns the result.
TODO: what is this really used for?
If rel_path begins with '$' it is returned unchanged.
Otherwise it is resolved against base_path if relative, then normalized.
"""
TODO: what is this really used for?
If rel_path begins with '$' it is returned unchanged.
Otherwise it is resolved against base_path if relative, then normalized.
"""
if rel_path.startswith("$") and not rel_path.startswith("${configuration}"):
return rel_path
return os.path.normpath(os.path.join(base_path, rel_path))
@ -126,19 +125,19 @@ def NormjoinPath(base_path, rel_path):
def CMakeStringEscape(a):
"""Escapes the string 'a' for use inside a CMake string.
This means escaping
'\' otherwise it may be seen as modifying the next character
'"' otherwise it will end the string
';' otherwise the string becomes a list
This means escaping
'\' otherwise it may be seen as modifying the next character
'"' otherwise it will end the string
';' otherwise the string becomes a list
The following do not need to be escaped
'#' when the lexer is in string state, this does not start a comment
The following do not need to be escaped
'#' when the lexer is in string state, this does not start a comment
The following are yet unknown
'$' generator variables (like ${obj}) must not be escaped,
but text $ should be escaped
what is wanted is to know which $ come from generator variables
"""
The following are yet unknown
'$' generator variables (like ${obj}) must not be escaped,
but text $ should be escaped
what is wanted is to know which $ come from generator variables
"""
return a.replace("\\", "\\\\").replace(";", "\\;").replace('"', '\\"')
@ -237,25 +236,25 @@ cmake_target_type_from_gyp_target_type = {
def StringToCMakeTargetName(a):
"""Converts the given string 'a' to a valid CMake target name.
All invalid characters are replaced by '_'.
Invalid for cmake: ' ', '/', '(', ')', '"'
Invalid for make: ':'
Invalid for unknown reasons but cause failures: '.'
"""
All invalid characters are replaced by '_'.
Invalid for cmake: ' ', '/', '(', ')', '"'
Invalid for make: ':'
Invalid for unknown reasons but cause failures: '.'
"""
return a.translate(_maketrans(' /():."', "_______"))
def WriteActions(target_name, actions, extra_sources, extra_deps, path_to_gyp, output):
"""Write CMake for the 'actions' in the target.
Args:
target_name: the name of the CMake target being generated.
actions: the Gyp 'actions' dict for this target.
extra_sources: [(<cmake_src>, <src>)] to append with generated source files.
extra_deps: [<cmake_target>] to append with generated targets.
path_to_gyp: relative path from CMakeLists.txt being generated to
the Gyp file in which the target being generated is defined.
"""
Args:
target_name: the name of the CMake target being generated.
actions: the Gyp 'actions' dict for this target.
extra_sources: [(<cmake_src>, <src>)] to append with generated source files.
extra_deps: [<cmake_target>] to append with generated targets.
path_to_gyp: relative path from CMakeLists.txt being generated to
the Gyp file in which the target being generated is defined.
"""
for action in actions:
action_name = StringToCMakeTargetName(action["action_name"])
action_target_name = f"{target_name}__{action_name}"
@ -337,14 +336,14 @@ def NormjoinRulePathForceCMakeSource(base_path, rel_path, rule_source):
def WriteRules(target_name, rules, extra_sources, extra_deps, path_to_gyp, output):
"""Write CMake for the 'rules' in the target.
Args:
target_name: the name of the CMake target being generated.
actions: the Gyp 'actions' dict for this target.
extra_sources: [(<cmake_src>, <src>)] to append with generated source files.
extra_deps: [<cmake_target>] to append with generated targets.
path_to_gyp: relative path from CMakeLists.txt being generated to
the Gyp file in which the target being generated is defined.
"""
Args:
target_name: the name of the CMake target being generated.
actions: the Gyp 'actions' dict for this target.
extra_sources: [(<cmake_src>, <src>)] to append with generated source files.
extra_deps: [<cmake_target>] to append with generated targets.
path_to_gyp: relative path from CMakeLists.txt being generated to
the Gyp file in which the target being generated is defined.
"""
for rule in rules:
rule_name = StringToCMakeTargetName(target_name + "__" + rule["rule_name"])
@ -455,13 +454,13 @@ def WriteRules(target_name, rules, extra_sources, extra_deps, path_to_gyp, outpu
def WriteCopies(target_name, copies, extra_deps, path_to_gyp, output):
"""Write CMake for the 'copies' in the target.
Args:
target_name: the name of the CMake target being generated.
actions: the Gyp 'actions' dict for this target.
extra_deps: [<cmake_target>] to append with generated targets.
path_to_gyp: relative path from CMakeLists.txt being generated to
the Gyp file in which the target being generated is defined.
"""
Args:
target_name: the name of the CMake target being generated.
actions: the Gyp 'actions' dict for this target.
extra_deps: [<cmake_target>] to append with generated targets.
path_to_gyp: relative path from CMakeLists.txt being generated to
the Gyp file in which the target being generated is defined.
"""
copy_name = target_name + "__copies"
# CMake gets upset with custom targets with OUTPUT which specify no output.
@ -585,23 +584,23 @@ def CreateCMakeTargetFullName(qualified_target):
class CMakeNamer:
"""Converts Gyp target names into CMake target names.
CMake requires that target names be globally unique. One way to ensure
this is to fully qualify the names of the targets. Unfortunately, this
ends up with all targets looking like "chrome_chrome_gyp_chrome" instead
of just "chrome". If this generator were only interested in building, it
would be possible to fully qualify all target names, then create
unqualified target names which depend on all qualified targets which
should have had that name. This is more or less what the 'make' generator
does with aliases. However, one goal of this generator is to create CMake
files for use with IDEs, and fully qualified names are not as user
friendly.
CMake requires that target names be globally unique. One way to ensure
this is to fully qualify the names of the targets. Unfortunately, this
ends up with all targets looking like "chrome_chrome_gyp_chrome" instead
of just "chrome". If this generator were only interested in building, it
would be possible to fully qualify all target names, then create
unqualified target names which depend on all qualified targets which
should have had that name. This is more or less what the 'make' generator
does with aliases. However, one goal of this generator is to create CMake
files for use with IDEs, and fully qualified names are not as user
friendly.
Since target name collision is rare, we do the above only when required.
Since target name collision is rare, we do the above only when required.
Toolset variants are always qualified from the base, as this is required for
building. However, it also makes sense for an IDE, as it is possible for
defines to be different.
"""
Toolset variants are always qualified from the base, as this is required for
building. However, it also makes sense for an IDE, as it is possible for
defines to be different.
"""
def __init__(self, target_list):
self.cmake_target_base_names_conflicting = set()

View File

@ -56,7 +56,7 @@ def CalculateVariables(default_variables, params):
def CalculateGeneratorInputInfo(params):
"""Calculate the generator specific info that gets fed to input (called by
gyp)."""
gyp)."""
generator_flags = params.get("generator_flags", {})
if generator_flags.get("adjust_static_libraries", False):
global generator_wants_static_library_dependencies_adjusted

View File

@ -69,7 +69,7 @@ def CalculateVariables(default_variables, params):
def CalculateGeneratorInputInfo(params):
"""Calculate the generator specific info that gets fed to input (called by
gyp)."""
gyp)."""
generator_flags = params.get("generator_flags", {})
if generator_flags.get("adjust_static_libraries", False):
global generator_wants_static_library_dependencies_adjusted
@ -86,10 +86,10 @@ def GetAllIncludeDirectories(
):
"""Calculate the set of include directories to be used.
Returns:
A list including all the include_dir's specified for every target followed
by any include directories that were added as cflag compiler options.
"""
Returns:
A list including all the include_dir's specified for every target followed
by any include directories that were added as cflag compiler options.
"""
gyp_includes_set = set()
compiler_includes_list = []
@ -178,11 +178,11 @@ def GetAllIncludeDirectories(
def GetCompilerPath(target_list, data, options):
"""Determine a command that can be used to invoke the compiler.
Returns:
If this is a gyp project that has explicit make settings, try to determine
the compiler from that. Otherwise, see if a compiler was specified via the
CC_target environment variable.
"""
Returns:
If this is a gyp project that has explicit make settings, try to determine
the compiler from that. Otherwise, see if a compiler was specified via the
CC_target environment variable.
"""
# First, see if the compiler is configured in make's settings.
build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
make_global_settings_dict = data[build_file].get("make_global_settings", {})
@ -202,10 +202,10 @@ def GetCompilerPath(target_list, data, options):
def GetAllDefines(target_list, target_dicts, data, config_name, params, compiler_path):
"""Calculate the defines for a project.
Returns:
A dict that includes explicit defines declared in gyp files along with all
of the default defines that the compiler uses.
"""
Returns:
A dict that includes explicit defines declared in gyp files along with all
of the default defines that the compiler uses.
"""
# Get defines declared in the gyp files.
all_defines = {}
@ -373,8 +373,8 @@ def GenerateClasspathFile(
target_list, target_dicts, toplevel_dir, toplevel_build, out_name
):
"""Generates a classpath file suitable for symbol navigation and code
completion of Java code (such as in Android projects) by finding all
.java and .jar files used as action inputs."""
completion of Java code (such as in Android projects) by finding all
.java and .jar files used as action inputs."""
gyp.common.EnsureDirExists(out_name)
result = ET.Element("classpath")

View File

@ -30,7 +30,6 @@ The specific formatting of the output generated by this module is subject
to change.
"""
import pprint
import gyp.common

View File

@ -13,7 +13,6 @@ by the input module.
The expected usage is "gyp -f gypsh -D OS=desired_os".
"""
import code
import sys

View File

@ -218,7 +218,7 @@ cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o
quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
""" % {'python': sys.executable} # noqa: E501
""" % {"python": sys.executable} # noqa: E501
LINK_COMMANDS_ANDROID = """\
quiet_cmd_alink = AR($(TOOLSET)) $@
@ -443,21 +443,27 @@ DEPFLAGS = %(makedep_args)s -MF $(depfile).raw
define fixup_dep
# The depfile may not exist if the input file didn't have any #includes.
touch $(depfile).raw
# Fixup path as in (1).""" +
(r"""
# Fixup path as in (1)."""
+ (
r"""
sed -e "s|^$(notdir $@)|$@|" -re 's/\\\\([^$$])/\/\1/g' $(depfile).raw >> $(depfile)"""
if sys.platform == 'win32' else r"""
sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)""") +
r"""
if sys.platform == "win32"
else r"""
sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)"""
)
+ r"""
# Add extra rules as in (2).
# We remove slashes and replace spaces with new lines;
# remove blank lines;
# delete the first line and append a colon to the remaining lines.""" +
("""
# delete the first line and append a colon to the remaining lines."""
+ (
"""
sed -e 's/\\\\\\\\$$//' -e 's/\\\\\\\\/\\//g' -e 'y| |\\n|' $(depfile).raw |\\"""
if sys.platform == 'win32' else """
sed -e 's|\\\\||' -e 'y| |\\n|' $(depfile).raw |\\""") +
r"""
if sys.platform == "win32"
else """
sed -e 's|\\\\||' -e 'y| |\\n|' $(depfile).raw |\\"""
)
+ r"""
grep -v '^$$' |\
sed -e 1d -e 's|$$|:|' \
>> $(depfile)
@ -616,7 +622,7 @@ cmd_mac_package_framework = %(python)s gyp-mac-tool package-framework "$@" $(4)
quiet_cmd_infoplist = INFOPLIST $@
cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@"
""" % {'python': sys.executable} # noqa: E501
""" % {"python": sys.executable} # noqa: E501
def WriteRootHeaderSuffixRules(writer):
@ -733,11 +739,13 @@ def QuoteIfNecessary(string):
string = '"' + string.replace('"', '\\"') + '"'
return string
def replace_sep(string):
if sys.platform == 'win32':
string = string.replace('\\\\', '/').replace('\\', '/')
if sys.platform == "win32":
string = string.replace("\\\\", "/").replace("\\", "/")
return string
def StringToMakefileVariable(string):
"""Convert a string to a value that is acceptable as a make variable name."""
return re.sub("[^a-zA-Z0-9_]", "_", string)
@ -1439,9 +1447,7 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
for obj in objs:
assert " " not in obj, "Spaces in object filenames not supported (%s)" % obj
self.WriteLn(
"# Add to the list of files we specially track dependencies for."
)
self.WriteLn("# Add to the list of files we specially track dependencies for.")
self.WriteLn("all_deps += $(OBJS)")
self.WriteLn()
@ -1498,7 +1504,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
"$(OBJS): GYP_OBJCFLAGS := "
"$(DEFS_$(BUILDTYPE)) "
"$(INCS_$(BUILDTYPE)) "
"%s " % precompiled_header.GetInclude("m")
"%s "
% precompiled_header.GetInclude("m")
+ "$(CFLAGS_$(BUILDTYPE)) "
"$(CFLAGS_C_$(BUILDTYPE)) "
"$(CFLAGS_OBJC_$(BUILDTYPE))"
@ -1507,7 +1514,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
"$(OBJS): GYP_OBJCXXFLAGS := "
"$(DEFS_$(BUILDTYPE)) "
"$(INCS_$(BUILDTYPE)) "
"%s " % precompiled_header.GetInclude("mm")
"%s "
% precompiled_header.GetInclude("mm")
+ "$(CFLAGS_$(BUILDTYPE)) "
"$(CFLAGS_CC_$(BUILDTYPE)) "
"$(CFLAGS_OBJCC_$(BUILDTYPE))"
@ -2383,9 +2391,13 @@ def WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)
"deps": replace_sep(
" ".join(sorted(SourceifyAndQuoteSpaces(bf) for bf in build_files))
),
"cmd": replace_sep(gyp.common.EncodePOSIXShellList(
[gyp_binary, "-fmake"] + gyp.RegenerateFlags(options) + build_files_args
)),
"cmd": replace_sep(
gyp.common.EncodePOSIXShellList(
[gyp_binary, "-fmake"]
+ gyp.RegenerateFlags(options)
+ build_files_args
)
),
}
)
@ -2458,8 +2470,8 @@ def GenerateOutput(target_list, target_dicts, data, params):
# wasm-ld doesn't support --start-group/--end-group
link_commands = LINK_COMMANDS_LINUX
if flavor in ["wasi", "wasm"]:
link_commands = link_commands.replace(' -Wl,--start-group', '').replace(
' -Wl,--end-group', ''
link_commands = link_commands.replace(" -Wl,--start-group", "").replace(
" -Wl,--end-group", ""
)
CC_target = replace_sep(GetEnvironFallback(("CC_target", "CC"), "$(CC)"))

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Unit tests for the msvs.py file. """
"""Unit tests for the msvs.py file."""
import unittest
from io import StringIO

View File

@ -1303,7 +1303,7 @@ class NinjaWriter:
ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
def WriteLink(self, spec, config_name, config, link_deps, compile_deps):
"""Write out a link step. Fills out target.binary. """
"""Write out a link step. Fills out target.binary."""
if self.flavor != "mac" or len(self.archs) == 1:
return self.WriteLinkForArch(
self.ninja, spec, config_name, config, link_deps, compile_deps
@ -1347,7 +1347,7 @@ class NinjaWriter:
def WriteLinkForArch(
self, ninja_file, spec, config_name, config, link_deps, compile_deps, arch=None
):
"""Write out a link step. Fills out target.binary. """
"""Write out a link step. Fills out target.binary."""
command = {
"executable": "link",
"loadable_module": "solink_module",
@ -1755,11 +1755,9 @@ class NinjaWriter:
+ " && ".join([ninja_syntax.escape(command) for command in postbuilds])
)
command_string = (
commands
+ "); G=$$?; "
commands + "); G=$$?; "
# Remove the final output if any postbuild failed.
"((exit $$G) || rm -rf %s) " % output
+ "&& exit $$G)"
"((exit $$G) || rm -rf %s) " % output + "&& exit $$G)"
)
if is_command_start:
return "(" + command_string + " && "
@ -1948,7 +1946,8 @@ class NinjaWriter:
)
else:
rspfile_content = gyp.msvs_emulation.EncodeRspFileList(
args, win_shell_flags.quote)
args, win_shell_flags.quote
)
command = (
"%s gyp-win-tool action-wrapper $arch " % sys.executable
+ rspfile
@ -2085,6 +2084,7 @@ def GetDefaultConcurrentLinks():
return pool_size
if sys.platform in ("win32", "cygwin"):
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", ctypes.c_ulong),
@ -2104,8 +2104,8 @@ def GetDefaultConcurrentLinks():
# VS 2015 uses 20% more working set than VS 2013 and can consume all RAM
# on a 64 GiB machine.
mem_limit = max(1, stat.ullTotalPhys // (5 * (2 ** 30))) # total / 5GiB
hard_cap = max(1, int(os.environ.get("GYP_LINK_CONCURRENCY_MAX") or 2 ** 32))
mem_limit = max(1, stat.ullTotalPhys // (5 * (2**30))) # total / 5GiB
hard_cap = max(1, int(os.environ.get("GYP_LINK_CONCURRENCY_MAX") or 2**32))
return min(mem_limit, hard_cap)
elif sys.platform.startswith("linux"):
if os.path.exists("/proc/meminfo"):
@ -2116,14 +2116,14 @@ def GetDefaultConcurrentLinks():
if not match:
continue
# Allow 8Gb per link on Linux because Gold is quite memory hungry
return max(1, int(match.group(1)) // (8 * (2 ** 20)))
return max(1, int(match.group(1)) // (8 * (2**20)))
return 1
elif sys.platform == "darwin":
try:
avail_bytes = int(subprocess.check_output(["sysctl", "-n", "hw.memsize"]))
# A static library debug build of Chromium's unit_tests takes ~2.7GB, so
# 4GB per ld process allows for some more bloat.
return max(1, avail_bytes // (4 * (2 ** 30))) # total / 4GB
return max(1, avail_bytes // (4 * (2**30))) # total / 4GB
except subprocess.CalledProcessError:
return 1
else:
@ -2411,8 +2411,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, config_name
"cc_s",
description="CC $out",
command=(
"$cc $defines $includes $cflags $cflags_c "
"$cflags_pch_c -c $in -o $out"
"$cc $defines $includes $cflags $cflags_c $cflags_pch_c -c $in -o $out"
),
)
master_ninja.rule(
@ -2523,8 +2522,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, config_name
"solink",
description="SOLINK $lib",
restat=True,
command=mtime_preserving_solink_base
% {"suffix": "@$link_file_list"},
command=mtime_preserving_solink_base % {"suffix": "@$link_file_list"},
rspfile="$link_file_list",
rspfile_content=(
"-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive $libs"
@ -2709,7 +2707,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, config_name
command="$env %(python)s gyp-mac-tool compile-ios-framework-header-map "
"$out $framework $in && $env %(python)s gyp-mac-tool "
"copy-ios-framework-headers $framework $copy_headers"
% {'python': sys.executable},
% {"python": sys.executable},
)
master_ninja.rule(
"mac_tool",

View File

@ -4,7 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Unit tests for the ninja.py file. """
"""Unit tests for the ninja.py file."""
import sys
import unittest

View File

@ -564,12 +564,12 @@ _xcode_variable_re = re.compile(r"(\$\((.*?)\))")
def ExpandXcodeVariables(string, expansions):
"""Expands Xcode-style $(VARIABLES) in string per the expansions dict.
In some rare cases, it is appropriate to expand Xcode variables when a
project file is generated. For any substring $(VAR) in string, if VAR is a
key in the expansions dict, $(VAR) will be replaced with expansions[VAR].
Any $(VAR) substring in string for which VAR is not a key in the expansions
dict will remain in the returned string.
"""
In some rare cases, it is appropriate to expand Xcode variables when a
project file is generated. For any substring $(VAR) in string, if VAR is a
key in the expansions dict, $(VAR) will be replaced with expansions[VAR].
Any $(VAR) substring in string for which VAR is not a key in the expansions
dict will remain in the returned string.
"""
matches = _xcode_variable_re.findall(string)
if matches is None:
@ -592,9 +592,9 @@ _xcode_define_re = re.compile(r"([\\\"\' ])")
def EscapeXcodeDefine(s):
"""We must escape the defines that we give to XCode so that it knows not to
split on spaces and to respect backslash and quote literals. However, we
must not quote the define, or Xcode will incorrectly interpret variables
especially $(inherited)."""
split on spaces and to respect backslash and quote literals. However, we
must not quote the define, or Xcode will incorrectly interpret variables
especially $(inherited)."""
return re.sub(_xcode_define_re, r"\\\1", s)
@ -679,9 +679,9 @@ def GenerateOutput(target_list, target_dicts, data, params):
project_attributes["BuildIndependentTargetsInParallel"] = "YES"
if upgrade_check_project_version:
project_attributes["LastUpgradeCheck"] = upgrade_check_project_version
project_attributes[
"LastTestingUpgradeCheck"
] = upgrade_check_project_version
project_attributes["LastTestingUpgradeCheck"] = (
upgrade_check_project_version
)
project_attributes["LastSwiftUpdateCheck"] = upgrade_check_project_version
pbxp.SetProperty("attributes", project_attributes)
@ -734,8 +734,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
"loadable_module+xcuitest": "com.apple.product-type.bundle.ui-testing",
"shared_library+bundle": "com.apple.product-type.framework",
"executable+extension+bundle": "com.apple.product-type.app-extension",
"executable+watch+extension+bundle":
"com.apple.product-type.watchkit-extension",
"executable+watch+extension+bundle": "com.apple.product-type.watchkit-extension", # noqa: E501
"executable+watch+bundle": "com.apple.product-type.application.watchapp",
"mac_kernel_extension+bundle": "com.apple.product-type.kernel-extension",
}
@ -780,8 +779,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
type_bundle_key += "+watch+extension+bundle"
elif is_watch_app:
assert is_bundle, (
"ios_watch_app flag requires mac_bundle "
"(target %s)" % target_name
"ios_watch_app flag requires mac_bundle (target %s)" % target_name
)
type_bundle_key += "+watch+bundle"
elif is_bundle:
@ -1103,7 +1101,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
eol = " \\"
makefile.write(f" {concrete_output}{eol}\n")
for (rule_source, concrete_outputs, message, action) in zip(
for rule_source, concrete_outputs, message, action in zip(
rule["rule_sources"],
concrete_outputs_by_rule_source,
messages,

View File

@ -4,7 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Unit tests for the xcode.py file. """
"""Unit tests for the xcode.py file."""
import sys
import unittest

View File

@ -139,21 +139,21 @@ generator_filelist_paths = None
def GetIncludedBuildFiles(build_file_path, aux_data, included=None):
"""Return a list of all build files included into build_file_path.
The returned list will contain build_file_path as well as all other files
that it included, either directly or indirectly. Note that the list may
contain files that were included into a conditional section that evaluated
to false and was not merged into build_file_path's dict.
The returned list will contain build_file_path as well as all other files
that it included, either directly or indirectly. Note that the list may
contain files that were included into a conditional section that evaluated
to false and was not merged into build_file_path's dict.
aux_data is a dict containing a key for each build file or included build
file. Those keys provide access to dicts whose "included" keys contain
lists of all other files included by the build file.
aux_data is a dict containing a key for each build file or included build
file. Those keys provide access to dicts whose "included" keys contain
lists of all other files included by the build file.
included should be left at its default None value by external callers. It
is used for recursion.
included should be left at its default None value by external callers. It
is used for recursion.
The returned list will not contain any duplicate entries. Each build file
in the list will be relative to the current directory.
"""
The returned list will not contain any duplicate entries. Each build file
in the list will be relative to the current directory.
"""
if included is None:
included = []
@ -171,10 +171,10 @@ def GetIncludedBuildFiles(build_file_path, aux_data, included=None):
def CheckedEval(file_contents):
"""Return the eval of a gyp file.
The gyp file is restricted to dictionaries and lists only, and
repeated keys are not allowed.
Note that this is slower than eval() is.
"""
The gyp file is restricted to dictionaries and lists only, and
repeated keys are not allowed.
Note that this is slower than eval() is.
"""
syntax_tree = ast.parse(file_contents)
assert isinstance(syntax_tree, ast.Module)
@ -508,9 +508,9 @@ def CallLoadTargetBuildFile(
):
"""Wrapper around LoadTargetBuildFile for parallel processing.
This wrapper is used when LoadTargetBuildFile is executed in
a worker process.
"""
This wrapper is used when LoadTargetBuildFile is executed in
a worker process.
"""
try:
signal.signal(signal.SIGINT, signal.SIG_IGN)
@ -559,10 +559,10 @@ class ParallelProcessingError(Exception):
class ParallelState:
"""Class to keep track of state when processing input files in parallel.
If build files are loaded in parallel, use this to keep track of
state during farming out and processing parallel jobs. It's stored
in a global so that the callback function can have access to it.
"""
If build files are loaded in parallel, use this to keep track of
state during farming out and processing parallel jobs. It's stored
in a global so that the callback function can have access to it.
"""
def __init__(self):
# The multiprocessing pool.
@ -584,8 +584,7 @@ class ParallelState:
self.error = False
def LoadTargetBuildFileCallback(self, result):
"""Handle the results of running LoadTargetBuildFile in another process.
"""
"""Handle the results of running LoadTargetBuildFile in another process."""
self.condition.acquire()
if not result:
self.error = True
@ -692,8 +691,8 @@ def FindEnclosingBracketGroup(input_str):
def IsStrCanonicalInt(string):
"""Returns True if |string| is in its canonical integer form.
The canonical form is such that str(int(string)) == string.
"""
The canonical form is such that str(int(string)) == string.
"""
if isinstance(string, str):
# This function is called a lot so for maximum performance, avoid
# involving regexps which would otherwise make the code much
@ -870,8 +869,9 @@ def ExpandVariables(input, phase, variables, build_file):
# This works around actions/rules which have more inputs than will
# fit on the command line.
if file_list:
contents_list = (contents if isinstance(contents, list)
else contents.split(" "))
contents_list = (
contents if isinstance(contents, list) else contents.split(" ")
)
replacement = contents_list[0]
if os.path.isabs(replacement):
raise GypError('| cannot handle absolute paths, got "%s"' % replacement)
@ -934,7 +934,6 @@ def ExpandVariables(input, phase, variables, build_file):
os.chdir(build_file_dir)
sys.path.append(os.getcwd())
try:
parsed_contents = shlex.split(contents)
try:
py_module = __import__(parsed_contents[0])
@ -965,7 +964,7 @@ def ExpandVariables(input, phase, variables, build_file):
stdout=subprocess.PIPE,
shell=use_shell,
cwd=build_file_dir,
check=False
check=False,
)
except Exception as e:
raise GypError(
@ -1003,9 +1002,7 @@ def ExpandVariables(input, phase, variables, build_file):
# ],
replacement = []
else:
raise GypError(
"Undefined variable " + contents + " in " + build_file
)
raise GypError("Undefined variable " + contents + " in " + build_file)
else:
replacement = variables[contents]
@ -1114,7 +1111,7 @@ cached_conditions_asts = {}
def EvalCondition(condition, conditions_key, phase, variables, build_file):
"""Returns the dict that should be used or None if the result was
that nothing should be used."""
that nothing should be used."""
if not isinstance(condition, list):
raise GypError(conditions_key + " must be a list")
if len(condition) < 2:
@ -1159,7 +1156,7 @@ def EvalCondition(condition, conditions_key, phase, variables, build_file):
def EvalSingleCondition(cond_expr, true_dict, false_dict, phase, variables, build_file):
"""Returns true_dict if cond_expr evaluates to true, and false_dict
otherwise."""
otherwise."""
# Do expansions on the condition itself. Since the condition can naturally
# contain variable references without needing to resort to GYP expansion
# syntax, this is of dubious value for variables, but someone might want to
@ -1289,10 +1286,10 @@ def ProcessVariablesAndConditionsInDict(
):
"""Handle all variable and command expansion and conditional evaluation.
This function is the public entry point for all variable expansions and
conditional evaluations. The variables_in dictionary will not be modified
by this function.
"""
This function is the public entry point for all variable expansions and
conditional evaluations. The variables_in dictionary will not be modified
by this function.
"""
# Make a copy of the variables_in dict that can be modified during the
# loading of automatics and the loading of the variables dict.
@ -1441,15 +1438,15 @@ def ProcessVariablesAndConditionsInList(the_list, phase, variables, build_file):
def BuildTargetsDict(data):
"""Builds a dict mapping fully-qualified target names to their target dicts.
|data| is a dict mapping loaded build files by pathname relative to the
current directory. Values in |data| are build file contents. For each
|data| value with a "targets" key, the value of the "targets" key is taken
as a list containing target dicts. Each target's fully-qualified name is
constructed from the pathname of the build file (|data| key) and its
"target_name" property. These fully-qualified names are used as the keys
in the returned dict. These keys provide access to the target dicts,
the dicts in the "targets" lists.
"""
|data| is a dict mapping loaded build files by pathname relative to the
current directory. Values in |data| are build file contents. For each
|data| value with a "targets" key, the value of the "targets" key is taken
as a list containing target dicts. Each target's fully-qualified name is
constructed from the pathname of the build file (|data| key) and its
"target_name" property. These fully-qualified names are used as the keys
in the returned dict. These keys provide access to the target dicts,
the dicts in the "targets" lists.
"""
targets = {}
for build_file in data["target_build_files"]:
@ -1467,13 +1464,13 @@ def BuildTargetsDict(data):
def QualifyDependencies(targets):
"""Make dependency links fully-qualified relative to the current directory.
|targets| is a dict mapping fully-qualified target names to their target
dicts. For each target in this dict, keys known to contain dependency
links are examined, and any dependencies referenced will be rewritten
so that they are fully-qualified and relative to the current directory.
All rewritten dependencies are suitable for use as keys to |targets| or a
similar dict.
"""
|targets| is a dict mapping fully-qualified target names to their target
dicts. For each target in this dict, keys known to contain dependency
links are examined, and any dependencies referenced will be rewritten
so that they are fully-qualified and relative to the current directory.
All rewritten dependencies are suitable for use as keys to |targets| or a
similar dict.
"""
all_dependency_sections = [
dep + op for dep in dependency_sections for op in ("", "!", "/")
@ -1516,18 +1513,18 @@ def QualifyDependencies(targets):
def ExpandWildcardDependencies(targets, data):
"""Expands dependencies specified as build_file:*.
For each target in |targets|, examines sections containing links to other
targets. If any such section contains a link of the form build_file:*, it
is taken as a wildcard link, and is expanded to list each target in
build_file. The |data| dict provides access to build file dicts.
For each target in |targets|, examines sections containing links to other
targets. If any such section contains a link of the form build_file:*, it
is taken as a wildcard link, and is expanded to list each target in
build_file. The |data| dict provides access to build file dicts.
Any target that does not wish to be included by wildcard can provide an
optional "suppress_wildcard" key in its target dict. When present and
true, a wildcard dependency link will not include such targets.
Any target that does not wish to be included by wildcard can provide an
optional "suppress_wildcard" key in its target dict. When present and
true, a wildcard dependency link will not include such targets.
All dependency names, including the keys to |targets| and the values in each
dependency list, must be qualified when this function is called.
"""
All dependency names, including the keys to |targets| and the values in each
dependency list, must be qualified when this function is called.
"""
for target, target_dict in targets.items():
target_build_file = gyp.common.BuildFile(target)
@ -1573,14 +1570,10 @@ def ExpandWildcardDependencies(targets, data):
if int(dependency_target_dict.get("suppress_wildcard", False)):
continue
dependency_target_name = dependency_target_dict["target_name"]
if (
dependency_target not in {"*", dependency_target_name}
):
if dependency_target not in {"*", dependency_target_name}:
continue
dependency_target_toolset = dependency_target_dict["toolset"]
if (
dependency_toolset not in {"*", dependency_target_toolset}
):
if dependency_toolset not in {"*", dependency_target_toolset}:
continue
dependency = gyp.common.QualifiedTarget(
dependency_build_file,
@ -1601,7 +1594,7 @@ def Unify(items):
def RemoveDuplicateDependencies(targets):
"""Makes sure every dependency appears only once in all targets's dependency
lists."""
lists."""
for target_name, target_dict in targets.items():
for dependency_key in dependency_sections:
dependencies = target_dict.get(dependency_key, [])
@ -1617,25 +1610,21 @@ def Filter(items, item):
def RemoveSelfDependencies(targets):
"""Remove self dependencies from targets that have the prune_self_dependency
variable set."""
variable set."""
for target_name, target_dict in targets.items():
for dependency_key in dependency_sections:
dependencies = target_dict.get(dependency_key, [])
if dependencies:
for t in dependencies:
if t == target_name and (
targets[t]
.get("variables", {})
.get("prune_self_dependency", 0)
targets[t].get("variables", {}).get("prune_self_dependency", 0)
):
target_dict[dependency_key] = Filter(
dependencies, target_name
)
target_dict[dependency_key] = Filter(dependencies, target_name)
def RemoveLinkDependenciesFromNoneTargets(targets):
"""Remove dependencies having the 'link_dependency' attribute from the 'none'
targets."""
targets."""
for target_name, target_dict in targets.items():
for dependency_key in dependency_sections:
dependencies = target_dict.get(dependency_key, [])
@ -1651,11 +1640,11 @@ def RemoveLinkDependenciesFromNoneTargets(targets):
class DependencyGraphNode:
"""
Attributes:
ref: A reference to an object that this DependencyGraphNode represents.
dependencies: List of DependencyGraphNodes on which this one depends.
dependents: List of DependencyGraphNodes that depend on this one.
"""
Attributes:
ref: A reference to an object that this DependencyGraphNode represents.
dependencies: List of DependencyGraphNodes on which this one depends.
dependents: List of DependencyGraphNodes that depend on this one.
"""
class CircularException(GypError):
pass
@ -1721,8 +1710,8 @@ class DependencyGraphNode:
def FindCycles(self):
"""
Returns a list of cycles in the graph, where each cycle is its own list.
"""
Returns a list of cycles in the graph, where each cycle is its own list.
"""
results = []
visited = set()
@ -1753,21 +1742,21 @@ class DependencyGraphNode:
def _AddImportedDependencies(self, targets, dependencies=None):
"""Given a list of direct dependencies, adds indirect dependencies that
other dependencies have declared to export their settings.
other dependencies have declared to export their settings.
This method does not operate on self. Rather, it operates on the list
of dependencies in the |dependencies| argument. For each dependency in
that list, if any declares that it exports the settings of one of its
own dependencies, those dependencies whose settings are "passed through"
are added to the list. As new items are added to the list, they too will
be processed, so it is possible to import settings through multiple levels
of dependencies.
This method does not operate on self. Rather, it operates on the list
of dependencies in the |dependencies| argument. For each dependency in
that list, if any declares that it exports the settings of one of its
own dependencies, those dependencies whose settings are "passed through"
are added to the list. As new items are added to the list, they too will
be processed, so it is possible to import settings through multiple levels
of dependencies.
This method is not terribly useful on its own, it depends on being
"primed" with a list of direct dependencies such as one provided by
DirectDependencies. DirectAndImportedDependencies is intended to be the
public entry point.
"""
This method is not terribly useful on its own, it depends on being
"primed" with a list of direct dependencies such as one provided by
DirectDependencies. DirectAndImportedDependencies is intended to be the
public entry point.
"""
if dependencies is None:
dependencies = []
@ -1795,9 +1784,9 @@ class DependencyGraphNode:
def DirectAndImportedDependencies(self, targets, dependencies=None):
"""Returns a list of a target's direct dependencies and all indirect
dependencies that a dependency has advertised settings should be exported
through the dependency for.
"""
dependencies that a dependency has advertised settings should be exported
through the dependency for.
"""
dependencies = self.DirectDependencies(dependencies)
return self._AddImportedDependencies(targets, dependencies)
@ -1823,19 +1812,19 @@ class DependencyGraphNode:
self, targets, include_shared_libraries, dependencies=None, initial=True
):
"""Returns an OrderedSet of dependency targets that are linked
into this target.
into this target.
This function has a split personality, depending on the setting of
|initial|. Outside callers should always leave |initial| at its default
setting.
This function has a split personality, depending on the setting of
|initial|. Outside callers should always leave |initial| at its default
setting.
When adding a target to the list of dependencies, this function will
recurse into itself with |initial| set to False, to collect dependencies
that are linked into the linkable target for which the list is being built.
When adding a target to the list of dependencies, this function will
recurse into itself with |initial| set to False, to collect dependencies
that are linked into the linkable target for which the list is being built.
If |include_shared_libraries| is False, the resulting dependencies will not
include shared_library targets that are linked into this target.
"""
If |include_shared_libraries| is False, the resulting dependencies will not
include shared_library targets that are linked into this target.
"""
if dependencies is None:
# Using a list to get ordered output and a set to do fast "is it
# already added" checks.
@ -1917,9 +1906,9 @@ class DependencyGraphNode:
def DependenciesForLinkSettings(self, targets):
"""
Returns a list of dependency targets whose link_settings should be merged
into this target.
"""
Returns a list of dependency targets whose link_settings should be merged
into this target.
"""
# TODO(sbaig) Currently, chrome depends on the bug that shared libraries'
# link_settings are propagated. So for now, we will allow it, unless the
@ -1932,8 +1921,8 @@ class DependencyGraphNode:
def DependenciesToLinkAgainst(self, targets):
"""
Returns a list of dependency targets that are linked into this target.
"""
Returns a list of dependency targets that are linked into this target.
"""
return self._LinkDependenciesInternal(targets, True)
@ -2446,7 +2435,7 @@ def SetUpConfigurations(target, target_dict):
merged_configurations = {}
configs = target_dict["configurations"]
for (configuration, old_configuration_dict) in configs.items():
for configuration, old_configuration_dict in configs.items():
# Skip abstract configurations (saves work only).
if old_configuration_dict.get("abstract"):
continue
@ -2454,7 +2443,7 @@ def SetUpConfigurations(target, target_dict):
# Get the inheritance relationship right by making a copy of the target
# dict.
new_configuration_dict = {}
for (key, target_val) in target_dict.items():
for key, target_val in target_dict.items():
key_ext = key[-1:]
key_base = key[:-1] if key_ext in key_suffixes else key
if key_base not in non_configuration_keys:
@ -2502,25 +2491,25 @@ def SetUpConfigurations(target, target_dict):
def ProcessListFiltersInDict(name, the_dict):
"""Process regular expression and exclusion-based filters on lists.
An exclusion list is in a dict key named with a trailing "!", like
"sources!". Every item in such a list is removed from the associated
main list, which in this example, would be "sources". Removed items are
placed into a "sources_excluded" list in the dict.
An exclusion list is in a dict key named with a trailing "!", like
"sources!". Every item in such a list is removed from the associated
main list, which in this example, would be "sources". Removed items are
placed into a "sources_excluded" list in the dict.
Regular expression (regex) filters are contained in dict keys named with a
trailing "/", such as "sources/" to operate on the "sources" list. Regex
filters in a dict take the form:
'sources/': [ ['exclude', '_(linux|mac|win)\\.cc$'],
['include', '_mac\\.cc$'] ],
The first filter says to exclude all files ending in _linux.cc, _mac.cc, and
_win.cc. The second filter then includes all files ending in _mac.cc that
are now or were once in the "sources" list. Items matching an "exclude"
filter are subject to the same processing as would occur if they were listed
by name in an exclusion list (ending in "!"). Items matching an "include"
filter are brought back into the main list if previously excluded by an
exclusion list or exclusion regex filter. Subsequent matching "exclude"
patterns can still cause items to be excluded after matching an "include".
"""
Regular expression (regex) filters are contained in dict keys named with a
trailing "/", such as "sources/" to operate on the "sources" list. Regex
filters in a dict take the form:
'sources/': [ ['exclude', '_(linux|mac|win)\\.cc$'],
['include', '_mac\\.cc$'] ],
The first filter says to exclude all files ending in _linux.cc, _mac.cc, and
_win.cc. The second filter then includes all files ending in _mac.cc that
are now or were once in the "sources" list. Items matching an "exclude"
filter are subject to the same processing as would occur if they were listed
by name in an exclusion list (ending in "!"). Items matching an "include"
filter are brought back into the main list if previously excluded by an
exclusion list or exclusion regex filter. Subsequent matching "exclude"
patterns can still cause items to be excluded after matching an "include".
"""
# Look through the dictionary for any lists whose keys end in "!" or "/".
# These are lists that will be treated as exclude lists and regular
@ -2682,12 +2671,12 @@ def ProcessListFiltersInList(name, the_list):
def ValidateTargetType(target, target_dict):
"""Ensures the 'type' field on the target is one of the known types.
Arguments:
target: string, name of target.
target_dict: dict, target spec.
Arguments:
target: string, name of target.
target_dict: dict, target spec.
Raises an exception on error.
"""
Raises an exception on error.
"""
VALID_TARGET_TYPES = (
"executable",
"loadable_module",
@ -2715,14 +2704,14 @@ def ValidateTargetType(target, target_dict):
def ValidateRulesInTarget(target, target_dict, extra_sources_for_rules):
"""Ensures that the rules sections in target_dict are valid and consistent,
and determines which sources they apply to.
and determines which sources they apply to.
Arguments:
target: string, name of target.
target_dict: dict, target spec containing "rules" and "sources" lists.
extra_sources_for_rules: a list of keys to scan for rule matches in
addition to 'sources'.
"""
Arguments:
target: string, name of target.
target_dict: dict, target spec containing "rules" and "sources" lists.
extra_sources_for_rules: a list of keys to scan for rule matches in
addition to 'sources'.
"""
# Dicts to map between values found in rules' 'rule_name' and 'extension'
# keys and the rule dicts themselves.
@ -2734,9 +2723,7 @@ def ValidateRulesInTarget(target, target_dict, extra_sources_for_rules):
# Make sure that there's no conflict among rule names and extensions.
rule_name = rule["rule_name"]
if rule_name in rule_names:
raise GypError(
f"rule {rule_name} exists in duplicate, target {target}"
)
raise GypError(f"rule {rule_name} exists in duplicate, target {target}")
rule_names[rule_name] = rule
rule_extension = rule["extension"]
@ -2835,8 +2822,7 @@ def ValidateActionsInTarget(target, target_dict, build_file):
def TurnIntIntoStrInDict(the_dict):
"""Given dict the_dict, recursively converts all integers into strings.
"""
"""Given dict the_dict, recursively converts all integers into strings."""
# Use items instead of iteritems because there's no need to try to look at
# reinserted keys and their associated values.
for k, v in the_dict.items():
@ -2854,8 +2840,7 @@ def TurnIntIntoStrInDict(the_dict):
def TurnIntIntoStrInList(the_list):
"""Given list the_list, recursively converts all integers into strings.
"""
"""Given list the_list, recursively converts all integers into strings."""
for index, item in enumerate(the_list):
if isinstance(item, int):
the_list[index] = str(item)
@ -2902,9 +2887,9 @@ def PruneUnwantedTargets(targets, flat_list, dependency_nodes, root_targets, dat
def VerifyNoCollidingTargets(targets):
"""Verify that no two targets in the same directory share the same name.
Arguments:
targets: A list of targets in the form 'path/to/file.gyp:target_name'.
"""
Arguments:
targets: A list of targets in the form 'path/to/file.gyp:target_name'.
"""
# Keep a dict going from 'subdirectory:target_name' to 'foo.gyp'.
used = {}
for target in targets:

View File

@ -8,7 +8,6 @@
These functions are executed via gyp-mac-tool when using the Makefile generator.
"""
import fcntl
import fnmatch
import glob
@ -31,7 +30,7 @@ def main(args):
class MacTool:
"""This class performs all the Mac tooling steps. The methods can either be
executed directly, or dispatched from an argument list."""
executed directly, or dispatched from an argument list."""
def Dispatch(self, args):
"""Dispatches a string command to a method."""
@ -47,7 +46,7 @@ class MacTool:
def ExecCopyBundleResource(self, source, dest, convert_to_binary):
"""Copies a resource file to the bundle/Resources directory, performing any
necessary compilation on each resource."""
necessary compilation on each resource."""
convert_to_binary = convert_to_binary == "True"
extension = os.path.splitext(source)[1].lower()
if os.path.isdir(source):
@ -155,15 +154,15 @@ class MacTool:
def _DetectInputEncoding(self, file_name):
"""Reads the first few bytes from file_name and tries to guess the text
encoding. Returns None as a guess if it can't detect it."""
encoding. Returns None as a guess if it can't detect it."""
with open(file_name, "rb") as fp:
try:
header = fp.read(3)
except Exception:
return None
if header.startswith((b"\xFE\xFF", b"\xFF\xFE")):
if header.startswith((b"\xfe\xff", b"\xff\xfe")):
return "UTF-16"
elif header.startswith(b"\xEF\xBB\xBF"):
elif header.startswith(b"\xef\xbb\xbf"):
return "UTF-8"
else:
return None
@ -254,7 +253,7 @@ class MacTool:
def ExecFilterLibtool(self, *cmd_list):
"""Calls libtool and filters out '/path/to/libtool: file: foo.o has no
symbols'."""
symbols'."""
libtool_re = re.compile(
r"^.*libtool: (?:for architecture: \S* )?file: .* has no symbols$"
)
@ -303,7 +302,7 @@ class MacTool:
def ExecPackageFramework(self, framework, version):
"""Takes a path to Something.framework and the Current version of that and
sets up all the symlinks."""
sets up all the symlinks."""
# Find the name of the binary based on the part before the ".framework".
binary = os.path.basename(framework).split(".")[0]
@ -332,7 +331,7 @@ class MacTool:
def _Relink(self, dest, link):
"""Creates a symlink to |dest| named |link|. If |link| already exists,
it is overwritten."""
it is overwritten."""
if os.path.lexists(link):
os.remove(link)
os.symlink(dest, link)
@ -357,14 +356,14 @@ class MacTool:
def ExecCompileXcassets(self, keys, *inputs):
"""Compiles multiple .xcassets files into a single .car file.
This invokes 'actool' to compile all the inputs .xcassets files. The
|keys| arguments is a json-encoded dictionary of extra arguments to
pass to 'actool' when the asset catalogs contains an application icon
or a launch image.
This invokes 'actool' to compile all the inputs .xcassets files. The
|keys| arguments is a json-encoded dictionary of extra arguments to
pass to 'actool' when the asset catalogs contains an application icon
or a launch image.
Note that 'actool' does not create the Assets.car file if the asset
catalogs does not contains imageset.
"""
Note that 'actool' does not create the Assets.car file if the asset
catalogs does not contains imageset.
"""
command_line = [
"xcrun",
"actool",
@ -437,13 +436,13 @@ class MacTool:
def ExecCodeSignBundle(self, key, entitlements, provisioning, path, preserve):
"""Code sign a bundle.
This function tries to code sign an iOS bundle, following the same
algorithm as Xcode:
1. pick the provisioning profile that best match the bundle identifier,
and copy it into the bundle as embedded.mobileprovision,
2. copy Entitlements.plist from user or SDK next to the bundle,
3. code sign the bundle.
"""
This function tries to code sign an iOS bundle, following the same
algorithm as Xcode:
1. pick the provisioning profile that best match the bundle identifier,
and copy it into the bundle as embedded.mobileprovision,
2. copy Entitlements.plist from user or SDK next to the bundle,
3. code sign the bundle.
"""
substitutions, overrides = self._InstallProvisioningProfile(
provisioning, self._GetCFBundleIdentifier()
)
@ -462,16 +461,16 @@ class MacTool:
def _InstallProvisioningProfile(self, profile, bundle_identifier):
"""Installs embedded.mobileprovision into the bundle.
Args:
profile: string, optional, short name of the .mobileprovision file
to use, if empty or the file is missing, the best file installed
will be used
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
Args:
profile: string, optional, short name of the .mobileprovision file
to use, if empty or the file is missing, the best file installed
will be used
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
Returns:
A tuple containing two dictionary: variables substitutions and values
to overrides when generating the entitlements file.
"""
Returns:
A tuple containing two dictionary: variables substitutions and values
to overrides when generating the entitlements file.
"""
source_path, provisioning_data, team_id = self._FindProvisioningProfile(
profile, bundle_identifier
)
@ -487,24 +486,24 @@ class MacTool:
def _FindProvisioningProfile(self, profile, bundle_identifier):
"""Finds the .mobileprovision file to use for signing the bundle.
Checks all the installed provisioning profiles (or if the user specified
the PROVISIONING_PROFILE variable, only consult it) and select the most
specific that correspond to the bundle identifier.
Checks all the installed provisioning profiles (or if the user specified
the PROVISIONING_PROFILE variable, only consult it) and select the most
specific that correspond to the bundle identifier.
Args:
profile: string, optional, short name of the .mobileprovision file
to use, if empty or the file is missing, the best file installed
will be used
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
Args:
profile: string, optional, short name of the .mobileprovision file
to use, if empty or the file is missing, the best file installed
will be used
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
Returns:
A tuple of the path to the selected provisioning profile, the data of
the embedded plist in the provisioning profile and the team identifier
to use for code signing.
Returns:
A tuple of the path to the selected provisioning profile, the data of
the embedded plist in the provisioning profile and the team identifier
to use for code signing.
Raises:
SystemExit: if no .mobileprovision can be used to sign the bundle.
"""
Raises:
SystemExit: if no .mobileprovision can be used to sign the bundle.
"""
profiles_dir = os.path.join(
os.environ["HOME"], "Library", "MobileDevice", "Provisioning Profiles"
)
@ -552,12 +551,12 @@ class MacTool:
def _LoadProvisioningProfile(self, profile_path):
"""Extracts the plist embedded in a provisioning profile.
Args:
profile_path: string, path to the .mobileprovision file
Args:
profile_path: string, path to the .mobileprovision file
Returns:
Content of the plist embedded in the provisioning profile as a dictionary.
"""
Returns:
Content of the plist embedded in the provisioning profile as a dictionary.
"""
with tempfile.NamedTemporaryFile() as temp:
subprocess.check_call(
["security", "cms", "-D", "-i", profile_path, "-o", temp.name]
@ -580,16 +579,16 @@ class MacTool:
def _LoadPlistMaybeBinary(self, plist_path):
"""Loads into a memory a plist possibly encoded in binary format.
This is a wrapper around plistlib.readPlist that tries to convert the
plist to the XML format if it can't be parsed (assuming that it is in
the binary format).
This is a wrapper around plistlib.readPlist that tries to convert the
plist to the XML format if it can't be parsed (assuming that it is in
the binary format).
Args:
plist_path: string, path to a plist file, in XML or binary format
Args:
plist_path: string, path to a plist file, in XML or binary format
Returns:
Content of the plist as a dictionary.
"""
Returns:
Content of the plist as a dictionary.
"""
try:
# First, try to read the file using plistlib that only supports XML,
# and if an exception is raised, convert a temporary copy to XML and
@ -605,13 +604,13 @@ class MacTool:
def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix):
"""Constructs a dictionary of variable substitutions for Entitlements.plist.
Args:
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
app_identifier_prefix: string, value for AppIdentifierPrefix
Args:
bundle_identifier: string, value of CFBundleIdentifier from Info.plist
app_identifier_prefix: string, value for AppIdentifierPrefix
Returns:
Dictionary of substitutions to apply when generating Entitlements.plist.
"""
Returns:
Dictionary of substitutions to apply when generating Entitlements.plist.
"""
return {
"CFBundleIdentifier": bundle_identifier,
"AppIdentifierPrefix": app_identifier_prefix,
@ -620,9 +619,9 @@ class MacTool:
def _GetCFBundleIdentifier(self):
"""Extracts CFBundleIdentifier value from Info.plist in the bundle.
Returns:
Value of CFBundleIdentifier in the Info.plist located in the bundle.
"""
Returns:
Value of CFBundleIdentifier in the Info.plist located in the bundle.
"""
info_plist_path = os.path.join(
os.environ["TARGET_BUILD_DIR"], os.environ["INFOPLIST_PATH"]
)
@ -632,19 +631,19 @@ class MacTool:
def _InstallEntitlements(self, entitlements, substitutions, overrides):
"""Generates and install the ${BundleName}.xcent entitlements file.
Expands variables "$(variable)" pattern in the source entitlements file,
add extra entitlements defined in the .mobileprovision file and the copy
the generated plist to "${BundlePath}.xcent".
Expands variables "$(variable)" pattern in the source entitlements file,
add extra entitlements defined in the .mobileprovision file and the copy
the generated plist to "${BundlePath}.xcent".
Args:
entitlements: string, optional, path to the Entitlements.plist template
to use, defaults to "${SDKROOT}/Entitlements.plist"
substitutions: dictionary, variable substitutions
overrides: dictionary, values to add to the entitlements
Args:
entitlements: string, optional, path to the Entitlements.plist template
to use, defaults to "${SDKROOT}/Entitlements.plist"
substitutions: dictionary, variable substitutions
overrides: dictionary, values to add to the entitlements
Returns:
Path to the generated entitlements file.
"""
Returns:
Path to the generated entitlements file.
"""
source_path = entitlements
target_path = os.path.join(
os.environ["BUILT_PRODUCTS_DIR"], os.environ["PRODUCT_NAME"] + ".xcent"
@ -664,15 +663,15 @@ class MacTool:
def _ExpandVariables(self, data, substitutions):
"""Expands variables "$(variable)" in data.
Args:
data: object, can be either string, list or dictionary
substitutions: dictionary, variable substitutions to perform
Args:
data: object, can be either string, list or dictionary
substitutions: dictionary, variable substitutions to perform
Returns:
Copy of data where each references to "$(variable)" has been replaced
by the corresponding value found in substitutions, or left intact if
the key was not found.
"""
Returns:
Copy of data where each references to "$(variable)" has been replaced
by the corresponding value found in substitutions, or left intact if
the key was not found.
"""
if isinstance(data, str):
for key, value in substitutions.items():
data = data.replace("$(%s)" % key, value)
@ -691,15 +690,15 @@ def NextGreaterPowerOf2(x):
def WriteHmap(output_name, filelist):
"""Generates a header map based on |filelist|.
Per Mark Mentovai:
A header map is structured essentially as a hash table, keyed by names used
in #includes, and providing pathnames to the actual files.
Per Mark Mentovai:
A header map is structured essentially as a hash table, keyed by names used
in #includes, and providing pathnames to the actual files.
The implementation below and the comment above comes from inspecting:
http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/include_server/headermap.py?txt
while also looking at the implementation in clang in:
https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp
"""
The implementation below and the comment above comes from inspecting:
http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/include_server/headermap.py?txt
while also looking at the implementation in clang in:
https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp
"""
magic = 1751998832
version = 1
_reserved = 0

View File

@ -74,8 +74,7 @@ def EncodeRspFileList(args, quote_cmd):
program = call + " " + os.path.normpath(program)
else:
program = os.path.normpath(args[0])
return (program + " "
+ " ".join(QuoteForRspFile(arg, quote_cmd) for arg in args[1:]))
return program + " " + " ".join(QuoteForRspFile(arg, quote_cmd) for arg in args[1:])
def _GenericRetrieve(root, default, path):
@ -934,14 +933,17 @@ class MsvsSettings:
includes whether it should run under cygwin (msvs_cygwin_shell), and
whether the commands should be quoted (msvs_quote_cmd)."""
# If the variable is unset, or set to 1 we use cygwin
cygwin = int(rule.get("msvs_cygwin_shell",
self.spec.get("msvs_cygwin_shell", 1))) != 0
cygwin = (
int(rule.get("msvs_cygwin_shell", self.spec.get("msvs_cygwin_shell", 1)))
!= 0
)
# Default to quoting. There's only a few special instances where the
# target command uses non-standard command line parsing and handle quotes
# and quote escaping differently.
quote_cmd = int(rule.get("msvs_quote_cmd", 1))
assert quote_cmd != 0 or cygwin != 1, \
"msvs_quote_cmd=0 only applicable for msvs_cygwin_shell=0"
assert quote_cmd != 0 or cygwin != 1, (
"msvs_quote_cmd=0 only applicable for msvs_cygwin_shell=0"
)
return MsvsSettings.RuleShellFlags(cygwin, quote_cmd)
def _HasExplicitRuleForExtension(self, spec, extension):
@ -1129,8 +1131,7 @@ def _ExtractImportantEnvironment(output_of_set):
for required in ("SYSTEMROOT", "TEMP", "TMP"):
if required not in env:
raise Exception(
'Environment variable "%s" '
"required to be set to valid path" % required
'Environment variable "%s" required to be set to valid path' % required
)
return env

View File

@ -17,8 +17,8 @@ __all__ = ["Error", "deepcopy"]
def deepcopy(x):
"""Deep copy operation on gyp objects such as strings, ints, dicts
and lists. More than twice as fast as copy.deepcopy but much less
generic."""
and lists. More than twice as fast as copy.deepcopy but much less
generic."""
try:
return _deepcopy_dispatch[type(x)](x)

View File

@ -9,7 +9,6 @@
These functions are executed via gyp-win-tool when using the ninja generator.
"""
import os
import re
import shutil
@ -33,11 +32,11 @@ def main(args):
class WinTool:
"""This class performs all the Windows tooling steps. The methods can either
be executed directly, or dispatched from an argument list."""
be executed directly, or dispatched from an argument list."""
def _UseSeparateMspdbsrv(self, env, args):
"""Allows to use a unique instance of mspdbsrv.exe per linker instead of a
shared one."""
shared one."""
if len(args) < 1:
raise Exception("Not enough arguments")
@ -114,9 +113,9 @@ class WinTool:
def ExecLinkWrapper(self, arch, use_separate_mspdbsrv, *args):
"""Filter diagnostic output from link that looks like:
' Creating library ui.dll.lib and object ui.dll.exp'
This happens when there are exports from the dll or exe.
"""
' Creating library ui.dll.lib and object ui.dll.exp'
This happens when there are exports from the dll or exe.
"""
env = self._GetEnv(arch)
if use_separate_mspdbsrv == "True":
self._UseSeparateMspdbsrv(env, args)
@ -158,10 +157,10 @@ class WinTool:
mt,
rc,
intermediate_manifest,
*manifests
*manifests,
):
"""A wrapper for handling creating a manifest resource and then executing
a link command."""
a link command."""
# The 'normal' way to do manifests is to have link generate a manifest
# based on gathering dependencies from the object files, then merge that
# manifest with other manifests supplied as sources, convert the merged
@ -245,8 +244,8 @@ class WinTool:
def ExecManifestWrapper(self, arch, *args):
"""Run manifest tool with environment set. Strip out undesirable warning
(some XML blocks are recognized by the OS loader, but not the manifest
tool)."""
(some XML blocks are recognized by the OS loader, but not the manifest
tool)."""
env = self._GetEnv(arch)
popen = subprocess.Popen(
args, shell=True, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
@ -259,8 +258,8 @@ class WinTool:
def ExecManifestToRc(self, arch, *args):
"""Creates a resource file pointing a SxS assembly manifest.
|args| is tuple containing path to resource file, path to manifest file
and resource name which can be "1" (for executables) or "2" (for DLLs)."""
|args| is tuple containing path to resource file, path to manifest file
and resource name which can be "1" (for executables) or "2" (for DLLs)."""
manifest_path, resource_path, resource_name = args
with open(resource_path, "w") as output:
output.write(
@ -270,8 +269,8 @@ class WinTool:
def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl, *flags):
"""Filter noisy filenames output from MIDL compile step that isn't
quietable via command line flags.
"""
quietable via command line flags.
"""
args = (
["midl", "/nologo"]
+ list(flags)
@ -327,7 +326,7 @@ class WinTool:
def ExecRcWrapper(self, arch, *args):
"""Filter logo banner from invocations of rc.exe. Older versions of RC
don't support the /nologo flag."""
don't support the /nologo flag."""
env = self._GetEnv(arch)
popen = subprocess.Popen(
args, shell=True, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
@ -344,7 +343,7 @@ class WinTool:
def ExecActionWrapper(self, arch, rspfile, *dir):
"""Runs an action command line from a response file using the environment
for |arch|. If |dir| is supplied, use that as the working directory."""
for |arch|. If |dir| is supplied, use that as the working directory."""
env = self._GetEnv(arch)
# TODO(scottmg): This is a temporary hack to get some specific variables
# through to actions that are set after gyp-time. http://crbug.com/333738.
@ -357,7 +356,7 @@ class WinTool:
def ExecClCompile(self, project_dir, selected_files):
"""Executed by msvs-ninja projects when the 'ClCompile' target is used to
build selected C/C++ files."""
build selected C/C++ files."""
project_dir = os.path.relpath(project_dir, BASE_DIR)
selected_files = selected_files.split(";")
ninja_targets = [

View File

@ -7,7 +7,6 @@ This module contains classes that help to emulate xcodebuild behavior on top of
other build systems, such as make and ninja.
"""
import copy
import os
import os.path
@ -31,7 +30,7 @@ XCODE_ARCHS_DEFAULT_CACHE = None
def XcodeArchsVariableMapping(archs, archs_including_64_bit=None):
"""Constructs a dictionary with expansion for $(ARCHS_STANDARD) variable,
and optionally for $(ARCHS_STANDARD_INCLUDING_64_BIT)."""
and optionally for $(ARCHS_STANDARD_INCLUDING_64_BIT)."""
mapping = {"$(ARCHS_STANDARD)": archs}
if archs_including_64_bit:
mapping["$(ARCHS_STANDARD_INCLUDING_64_BIT)"] = archs_including_64_bit
@ -40,10 +39,10 @@ def XcodeArchsVariableMapping(archs, archs_including_64_bit=None):
class XcodeArchsDefault:
"""A class to resolve ARCHS variable from xcode_settings, resolving Xcode
macros and implementing filtering by VALID_ARCHS. The expansion of macros
depends on the SDKROOT used ("macosx", "iphoneos", "iphonesimulator") and
on the version of Xcode.
"""
macros and implementing filtering by VALID_ARCHS. The expansion of macros
depends on the SDKROOT used ("macosx", "iphoneos", "iphonesimulator") and
on the version of Xcode.
"""
# Match variable like $(ARCHS_STANDARD).
variable_pattern = re.compile(r"\$\([a-zA-Z_][a-zA-Z0-9_]*\)$")
@ -82,8 +81,8 @@ class XcodeArchsDefault:
def ActiveArchs(self, archs, valid_archs, sdkroot):
"""Expands variables references in ARCHS, and filter by VALID_ARCHS if it
is defined (if not set, Xcode accept any value in ARCHS, otherwise, only
values present in VALID_ARCHS are kept)."""
is defined (if not set, Xcode accept any value in ARCHS, otherwise, only
values present in VALID_ARCHS are kept)."""
expanded_archs = self._ExpandArchs(archs or self._default, sdkroot or "")
if valid_archs:
filtered_archs = []
@ -96,24 +95,24 @@ class XcodeArchsDefault:
def GetXcodeArchsDefault():
"""Returns the |XcodeArchsDefault| object to use to expand ARCHS for the
installed version of Xcode. The default values used by Xcode for ARCHS
and the expansion of the variables depends on the version of Xcode used.
installed version of Xcode. The default values used by Xcode for ARCHS
and the expansion of the variables depends on the version of Xcode used.
For all version anterior to Xcode 5.0 or posterior to Xcode 5.1 included
uses $(ARCHS_STANDARD) if ARCHS is unset, while Xcode 5.0 to 5.0.2 uses
$(ARCHS_STANDARD_INCLUDING_64_BIT). This variable was added to Xcode 5.0
and deprecated with Xcode 5.1.
For all version anterior to Xcode 5.0 or posterior to Xcode 5.1 included
uses $(ARCHS_STANDARD) if ARCHS is unset, while Xcode 5.0 to 5.0.2 uses
$(ARCHS_STANDARD_INCLUDING_64_BIT). This variable was added to Xcode 5.0
and deprecated with Xcode 5.1.
For "macosx" SDKROOT, all version starting with Xcode 5.0 includes 64-bit
architecture as part of $(ARCHS_STANDARD) and default to only building it.
For "macosx" SDKROOT, all version starting with Xcode 5.0 includes 64-bit
architecture as part of $(ARCHS_STANDARD) and default to only building it.
For "iphoneos" and "iphonesimulator" SDKROOT, 64-bit architectures are part
of $(ARCHS_STANDARD_INCLUDING_64_BIT) from Xcode 5.0. From Xcode 5.1, they
are also part of $(ARCHS_STANDARD).
For "iphoneos" and "iphonesimulator" SDKROOT, 64-bit architectures are part
of $(ARCHS_STANDARD_INCLUDING_64_BIT) from Xcode 5.0. From Xcode 5.1, they
are also part of $(ARCHS_STANDARD).
All these rules are coded in the construction of the |XcodeArchsDefault|
object to use depending on the version of Xcode detected. The object is
for performance reason."""
All these rules are coded in the construction of the |XcodeArchsDefault|
object to use depending on the version of Xcode detected. The object is
for performance reason."""
global XCODE_ARCHS_DEFAULT_CACHE
if XCODE_ARCHS_DEFAULT_CACHE:
return XCODE_ARCHS_DEFAULT_CACHE
@ -190,8 +189,8 @@ class XcodeSettings:
def _ConvertConditionalKeys(self, configname):
"""Converts or warns on conditional keys. Xcode supports conditional keys,
such as CODE_SIGN_IDENTITY[sdk=iphoneos*]. This is a partial implementation
with some keys converted while the rest force a warning."""
such as CODE_SIGN_IDENTITY[sdk=iphoneos*]. This is a partial implementation
with some keys converted while the rest force a warning."""
settings = self.xcode_settings[configname]
conditional_keys = [key for key in settings if key.endswith("]")]
for key in conditional_keys:
@ -256,13 +255,13 @@ class XcodeSettings:
def GetFrameworkVersion(self):
"""Returns the framework version of the current target. Only valid for
bundles."""
bundles."""
assert self._IsBundle()
return self.GetPerTargetSetting("FRAMEWORK_VERSION", default="A")
def GetWrapperExtension(self):
"""Returns the bundle extension (.app, .framework, .plugin, etc). Only
valid for bundles."""
valid for bundles."""
assert self._IsBundle()
if self.spec["type"] in ("loadable_module", "shared_library"):
default_wrapper_extension = {
@ -297,13 +296,13 @@ class XcodeSettings:
def GetWrapperName(self):
"""Returns the directory name of the bundle represented by this target.
Only valid for bundles."""
Only valid for bundles."""
assert self._IsBundle()
return self.GetProductName() + self.GetWrapperExtension()
def GetBundleContentsFolderPath(self):
"""Returns the qualified path to the bundle's contents folder. E.g.
Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles."""
Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles."""
if self.isIOS:
return self.GetWrapperName()
assert self._IsBundle()
@ -317,7 +316,7 @@ class XcodeSettings:
def GetBundleResourceFolder(self):
"""Returns the qualified path to the bundle's resource folder. E.g.
Chromium.app/Contents/Resources. Only valid for bundles."""
Chromium.app/Contents/Resources. Only valid for bundles."""
assert self._IsBundle()
if self.isIOS:
return self.GetBundleContentsFolderPath()
@ -325,7 +324,7 @@ class XcodeSettings:
def GetBundleExecutableFolderPath(self):
"""Returns the qualified path to the bundle's executables folder. E.g.
Chromium.app/Contents/MacOS. Only valid for bundles."""
Chromium.app/Contents/MacOS. Only valid for bundles."""
assert self._IsBundle()
if self.spec["type"] in ("shared_library") or self.isIOS:
return self.GetBundleContentsFolderPath()
@ -334,25 +333,25 @@ class XcodeSettings:
def GetBundleJavaFolderPath(self):
"""Returns the qualified path to the bundle's Java resource folder.
E.g. Chromium.app/Contents/Resources/Java. Only valid for bundles."""
E.g. Chromium.app/Contents/Resources/Java. Only valid for bundles."""
assert self._IsBundle()
return os.path.join(self.GetBundleResourceFolder(), "Java")
def GetBundleFrameworksFolderPath(self):
"""Returns the qualified path to the bundle's frameworks folder. E.g,
Chromium.app/Contents/Frameworks. Only valid for bundles."""
Chromium.app/Contents/Frameworks. Only valid for bundles."""
assert self._IsBundle()
return os.path.join(self.GetBundleContentsFolderPath(), "Frameworks")
def GetBundleSharedFrameworksFolderPath(self):
"""Returns the qualified path to the bundle's frameworks folder. E.g,
Chromium.app/Contents/SharedFrameworks. Only valid for bundles."""
Chromium.app/Contents/SharedFrameworks. Only valid for bundles."""
assert self._IsBundle()
return os.path.join(self.GetBundleContentsFolderPath(), "SharedFrameworks")
def GetBundleSharedSupportFolderPath(self):
"""Returns the qualified path to the bundle's shared support folder. E.g,
Chromium.app/Contents/SharedSupport. Only valid for bundles."""
Chromium.app/Contents/SharedSupport. Only valid for bundles."""
assert self._IsBundle()
if self.spec["type"] == "shared_library":
return self.GetBundleResourceFolder()
@ -361,19 +360,19 @@ class XcodeSettings:
def GetBundlePlugInsFolderPath(self):
"""Returns the qualified path to the bundle's plugins folder. E.g,
Chromium.app/Contents/PlugIns. Only valid for bundles."""
Chromium.app/Contents/PlugIns. Only valid for bundles."""
assert self._IsBundle()
return os.path.join(self.GetBundleContentsFolderPath(), "PlugIns")
def GetBundleXPCServicesFolderPath(self):
"""Returns the qualified path to the bundle's XPC services folder. E.g,
Chromium.app/Contents/XPCServices. Only valid for bundles."""
Chromium.app/Contents/XPCServices. Only valid for bundles."""
assert self._IsBundle()
return os.path.join(self.GetBundleContentsFolderPath(), "XPCServices")
def GetBundlePlistPath(self):
"""Returns the qualified path to the bundle's plist file. E.g.
Chromium.app/Contents/Info.plist. Only valid for bundles."""
Chromium.app/Contents/Info.plist. Only valid for bundles."""
assert self._IsBundle()
if (
self.spec["type"] in ("executable", "loadable_module")
@ -439,7 +438,7 @@ class XcodeSettings:
def _GetBundleBinaryPath(self):
"""Returns the name of the bundle binary of by this target.
E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles."""
E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles."""
assert self._IsBundle()
return os.path.join(
self.GetBundleExecutableFolderPath(), self.GetExecutableName()
@ -470,14 +469,14 @@ class XcodeSettings:
def _GetStandaloneBinaryPath(self):
"""Returns the name of the non-bundle binary represented by this target.
E.g. hello_world. Only valid for non-bundles."""
E.g. hello_world. Only valid for non-bundles."""
assert not self._IsBundle()
assert self.spec["type"] in {
"executable",
"shared_library",
"static_library",
"loadable_module",
}, ("Unexpected type %s" % self.spec["type"])
}, "Unexpected type %s" % self.spec["type"]
target = self.spec["target_name"]
if self.spec["type"] in {"loadable_module", "shared_library", "static_library"}:
if target[:3] == "lib":
@ -490,7 +489,7 @@ class XcodeSettings:
def GetExecutableName(self):
"""Returns the executable name of the bundle represented by this target.
E.g. Chromium."""
E.g. Chromium."""
if self._IsBundle():
return self.spec.get("product_name", self.spec["target_name"])
else:
@ -498,7 +497,7 @@ class XcodeSettings:
def GetExecutablePath(self):
"""Returns the qualified path to the primary executable of the bundle
represented by this target. E.g. Chromium.app/Contents/MacOS/Chromium."""
represented by this target. E.g. Chromium.app/Contents/MacOS/Chromium."""
if self._IsBundle():
return self._GetBundleBinaryPath()
else:
@ -568,7 +567,7 @@ class XcodeSettings:
def GetCflags(self, configname, arch=None):
"""Returns flags that need to be added to .c, .cc, .m, and .mm
compilations."""
compilations."""
# This functions (and the similar ones below) do not offer complete
# emulation of all xcode_settings keys. They're implemented on demand.
@ -863,7 +862,7 @@ class XcodeSettings:
def _MapLinkerFlagFilename(self, ldflag, gyp_to_build_path):
"""Checks if ldflag contains a filename and if so remaps it from
gyp-directory-relative to build-directory-relative."""
gyp-directory-relative to build-directory-relative."""
# This list is expanded on demand.
# They get matched as:
# -exported_symbols_list file
@ -895,13 +894,13 @@ class XcodeSettings:
def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None):
"""Returns flags that need to be passed to the linker.
Args:
configname: The name of the configuration to get ld flags for.
product_dir: The directory where products such static and dynamic
libraries are placed. This is added to the library search path.
gyp_to_build_path: A function that converts paths relative to the
current gyp file to paths relative to the build directory.
"""
Args:
configname: The name of the configuration to get ld flags for.
product_dir: The directory where products such static and dynamic
libraries are placed. This is added to the library search path.
gyp_to_build_path: A function that converts paths relative to the
current gyp file to paths relative to the build directory.
"""
self.configname = configname
ldflags = []
@ -1001,9 +1000,9 @@ class XcodeSettings:
def GetLibtoolflags(self, configname):
"""Returns flags that need to be passed to the static linker.
Args:
configname: The name of the configuration to get ld flags for.
"""
Args:
configname: The name of the configuration to get ld flags for.
"""
self.configname = configname
libtoolflags = []
@ -1016,7 +1015,7 @@ class XcodeSettings:
def GetPerTargetSettings(self):
"""Gets a list of all the per-target settings. This will only fetch keys
whose values are the same across all configurations."""
whose values are the same across all configurations."""
first_pass = True
result = {}
for configname in sorted(self.xcode_settings.keys()):
@ -1039,7 +1038,7 @@ class XcodeSettings:
def GetPerTargetSetting(self, setting, default=None):
"""Tries to get xcode_settings.setting from spec. Assumes that the setting
has the same value in all configurations and throws otherwise."""
has the same value in all configurations and throws otherwise."""
is_first_pass = True
result = None
for configname in sorted(self.xcode_settings.keys()):
@ -1057,15 +1056,14 @@ class XcodeSettings:
def _GetStripPostbuilds(self, configname, output_binary, quiet):
"""Returns a list of shell commands that contain the shell commands
necessary to strip this target's binary. These should be run as postbuilds
before the actual postbuilds run."""
necessary to strip this target's binary. These should be run as postbuilds
before the actual postbuilds run."""
self.configname = configname
result = []
if self._Test("DEPLOYMENT_POSTPROCESSING", "YES", default="NO") and self._Test(
"STRIP_INSTALLED_PRODUCT", "YES", default="NO"
):
default_strip_style = "debugging"
if (
self.spec["type"] == "loadable_module" or self._IsIosAppExtension()
@ -1092,8 +1090,8 @@ class XcodeSettings:
def _GetDebugInfoPostbuilds(self, configname, output, output_binary, quiet):
"""Returns a list of shell commands that contain the shell commands
necessary to massage this target's debug information. These should be run
as postbuilds before the actual postbuilds run."""
necessary to massage this target's debug information. These should be run
as postbuilds before the actual postbuilds run."""
self.configname = configname
# For static libraries, no dSYMs are created.
@ -1114,7 +1112,7 @@ class XcodeSettings:
def _GetTargetPostbuilds(self, configname, output, output_binary, quiet=False):
"""Returns a list of shell commands that contain the shell commands
to run as postbuilds for this target, before the actual postbuilds."""
to run as postbuilds for this target, before the actual postbuilds."""
# dSYMs need to build before stripping happens.
return self._GetDebugInfoPostbuilds(
configname, output, output_binary, quiet
@ -1122,11 +1120,10 @@ class XcodeSettings:
def _GetIOSPostbuilds(self, configname, output_binary):
"""Return a shell command to codesign the iOS output binary so it can
be deployed to a device. This should be run as the very last step of the
build."""
be deployed to a device. This should be run as the very last step of the
build."""
if not (
(self.isIOS
and (self.spec["type"] == "executable" or self._IsXCTest()))
(self.isIOS and (self.spec["type"] == "executable" or self._IsXCTest()))
or self.IsIosFramework()
):
return []
@ -1240,7 +1237,7 @@ class XcodeSettings:
self, configname, output, output_binary, postbuilds=[], quiet=False
):
"""Returns a list of shell commands that should run before and after
|postbuilds|."""
|postbuilds|."""
assert output_binary is not None
pre = self._GetTargetPostbuilds(configname, output, output_binary, quiet)
post = self._GetIOSPostbuilds(configname, output_binary)
@ -1276,8 +1273,8 @@ class XcodeSettings:
def AdjustLibraries(self, libraries, config_name=None):
"""Transforms entries like 'Cocoa.framework' in libraries into entries like
'-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc.
"""
'-framework Cocoa', 'libcrypto.dylib' into '-lcrypto', etc.
"""
libraries = [self._AdjustLibrary(library, config_name) for library in libraries]
return libraries
@ -1342,10 +1339,10 @@ class XcodeSettings:
def _DefaultSdkRoot(self):
"""Returns the default SDKROOT to use.
Prior to version 5.0.0, if SDKROOT was not explicitly set in the Xcode
project, then the environment variable was empty. Starting with this
version, Xcode uses the name of the newest SDK installed.
"""
Prior to version 5.0.0, if SDKROOT was not explicitly set in the Xcode
project, then the environment variable was empty. Starting with this
version, Xcode uses the name of the newest SDK installed.
"""
xcode_version, _ = XcodeVersion()
if xcode_version < "0500":
return ""
@ -1370,39 +1367,39 @@ class XcodeSettings:
class MacPrefixHeader:
"""A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature.
This feature consists of several pieces:
* If GCC_PREFIX_HEADER is present, all compilations in that project get an
additional |-include path_to_prefix_header| cflag.
* If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is
instead compiled, and all other compilations in the project get an
additional |-include path_to_compiled_header| instead.
+ Compiled prefix headers have the extension gch. There is one gch file for
every language used in the project (c, cc, m, mm), since gch files for
different languages aren't compatible.
+ gch files themselves are built with the target's normal cflags, but they
obviously don't get the |-include| flag. Instead, they need a -x flag that
describes their language.
+ All o files in the target need to depend on the gch file, to make sure
it's built before any o file is built.
This feature consists of several pieces:
* If GCC_PREFIX_HEADER is present, all compilations in that project get an
additional |-include path_to_prefix_header| cflag.
* If GCC_PRECOMPILE_PREFIX_HEADER is present too, then the prefix header is
instead compiled, and all other compilations in the project get an
additional |-include path_to_compiled_header| instead.
+ Compiled prefix headers have the extension gch. There is one gch file for
every language used in the project (c, cc, m, mm), since gch files for
different languages aren't compatible.
+ gch files themselves are built with the target's normal cflags, but they
obviously don't get the |-include| flag. Instead, they need a -x flag that
describes their language.
+ All o files in the target need to depend on the gch file, to make sure
it's built before any o file is built.
This class helps with some of these tasks, but it needs help from the build
system for writing dependencies to the gch files, for writing build commands
for the gch files, and for figuring out the location of the gch files.
"""
This class helps with some of these tasks, but it needs help from the build
system for writing dependencies to the gch files, for writing build commands
for the gch files, and for figuring out the location of the gch files.
"""
def __init__(
self, xcode_settings, gyp_path_to_build_path, gyp_path_to_build_output
):
"""If xcode_settings is None, all methods on this class are no-ops.
Args:
gyp_path_to_build_path: A function that takes a gyp-relative path,
and returns a path relative to the build directory.
gyp_path_to_build_output: A function that takes a gyp-relative path and
a language code ('c', 'cc', 'm', or 'mm'), and that returns a path
to where the output of precompiling that path for that language
should be placed (without the trailing '.gch').
"""
Args:
gyp_path_to_build_path: A function that takes a gyp-relative path,
and returns a path relative to the build directory.
gyp_path_to_build_output: A function that takes a gyp-relative path and
a language code ('c', 'cc', 'm', or 'mm'), and that returns a path
to where the output of precompiling that path for that language
should be placed (without the trailing '.gch').
"""
# This doesn't support per-configuration prefix headers. Good enough
# for now.
self.header = None
@ -1447,9 +1444,9 @@ class MacPrefixHeader:
def GetObjDependencies(self, sources, objs, arch=None):
"""Given a list of source files and the corresponding object files, returns
a list of (source, object, gch) tuples, where |gch| is the build-directory
relative path to the gch file each object file depends on. |compilable[i]|
has to be the source file belonging to |objs[i]|."""
a list of (source, object, gch) tuples, where |gch| is the build-directory
relative path to the gch file each object file depends on. |compilable[i]|
has to be the source file belonging to |objs[i]|."""
if not self.header or not self.compile_headers:
return []
@ -1470,8 +1467,8 @@ class MacPrefixHeader:
def GetPchBuildCommands(self, arch=None):
"""Returns [(path_to_gch, language_flag, language, header)].
|path_to_gch| and |header| are relative to the build directory.
"""
|path_to_gch| and |header| are relative to the build directory.
"""
if not self.header or not self.compile_headers:
return []
return [
@ -1555,8 +1552,8 @@ def CLTVersion():
def GetStdoutQuiet(cmdlist):
"""Returns the content of standard output returned by invoking |cmdlist|.
Ignores the stderr.
Raises |GypError| if the command return with a non-zero return code."""
Ignores the stderr.
Raises |GypError| if the command return with a non-zero return code."""
job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = job.communicate()[0].decode("utf-8")
if job.returncode != 0:
@ -1566,7 +1563,7 @@ def GetStdoutQuiet(cmdlist):
def GetStdout(cmdlist):
"""Returns the content of standard output returned by invoking |cmdlist|.
Raises |GypError| if the command return with a non-zero return code."""
Raises |GypError| if the command return with a non-zero return code."""
job = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
out = job.communicate()[0].decode("utf-8")
if job.returncode != 0:
@ -1577,9 +1574,9 @@ def GetStdout(cmdlist):
def MergeGlobalXcodeSettingsToSpec(global_dict, spec):
"""Merges the global xcode_settings dictionary into each configuration of the
target represented by spec. For keys that are both in the global and the local
xcode_settings dict, the local key gets precedence.
"""
target represented by spec. For keys that are both in the global and the local
xcode_settings dict, the local key gets precedence.
"""
# The xcode generator special-cases global xcode_settings and does something
# that amounts to merging in the global xcode_settings into each local
# xcode_settings dict.
@ -1594,9 +1591,9 @@ def MergeGlobalXcodeSettingsToSpec(global_dict, spec):
def IsMacBundle(flavor, spec):
"""Returns if |spec| should be treated as a bundle.
Bundles are directories with a certain subdirectory structure, instead of
just a single file. Bundle rules do not produce a binary but also package
resources into that directory."""
Bundles are directories with a certain subdirectory structure, instead of
just a single file. Bundle rules do not produce a binary but also package
resources into that directory."""
is_mac_bundle = (
int(spec.get("mac_xctest_bundle", 0)) != 0
or int(spec.get("mac_xcuitest_bundle", 0)) != 0
@ -1613,14 +1610,14 @@ def IsMacBundle(flavor, spec):
def GetMacBundleResources(product_dir, xcode_settings, resources):
"""Yields (output, resource) pairs for every resource in |resources|.
Only call this for mac bundle targets.
Only call this for mac bundle targets.
Args:
product_dir: Path to the directory containing the output bundle,
relative to the build directory.
xcode_settings: The XcodeSettings of the current target.
resources: A list of bundle resources, relative to the build directory.
"""
Args:
product_dir: Path to the directory containing the output bundle,
relative to the build directory.
xcode_settings: The XcodeSettings of the current target.
resources: A list of bundle resources, relative to the build directory.
"""
dest = os.path.join(product_dir, xcode_settings.GetBundleResourceFolder())
for res in resources:
output = dest
@ -1651,24 +1648,24 @@ def GetMacBundleResources(product_dir, xcode_settings, resources):
def GetMacInfoPlist(product_dir, xcode_settings, gyp_path_to_build_path):
"""Returns (info_plist, dest_plist, defines, extra_env), where:
* |info_plist| is the source plist path, relative to the
build directory,
* |dest_plist| is the destination plist path, relative to the
build directory,
* |defines| is a list of preprocessor defines (empty if the plist
shouldn't be preprocessed,
* |extra_env| is a dict of env variables that should be exported when
invoking |mac_tool copy-info-plist|.
* |info_plist| is the source plist path, relative to the
build directory,
* |dest_plist| is the destination plist path, relative to the
build directory,
* |defines| is a list of preprocessor defines (empty if the plist
shouldn't be preprocessed,
* |extra_env| is a dict of env variables that should be exported when
invoking |mac_tool copy-info-plist|.
Only call this for mac bundle targets.
Only call this for mac bundle targets.
Args:
product_dir: Path to the directory containing the output bundle,
relative to the build directory.
xcode_settings: The XcodeSettings of the current target.
gyp_to_build_path: A function that converts paths relative to the
current gyp file to paths relative to the build directory.
"""
Args:
product_dir: Path to the directory containing the output bundle,
relative to the build directory.
xcode_settings: The XcodeSettings of the current target.
gyp_to_build_path: A function that converts paths relative to the
current gyp file to paths relative to the build directory.
"""
info_plist = xcode_settings.GetPerTargetSetting("INFOPLIST_FILE")
if not info_plist:
return None, None, [], {}
@ -1706,18 +1703,18 @@ def _GetXcodeEnv(
xcode_settings, built_products_dir, srcroot, configuration, additional_settings=None
):
"""Return the environment variables that Xcode would set. See
http://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW153
for a full list.
http://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW153
for a full list.
Args:
xcode_settings: An XcodeSettings object. If this is None, this function
returns an empty dict.
built_products_dir: Absolute path to the built products dir.
srcroot: Absolute path to the source root.
configuration: The build configuration name.
additional_settings: An optional dict with more values to add to the
result.
"""
Args:
xcode_settings: An XcodeSettings object. If this is None, this function
returns an empty dict.
built_products_dir: Absolute path to the built products dir.
srcroot: Absolute path to the source root.
configuration: The build configuration name.
additional_settings: An optional dict with more values to add to the
result.
"""
if not xcode_settings:
return {}
@ -1771,17 +1768,17 @@ def _GetXcodeEnv(
)
env["CONTENTS_FOLDER_PATH"] = xcode_settings.GetBundleContentsFolderPath()
env["EXECUTABLE_FOLDER_PATH"] = xcode_settings.GetBundleExecutableFolderPath()
env[
"UNLOCALIZED_RESOURCES_FOLDER_PATH"
] = xcode_settings.GetBundleResourceFolder()
env["UNLOCALIZED_RESOURCES_FOLDER_PATH"] = (
xcode_settings.GetBundleResourceFolder()
)
env["JAVA_FOLDER_PATH"] = xcode_settings.GetBundleJavaFolderPath()
env["FRAMEWORKS_FOLDER_PATH"] = xcode_settings.GetBundleFrameworksFolderPath()
env[
"SHARED_FRAMEWORKS_FOLDER_PATH"
] = xcode_settings.GetBundleSharedFrameworksFolderPath()
env[
"SHARED_SUPPORT_FOLDER_PATH"
] = xcode_settings.GetBundleSharedSupportFolderPath()
env["SHARED_FRAMEWORKS_FOLDER_PATH"] = (
xcode_settings.GetBundleSharedFrameworksFolderPath()
)
env["SHARED_SUPPORT_FOLDER_PATH"] = (
xcode_settings.GetBundleSharedSupportFolderPath()
)
env["PLUGINS_FOLDER_PATH"] = xcode_settings.GetBundlePlugInsFolderPath()
env["XPCSERVICES_FOLDER_PATH"] = xcode_settings.GetBundleXPCServicesFolderPath()
env["INFOPLIST_PATH"] = xcode_settings.GetBundlePlistPath()
@ -1817,8 +1814,8 @@ def _GetXcodeEnv(
def _NormalizeEnvVarReferences(str):
"""Takes a string containing variable references in the form ${FOO}, $(FOO),
or $FOO, and returns a string with all variable references in the form ${FOO}.
"""
or $FOO, and returns a string with all variable references in the form ${FOO}.
"""
# $FOO -> ${FOO}
str = re.sub(r"\$([a-zA-Z_][a-zA-Z0-9_]*)", r"${\1}", str)
@ -1834,9 +1831,9 @@ def _NormalizeEnvVarReferences(str):
def ExpandEnvVars(string, expansions):
"""Expands ${VARIABLES}, $(VARIABLES), and $VARIABLES in string per the
expansions list. If the variable expands to something that references
another variable, this variable is expanded as well if it's in env --
until no variables present in env are left."""
expansions list. If the variable expands to something that references
another variable, this variable is expanded as well if it's in env --
until no variables present in env are left."""
for k, v in reversed(expansions):
string = string.replace("${" + k + "}", v)
string = string.replace("$(" + k + ")", v)
@ -1846,11 +1843,11 @@ def ExpandEnvVars(string, expansions):
def _TopologicallySortedEnvVarKeys(env):
"""Takes a dict |env| whose values are strings that can refer to other keys,
for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of
env such that key2 is after key1 in L if env[key2] refers to env[key1].
for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of
env such that key2 is after key1 in L if env[key2] refers to env[key1].
Throws an Exception in case of dependency cycles.
"""
Throws an Exception in case of dependency cycles.
"""
# Since environment variables can refer to other variables, the evaluation
# order is important. Below is the logic to compute the dependency graph
# and sort it.
@ -1891,7 +1888,7 @@ def GetSortedXcodeEnv(
def GetSpecPostbuildCommands(spec, quiet=False):
"""Returns the list of postbuilds explicitly defined on |spec|, in a form
executable by a shell."""
executable by a shell."""
postbuilds = []
for postbuild in spec.get("postbuilds", []):
if not quiet:
@ -1905,7 +1902,7 @@ def GetSpecPostbuildCommands(spec, quiet=False):
def _HasIOSTarget(targets):
"""Returns true if any target contains the iOS specific key
IPHONEOS_DEPLOYMENT_TARGET."""
IPHONEOS_DEPLOYMENT_TARGET."""
for target_dict in targets.values():
for config in target_dict["configurations"].values():
if config.get("xcode_settings", {}).get("IPHONEOS_DEPLOYMENT_TARGET"):
@ -1915,7 +1912,7 @@ def _HasIOSTarget(targets):
def _AddIOSDeviceConfigurations(targets):
"""Clone all targets and append -iphoneos to the name. Configure these targets
to build for iOS devices and use correct architectures for those builds."""
to build for iOS devices and use correct architectures for those builds."""
for target_dict in targets.values():
toolset = target_dict["toolset"]
configs = target_dict["configurations"]
@ -1931,7 +1928,7 @@ def _AddIOSDeviceConfigurations(targets):
def CloneConfigurationForDeviceAndEmulator(target_dicts):
"""If |target_dicts| contains any iOS targets, automatically create -iphoneos
targets for iOS device builds."""
targets for iOS device builds."""
if _HasIOSTarget(target_dicts):
return _AddIOSDeviceConfigurations(target_dicts)
return target_dicts

View File

@ -21,7 +21,7 @@ import gyp.generator.ninja
def _WriteWorkspace(main_gyp, sources_gyp, params):
""" Create a workspace to wrap main and sources gyp paths. """
"""Create a workspace to wrap main and sources gyp paths."""
(build_file_root, build_file_ext) = os.path.splitext(main_gyp)
workspace_path = build_file_root + ".xcworkspace"
options = params["options"]
@ -57,7 +57,7 @@ def _WriteWorkspace(main_gyp, sources_gyp, params):
def _TargetFromSpec(old_spec, params):
""" Create fake target for xcode-ninja wrapper. """
"""Create fake target for xcode-ninja wrapper."""
# Determine ninja top level build dir (e.g. /path/to/out).
ninja_toplevel = None
jobs = 0
@ -102,9 +102,9 @@ def _TargetFromSpec(old_spec, params):
new_xcode_settings[key] = old_xcode_settings[key]
ninja_target["configurations"][config] = {}
ninja_target["configurations"][config][
"xcode_settings"
] = new_xcode_settings
ninja_target["configurations"][config]["xcode_settings"] = (
new_xcode_settings
)
ninja_target["mac_bundle"] = old_spec.get("mac_bundle", 0)
ninja_target["mac_xctest_bundle"] = old_spec.get("mac_xctest_bundle", 0)
@ -137,13 +137,13 @@ def _TargetFromSpec(old_spec, params):
def IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
"""Limit targets for Xcode wrapper.
Xcode sometimes performs poorly with too many targets, so only include
proper executable targets, with filters to customize.
Arguments:
target_extras: Regular expression to always add, matching any target.
executable_target_pattern: Regular expression limiting executable targets.
spec: Specifications for target.
"""
Xcode sometimes performs poorly with too many targets, so only include
proper executable targets, with filters to customize.
Arguments:
target_extras: Regular expression to always add, matching any target.
executable_target_pattern: Regular expression limiting executable targets.
spec: Specifications for target.
"""
target_name = spec.get("target_name")
# Always include targets matching target_extras.
if target_extras is not None and re.search(target_extras, target_name):
@ -154,7 +154,6 @@ def IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
spec.get("type", "") == "executable"
and spec.get("product_extension", "") != "bundle"
):
# If there is a filter and the target does not match, exclude the target.
if executable_target_pattern is not None:
if not re.search(executable_target_pattern, target_name):
@ -166,14 +165,14 @@ def IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
def CreateWrapper(target_list, target_dicts, data, params):
"""Initialize targets for the ninja wrapper.
This sets up the necessary variables in the targets to generate Xcode projects
that use ninja as an external builder.
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
data: Dict of flattened build files keyed on gyp path.
params: Dict of global options for gyp.
"""
This sets up the necessary variables in the targets to generate Xcode projects
that use ninja as an external builder.
Arguments:
target_list: List of target pairs: 'base/base.gyp:base'.
target_dicts: Dict of target properties keyed on target pair.
data: Dict of flattened build files keyed on gyp path.
params: Dict of global options for gyp.
"""
orig_gyp = params["build_files"][0]
for gyp_name, gyp_dict in data.items():
if gyp_name == orig_gyp:

View File

@ -176,12 +176,12 @@ _path_leading_variable = re.compile(r"^\$\((.*?)\)(/(.*))?$")
def SourceTreeAndPathFromPath(input_path):
"""Given input_path, returns a tuple with sourceTree and path values.
Examples:
input_path (source_tree, output_path)
'$(VAR)/path' ('VAR', 'path')
'$(VAR)' ('VAR', None)
'path' (None, 'path')
"""
Examples:
input_path (source_tree, output_path)
'$(VAR)/path' ('VAR', 'path')
'$(VAR)' ('VAR', None)
'path' (None, 'path')
"""
if source_group_match := _path_leading_variable.match(input_path):
source_tree = source_group_match.group(1)
@ -200,70 +200,70 @@ def ConvertVariablesToShellSyntax(input_string):
class XCObject:
"""The abstract base of all class types used in Xcode project files.
Class variables:
_schema: A dictionary defining the properties of this class. The keys to
_schema are string property keys as used in project files. Values
are a list of four or five elements:
[ is_list, property_type, is_strong, is_required, default ]
is_list: True if the property described is a list, as opposed
to a single element.
property_type: The type to use as the value of the property,
or if is_list is True, the type to use for each
element of the value's list. property_type must
be an XCObject subclass, or one of the built-in
types str, int, or dict.
is_strong: If property_type is an XCObject subclass, is_strong
is True to assert that this class "owns," or serves
as parent, to the property value (or, if is_list is
True, values). is_strong must be False if
property_type is not an XCObject subclass.
is_required: True if the property is required for the class.
Note that is_required being True does not preclude
an empty string ("", in the case of property_type
str) or list ([], in the case of is_list True) from
being set for the property.
default: Optional. If is_required is True, default may be set
to provide a default value for objects that do not supply
their own value. If is_required is True and default
is not provided, users of the class must supply their own
value for the property.
Note that although the values of the array are expressed in
boolean terms, subclasses provide values as integers to conserve
horizontal space.
_should_print_single_line: False in XCObject. Subclasses whose objects
should be written to the project file in the
alternate single-line format, such as
PBXFileReference and PBXBuildFile, should
set this to True.
_encode_transforms: Used by _EncodeString to encode unprintable characters.
The index into this list is the ordinal of the
character to transform; each value is a string
used to represent the character in the output. XCObject
provides an _encode_transforms list suitable for most
XCObject subclasses.
_alternate_encode_transforms: Provided for subclasses that wish to use
the alternate encoding rules. Xcode seems
to use these rules when printing objects in
single-line format. Subclasses that desire
this behavior should set _encode_transforms
to _alternate_encode_transforms.
_hashables: A list of XCObject subclasses that can be hashed by ComputeIDs
to construct this object's ID. Most classes that need custom
hashing behavior should do it by overriding Hashables,
but in some cases an object's parent may wish to push a
hashable value into its child, and it can do so by appending
to _hashables.
Attributes:
id: The object's identifier, a 24-character uppercase hexadecimal string.
Usually, objects being created should not set id until the entire
project file structure is built. At that point, UpdateIDs() should
be called on the root object to assign deterministic values for id to
each object in the tree.
parent: The object's parent. This is set by a parent XCObject when a child
object is added to it.
_properties: The object's property dictionary. An object's properties are
described by its class' _schema variable.
"""
Class variables:
_schema: A dictionary defining the properties of this class. The keys to
_schema are string property keys as used in project files. Values
are a list of four or five elements:
[ is_list, property_type, is_strong, is_required, default ]
is_list: True if the property described is a list, as opposed
to a single element.
property_type: The type to use as the value of the property,
or if is_list is True, the type to use for each
element of the value's list. property_type must
be an XCObject subclass, or one of the built-in
types str, int, or dict.
is_strong: If property_type is an XCObject subclass, is_strong
is True to assert that this class "owns," or serves
as parent, to the property value (or, if is_list is
True, values). is_strong must be False if
property_type is not an XCObject subclass.
is_required: True if the property is required for the class.
Note that is_required being True does not preclude
an empty string ("", in the case of property_type
str) or list ([], in the case of is_list True) from
being set for the property.
default: Optional. If is_required is True, default may be set
to provide a default value for objects that do not supply
their own value. If is_required is True and default
is not provided, users of the class must supply their own
value for the property.
Note that although the values of the array are expressed in
boolean terms, subclasses provide values as integers to conserve
horizontal space.
_should_print_single_line: False in XCObject. Subclasses whose objects
should be written to the project file in the
alternate single-line format, such as
PBXFileReference and PBXBuildFile, should
set this to True.
_encode_transforms: Used by _EncodeString to encode unprintable characters.
The index into this list is the ordinal of the
character to transform; each value is a string
used to represent the character in the output. XCObject
provides an _encode_transforms list suitable for most
XCObject subclasses.
_alternate_encode_transforms: Provided for subclasses that wish to use
the alternate encoding rules. Xcode seems
to use these rules when printing objects in
single-line format. Subclasses that desire
this behavior should set _encode_transforms
to _alternate_encode_transforms.
_hashables: A list of XCObject subclasses that can be hashed by ComputeIDs
to construct this object's ID. Most classes that need custom
hashing behavior should do it by overriding Hashables,
but in some cases an object's parent may wish to push a
hashable value into its child, and it can do so by appending
to _hashables.
Attributes:
id: The object's identifier, a 24-character uppercase hexadecimal string.
Usually, objects being created should not set id until the entire
project file structure is built. At that point, UpdateIDs() should
be called on the root object to assign deterministic values for id to
each object in the tree.
parent: The object's parent. This is set by a parent XCObject when a child
object is added to it.
_properties: The object's property dictionary. An object's properties are
described by its class' _schema variable.
"""
_schema = {}
_should_print_single_line = False
@ -305,12 +305,12 @@ class XCObject:
def Copy(self):
"""Make a copy of this object.
The new object will have its own copy of lists and dicts. Any XCObject
objects owned by this object (marked "strong") will be copied in the
new object, even those found in lists. If this object has any weak
references to other XCObjects, the same references are added to the new
object without making a copy.
"""
The new object will have its own copy of lists and dicts. Any XCObject
objects owned by this object (marked "strong") will be copied in the
new object, even those found in lists. If this object has any weak
references to other XCObjects, the same references are added to the new
object without making a copy.
"""
that = self.__class__(id=self.id, parent=self.parent)
for key, value in self._properties.items():
@ -359,9 +359,9 @@ class XCObject:
def Name(self):
"""Return the name corresponding to an object.
Not all objects necessarily need to be nameable, and not all that do have
a "name" property. Override as needed.
"""
Not all objects necessarily need to be nameable, and not all that do have
a "name" property. Override as needed.
"""
# If the schema indicates that "name" is required, try to access the
# property even if it doesn't exist. This will result in a KeyError
@ -377,12 +377,12 @@ class XCObject:
def Comment(self):
"""Return a comment string for the object.
Most objects just use their name as the comment, but PBXProject uses
different values.
Most objects just use their name as the comment, but PBXProject uses
different values.
The returned comment is not escaped and does not have any comment marker
strings applied to it.
"""
The returned comment is not escaped and does not have any comment marker
strings applied to it.
"""
return self.Name()
@ -402,26 +402,26 @@ class XCObject:
def ComputeIDs(self, recursive=True, overwrite=True, seed_hash=None):
"""Set "id" properties deterministically.
An object's "id" property is set based on a hash of its class type and
name, as well as the class type and name of all ancestor objects. As
such, it is only advisable to call ComputeIDs once an entire project file
tree is built.
An object's "id" property is set based on a hash of its class type and
name, as well as the class type and name of all ancestor objects. As
such, it is only advisable to call ComputeIDs once an entire project file
tree is built.
If recursive is True, recurse into all descendant objects and update their
hashes.
If recursive is True, recurse into all descendant objects and update their
hashes.
If overwrite is True, any existing value set in the "id" property will be
replaced.
"""
If overwrite is True, any existing value set in the "id" property will be
replaced.
"""
def _HashUpdate(hash, data):
"""Update hash with data's length and contents.
If the hash were updated only with the value of data, it would be
possible for clowns to induce collisions by manipulating the names of
their objects. By adding the length, it's exceedingly less likely that
ID collisions will be encountered, intentionally or not.
"""
If the hash were updated only with the value of data, it would be
possible for clowns to induce collisions by manipulating the names of
their objects. By adding the length, it's exceedingly less likely that
ID collisions will be encountered, intentionally or not.
"""
hash.update(struct.pack(">i", len(data)))
if isinstance(data, str):
@ -464,8 +464,7 @@ class XCObject:
self.id = "%08X%08X%08X" % tuple(id_ints)
def EnsureNoIDCollisions(self):
"""Verifies that no two objects have the same ID. Checks all descendants.
"""
"""Verifies that no two objects have the same ID. Checks all descendants."""
ids = {}
descendants = self.Descendants()
@ -498,8 +497,8 @@ class XCObject:
def Descendants(self):
"""Returns a list of all of this object's descendants, including this
object.
"""
object.
"""
children = self.Children()
descendants = [self]
@ -515,8 +514,8 @@ class XCObject:
def _EncodeComment(self, comment):
"""Encodes a comment to be placed in the project file output, mimicking
Xcode behavior.
"""
Xcode behavior.
"""
# This mimics Xcode behavior by wrapping the comment in "/*" and "*/". If
# the string already contains a "*/", it is turned into "(*)/". This keeps
@ -543,8 +542,8 @@ class XCObject:
def _EncodeString(self, value):
"""Encodes a string to be placed in the project file output, mimicking
Xcode behavior.
"""
Xcode behavior.
"""
# Use quotation marks when any character outside of the range A-Z, a-z, 0-9,
# $ (dollar sign), . (period), and _ (underscore) is present. Also use
@ -585,18 +584,18 @@ class XCObject:
def _XCPrintableValue(self, tabs, value, flatten_list=False):
"""Returns a representation of value that may be printed in a project file,
mimicking Xcode's behavior.
mimicking Xcode's behavior.
_XCPrintableValue can handle str and int values, XCObjects (which are
made printable by returning their id property), and list and dict objects
composed of any of the above types. When printing a list or dict, and
_should_print_single_line is False, the tabs parameter is used to determine
how much to indent the lines corresponding to the items in the list or
dict.
_XCPrintableValue can handle str and int values, XCObjects (which are
made printable by returning their id property), and list and dict objects
composed of any of the above types. When printing a list or dict, and
_should_print_single_line is False, the tabs parameter is used to determine
how much to indent the lines corresponding to the items in the list or
dict.
If flatten_list is True, single-element lists will be transformed into
strings.
"""
If flatten_list is True, single-element lists will be transformed into
strings.
"""
printable = ""
comment = None
@ -657,12 +656,12 @@ class XCObject:
def _XCKVPrint(self, file, tabs, key, value):
"""Prints a key and value, members of an XCObject's _properties dictionary,
to file.
to file.
tabs is an int identifying the indentation level. If the class'
_should_print_single_line variable is True, tabs is ignored and the
key-value pair will be followed by a space instead of a newline.
"""
tabs is an int identifying the indentation level. If the class'
_should_print_single_line variable is True, tabs is ignored and the
key-value pair will be followed by a space instead of a newline.
"""
if self._should_print_single_line:
printable = ""
@ -720,8 +719,8 @@ class XCObject:
def Print(self, file=sys.stdout):
"""Prints a reprentation of this object to file, adhering to Xcode output
formatting.
"""
formatting.
"""
self.VerifyHasRequiredProperties()
@ -759,15 +758,15 @@ class XCObject:
def UpdateProperties(self, properties, do_copy=False):
"""Merge the supplied properties into the _properties dictionary.
The input properties must adhere to the class schema or a KeyError or
TypeError exception will be raised. If adding an object of an XCObject
subclass and the schema indicates a strong relationship, the object's
parent will be set to this object.
The input properties must adhere to the class schema or a KeyError or
TypeError exception will be raised. If adding an object of an XCObject
subclass and the schema indicates a strong relationship, the object's
parent will be set to this object.
If do_copy is True, then lists, dicts, strong-owned XCObjects, and
strong-owned XCObjects in lists will be copied instead of having their
references added.
"""
If do_copy is True, then lists, dicts, strong-owned XCObjects, and
strong-owned XCObjects in lists will be copied instead of having their
references added.
"""
if properties is None:
return
@ -908,8 +907,8 @@ class XCObject:
def VerifyHasRequiredProperties(self):
"""Ensure that all properties identified as required by the schema are
set.
"""
set.
"""
# TODO(mark): A stronger verification mechanism is needed. Some
# subclasses need to perform validation beyond what the schema can enforce.
@ -920,7 +919,7 @@ class XCObject:
def _SetDefaultsFromSchema(self):
"""Assign object default values according to the schema. This will not
overwrite properties that have already been set."""
overwrite properties that have already been set."""
defaults = {}
for property, attributes in self._schema.items():
@ -942,7 +941,7 @@ class XCObject:
class XCHierarchicalElement(XCObject):
"""Abstract base for PBXGroup and PBXFileReference. Not represented in a
project file."""
project file."""
# TODO(mark): Do name and path belong here? Probably so.
# If path is set and name is not, name may have a default value. Name will
@ -1008,27 +1007,27 @@ class XCHierarchicalElement(XCObject):
def Hashables(self):
"""Custom hashables for XCHierarchicalElements.
XCHierarchicalElements are special. Generally, their hashes shouldn't
change if the paths don't change. The normal XCObject implementation of
Hashables adds a hashable for each object, which means that if
the hierarchical structure changes (possibly due to changes caused when
TakeOverOnlyChild runs and encounters slight changes in the hierarchy),
the hashes will change. For example, if a project file initially contains
a/b/f1 and a/b becomes collapsed into a/b, f1 will have a single parent
a/b. If someone later adds a/f2 to the project file, a/b can no longer be
collapsed, and f1 winds up with parent b and grandparent a. That would
be sufficient to change f1's hash.
XCHierarchicalElements are special. Generally, their hashes shouldn't
change if the paths don't change. The normal XCObject implementation of
Hashables adds a hashable for each object, which means that if
the hierarchical structure changes (possibly due to changes caused when
TakeOverOnlyChild runs and encounters slight changes in the hierarchy),
the hashes will change. For example, if a project file initially contains
a/b/f1 and a/b becomes collapsed into a/b, f1 will have a single parent
a/b. If someone later adds a/f2 to the project file, a/b can no longer be
collapsed, and f1 winds up with parent b and grandparent a. That would
be sufficient to change f1's hash.
To counteract this problem, hashables for all XCHierarchicalElements except
for the main group (which has neither a name nor a path) are taken to be
just the set of path components. Because hashables are inherited from
parents, this provides assurance that a/b/f1 has the same set of hashables
whether its parent is b or a/b.
To counteract this problem, hashables for all XCHierarchicalElements except
for the main group (which has neither a name nor a path) are taken to be
just the set of path components. Because hashables are inherited from
parents, this provides assurance that a/b/f1 has the same set of hashables
whether its parent is b or a/b.
The main group is a special case. As it is permitted to have no name or
path, it is permitted to use the standard XCObject hash mechanism. This
is not considered a problem because there can be only one main group.
"""
The main group is a special case. As it is permitted to have no name or
path, it is permitted to use the standard XCObject hash mechanism. This
is not considered a problem because there can be only one main group.
"""
if self == self.PBXProjectAncestor()._properties["mainGroup"]:
# super
@ -1157,12 +1156,12 @@ class XCHierarchicalElement(XCObject):
class PBXGroup(XCHierarchicalElement):
"""
Attributes:
_children_by_path: Maps pathnames of children of this PBXGroup to the
actual child XCHierarchicalElement objects.
_variant_children_by_name_and_path: Maps (name, path) tuples of
PBXVariantGroup children to the actual child PBXVariantGroup objects.
"""
Attributes:
_children_by_path: Maps pathnames of children of this PBXGroup to the
actual child XCHierarchicalElement objects.
_variant_children_by_name_and_path: Maps (name, path) tuples of
PBXVariantGroup children to the actual child PBXVariantGroup objects.
"""
_schema = XCHierarchicalElement._schema.copy()
_schema.update(
@ -1281,20 +1280,20 @@ class PBXGroup(XCHierarchicalElement):
def AddOrGetFileByPath(self, path, hierarchical):
"""Returns an existing or new file reference corresponding to path.
If hierarchical is True, this method will create or use the necessary
hierarchical group structure corresponding to path. Otherwise, it will
look in and create an item in the current group only.
If hierarchical is True, this method will create or use the necessary
hierarchical group structure corresponding to path. Otherwise, it will
look in and create an item in the current group only.
If an existing matching reference is found, it is returned, otherwise, a
new one will be created, added to the correct group, and returned.
If an existing matching reference is found, it is returned, otherwise, a
new one will be created, added to the correct group, and returned.
If path identifies a directory by virtue of carrying a trailing slash,
this method returns a PBXFileReference of "folder" type. If path
identifies a variant, by virtue of it identifying a file inside a directory
with an ".lproj" extension, this method returns a PBXVariantGroup
containing the variant named by path, and possibly other variants. For
all other paths, a "normal" PBXFileReference will be returned.
"""
If path identifies a directory by virtue of carrying a trailing slash,
this method returns a PBXFileReference of "folder" type. If path
identifies a variant, by virtue of it identifying a file inside a directory
with an ".lproj" extension, this method returns a PBXVariantGroup
containing the variant named by path, and possibly other variants. For
all other paths, a "normal" PBXFileReference will be returned.
"""
# Adding or getting a directory? Directories end with a trailing slash.
is_dir = False
@ -1379,15 +1378,15 @@ class PBXGroup(XCHierarchicalElement):
def AddOrGetVariantGroupByNameAndPath(self, name, path):
"""Returns an existing or new PBXVariantGroup for name and path.
If a PBXVariantGroup identified by the name and path arguments is already
present as a child of this object, it is returned. Otherwise, a new
PBXVariantGroup with the correct properties is created, added as a child,
and returned.
If a PBXVariantGroup identified by the name and path arguments is already
present as a child of this object, it is returned. Otherwise, a new
PBXVariantGroup with the correct properties is created, added as a child,
and returned.
This method will generally be called by AddOrGetFileByPath, which knows
when to create a variant group based on the structure of the pathnames
passed to it.
"""
This method will generally be called by AddOrGetFileByPath, which knows
when to create a variant group based on the structure of the pathnames
passed to it.
"""
key = (name, path)
if key in self._variant_children_by_name_and_path:
@ -1405,19 +1404,19 @@ class PBXGroup(XCHierarchicalElement):
def TakeOverOnlyChild(self, recurse=False):
"""If this PBXGroup has only one child and it's also a PBXGroup, take
it over by making all of its children this object's children.
it over by making all of its children this object's children.
This function will continue to take over only children when those children
are groups. If there are three PBXGroups representing a, b, and c, with
c inside b and b inside a, and a and b have no other children, this will
result in a taking over both b and c, forming a PBXGroup for a/b/c.
This function will continue to take over only children when those children
are groups. If there are three PBXGroups representing a, b, and c, with
c inside b and b inside a, and a and b have no other children, this will
result in a taking over both b and c, forming a PBXGroup for a/b/c.
If recurse is True, this function will recurse into children and ask them
to collapse themselves by taking over only children as well. Assuming
an example hierarchy with files at a/b/c/d1, a/b/c/d2, and a/b/c/d3/e/f
(d1, d2, and f are files, the rest are groups), recursion will result in
a group for a/b/c containing a group for d3/e.
"""
If recurse is True, this function will recurse into children and ask them
to collapse themselves by taking over only children as well. Assuming
an example hierarchy with files at a/b/c/d1, a/b/c/d2, and a/b/c/d3/e/f
(d1, d2, and f are files, the rest are groups), recursion will result in
a group for a/b/c containing a group for d3/e.
"""
# At this stage, check that child class types are PBXGroup exactly,
# instead of using isinstance. The only subclass of PBXGroup,
@ -1716,16 +1715,16 @@ class XCConfigurationList(XCObject):
def HasBuildSetting(self, key):
"""Determines the state of a build setting in all XCBuildConfiguration
child objects.
child objects.
If all child objects have key in their build settings, and the value is the
same in all child objects, returns 1.
If all child objects have key in their build settings, and the value is the
same in all child objects, returns 1.
If no child objects have the key in their build settings, returns 0.
If no child objects have the key in their build settings, returns 0.
If some, but not all, child objects have the key in their build settings,
or if any children have different values for the key, returns -1.
"""
If some, but not all, child objects have the key in their build settings,
or if any children have different values for the key, returns -1.
"""
has = None
value = None
@ -1751,9 +1750,9 @@ class XCConfigurationList(XCObject):
def GetBuildSetting(self, key):
"""Gets the build setting for key.
All child XCConfiguration objects must have the same value set for the
setting, or a ValueError will be raised.
"""
All child XCConfiguration objects must have the same value set for the
setting, or a ValueError will be raised.
"""
# TODO(mark): This is wrong for build settings that are lists. The list
# contents should be compared (and a list copy returned?)
@ -1770,31 +1769,30 @@ class XCConfigurationList(XCObject):
def SetBuildSetting(self, key, value):
"""Sets the build setting for key to value in all child
XCBuildConfiguration objects.
"""
XCBuildConfiguration objects.
"""
for configuration in self._properties["buildConfigurations"]:
configuration.SetBuildSetting(key, value)
def AppendBuildSetting(self, key, value):
"""Appends value to the build setting for key, which is treated as a list,
in all child XCBuildConfiguration objects.
"""
in all child XCBuildConfiguration objects.
"""
for configuration in self._properties["buildConfigurations"]:
configuration.AppendBuildSetting(key, value)
def DelBuildSetting(self, key):
"""Deletes the build setting key from all child XCBuildConfiguration
objects.
"""
objects.
"""
for configuration in self._properties["buildConfigurations"]:
configuration.DelBuildSetting(key)
def SetBaseConfiguration(self, value):
"""Sets the build configuration in all child XCBuildConfiguration objects.
"""
"""Sets the build configuration in all child XCBuildConfiguration objects."""
for configuration in self._properties["buildConfigurations"]:
configuration.SetBaseConfiguration(value)
@ -1834,14 +1832,14 @@ class PBXBuildFile(XCObject):
class XCBuildPhase(XCObject):
"""Abstract base for build phase classes. Not represented in a project
file.
file.
Attributes:
_files_by_path: A dict mapping each path of a child in the files list by
path (keys) to the corresponding PBXBuildFile children (values).
_files_by_xcfilelikeelement: A dict mapping each XCFileLikeElement (keys)
to the corresponding PBXBuildFile children (values).
"""
Attributes:
_files_by_path: A dict mapping each path of a child in the files list by
path (keys) to the corresponding PBXBuildFile children (values).
_files_by_xcfilelikeelement: A dict mapping each XCFileLikeElement (keys)
to the corresponding PBXBuildFile children (values).
"""
# TODO(mark): Some build phase types, like PBXShellScriptBuildPhase, don't
# actually have a "files" list. XCBuildPhase should not have "files" but
@ -1880,8 +1878,8 @@ class XCBuildPhase(XCObject):
def _AddPathToDict(self, pbxbuildfile, path):
"""Adds path to the dict tracking paths belonging to this build phase.
If the path is already a member of this build phase, raises an exception.
"""
If the path is already a member of this build phase, raises an exception.
"""
if path in self._files_by_path:
raise ValueError("Found multiple build files with path " + path)
@ -1890,28 +1888,28 @@ class XCBuildPhase(XCObject):
def _AddBuildFileToDicts(self, pbxbuildfile, path=None):
"""Maintains the _files_by_path and _files_by_xcfilelikeelement dicts.
If path is specified, then it is the path that is being added to the
phase, and pbxbuildfile must contain either a PBXFileReference directly
referencing that path, or it must contain a PBXVariantGroup that itself
contains a PBXFileReference referencing the path.
If path is specified, then it is the path that is being added to the
phase, and pbxbuildfile must contain either a PBXFileReference directly
referencing that path, or it must contain a PBXVariantGroup that itself
contains a PBXFileReference referencing the path.
If path is not specified, either the PBXFileReference's path or the paths
of all children of the PBXVariantGroup are taken as being added to the
phase.
If path is not specified, either the PBXFileReference's path or the paths
of all children of the PBXVariantGroup are taken as being added to the
phase.
If the path is already present in the phase, raises an exception.
If the path is already present in the phase, raises an exception.
If the PBXFileReference or PBXVariantGroup referenced by pbxbuildfile
are already present in the phase, referenced by a different PBXBuildFile
object, raises an exception. This does not raise an exception when
a PBXFileReference or PBXVariantGroup reappear and are referenced by the
same PBXBuildFile that has already introduced them, because in the case
of PBXVariantGroup objects, they may correspond to multiple paths that are
not all added simultaneously. When this situation occurs, the path needs
to be added to _files_by_path, but nothing needs to change in
_files_by_xcfilelikeelement, and the caller should have avoided adding
the PBXBuildFile if it is already present in the list of children.
"""
If the PBXFileReference or PBXVariantGroup referenced by pbxbuildfile
are already present in the phase, referenced by a different PBXBuildFile
object, raises an exception. This does not raise an exception when
a PBXFileReference or PBXVariantGroup reappear and are referenced by the
same PBXBuildFile that has already introduced them, because in the case
of PBXVariantGroup objects, they may correspond to multiple paths that are
not all added simultaneously. When this situation occurs, the path needs
to be added to _files_by_path, but nothing needs to change in
_files_by_xcfilelikeelement, and the caller should have avoided adding
the PBXBuildFile if it is already present in the list of children.
"""
xcfilelikeelement = pbxbuildfile._properties["fileRef"]
@ -2102,9 +2100,9 @@ class PBXCopyFilesBuildPhase(XCBuildPhase):
def SetDestination(self, path):
"""Set the dstSubfolderSpec and dstPath properties from path.
path may be specified in the same notation used for XCHierarchicalElements,
specifically, "$(DIR)/path".
"""
path may be specified in the same notation used for XCHierarchicalElements,
specifically, "$(DIR)/path".
"""
if path_tree_match := self.path_tree_re.search(path):
path_tree = path_tree_match.group(1)
@ -2178,9 +2176,7 @@ class PBXCopyFilesBuildPhase(XCBuildPhase):
subfolder = 0
relative_path = path[1:]
else:
raise ValueError(
f"Can't use path {path} in a {self.__class__.__name__}"
)
raise ValueError(f"Can't use path {path} in a {self.__class__.__name__}")
self._properties["dstPath"] = relative_path
self._properties["dstSubfolderSpec"] = subfolder
@ -2530,9 +2526,9 @@ class PBXNativeTarget(XCTarget):
# loadable modules, but there's precedent: Python loadable modules on
# Mac OS X use an .so extension.
if self._properties["productType"] == "com.googlecode.gyp.xcode.bundle":
self._properties[
"productType"
] = "com.apple.product-type.library.dynamic"
self._properties["productType"] = (
"com.apple.product-type.library.dynamic"
)
self.SetBuildSetting("MACH_O_TYPE", "mh_bundle")
self.SetBuildSetting("DYLIB_CURRENT_VERSION", "")
self.SetBuildSetting("DYLIB_COMPATIBILITY_VERSION", "")
@ -2540,9 +2536,10 @@ class PBXNativeTarget(XCTarget):
force_extension = suffix[1:]
if (
self._properties["productType"] in {
self._properties["productType"]
in {
"com.apple.product-type-bundle.unit.test",
"com.apple.product-type-bundle.ui-testing"
"com.apple.product-type-bundle.ui-testing",
}
) and force_extension is None:
force_extension = suffix[1:]
@ -2694,10 +2691,8 @@ class PBXNativeTarget(XCTarget):
other._properties["productType"] == static_library_type
or (
(
other._properties["productType"] in {
shared_library_type,
framework_type
}
other._properties["productType"]
in {shared_library_type, framework_type}
)
and (
(not other.HasBuildSetting("MACH_O_TYPE"))
@ -2706,7 +2701,6 @@ class PBXNativeTarget(XCTarget):
)
)
):
file_ref = other.GetProperty("productReference")
pbxproject = self.PBXProjectAncestor()
@ -2732,13 +2726,13 @@ class PBXProject(XCContainerPortal):
# PBXContainerItemProxy.
"""
Attributes:
path: "sample.xcodeproj". TODO(mark) Document me!
_other_pbxprojects: A dictionary, keyed by other PBXProject objects. Each
value is a reference to the dict in the
projectReferences list associated with the keyed
PBXProject.
"""
Attributes:
path: "sample.xcodeproj". TODO(mark) Document me!
_other_pbxprojects: A dictionary, keyed by other PBXProject objects. Each
value is a reference to the dict in the
projectReferences list associated with the keyed
PBXProject.
"""
_schema = XCContainerPortal._schema.copy()
_schema.update(
@ -2833,17 +2827,17 @@ class PBXProject(XCContainerPortal):
def RootGroupForPath(self, path):
"""Returns a PBXGroup child of this object to which path should be added.
This method is intended to choose between SourceGroup and
IntermediatesGroup on the basis of whether path is present in a source
directory or an intermediates directory. For the purposes of this
determination, any path located within a derived file directory such as
PROJECT_DERIVED_FILE_DIR is treated as being in an intermediates
directory.
This method is intended to choose between SourceGroup and
IntermediatesGroup on the basis of whether path is present in a source
directory or an intermediates directory. For the purposes of this
determination, any path located within a derived file directory such as
PROJECT_DERIVED_FILE_DIR is treated as being in an intermediates
directory.
The returned value is a two-element tuple. The first element is the
PBXGroup, and the second element specifies whether that group should be
organized hierarchically (True) or as a single flat list (False).
"""
The returned value is a two-element tuple. The first element is the
PBXGroup, and the second element specifies whether that group should be
organized hierarchically (True) or as a single flat list (False).
"""
# TODO(mark): make this a class variable and bind to self on call?
# Also, this list is nowhere near exhaustive.
@ -2869,11 +2863,11 @@ class PBXProject(XCContainerPortal):
def AddOrGetFileInRootGroup(self, path):
"""Returns a PBXFileReference corresponding to path in the correct group
according to RootGroupForPath's heuristics.
according to RootGroupForPath's heuristics.
If an existing PBXFileReference for path exists, it will be returned.
Otherwise, one will be created and returned.
"""
If an existing PBXFileReference for path exists, it will be returned.
Otherwise, one will be created and returned.
"""
(group, hierarchical) = self.RootGroupForPath(path)
return group.AddOrGetFileByPath(path, hierarchical)
@ -2923,17 +2917,17 @@ class PBXProject(XCContainerPortal):
def AddOrGetProjectReference(self, other_pbxproject):
"""Add a reference to another project file (via PBXProject object) to this
one.
one.
Returns [ProductGroup, ProjectRef]. ProductGroup is a PBXGroup object in
this project file that contains a PBXReferenceProxy object for each
product of each PBXNativeTarget in the other project file. ProjectRef is
a PBXFileReference to the other project file.
Returns [ProductGroup, ProjectRef]. ProductGroup is a PBXGroup object in
this project file that contains a PBXReferenceProxy object for each
product of each PBXNativeTarget in the other project file. ProjectRef is
a PBXFileReference to the other project file.
If this project file already references the other project file, the
existing ProductGroup and ProjectRef are returned. The ProductGroup will
still be updated if necessary.
"""
If this project file already references the other project file, the
existing ProductGroup and ProjectRef are returned. The ProductGroup will
still be updated if necessary.
"""
if "projectReferences" not in self._properties:
self._properties["projectReferences"] = []
@ -2985,7 +2979,7 @@ class PBXProject(XCContainerPortal):
# Xcode seems to sort this list case-insensitively
self._properties["projectReferences"] = sorted(
self._properties["projectReferences"],
key=lambda x: x["ProjectRef"].Name().lower()
key=lambda x: x["ProjectRef"].Name().lower(),
)
else:
# The link already exists. Pull out the relevant data.
@ -3010,11 +3004,8 @@ class PBXProject(XCContainerPortal):
# define an explicit value for 'SYMROOT'.
symroots = self._DefinedSymroots(target)
for s in self._DefinedSymroots(target):
if (
(s is not None
and not self._IsUniqueSymrootForTarget(s))
or (s is None
and not inherit_unique_symroot)
if (s is not None and not self._IsUniqueSymrootForTarget(s)) or (
s is None and not inherit_unique_symroot
):
return False
return True if symroots else inherit_unique_symroot
@ -3118,7 +3109,8 @@ class PBXProject(XCContainerPortal):
product_group._properties["children"] = sorted(
product_group._properties["children"],
key=cmp_to_key(
lambda x, y, rp=remote_products: CompareProducts(x, y, rp)),
lambda x, y, rp=remote_products: CompareProducts(x, y, rp)
),
)
@ -3152,9 +3144,7 @@ class XCProjectFile(XCObject):
self._XCPrint(file, 0, "{ ")
else:
self._XCPrint(file, 0, "{\n")
for property, value in sorted(
self._properties.items()
):
for property, value in sorted(self._properties.items()):
if property == "objects":
self._PrintObjects(file)
else:
@ -3180,9 +3170,7 @@ class XCProjectFile(XCObject):
for class_name in sorted(objects_by_class):
self._XCPrint(file, 0, "\n")
self._XCPrint(file, 0, "/* Begin " + class_name + " section */\n")
for object in sorted(
objects_by_class[class_name], key=attrgetter("id")
):
for object in sorted(objects_by_class[class_name], key=attrgetter("id")):
object.Print(file)
self._XCPrint(file, 0, "/* End " + class_name + " section */\n")

View File

@ -9,7 +9,6 @@ Working around this: http://bugs.python.org/issue5752
TODO(bradnelson): Consider dropping this when we drop XP support.
"""
import xml.dom.minidom

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "gyp-next"
version = "0.20.3"
version = "0.20.4"
authors = [
{ name="Node.js contributors", email="ryzokuken@disroot.org" },
]

View File

@ -5,7 +5,6 @@
"""gyptest.py -- test runner for GYP tests."""
import argparse
import os
import platform

View File

@ -8,7 +8,6 @@
generate input suitable for graphviz to render a dependency graph of
targets."""
import collections
import json
import sys
@ -22,7 +21,7 @@ def ParseTarget(target):
def LoadEdges(filename, targets):
"""Load the edges map from the dump file, and filter it to only
show targets in |targets| and their depedendents."""
show targets in |targets| and their depedendents."""
file = open("dump.json")
edges = json.load(file)
@ -43,7 +42,7 @@ def LoadEdges(filename, targets):
def WriteGraph(edges):
"""Print a graphviz graph to stdout.
|edges| is a map of target to a list of other targets it depends on."""
|edges| is a map of target to a list of other targets it depends on."""
# Bucket targets by file.
files = collections.defaultdict(list)
@ -64,9 +63,7 @@ def WriteGraph(edges):
# the display by making it a box without an internal node.
target = targets[0]
build_file, target_name, toolset = ParseTarget(target)
print(
f' "{target}" [shape=box, label="{filename}\\n{target_name}"]'
)
print(f' "{target}" [shape=box, label="{filename}\\n{target_name}"]')
else:
# Group multiple nodes together in a subgraph.
print(' subgraph "cluster_%s" {' % filename)

View File

@ -6,7 +6,6 @@
"""Pretty-prints the contents of a GYP file."""
import re
import sys
@ -49,7 +48,7 @@ def mask_quotes(input):
def do_split(input, masked_input, search_re):
output = []
mask_output = []
for (line, masked_line) in zip(input, masked_input):
for line, masked_line in zip(input, masked_input):
m = search_re.match(masked_line)
while m:
split = len(m.group(1))
@ -63,13 +62,13 @@ def do_split(input, masked_input, search_re):
def split_double_braces(input):
"""Masks out the quotes and comments, and then splits appropriate
lines (lines that matche the double_*_brace re's above) before
indenting them below.
lines (lines that matche the double_*_brace re's above) before
indenting them below.
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).
"""
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*)([\]\}\)])")
@ -85,8 +84,8 @@ def split_double_braces(input):
def count_braces(line):
"""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.
"""
It starts at zero and subtracts for closed braces, and adds for open braces.
"""
open_braces = ["[", "(", "{"]
close_braces = ["]", ")", "}"]
closing_prefix_re = re.compile(r"[^\s\]\}\)]\s*[\]\}\)]+,?\s*$")

View File

@ -6,13 +6,12 @@
"""Prints the information in a sln file in a diffable way.
It first outputs each projects in alphabetical order with their
dependencies.
It first outputs each projects in alphabetical order with their
dependencies.
Then it outputs a possible build order.
Then it outputs a possible build order.
"""
import os
import re
import sys
@ -113,7 +112,7 @@ def PrintDependencies(projects, deps):
print("---------------------------------------")
print("-- --")
for (project, dep_list) in sorted(deps.items()):
for project, dep_list in sorted(deps.items()):
print("Project : %s" % project)
print("Path : %s" % projects[project][0])
if dep_list:
@ -131,7 +130,7 @@ def PrintBuildOrder(projects, deps):
print("-- --")
built = []
for (project, _) in sorted(deps.items()):
for project, _ in sorted(deps.items()):
if project not in built:
BuildProject(project, built, projects, deps)
@ -139,7 +138,6 @@ def PrintBuildOrder(projects, deps):
def PrintVCProj(projects):
for project in projects:
print("-------------------------------------")
print("-------------------------------------")

View File

@ -6,13 +6,12 @@
"""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.
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.
It outputs the resulting xml to stdout.
"""
import os
import sys
from xml.dom.minidom import Node, parse
@ -48,11 +47,11 @@ class CmpNode:
node_string += node.getAttribute("Name")
all_nodes = []
for (name, value) in node.attributes.items():
for name, value in node.attributes.items():
all_nodes.append((name, value))
all_nodes.sort(CmpTuple())
for (name, value) in all_nodes:
for name, value in all_nodes:
node_string += name
node_string += value
@ -81,10 +80,10 @@ def PrettyPrintNode(node, indent=0):
print("{}<{}".format(" " * indent, node.nodeName))
all_attributes = []
for (name, value) in node.attributes.items():
for name, value in node.attributes.items():
all_attributes.append((name, value))
all_attributes.sort(CmpTuple())
for (name, value) in all_attributes:
for name, value in all_attributes:
print('{} {}="{}"'.format(" " * indent, name, value))
print("%s>" % (" " * indent))
if node.nodeValue:
@ -130,7 +129,7 @@ def FixFilenames(filenames, current_directory):
def AbsoluteNode(node):
"""Makes all the properties we know about in this node absolute."""
if node.attributes:
for (name, value) in node.attributes.items():
for name, value in node.attributes.items():
if name in [
"InheritedPropertySheets",
"RelativePath",
@ -163,7 +162,7 @@ def CleanupVcproj(node):
# 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():
for name, value in node.attributes.items():
sorted_list = sorted(value.split(";"))
unique_list = []
for i in sorted_list:
@ -252,7 +251,7 @@ def MergeAttributes(node1, node2):
if not node2.attributes:
return
for (name, value2) in node2.attributes.items():
for name, value2 in node2.attributes.items():
# Don't merge the 'Name' attribute.
if name == "Name":
continue