| # Copyright 2017 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Rules that allows select() to differentiate between Apple OS versions.""" |
| |
| def _strip_version(version): |
| """Strip trailing characters that aren't digits or '.' from version names. |
| |
| Some OS versions look like "9.0gm", which is not useful for select() |
| statements. Thus, we strip the trailing "gm" part. |
| |
| Args: |
| version: the version string |
| |
| Returns: |
| The version with trailing letters stripped. |
| """ |
| result = "" |
| string = str(version) |
| for i in range(len(string)): |
| ch = string[i] |
| if not ch.isdigit() and ch != ".": |
| break |
| |
| result += ch |
| |
| return result |
| |
| def _strip_or_pad_version(version, num_components): |
| """Strips or pads a version string to the given number of components. |
| |
| If the version string contains fewer than the requested number of |
| components, it will be padded with zeros. |
| |
| Args: |
| version: The version string. |
| num_components: The desired number of components. |
| |
| Returns: |
| The version, stripped or padded to the requested number of components. |
| """ |
| version_string = str(version) |
| components = version_string.split(".") |
| if num_components <= len(components): |
| return ".".join(components[:num_components]) |
| return version_string + (".0" * (num_components - len(components))) |
| |
| _VERSION_PRECISION_COMPONENTS = { |
| "major": 1, |
| "minor": 2, |
| "patch": 3, |
| } |
| |
| def _xcode_version_flag_impl(ctx): |
| """A rule that allows select() to differentiate between Xcode versions.""" |
| xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig] |
| xcode_version = xcode_config.xcode_version() |
| precision = ctx.attr.precision |
| |
| if not precision: |
| value = _strip_version(xcode_version) |
| elif precision == "exact": |
| value = str(xcode_version) |
| else: |
| num_components = _VERSION_PRECISION_COMPONENTS[precision] |
| value = _strip_or_pad_version(xcode_version, num_components) |
| |
| return config_common.FeatureFlagInfo(value = value) |
| |
| def _ios_sdk_version_flag_impl(ctx): |
| """A rule that allows select() to select based on the iOS SDK version.""" |
| xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig] |
| |
| return config_common.FeatureFlagInfo(value = _strip_version( |
| xcode_config.sdk_version_for_platform( |
| apple_common.platform.ios_device, |
| ), |
| )) |
| |
| def _tvos_sdk_version_flag_impl(ctx): |
| """A rule that allows select() to select based on the tvOS SDK version.""" |
| xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig] |
| |
| return config_common.FeatureFlagInfo(value = _strip_version( |
| xcode_config.sdk_version_for_platform( |
| apple_common.platform.tvos_device, |
| ), |
| )) |
| |
| def _watchos_sdk_version_flag_impl(ctx): |
| """A rule that allows select() to select based on the watchOS SDK version.""" |
| xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig] |
| |
| return config_common.FeatureFlagInfo(value = _strip_version( |
| xcode_config.sdk_version_for_platform( |
| apple_common.platform.watchos_device, |
| ), |
| )) |
| |
| def _macos_sdk_version_flag_impl(ctx): |
| """A rule that allows select() to select based on the macOS SDK version.""" |
| xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig] |
| |
| return config_common.FeatureFlagInfo(value = _strip_version( |
| xcode_config.sdk_version_for_platform( |
| apple_common.platform.macos, |
| ), |
| )) |
| |
| xcode_version_flag = rule( |
| implementation = _xcode_version_flag_impl, |
| attrs = { |
| "precision": attr.string( |
| doc = """\ |
| The desired precision with which the version number will be provided. The |
| permitted values of this attribute are given below, with examples of the value |
| that the rule would provide if the selected `xcode_version` target reported |
| `version = "1.2.3.4X789"`: |
| |
| - `major`: Provide only the major component of the version number (e.g., `1`). |
| - `minor`: Provide only the major and minor components of the version number |
| (e.g., `1.2`). |
| - `patch`: Provide only the major, minor, and patch components of the version |
| number (e.g., `1.2.3`). |
| - `exact`: Provide the version number reported by the `xcode_version` target |
| unmodified (e.g., `1.2.3.4X789`). |
| |
| If `minor` or `patch` precision is requested and the version number reported by |
| the `xcode_version` has a lower precision, the result will be padded with `.0` |
| segments as necessary to ensure that it is consistent. For example, if the |
| selected `xcode_version` reported `version = "1"`, then `minor` precision would |
| be `1.0` and `patch` precision would be `1.0.0`. |
| |
| Setting the precision lets you write `config_setting`s that test for more or |
| less specific versions of Xcode. For example, if you want to handle all `11.*` |
| versions of Xcode identically but do something different for Xcode 12.0 and |
| 12.1, you can express that as follows: |
| |
| ``` |
| config_setting( |
| name = "xcode_11_any", |
| flag_values = { |
| "//tools/osx:xcode_version_flag_major": "11", |
| }, |
| ) |
| |
| config_setting( |
| name = "xcode_12_0", |
| flag_values = { |
| "//tools/osx:xcode_version_flag_minor": "12.0", |
| }, |
| ) |
| |
| config_setting( |
| name = "xcode_12_1", |
| flag_values = { |
| "//tools/osx:xcode_version_flag_minor": "12.1", |
| }, |
| ) |
| ``` |
| """, |
| mandatory = False, |
| values = ["major", "minor", "patch", "exact"], |
| ), |
| "_xcode_config": attr.label(default = configuration_field( |
| fragment = "apple", |
| name = "xcode_config_label", |
| )), |
| }, |
| ) |
| |
| ios_sdk_version_flag = rule( |
| implementation = _ios_sdk_version_flag_impl, |
| attrs = { |
| "_xcode_config": attr.label(default = configuration_field( |
| fragment = "apple", |
| name = "xcode_config_label", |
| )), |
| }, |
| ) |
| |
| tvos_sdk_version_flag = rule( |
| implementation = _tvos_sdk_version_flag_impl, |
| attrs = { |
| "_xcode_config": attr.label(default = configuration_field( |
| fragment = "apple", |
| name = "xcode_config_label", |
| )), |
| }, |
| ) |
| |
| watchos_sdk_version_flag = rule( |
| implementation = _watchos_sdk_version_flag_impl, |
| attrs = { |
| "_xcode_config": attr.label(default = configuration_field( |
| fragment = "apple", |
| name = "xcode_config_label", |
| )), |
| }, |
| ) |
| |
| macos_sdk_version_flag = rule( |
| implementation = _macos_sdk_version_flag_impl, |
| attrs = { |
| "_xcode_config": attr.label(default = configuration_field( |
| fragment = "apple", |
| name = "xcode_config_label", |
| )), |
| }, |
| ) |