summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bazelci/presubmit.yml87
-rwxr-xr-x.bazelci/update_workspace_to_deps_heads.sh33
-rw-r--r--.bazelrc6
-rw-r--r--.bcr/config.yml3
-rw-r--r--.bcr/metadata.template.json20
-rw-r--r--.bcr/presubmit.yml17
-rw-r--r--.bcr/source.template.json5
-rwxr-xr-x.github/generate-notes.sh41
-rw-r--r--.github/workflows/create-release.yml26
-rw-r--r--.gitignore1
-rw-r--r--.pre-commit-config.yaml9
-rw-r--r--AUTHORS9
-rw-r--r--BUILD39
-rw-r--r--CODEOWNERS1
-rw-r--r--CONTRIBUTING.md34
-rw-r--r--LICENSE202
-rw-r--r--METADATA14
-rw-r--r--MODULE.bazel22
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--OWNERS1
-rw-r--r--README.md92
-rw-r--r--WORKSPACE46
-rw-r--r--WORKSPACE.bzlmod0
-rw-r--r--configs/BUILD59
-rw-r--r--configs/platforms.bzl126
-rw-r--r--constraints/BUILD52
-rw-r--r--crosstool/BUILD23
-rw-r--r--crosstool/BUILD.toolchains23
-rw-r--r--crosstool/BUILD.tpl89
-rw-r--r--crosstool/cc_toolchain_config.bzl2642
-rwxr-xr-xcrosstool/libtool.sh141
-rw-r--r--crosstool/libtool_check_unique.cc109
-rw-r--r--crosstool/make_hashed_objlist.py58
-rw-r--r--crosstool/osx_cc_configure.bzl195
-rw-r--r--crosstool/setup.bzl71
-rw-r--r--crosstool/wrapped_clang.cc472
-rwxr-xr-xcrosstool/xcrunwrapper.sh44
-rw-r--r--doc/BUILD71
-rw-r--r--doc/README.md50
-rw-r--r--doc/apple_support.md395
-rw-r--r--doc/rules.md143
-rw-r--r--doc/xcode_support.md62
-rw-r--r--lib/BUILD72
-rw-r--r--lib/apple_support.bzl625
-rw-r--r--lib/lipo.bzl146
-rw-r--r--lib/repositories.bzl59
-rw-r--r--lib/swizzle_absolute_xcttestsourcelocation.m55
-rw-r--r--lib/transitions.bzl49
-rw-r--r--lib/xcode_support.bzl44
-rw-r--r--platform_mappings137
-rw-r--r--platforms/BUILD46
-rw-r--r--rules/BUILD54
-rw-r--r--rules/apple_genrule.bzl64
-rw-r--r--rules/private/BUILD24
-rw-r--r--rules/private/apple_genrule.bzl198
-rw-r--r--rules/rules.doc.bzl11
-rw-r--r--rules/toolchain_substitution.bzl52
-rw-r--r--rules/universal_binary.bzl87
-rw-r--r--test/BUILD126
-rwxr-xr-xtest/apple_genrule_test.sh36
-rw-r--r--test/apple_support_test.bzl281
-rw-r--r--test/binary_tests.bzl145
-rw-r--r--test/cc_test_with_objc_deps.cc14
-rw-r--r--test/fixtures/linking/BUILD0
-rw-r--r--test/linking_tests.bzl66
-rw-r--r--test/main.c3
-rw-r--r--test/main.cc3
-rw-r--r--test/main.m3
-rw-r--r--test/objc_lib.h3
-rw-r--r--test/objc_lib.m5
-rw-r--r--test/rules/BUILD0
-rw-r--r--test/rules/action_command_line_test.bzl149
-rw-r--r--test/rules/apple_verification_test.bzl180
-rw-r--r--test/shell/BUILD35
-rwxr-xr-xtest/shell/integration_test_setup.sh25
-rwxr-xr-xtest/shell/testenv.sh23
-rw-r--r--test/shell/unittest.bash846
-rw-r--r--test/shell/unittest_utils.sh181
-rwxr-xr-xtest/shell/verify_binary.sh34
-rwxr-xr-xtest/shell/wrapped_clang_test.sh106
-rw-r--r--test/starlark_apple_binary.bzl84
-rw-r--r--test/starlark_apple_static_library.bzl90
-rw-r--r--test/test_data/BUILD141
-rw-r--r--test/test_data/cc_lib.cc3
-rw-r--r--test/test_data/ios_main.m5
-rw-r--r--test/test_data/main.cc32
-rw-r--r--test/test_data/main.m3
-rw-r--r--test/test_data/objc lib with spaces.m3
-rw-r--r--test/test_data/objc_lib.m3
-rw-r--r--test/test_data/objc_lib_with_unused_symbol.m11
-rw-r--r--test/test_data/watch_main.m9
-rw-r--r--test/transitions.bzl214
-rw-r--r--test/underlying_lib.h1
-rw-r--r--test/universal_binary_test.bzl114
-rwxr-xr-xtest/verify_archive_timestamps.sh24
-rwxr-xr-xtest/verify_stripped_symbols.sh9
-rwxr-xr-xtest/verify_unused_symbol_exists.sh9
-rw-r--r--test/xcode_support_test.bzl59
-rw-r--r--tools/BUILD17
99 files changed, 10351 insertions, 0 deletions
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
new file mode 100644
index 0000000..7cc5cab
--- /dev/null
+++ b/.bazelci/presubmit.yml
@@ -0,0 +1,87 @@
+---
+x_defaults:
+ # YAML has a feature for "repeated nodes", BazelCI is fine with extra nodes
+ # it doesn't know about; so that is used to avoid repeating common subparts.
+ common: &common
+ platform: macos
+ xcode_version: "14.2"
+ build_targets:
+ - "//..."
+ test_targets:
+ - "//..."
+
+ linux_common: &linux_common
+ platform: ubuntu2004
+ build_targets:
+ - "//test/test_data:multi_arch_cc_binary"
+
+tasks:
+ macos_latest:
+ name: "Current LTS"
+ bazel: latest
+ <<: *common
+ build_flags:
+ # Bazel 6.x has dependencies that produce warnings now
+ # TODO: Remove once latest is Bazel 7+
+ - "--features=-treat_warnings_as_errors"
+ test_flags:
+ # Bazel 6.x has dependencies that produce warnings now
+ # TODO: Remove once latest is Bazel 7+
+ - "--features=-treat_warnings_as_errors"
+
+ macos_latest_bzlmod:
+ name: "Current LTS using Bzlmod"
+ bazel: latest
+ <<: *common
+ build_targets:
+ - "//..."
+ - "--"
+ - "-//doc/..."
+ test_targets:
+ - "//..."
+ - "--"
+ - "-//doc/..."
+ build_flags:
+ - "--enable_bzlmod"
+ # Bazel 6.x has dependencies that produce warnings now
+ # TODO: Remove once latest is Bazel 7+
+ - "--features=-treat_warnings_as_errors"
+ test_flags:
+ - "--enable_bzlmod"
+ # Bazel 6.x has dependencies that produce warnings now
+ # TODO: Remove once latest is Bazel 7+
+ - "--features=-treat_warnings_as_errors"
+
+ macos_last_green:
+ name: "Last Green Bazel"
+ bazel: last_green
+ <<: *common
+
+ macos_latest_head_deps:
+ name: "Current LTS with Head Deps"
+ bazel: latest
+ shell_commands:
+ # Update the WORKSPACE to use head versions of some deps to ensure nothing
+ # has landed on them breaking this project.
+ - .bazelci/update_workspace_to_deps_heads.sh
+ <<: *common
+ build_flags:
+ # Bazel 6.x has dependencies that produce warnings now
+ # TODO: Remove once latest is Bazel 7+
+ - "--features=-treat_warnings_as_errors"
+ test_flags:
+ # Bazel 6.x has dependencies that produce warnings now
+ # TODO: Remove once latest is Bazel 7+
+ - "--features=-treat_warnings_as_errors"
+
+ linux_latest:
+ name: "Current LTS"
+ bazel: latest
+ <<: *linux_common
+
+ linux_last_green:
+ name: "Last Green Bazel"
+ bazel: last_green
+ <<: *linux_common
+
+buildifier: latest
diff --git a/.bazelci/update_workspace_to_deps_heads.sh b/.bazelci/update_workspace_to_deps_heads.sh
new file mode 100755
index 0000000..33f486d
--- /dev/null
+++ b/.bazelci/update_workspace_to_deps_heads.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copyright 2019 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.
+
+set -eu
+
+# Modify the WORKSPACE to pull in the master branches of some deps.
+sed \
+ -i "" \
+ -e \
+ '/^workspace.*/a \
+\
+load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")\
+\
+git_repository(\
+\ name = "bazel_skylib",\
+\ remote = "https://github.com/bazelbuild/bazel-skylib.git",\
+\ branch = "main",\
+)\
+' \
+ WORKSPACE
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..6716d81
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,6 @@
+build --enable_platform_specific_config
+build --features=treat_warnings_as_errors
+
+build:macos --apple_crosstool_top=@local_config_apple_cc//:toolchain
+build:macos --crosstool_top=@local_config_apple_cc//:toolchain
+build:macos --host_crosstool_top=@local_config_apple_cc//:toolchain
diff --git a/.bcr/config.yml b/.bcr/config.yml
new file mode 100644
index 0000000..346fa34
--- /dev/null
+++ b/.bcr/config.yml
@@ -0,0 +1,3 @@
+fixedReleaser:
+ login: BalestraPatrick
+ email: me@patrickbalestra.com
diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json
new file mode 100644
index 0000000..bb4e445
--- /dev/null
+++ b/.bcr/metadata.template.json
@@ -0,0 +1,20 @@
+{
+ "homepage": "https://github.com/bazelbuild/apple_support",
+ "maintainers": [
+ {
+ "email": "keithbsmiley@gmail.com",
+ "github": "keith",
+ "name": "Keith Smiley"
+ },
+ {
+ "email": "me@patrickbalestra.com",
+ "github": "BalestraPatrick",
+ "name": "Patrick Balestra"
+ }
+ ],
+ "repository": [
+ "github:bazelbuild/apple_support"
+ ],
+ "versions": [],
+ "yanked_versions": {}
+}
diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml
new file mode 100644
index 0000000..b8209c4
--- /dev/null
+++ b/.bcr/presubmit.yml
@@ -0,0 +1,17 @@
+matrix:
+ platform: ["centos7", "debian10", "macos", "ubuntu2004", "windows"]
+
+tasks:
+ verify_targets:
+ name: "Build targets under //lib"
+ platform: ${{ platform }}
+ build_targets:
+ - '@apple_support//lib/...'
+ run_tests:
+ name: "Run test targets"
+ platform: "macos"
+ test_targets:
+ - '@apple_support//test/...'
+ - '--'
+ # Needs the new toolchain
+ - '-@apple_support//test:linking_disable_objc_apple_link_test'
diff --git a/.bcr/source.template.json b/.bcr/source.template.json
new file mode 100644
index 0000000..3965fd9
--- /dev/null
+++ b/.bcr/source.template.json
@@ -0,0 +1,5 @@
+{
+ "integrity": "",
+ "strip_prefix": "",
+ "url": "https://github.com/bazelbuild/apple_support/releases/download/{TAG}/apple_support.{TAG}.tar.gz"
+}
diff --git a/.github/generate-notes.sh b/.github/generate-notes.sh
new file mode 100755
index 0000000..1621807
--- /dev/null
+++ b/.github/generate-notes.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+set -euo pipefail
+
+readonly new_version=$1
+readonly release_archive="apple_support.$new_version.tar.gz"
+
+sha=$(shasum -a 256 "$release_archive" | cut -d " " -f1)
+
+cat <<EOF
+## What's Changed
+
+TODO
+
+This release is compatible with: TODO
+
+### MODULE.bazel Snippet
+
+\`\`\`bzl
+bazel_dep(name = "apple_support", version = "$new_version", repo_name = "build_bazel_apple_support")
+\`\`\`
+
+### Workspace Snippet
+
+\`\`\`bzl
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "build_bazel_apple_support",
+ sha256 = "$sha",
+ url = "https://github.com/bazelbuild/apple_support/releases/download/$new_version/apple_support.$new_version.tar.gz",
+)
+
+load(
+ "@build_bazel_apple_support//lib:repositories.bzl",
+ "apple_support_dependencies",
+)
+
+apple_support_dependencies()
+\`\`\`
+EOF
diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
new file mode 100644
index 0000000..6d7335e
--- /dev/null
+++ b/.github/workflows/create-release.yml
@@ -0,0 +1,26 @@
+name: Create Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: 'The new version to tag, ex: 1.0.5'
+ required: true
+ type: string
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Create Release
+ run: |
+ set -euo pipefail
+
+ COPYFILE_DISABLE=1 tar czvf "apple_support.$TAG.tar.gz" ./*
+ ./.github/generate-notes.sh "$TAG" | tee notes.md
+ gh release create "$TAG" --title "$TAG" --target "$GITHUB_REF_NAME" --notes-file notes.md "apple_support.$TAG.tar.gz"
+ env:
+ TAG: ${{ inputs.tag }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a6ef824
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/bazel-*
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..47f6895
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,9 @@
+# See CONTRIBUTING.md for instructions.
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+ - repo: https://github.com/keith/pre-commit-buildifier
+ rev: 6.3.3
+ hooks:
+ - id: buildifier
+ - id: buildifier-lint
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8f95963
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+# This the official list of Bazel authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+
+Google Inc.
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..3e989df
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,39 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+licenses(["notice"])
+
+exports_files(["LICENSE"])
+
+# An umbrella bzl_library for anything that needs it (like to then use stardoc),
+# but odds are using the specific sub bzl_library to with the public bzl files
+# are a better choice to get the proper subset of dependencies.
+bzl_library(
+ name = "apple_support",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:apple_support",
+ "//lib:lipo",
+ "//lib:xcode_support",
+ "//rules:apple_genrule",
+ ],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = 1,
+ srcs = [
+ "WORKSPACE",
+ "//configs:for_bazel_tests",
+ "//constraints:for_bazel_tests",
+ "//crosstool:for_bazel_tests",
+ "//lib:for_bazel_tests",
+ "//platforms:for_bazel_tests",
+ "//rules:for_bazel_tests",
+ "//tools:for_bazel_tests",
+ ],
+ # Exposed publicly just so other rules can use this if they set up
+ # integration tests that need to copy all the support files into
+ # a temporary workspace for the tests.
+ visibility = ["//visibility:public"],
+)
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..50a5cca
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1 @@
+* @BalestraPatrick @brentleyjones @keith @luispadron @mattrobmattrob @segiddins @thii
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..81b9e03
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,34 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Setting up your development environment
+
+To enforce a consistent code style through our code base, we use `buildifier`
+from the [bazelbuild/buildtools](https://github.com/bazelbuild/buildtools) to
+format `BUILD` and `*.bzl` files. We also use `buildifier --lint=warn` to check
+for common issues.
+
+You can download `buildifier` from
+[bazelbuild/buildtools Releases Page](https://github.com/bazelbuild/buildtools/releases).
+
+Bazel's CI is configured to ensure that files in pull requests are formatted
+correctly and that there are no lint issues.
+
+## Community Guidelines
+
+This project follows [Google's Open Source Community
+Guidelines](https://opensource.google.com/conduct/).
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..cdb83f7
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,14 @@
+name: "apple_support"
+description:
+ "Apple Support for Bazel"
+
+third_party {
+ identifier {
+ type: "Git"
+ value: "https://github.com/bazelbuild/apple_support"
+ primary_source: true
+ }
+ version: "1.11.1"
+ last_upgrade_date { year: 2023 month: 12 day: 13 }
+ license_type: NOTICE
+}
diff --git a/MODULE.bazel b/MODULE.bazel
new file mode 100644
index 0000000..c0309a2
--- /dev/null
+++ b/MODULE.bazel
@@ -0,0 +1,22 @@
+module(
+ name = "apple_support",
+ version = "0",
+ bazel_compatibility = [">=6.0.0"],
+ compatibility_level = 1,
+ repo_name = "build_bazel_apple_support",
+)
+
+bazel_dep(name = "bazel_skylib", version = "1.3.0")
+bazel_dep(name = "platforms", version = "0.0.7")
+
+bazel_dep(
+ name = "stardoc",
+ version = "0.6.2",
+ dev_dependency = True,
+ repo_name = "io_bazel_stardoc",
+)
+
+apple_cc_configure = use_extension("//crosstool:setup.bzl", "apple_cc_configure_extension")
+use_repo(apple_cc_configure, "local_config_apple_cc", "local_config_apple_cc_toolchains")
+
+register_toolchains("@local_config_apple_cc_toolchains//:all")
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..2e8f086
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/system/core:main:/janitors/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b02a8f0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,92 @@
+# Apple Support for [Bazel](https://bazel.build)
+
+This repository contains the [Apple CC toolchain](#toolchain-setup),
+Apple related [platforms](platforms/BUILD) and
+[constraints](constraints/BUILD) definitions, and small helper functions
+for rules authors targeting Apple platforms.
+
+If you want to build iOS, tvOS, visionOS, watchOS, or macOS apps, use
+[`rules_apple`][rules_apple].
+
+If you want to build Swift use
+[`rules_swift`](https://github.com/bazelbuild/rules_swift).
+
+See [the documentation](doc) for the helper rules provided by this
+repository.
+
+## Installation
+
+Copy the `MODULE.bazel` or `WORKSPACE` snippets from [the releases
+page](https://github.com/bazelbuild/apple_support/releases) into your
+project.
+
+## Toolchain setup
+
+The Apple CC toolchain in by this repository provides toolchains for
+building for Apple platforms besides macOS. Since Bazel 7 this toolchain
+is required when targeting those platforms but the toolchain also
+supports Bazel 6.
+
+To use the Apple CC toolchain, pull this repository into your build and
+add this to your `.bazelrc`:
+
+```bzl
+build --enable_platform_specific_config
+build:macos --apple_crosstool_top=@local_config_apple_cc//:toolchain
+build:macos --crosstool_top=@local_config_apple_cc//:toolchain
+build:macos --host_crosstool_top=@local_config_apple_cc//:toolchain
+```
+
+This ensures that all rules provided by [`rules_apple`][rules_apple], as
+well as other rules like `cc_binary`, all use the toolchain provided by
+this repository when building on macOS.
+
+NOTE: This toolchain requires a full Xcode installation, not just the
+Xcode Command Line Tools. If you only need to build for macOS and don't
+want to require a full Xcode installation in your build, use the builtin
+Unix toolchain provided by Bazel.
+
+### bzlmod
+
+If you're using bzlmod with the `--crosstool_top` configurations you
+must expose the `local_config_apple_cc` repository to your project by
+putting this in your `MODULE.bazel`:
+
+```bzl
+apple_cc_configure = use_extension("@build_bazel_apple_support//crosstool:setup.bzl", "apple_cc_configure_extension")
+use_repo(apple_cc_configure, "local_config_apple_cc")
+```
+
+### Incompatible toolchain resolution
+
+Bazel 7.x enabled a new discovery method for CC toolchains. With this
+new method you no longer need to pass any `--*crosstool_top` flags.
+Instead you just need to depend on `apple_support` and bazel
+automatically picks the right toolchain based on what you're building.
+If you have any issues with this you can temporarily disable it with
+`--incompatible_enable_cc_toolchain_resolution=false`. If you do please
+file an issue here.
+
+NOTE: If you're using bzlmod and depend on both `apple_support` and
+`rules_cc` in your `MODULE.bazel`, `apple_support`'s `bazel_dep` must
+come _before_ `rules_cc` in order to take precedence over the default CC
+toolchains.
+
+## Toolchain configuration
+
+There are many different flags you can flip to configure how the
+toolchain works. Here are some of the more commonly useful ones:
+
+- Setting `DEVELOPER_DIR` in the environment. This is recommended so
+ that the toolchain can be invalidated when the `DEVELOPER_DIR`
+ changes, which ensures that toolchain binaries will be rebuilt with
+ the new version of Xcode so that caches are correctly shared across
+ machines.
+- Setting `BAZEL_ALLOW_NON_APPLICATIONS_XCODE=1` in the environment (or
+ using `--repo_env`) allows the toolchain to discover Xcode versions
+ outside of the `/Applications` directory to avoid header inclusion
+ errors from bazel. This is not enabled by default because
+ `/Applications` is the standard directory, and this improves toolchain
+ setup performance.
+
+[rules_apple]: https://github.com/bazelbuild/rules_apple
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..a278ce9
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,46 @@
+workspace(name = "build_bazel_apple_support")
+
+load(
+ "@build_bazel_apple_support//lib:repositories.bzl",
+ "apple_support_dependencies",
+)
+
+apple_support_dependencies()
+
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+
+bazel_skylib_workspace()
+
+# For API doc generation
+# This is a dev dependency, users should not need to install it
+# so we declare it in the WORKSPACE
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "io_bazel_stardoc",
+ sha256 = "62bd2e60216b7a6fec3ac79341aa201e0956477e7c8f6ccc286f279ad1d96432",
+ urls = [
+ "https://mirror.bazel.build/github.com/bazelbuild/stardoc/releases/download/0.6.2/stardoc-0.6.2.tar.gz",
+ "https://github.com/bazelbuild/stardoc/releases/download/0.6.2/stardoc-0.6.2.tar.gz",
+ ],
+)
+
+load("@io_bazel_stardoc//:setup.bzl", "stardoc_repositories")
+
+stardoc_repositories()
+
+load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
+
+rules_jvm_external_deps()
+
+load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
+
+rules_jvm_external_setup()
+
+load("@io_bazel_stardoc//:deps.bzl", "stardoc_external_deps")
+
+stardoc_external_deps()
+
+load("@stardoc_maven//:defs.bzl", stardoc_pinned_maven_install = "pinned_maven_install")
+
+stardoc_pinned_maven_install()
diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/WORKSPACE.bzlmod
diff --git a/configs/BUILD b/configs/BUILD
new file mode 100644
index 0000000..22f2cfc
--- /dev/null
+++ b/configs/BUILD
@@ -0,0 +1,59 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("@bazel_skylib//lib:selects.bzl", "selects")
+load(":platforms.bzl", "APPLE_PLATFORMS_CONSTRAINTS")
+
+package(default_visibility = ["//visibility:public"])
+
+selects.config_setting_group(
+ name = "apple",
+ match_any = [
+ cpu
+ for cpu in APPLE_PLATFORMS_CONSTRAINTS.keys()
+ ] + ["darwin"],
+)
+
+[
+ config_setting(
+ name = cpu,
+ values = {"cpu": cpu},
+ )
+ for cpu in APPLE_PLATFORMS_CONSTRAINTS.keys()
+]
+
+# TODO: Remove all references to 'darwin' as a CPU once we no longer support bazel 6.x
+config_setting(
+ name = "darwin",
+ values = {"cpu": "darwin"},
+)
+
+selects.config_setting_group(
+ name = "any_device",
+ match_any = [
+ cpu
+ for cpu, constraints in APPLE_PLATFORMS_CONSTRAINTS.items()
+ if "@build_bazel_apple_support//constraints:device" in constraints
+ ] + ["darwin"],
+)
+
+selects.config_setting_group(
+ name = "any_simulator",
+ match_any = [
+ cpu
+ for cpu, constraints in APPLE_PLATFORMS_CONSTRAINTS.items()
+ if "@build_bazel_apple_support//constraints:simulator" in constraints
+ ],
+)
+
+bzl_library(
+ name = "platforms",
+ srcs = ["platforms.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = True,
+ srcs = glob(["**"]),
+ visibility = ["//:__pkg__"],
+)
diff --git a/configs/platforms.bzl b/configs/platforms.bzl
new file mode 100644
index 0000000..c485b2d
--- /dev/null
+++ b/configs/platforms.bzl
@@ -0,0 +1,126 @@
+"""Defines all the Apple CPUs and their constraints"""
+
+APPLE_PLATFORMS_CONSTRAINTS = {
+ "darwin_arm64": [
+ "@platforms//os:macos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "darwin_arm64e": [
+ "@platforms//os:macos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "darwin_x86_64": [
+ "@platforms//os:macos",
+ "@platforms//cpu:x86_64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "ios_arm64": [
+ "@platforms//os:ios",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "ios_arm64e": [
+ "@platforms//os:ios",
+ "@platforms//cpu:arm64e",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "ios_x86_64": [
+ "@platforms//os:ios",
+ "@platforms//cpu:x86_64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "ios_sim_arm64": [
+ "@platforms//os:ios",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "tvos_arm64": [
+ "@platforms//os:tvos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "tvos_x86_64": [
+ "@platforms//os:tvos",
+ "@platforms//cpu:x86_64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "tvos_sim_arm64": [
+ "@platforms//os:tvos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "visionos_arm64": [
+ "@platforms//os:visionos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "visionos_sim_arm64": [
+ "@platforms//os:visionos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "visionos_x86_64": [
+ "@platforms//os:visionos",
+ "@platforms//cpu:x86_64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "watchos_arm64": [
+ "@platforms//os:watchos",
+ "@platforms//cpu:arm64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+ "watchos_arm64_32": [
+ "@platforms//os:watchos",
+ "@platforms//cpu:arm64_32",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "watchos_armv7k": [
+ "@platforms//os:watchos",
+ "@platforms//cpu:armv7k",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:device",
+ ],
+ "watchos_x86_64": [
+ "@platforms//os:watchos",
+ "@platforms//cpu:x86_64",
+ "@build_bazel_apple_support//constraints:apple",
+ "@build_bazel_apple_support//constraints:simulator",
+ ],
+}
+
+CPU_TO_DEFAULT_PLATFORM_NAME = {
+ "darwin_arm64": "macos_arm64",
+ "darwin_arm64e": "macos_arm64e",
+ "darwin_x86_64": "macos_x86_64",
+ "ios_arm64": "ios_arm64",
+ "ios_arm64e": "ios_arm64e",
+ "ios_x86_64": "ios_x86_64",
+ "ios_sim_arm64": "ios_sim_arm64",
+ "tvos_arm64": "tvos_arm64",
+ "tvos_x86_64": "tvos_x86_64",
+ "tvos_sim_arm64": "tvos_sim_arm64",
+ "visionos_arm64": "visionos_arm64",
+ "visionos_sim_arm64": "visionos_sim_arm64",
+ "visionos_x86_64": "visionos_x86_64",
+ "watchos_arm64": "watchos_arm64",
+ "watchos_arm64_32": "watchos_arm64_32",
+ "watchos_armv7k": "watchos_armv7k",
+ "watchos_x86_64": "watchos_x86_64",
+}
diff --git a/constraints/BUILD b/constraints/BUILD
new file mode 100644
index 0000000..6abdc43
--- /dev/null
+++ b/constraints/BUILD
@@ -0,0 +1,52 @@
+# Standard constraint_settings and constraint_values for items common to rules_swift and rules_apple.
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+filegroup(
+ name = "srcs",
+ srcs = ["BUILD"],
+)
+
+# Constraint indicating the target environment (e.g. device, simulator).
+constraint_setting(
+ name = "target_environment",
+ default_constraint_value = ":device",
+ visibility = ["//visibility:private"],
+)
+
+constraint_value(
+ name = "device",
+ constraint_setting = ":target_environment",
+)
+
+constraint_value(
+ name = "simulator",
+ constraint_setting = ":target_environment",
+)
+
+constraint_value(
+ name = "catalyst",
+ constraint_setting = ":target_environment",
+)
+
+# Constraint indicating if the platform belongs to an "Apple" platform (iOS, watchOS, tvOS, etc.)
+constraint_setting(
+ name = "target_vendor",
+ default_constraint_value = ":apple",
+ visibility = ["//visibility:private"],
+)
+
+constraint_value(
+ name = "apple",
+ constraint_setting = ":target_vendor",
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = True,
+ srcs = glob(["**"]),
+ visibility = ["//:__pkg__"],
+)
diff --git a/crosstool/BUILD b/crosstool/BUILD
new file mode 100644
index 0000000..41b6e00
--- /dev/null
+++ b/crosstool/BUILD
@@ -0,0 +1,23 @@
+package(default_visibility = ["//visibility:public"])
+
+# Files which shouldn't be publicly visible and dependencies of all objc_* or ios_* rules should be excluded.
+exports_files(glob(
+ ["**"],
+))
+
+cc_binary(
+ name = "wrapped_clang",
+ testonly = True,
+ srcs = [
+ "wrapped_clang.cc",
+ ],
+ copts = ["-std=c++17"],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = True,
+ srcs = glob(["**"]),
+ visibility = ["//:__pkg__"],
+)
diff --git a/crosstool/BUILD.toolchains b/crosstool/BUILD.toolchains
new file mode 100644
index 0000000..d52bef7
--- /dev/null
+++ b/crosstool/BUILD.toolchains
@@ -0,0 +1,23 @@
+load("@build_bazel_apple_support//configs:platforms.bzl", "APPLE_PLATFORMS_CONSTRAINTS")
+
+_OSX_DEVELOPER_PLATFORM_CPUS = [
+ "arm64",
+ "x86_64",
+]
+
+[
+ toolchain(
+ name = "cc-toolchain-" + arch + "-" + cpu,
+ exec_compatible_with = [
+ # These only execute on macOS.
+ "@platforms//os:macos",
+ "@platforms//cpu:" + cpu,
+ ],
+ target_compatible_with = APPLE_PLATFORMS_CONSTRAINTS[arch],
+ toolchain = "@local_config_apple_cc//:cc-compiler-" + arch,
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+ visibility = ["//visibility:public"],
+ )
+ for arch in APPLE_PLATFORMS_CONSTRAINTS.keys()
+ for cpu in _OSX_DEVELOPER_PLATFORM_CPUS
+]
diff --git a/crosstool/BUILD.tpl b/crosstool/BUILD.tpl
new file mode 100644
index 0000000..830a94c
--- /dev/null
+++ b/crosstool/BUILD.tpl
@@ -0,0 +1,89 @@
+package(default_visibility = ["//visibility:public"])
+
+load("@build_bazel_apple_support//configs:platforms.bzl", "APPLE_PLATFORMS_CONSTRAINTS")
+load(":cc_toolchain_config.bzl", "cc_toolchain_config")
+
+_APPLE_ARCHS = APPLE_PLATFORMS_CONSTRAINTS.keys()
+
+CC_TOOLCHAINS = [(
+ cpu + "|clang",
+ ":cc-compiler-" + cpu,
+) for cpu in _APPLE_ARCHS] + [(
+ cpu,
+ ":cc-compiler-" + cpu,
+) for cpu in _APPLE_ARCHS] + [
+ ("k8|clang", ":cc-compiler-darwin_x86_64"),
+ ("darwin|clang", ":cc-compiler-darwin_x86_64"),
+ ("k8", ":cc-compiler-darwin_x86_64"),
+ ("darwin", ":cc-compiler-darwin_x86_64"),
+]
+
+cc_library(
+ name = "link_extra_lib",
+)
+
+cc_library(
+ name = "malloc",
+)
+
+filegroup(
+ name = "empty",
+ srcs = [],
+)
+
+filegroup(
+ name = "cc_wrapper",
+ srcs = ["cc_wrapper.sh"],
+)
+
+cc_toolchain_suite(
+ name = "toolchain",
+ toolchains = dict(CC_TOOLCHAINS),
+)
+
+[
+ filegroup(
+ name = "osx_tools_" + arch,
+ srcs = [
+ ":cc_wrapper",
+ ":libtool",
+ ":libtool_check_unique",
+ ":make_hashed_objlist.py",
+ ":wrapped_clang",
+ ":wrapped_clang_pp",
+ ":xcrunwrapper.sh",
+ ],
+ )
+ for arch in _APPLE_ARCHS
+]
+
+[
+ apple_cc_toolchain(
+ name = "cc-compiler-" + arch,
+ all_files = ":osx_tools_" + arch,
+ ar_files = ":osx_tools_" + arch,
+ as_files = ":osx_tools_" + arch,
+ compiler_files = ":osx_tools_" + arch,
+ dwp_files = ":empty",
+ linker_files = ":osx_tools_" + arch,
+ objcopy_files = ":empty",
+ strip_files = ":osx_tools_" + arch,
+ supports_param_files = 1,
+ toolchain_config = arch,
+ toolchain_identifier = arch,
+ )
+ for arch in _APPLE_ARCHS
+]
+
+[
+ cc_toolchain_config(
+ name = arch,
+ compiler = "clang",
+ cpu = arch,
+ cxx_builtin_include_directories = [
+%{cxx_builtin_include_directories}
+ ],
+ tool_paths_overrides = {%{tool_paths_overrides}},
+ )
+ for arch in _APPLE_ARCHS
+]
diff --git a/crosstool/cc_toolchain_config.bzl b/crosstool/cc_toolchain_config.bzl
new file mode 100644
index 0000000..040445c
--- /dev/null
+++ b/crosstool/cc_toolchain_config.bzl
@@ -0,0 +1,2642 @@
+# Copyright 2019 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.
+"""A C++ toolchain configuration rule for macOS."""
+
+load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+load(
+ "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "action_config",
+ "artifact_name_pattern",
+ "env_entry",
+ "env_set",
+ "feature",
+ "feature_set",
+ "flag_group",
+ "flag_set",
+ "make_variable",
+ "tool",
+ "tool_path",
+ "variable_with_value",
+ "with_feature_set",
+)
+load("@build_bazel_apple_support//lib:xcode_support.bzl", "xcode_support")
+
+# TODO: Remove when we drop bazel 6.x support
+_OBJC_ARCHIVE_ACTION_NAME = "objc-archive"
+_OBJCPP_EXECUTABLE_ACTION_NAME = "objc++-executable"
+
+_DYNAMIC_LINK_ACTIONS = [
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_executable,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ACTION_NAMES.objc_executable,
+ _OBJCPP_EXECUTABLE_ACTION_NAME,
+]
+
+def _impl(ctx):
+ if ctx.attr.cpu.startswith("darwin"):
+ platform_type = apple_common.platform_type.macos
+ elif ctx.attr.cpu.startswith("ios"):
+ platform_type = apple_common.platform_type.ios
+ elif ctx.attr.cpu.startswith("tvos"):
+ platform_type = apple_common.platform_type.tvos
+ elif ctx.attr.cpu.startswith("watchos"):
+ platform_type = apple_common.platform_type.watchos
+ elif ctx.attr.cpu.startswith("visionos"):
+ # TODO: Remove when we drop bazel 5.x support, falling back to iOS
+ # doesn't hurt since you can't build for visionOS in this case anyways
+ platform_type = getattr(apple_common.platform_type, "visionos", None) or apple_common.platform_type.ios
+ else:
+ fail("""\
+Unknown CPU: {cpu}. Please update 'apple_support' to the latest version. If \
+you are sure you are on the latest version, try 'bazel shutdown' to work \
+around a Bazel staleness bug. Finally, if you still encounter this error, \
+please file an issue at https://github.com/bazelbuild/apple_support/issues/new
+""".format(cpu = ctx.attr.cpu))
+
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ xcode_execution_requirements = xcode_config.execution_info().keys()
+ target_os_version = xcode_config.minimum_os_for_platform_type(platform_type)
+
+ if (ctx.attr.cpu == "ios_arm64"):
+ target_system_name = "arm64-apple-ios{}".format(target_os_version)
+ elif (ctx.attr.cpu == "tvos_arm64"):
+ target_system_name = "arm64-apple-tvos{}".format(target_os_version)
+ elif (ctx.attr.cpu == "visionos_arm64"):
+ target_system_name = "arm64-apple-xros{}".format(target_os_version)
+ elif (ctx.attr.cpu == "watchos_arm64_32"):
+ target_system_name = "arm64_32-apple-watchos{}".format(target_os_version)
+ elif (ctx.attr.cpu == "ios_arm64e"):
+ target_system_name = "arm64e-apple-ios{}".format(target_os_version)
+ elif (ctx.attr.cpu == "watchos_armv7k"):
+ target_system_name = "armv7k-apple-watchos{}".format(target_os_version)
+ elif (ctx.attr.cpu == "ios_x86_64"):
+ target_system_name = "x86_64-apple-ios{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "ios_sim_arm64"):
+ target_system_name = "arm64-apple-ios{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "tvos_sim_arm64"):
+ target_system_name = "arm64-apple-tvos{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "visionos_sim_arm64"):
+ target_system_name = "arm64-apple-xros{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "watchos_arm64"):
+ target_system_name = "arm64-apple-watchos{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "darwin_x86_64"):
+ target_system_name = "x86_64-apple-macosx{}".format(target_os_version)
+ elif (ctx.attr.cpu == "darwin_arm64"):
+ target_system_name = "arm64-apple-macosx{}".format(target_os_version)
+ elif (ctx.attr.cpu == "darwin_arm64e"):
+ target_system_name = "arm64e-apple-macosx{}".format(target_os_version)
+ elif (ctx.attr.cpu == "tvos_x86_64"):
+ target_system_name = "x86_64-apple-tvos{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "visionos_x86_64"):
+ target_system_name = "x86_64-apple-xros{}-simulator".format(target_os_version)
+ elif (ctx.attr.cpu == "watchos_x86_64"):
+ target_system_name = "x86_64-apple-watchos{}-simulator".format(target_os_version)
+ else:
+ fail("""\
+Unknown CPU: {cpu}. Please update 'apple_support' to the latest version. If \
+you are sure you are on the latest version, try 'bazel shutdown' to work \
+around a Bazel staleness bug. Finally, if you still encounter this error, \
+please file an issue at https://github.com/bazelbuild/apple_support/issues/new
+""".format(cpu = ctx.attr.cpu))
+
+ if ctx.attr.cpu.startswith("darwin_"):
+ target_libc = "macosx"
+ else:
+ target_libc = ctx.attr.cpu.split("_")[0]
+
+ if ctx.attr.cpu == "darwin_x86_64":
+ abi_libc_version = "darwin_x86_64"
+ abi_version = "darwin_x86_64"
+ else:
+ abi_libc_version = "local"
+ abi_version = "local"
+
+ arch = ctx.attr.cpu.split("_", 1)[-1]
+ if ctx.attr.cpu in ["ios_sim_arm64", "tvos_sim_arm64", "visionos_sim_arm64", "watchos_arm64"]:
+ arch = "arm64"
+
+ all_link_actions = [
+ ACTION_NAMES.cpp_link_executable,
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ]
+
+ strip_action = action_config(
+ action_name = ACTION_NAMES.strip,
+ flag_sets = [
+ flag_set(
+ flag_groups = [
+ flag_group(flags = ["-S", "-o", "%{output_file}"]),
+ flag_group(
+ flags = ["%{stripopts}"],
+ iterate_over = "stripopts",
+ ),
+ flag_group(flags = ["%{input_file}"]),
+ ],
+ ),
+ ],
+ tools = [tool(path = "/usr/bin/strip")],
+ )
+
+ cpp_header_parsing_action = action_config(
+ action_name = ACTION_NAMES.cpp_header_parsing,
+ implies = [
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "unfiltered_cxx_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objc_compile_action = action_config(
+ action_name = ACTION_NAMES.objc_compile,
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ flag_groups = [flag_group(flags = ["-target", target_system_name])],
+ ),
+ ],
+ implies = [
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "objc_actions",
+ "apply_default_compiler_flags",
+ "apply_default_warnings",
+ "framework_paths",
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "apply_simulator_compiler_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objc_link_flag_feature = feature(
+ name = "objc_link_flag",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_executable, _OBJCPP_EXECUTABLE_ACTION_NAME],
+ flag_groups = [flag_group(flags = ["-ObjC"])],
+ with_features = [with_feature_set(not_features = ["kernel_extension"])],
+ ),
+ ],
+ )
+
+ objcpp_executable_action = action_config(
+ action_name = _OBJCPP_EXECUTABLE_ACTION_NAME,
+ flag_sets = [
+ flag_set(
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Xlinker",
+ "-objc_abi_version",
+ "-Xlinker",
+ "2",
+ ],
+ ),
+ ],
+ with_features = [with_feature_set(not_features = ["kernel_extension"])],
+ ),
+ flag_set(
+ flag_groups = [
+ flag_group(flags = ["-target", target_system_name]),
+ flag_group(
+ flags = ["-l%{library_names}"],
+ iterate_over = "library_names",
+ ),
+ flag_group(flags = ["-filelist", "%{filelist}"]),
+ flag_group(flags = ["-o", "%{linked_binary}"]),
+ flag_group(
+ flags = ["-force_load", "%{force_load_exec_paths}"],
+ iterate_over = "force_load_exec_paths",
+ ),
+ flag_group(
+ flags = ["%{dep_linkopts}"],
+ iterate_over = "dep_linkopts",
+ ),
+ flag_group(
+ flags = ["-Wl,%{attr_linkopts}"],
+ iterate_over = "attr_linkopts",
+ ),
+ ],
+ ),
+ ],
+ implies = [
+ "include_system_dirs",
+ "framework_paths",
+ "strip_debug_symbols",
+ "apple_env",
+ "apply_implicit_frameworks",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ cpp_link_dynamic_library_action = action_config(
+ action_name = ACTION_NAMES.cpp_link_dynamic_library,
+ implies = [
+ "has_configured_linker_path",
+ "shared_flag",
+ "linkstamps",
+ "output_execpath_flags",
+ "runtime_root_flags",
+ "input_param_flags",
+ "strip_debug_symbols",
+ "linker_param_file",
+ "apple_env",
+ "sysroot",
+ "apply_implicit_frameworks",
+ ],
+ tools = [
+ tool(
+ path = "cc_wrapper.sh",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ cpp_link_static_library_action = action_config(
+ action_name = ACTION_NAMES.cpp_link_static_library,
+ implies = [
+ "runtime_root_flags",
+ "archiver_flags",
+ "input_param_flags",
+ "linker_param_file",
+ "apple_env",
+ ],
+ tools = [
+ tool(
+ path = "libtool",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ c_compile_action = action_config(
+ action_name = ACTION_NAMES.c_compile,
+ implies = [
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "unfiltered_cxx_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ cpp_compile_action = action_config(
+ action_name = ACTION_NAMES.cpp_compile,
+ implies = [
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "unfiltered_cxx_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang_pp",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objcpp_compile_action = action_config(
+ action_name = ACTION_NAMES.objcpp_compile,
+ flag_sets = [
+ flag_set(
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-target",
+ target_system_name,
+ "-stdlib=libc++",
+ "-std=gnu++14",
+ ],
+ ),
+ ],
+ ),
+ ],
+ implies = [
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "apply_default_compiler_flags",
+ "apply_default_warnings",
+ "framework_paths",
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "apply_simulator_compiler_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang_pp",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ assemble_action = action_config(
+ action_name = ACTION_NAMES.assemble,
+ implies = [
+ "objc_arc",
+ "no_objc_arc",
+ "include_system_dirs",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "unfiltered_cxx_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ preprocess_assemble_action = action_config(
+ action_name = ACTION_NAMES.preprocess_assemble,
+ implies = [
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "unfiltered_cxx_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objc_archive_action = action_config(
+ action_name = _OBJC_ARCHIVE_ACTION_NAME,
+ flag_sets = [
+ flag_set(
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-D",
+ "-no_warning_for_no_symbols",
+ "-static",
+ "-filelist",
+ "%{obj_list_path}",
+ "-arch_only",
+ arch,
+ "-syslibroot",
+ "%{sdk_dir}",
+ "-o",
+ "%{output_execpath}",
+ ],
+ ),
+ ],
+ ),
+ ],
+ implies = ["apple_env"],
+ tools = [
+ tool(
+ path = "libtool",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objc_executable_action = action_config(
+ action_name = "objc-executable",
+ flag_sets = [
+ flag_set(
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Xlinker",
+ "-objc_abi_version",
+ "-Xlinker",
+ "2",
+ ],
+ ),
+ ],
+ with_features = [with_feature_set(not_features = ["kernel_extension"])],
+ ),
+ flag_set(
+ flag_groups = [
+ flag_group(flags = ["-target", target_system_name]),
+ flag_group(
+ flags = ["-l%{library_names}"],
+ iterate_over = "library_names",
+ ),
+ flag_group(flags = ["-filelist", "%{filelist}"]),
+ flag_group(flags = ["-o", "%{linked_binary}"]),
+ flag_group(
+ flags = ["-force_load", "%{force_load_exec_paths}"],
+ iterate_over = "force_load_exec_paths",
+ ),
+ flag_group(
+ flags = ["%{dep_linkopts}"],
+ iterate_over = "dep_linkopts",
+ ),
+ flag_group(
+ flags = ["-Wl,%{attr_linkopts}"],
+ iterate_over = "attr_linkopts",
+ ),
+ ],
+ ),
+ ],
+ implies = [
+ "include_system_dirs",
+ "framework_paths",
+ "strip_debug_symbols",
+ "apple_env",
+ "apply_implicit_frameworks",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ cpp_link_executable_action = action_config(
+ action_name = ACTION_NAMES.cpp_link_executable,
+ implies = [
+ "linkstamps",
+ "output_execpath_flags",
+ "runtime_root_flags",
+ "input_param_flags",
+ "force_pic_flags",
+ "strip_debug_symbols",
+ "linker_param_file",
+ "apple_env",
+ "sysroot",
+ "apply_implicit_frameworks",
+ ],
+ tools = [
+ tool(
+ path = "cc_wrapper.sh",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ linkstamp_compile_action = action_config(
+ action_name = ACTION_NAMES.linkstamp_compile,
+ implies = [
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ cpp_module_compile_action = action_config(
+ action_name = ACTION_NAMES.cpp_module_compile,
+ implies = [
+ "preprocessor_defines",
+ "include_system_dirs",
+ "objc_arc",
+ "no_objc_arc",
+ "apple_env",
+ "user_compile_flags",
+ "sysroot",
+ "unfiltered_compile_flags",
+ "compiler_input_flags",
+ "compiler_output_flags",
+ "unfiltered_cxx_flags",
+ ],
+ tools = [
+ tool(
+ path = "wrapped_clang",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ cpp_link_nodeps_dynamic_library_action = action_config(
+ action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ implies = [
+ "has_configured_linker_path",
+ "shared_flag",
+ "linkstamps",
+ "output_execpath_flags",
+ "runtime_root_flags",
+ "input_param_flags",
+ "strip_debug_symbols",
+ "linker_param_file",
+ "apple_env",
+ "sysroot",
+ "apply_implicit_frameworks",
+ ],
+ tools = [
+ tool(
+ path = "cc_wrapper.sh",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objc_fully_link_action = action_config(
+ action_name = "objc-fully-link",
+ flag_sets = [
+ flag_set(
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-D",
+ "-no_warning_for_no_symbols",
+ "-static",
+ "-arch_only",
+ arch,
+ "-syslibroot",
+ "%{sdk_dir}",
+ "-o",
+ "%{fully_linked_archive_path}",
+ ],
+ ),
+ flag_group(
+ flags = ["%{objc_library_exec_paths}"],
+ iterate_over = "objc_library_exec_paths",
+ ),
+ flag_group(
+ flags = ["%{cc_library_exec_paths}"],
+ iterate_over = "cc_library_exec_paths",
+ ),
+ flag_group(
+ flags = ["%{imported_library_exec_paths}"],
+ iterate_over = "imported_library_exec_paths",
+ ),
+ ],
+ ),
+ ],
+ implies = ["apple_env"],
+ tools = [
+ tool(
+ path = "libtool",
+ execution_requirements = xcode_execution_requirements,
+ ),
+ ],
+ )
+
+ objcopy_embed_data_action = action_config(
+ action_name = "objcopy_embed_data",
+ enabled = True,
+ tools = [tool(path = "/usr/bin/objcopy")],
+ )
+
+ action_configs = [
+ strip_action,
+ c_compile_action,
+ cpp_compile_action,
+ linkstamp_compile_action,
+ cpp_module_compile_action,
+ cpp_header_parsing_action,
+ objc_compile_action,
+ objcpp_compile_action,
+ assemble_action,
+ preprocess_assemble_action,
+ objc_archive_action,
+ objc_executable_action,
+ objcpp_executable_action,
+ cpp_link_executable_action,
+ cpp_link_dynamic_library_action,
+ cpp_link_nodeps_dynamic_library_action,
+ cpp_link_static_library_action,
+ objc_fully_link_action,
+ objcopy_embed_data_action,
+ ]
+
+ if (ctx.attr.cpu == "ios_arm64" or
+ ctx.attr.cpu == "ios_arm64e" or
+ ctx.attr.cpu == "ios_sim_arm64" or
+ ctx.attr.cpu == "ios_x86_64" or
+ ctx.attr.cpu == "watchos_arm64_32" or
+ ctx.attr.cpu == "watchos_armv7k" or
+ ctx.attr.cpu == "watchos_x86_64" or
+ ctx.attr.cpu == "watchos_arm64"):
+ apply_default_compiler_flags_feature = feature(
+ name = "apply_default_compiler_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile],
+ flag_groups = [flag_group(flags = ["-DOS_IOS", "-fno-autolink"])],
+ ),
+ ],
+ )
+ elif (ctx.attr.cpu == "darwin_x86_64" or
+ ctx.attr.cpu == "darwin_arm64" or
+ ctx.attr.cpu == "darwin_arm64e"):
+ apply_default_compiler_flags_feature = feature(
+ name = "apply_default_compiler_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile],
+ flag_groups = [flag_group(flags = ["-DOS_MACOSX", "-fno-autolink"])],
+ ),
+ ],
+ )
+ elif (ctx.attr.cpu == "tvos_arm64" or
+ ctx.attr.cpu == "tvos_x86_64" or
+ ctx.attr.cpu == "tvos_sim_arm64"):
+ apply_default_compiler_flags_feature = feature(
+ name = "apply_default_compiler_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile],
+ flag_groups = [flag_group(flags = ["-DOS_TVOS", "-fno-autolink"])],
+ ),
+ ],
+ )
+ elif (
+ ctx.attr.cpu == "visionos_arm64" or
+ ctx.attr.cpu == "visionos_x86_64" or
+ ctx.attr.cpu == "visionos_sim_arm64"
+ ):
+ apply_default_compiler_flags_feature = feature(
+ name = "apply_default_compiler_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile],
+ flag_groups = [flag_group(flags = ["-fno-autolink"])],
+ ),
+ ],
+ )
+ else:
+ fail("""\
+Unknown CPU: {cpu}. Please update 'apple_support' to the latest version. If \
+you are sure you are on the latest version, try 'bazel shutdown' to work \
+around a Bazel staleness bug. Finally, if you still encounter this error, \
+please file an issue at https://github.com/bazelbuild/apple_support/issues/new
+""".format(cpu = ctx.attr.cpu))
+
+ runtime_root_flags_feature = feature(
+ name = "runtime_root_flags",
+ flag_sets = [
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Xlinker",
+ "-rpath",
+ "-Xlinker",
+ "@loader_path/%{runtime_library_search_directories}",
+ ],
+ iterate_over = "runtime_library_search_directories",
+ expand_if_available = "runtime_library_search_directories",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = ["%{runtime_root_flags}"],
+ iterate_over = "runtime_root_flags",
+ expand_if_available = "runtime_root_flags",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = ["%{runtime_root_entries}"],
+ iterate_over = "runtime_root_entries",
+ expand_if_available = "runtime_root_entries",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ objc_arc_feature = feature(
+ name = "objc_arc",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-fobjc-arc"],
+ expand_if_available = "objc_arc",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ unfiltered_cxx_flags_feature = feature(
+ name = "unfiltered_cxx_flags",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ],
+ flag_groups = [
+ flag_group(flags = ["-no-canonical-prefixes", "-pthread"]),
+ ],
+ ),
+ ],
+ )
+
+ compiler_input_flags_feature = feature(
+ name = "compiler_input_flags",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-c", "%{source_file}"],
+ expand_if_available = "source_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ strip_debug_symbols_feature = feature(
+ name = "strip_debug_symbols",
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = ["-Wl,-S"],
+ expand_if_available = "strip_debug_symbols",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ shared_flag_feature = feature(
+ name = "shared_flag",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ],
+ flag_groups = [flag_group(flags = ["-shared"])],
+ ),
+ ],
+ )
+
+ if (ctx.attr.cpu == "ios_x86_64" or
+ ctx.attr.cpu == "ios_sim_arm64" or
+ ctx.attr.cpu == "tvos_x86_64" or
+ ctx.attr.cpu == "tvos_sim_arm64" or
+ ctx.attr.cpu == "visionos_sim_arm64" or
+ ctx.attr.cpu == "visionos_x86_64" or
+ ctx.attr.cpu == "watchos_x86_64" or
+ ctx.attr.cpu == "watchos_arm64"):
+ apply_simulator_compiler_flags_feature = feature(
+ name = "apply_simulator_compiler_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-fexceptions",
+ "-fasm-blocks",
+ "-fobjc-abi-version=2",
+ "-fobjc-legacy-dispatch",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+ else:
+ apply_simulator_compiler_flags_feature = feature(name = "apply_simulator_compiler_flags")
+
+ user_link_flags_feature = feature(
+ name = "user_link_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = ["%{user_link_flags}"],
+ iterate_over = "user_link_flags",
+ expand_if_available = "user_link_flags",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ includes_feature = feature(
+ name = "includes",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ACTION_NAMES.clif_match,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-include", "%{includes}"],
+ iterate_over = "includes",
+ expand_if_available = "includes",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ gcc_coverage_map_format_feature = feature(
+ name = "gcc_coverage_map_format",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-fprofile-arcs", "-ftest-coverage", "-g"],
+ ),
+ ],
+ ),
+ flag_set(
+ actions = [
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ACTION_NAMES.cpp_link_executable,
+ ],
+ flag_groups = [flag_group(flags = ["--coverage"])],
+ ),
+ ],
+ requires = [feature_set(features = ["coverage"])],
+ )
+
+ default_link_flags_feature = feature(
+ name = "default_link_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-no-canonical-prefixes",
+ "-target",
+ target_system_name,
+ "-fobjc-link-runtime",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ no_deduplicate_feature = feature(
+ name = "no_deduplicate",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Xlinker",
+ "-no_deduplicate",
+ ],
+ ),
+ ],
+ with_features = [
+ with_feature_set(not_features = ["opt"]),
+ ],
+ ),
+ ],
+ )
+
+ output_execpath_flags_feature = feature(
+ name = "output_execpath_flags",
+ flag_sets = [
+ flag_set(
+ actions = all_link_actions,
+ flag_groups = [
+ flag_group(
+ flags = ["-o", "%{output_execpath}"],
+ expand_if_available = "output_execpath",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ pic_feature = feature(
+ name = "pic",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.preprocess_assemble,
+ ],
+ flag_groups = [
+ flag_group(flags = ["-fPIC"], expand_if_available = "pic"),
+ ],
+ ),
+ ],
+ )
+
+ framework_paths_feature = feature(
+ name = "framework_paths",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-F%{framework_include_paths}"],
+ iterate_over = "framework_include_paths",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = [
+ "objc-executable",
+ _OBJCPP_EXECUTABLE_ACTION_NAME,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-F%{framework_paths}"],
+ iterate_over = "framework_paths",
+ ),
+ flag_group(
+ flags = ["-framework", "%{framework_names}"],
+ iterate_over = "framework_names",
+ ),
+ flag_group(
+ flags = ["-weak_framework", "%{weak_framework_names}"],
+ iterate_over = "weak_framework_names",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ compiler_output_flags_feature = feature(
+ name = "compiler_output_flags",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-S"],
+ expand_if_available = "output_assembly_file",
+ ),
+ flag_group(
+ flags = ["-E"],
+ expand_if_available = "output_preprocess_file",
+ ),
+ flag_group(
+ flags = ["-o", "%{output_file}"],
+ expand_if_available = "output_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ pch_feature = feature(
+ name = "pch",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-include",
+ "%{pch_file}",
+ ],
+ expand_if_available = "pch_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ include_system_dirs_feature = feature(
+ name = "include_system_dirs",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ "objc-executable",
+ _OBJCPP_EXECUTABLE_ACTION_NAME,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-isysroot",
+ "%{sdk_dir}",
+ "-F%{sdk_framework_dir}",
+ "-F%{platform_developer_framework_dir}",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ input_param_flags_feature = feature(
+ name = "input_param_flags",
+ flag_sets = [
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = ["-L%{library_search_directories}"],
+ iterate_over = "library_search_directories",
+ expand_if_available = "library_search_directories",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = ["%{libopts}"],
+ iterate_over = "libopts",
+ expand_if_available = "libopts",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = ["-Wl,-force_load,%{whole_archive_linker_params}"],
+ iterate_over = "whole_archive_linker_params",
+ expand_if_available = "whole_archive_linker_params",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = ["%{linker_input_params}"],
+ iterate_over = "linker_input_params",
+ expand_if_available = "linker_input_params",
+ ),
+ ],
+ ),
+ flag_set(
+ actions = all_link_actions +
+ [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ iterate_over = "libraries_to_link",
+ flag_groups = [
+ flag_group(
+ iterate_over = "libraries_to_link.object_files",
+ flag_groups = [
+ flag_group(
+ flags = ["%{libraries_to_link.object_files}"],
+ expand_if_false = "libraries_to_link.is_whole_archive",
+ ),
+ flag_group(
+ flags = ["-Wl,-force_load,%{libraries_to_link.object_files}"],
+ expand_if_true = "libraries_to_link.is_whole_archive",
+ ),
+ ],
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "object_file_group",
+ ),
+ ),
+ flag_group(
+ flag_groups = [
+ flag_group(
+ flags = ["%{libraries_to_link.name}"],
+ expand_if_false = "libraries_to_link.is_whole_archive",
+ ),
+ flag_group(
+ flags = ["-Wl,-force_load,%{libraries_to_link.name}"],
+ expand_if_true = "libraries_to_link.is_whole_archive",
+ ),
+ ],
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "object_file",
+ ),
+ ),
+ flag_group(
+ flag_groups = [
+ flag_group(
+ flags = ["%{libraries_to_link.name}"],
+ expand_if_false = "libraries_to_link.is_whole_archive",
+ ),
+ flag_group(
+ flags = ["-Wl,-force_load,%{libraries_to_link.name}"],
+ expand_if_true = "libraries_to_link.is_whole_archive",
+ ),
+ ],
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "interface_library",
+ ),
+ ),
+ flag_group(
+ flag_groups = [
+ flag_group(
+ flags = ["%{libraries_to_link.name}"],
+ expand_if_false = "libraries_to_link.is_whole_archive",
+ ),
+ flag_group(
+ flags = ["-Wl,-force_load,%{libraries_to_link.name}"],
+ expand_if_true = "libraries_to_link.is_whole_archive",
+ ),
+ ],
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "static_library",
+ ),
+ ),
+ flag_group(
+ flag_groups = [
+ flag_group(
+ flags = ["-l%{libraries_to_link.name}"],
+ expand_if_false = "libraries_to_link.is_whole_archive",
+ ),
+ flag_group(
+ flags = ["-Wl,-force_load,-l%{libraries_to_link.name}"],
+ expand_if_true = "libraries_to_link.is_whole_archive",
+ ),
+ ],
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "dynamic_library",
+ ),
+ ),
+ flag_group(
+ flag_groups = [
+ flag_group(
+ flags = ["-l:%{libraries_to_link.name}"],
+ expand_if_false = "libraries_to_link.is_whole_archive",
+ ),
+ flag_group(
+ flags = ["-Wl,-force_load,-l:%{libraries_to_link.name}"],
+ expand_if_true = "libraries_to_link.is_whole_archive",
+ ),
+ ],
+ expand_if_equal = variable_with_value(
+ name = "libraries_to_link.type",
+ value = "versioned_dynamic_library",
+ ),
+ ),
+ ],
+ expand_if_available = "libraries_to_link",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ per_object_debug_info_feature = feature(
+ name = "per_object_debug_info",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-gsplit-dwarf", "-g"],
+ expand_if_available = "per_object_debug_info_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ lipo_feature = feature(
+ name = "lipo",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+ flag_groups = [flag_group(flags = ["-fripa"])],
+ ),
+ ],
+ requires = [
+ feature_set(features = ["autofdo"]),
+ feature_set(features = ["fdo_optimize"]),
+ feature_set(features = ["fdo_instrument"]),
+ ],
+ )
+
+ apple_env_feature = feature(
+ name = "apple_env",
+ env_sets = [
+ env_set(
+ actions = _DYNAMIC_LINK_ACTIONS + [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ _OBJC_ARCHIVE_ACTION_NAME,
+ "objc-fully-link",
+ ACTION_NAMES.cpp_link_static_library,
+ ACTION_NAMES.linkstamp_compile,
+ ],
+ env_entries = [
+ env_entry(
+ key = "XCODE_VERSION_OVERRIDE",
+ value = "%{xcode_version_override_value}",
+ ),
+ env_entry(
+ key = "APPLE_SDK_VERSION_OVERRIDE",
+ value = "%{apple_sdk_version_override_value}",
+ ),
+ env_entry(
+ key = "APPLE_SDK_PLATFORM",
+ value = "%{apple_sdk_platform_value}",
+ ),
+ env_entry(
+ key = "ZERO_AR_DATE",
+ value = "1",
+ ),
+ ] + [env_entry(key = key, value = value) for key, value in ctx.attr.extra_env.items()],
+ ),
+ ],
+ )
+
+ if (ctx.attr.cpu == "ios_arm64" or
+ ctx.attr.cpu == "ios_arm64e" or
+ ctx.attr.cpu == "ios_x86_64" or
+ ctx.attr.cpu == "ios_sim_arm64" or
+ ctx.attr.cpu == "tvos_arm64" or
+ ctx.attr.cpu == "tvos_x86_64" or
+ ctx.attr.cpu == "tvos_sim_arm64" or
+ ctx.attr.cpu == "visionos_arm64" or
+ ctx.attr.cpu == "visionos_x86_64" or
+ ctx.attr.cpu == "visionos_sim_arm64" or
+ ctx.attr.cpu == "watchos_arm64_32" or
+ ctx.attr.cpu == "watchos_armv7k" or
+ ctx.attr.cpu == "watchos_x86_64" or
+ ctx.attr.cpu == "watchos_arm64"):
+ apply_implicit_frameworks_feature = feature(
+ name = "apply_implicit_frameworks",
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = ["-framework", "Foundation", "-framework", "UIKit"],
+ ),
+ ],
+ ),
+ ],
+ )
+ elif (ctx.attr.cpu == "darwin_x86_64" or
+ ctx.attr.cpu == "darwin_arm64" or
+ ctx.attr.cpu == "darwin_arm64e"):
+ apply_implicit_frameworks_feature = feature(
+ name = "apply_implicit_frameworks",
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [flag_group(flags = ["-framework", "Foundation"])],
+ with_features = [with_feature_set(not_features = ["kernel_extension"])],
+ ),
+ ],
+ )
+ else:
+ apply_implicit_frameworks_feature = None
+
+ random_seed_feature = feature(
+ name = "random_seed",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.cpp_module_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-frandom-seed=%{output_file}"],
+ expand_if_available = "output_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ llvm_coverage_map_format_feature = feature(
+ name = "llvm_coverage_map_format",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-fprofile-instr-generate", "-fcoverage-mapping", "-g"],
+ ),
+ ],
+ ),
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [flag_group(flags = ["-fprofile-instr-generate"])],
+ ),
+ ],
+ requires = [feature_set(features = ["coverage"])],
+ )
+
+ coverage_prefix_map_feature = feature(
+ name = "coverage_prefix_map",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-fcoverage-prefix-map=__BAZEL_EXECUTION_ROOT__=."],
+ ),
+ ],
+ ),
+ ],
+ requires = [feature_set(features = ["coverage"])],
+ )
+
+ force_pic_flags_feature = feature(
+ name = "force_pic_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.cpp_link_executable],
+ flag_groups = [
+ flag_group(
+ flags = ["-Wl,-pie"],
+ expand_if_available = "force_pic",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ sysroot_feature = feature(
+ name = "sysroot",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_link_executable,
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.clif_match,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["--sysroot=%{sysroot}"],
+ expand_if_available = "sysroot",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ autofdo_feature = feature(
+ name = "autofdo",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-fauto-profile=%{fdo_profile_path}",
+ "-fprofile-correction",
+ ],
+ expand_if_available = "fdo_profile_path",
+ ),
+ ],
+ ),
+ ],
+ provides = ["profile"],
+ )
+
+ link_libcpp_feature = feature(
+ name = "link_libc++",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [flag_group(flags = ["-lc++"])],
+ with_features = [with_feature_set(not_features = ["kernel_extension"])],
+ ),
+ ],
+ )
+
+ objc_actions_feature = feature(
+ name = "objc_actions",
+ implies = [
+ "objc-compile",
+ "objc++-compile",
+ "objc-fully-link",
+ _OBJC_ARCHIVE_ACTION_NAME,
+ "objc-executable",
+ _OBJCPP_EXECUTABLE_ACTION_NAME,
+ "assemble",
+ "preprocess-assemble",
+ "c-compile",
+ "c++-compile",
+ "c++-link-static-library",
+ "c++-link-dynamic-library",
+ "c++-link-nodeps-dynamic-library",
+ "c++-link-executable",
+ ],
+ )
+
+ unfiltered_compile_flags_feature = feature(
+ name = "unfiltered_compile_flags",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.linkstamp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-no-canonical-prefixes",
+ "-Wno-builtin-macro-redefined",
+ "-D__DATE__=\"redacted\"",
+ "-D__TIMESTAMP__=\"redacted\"",
+ "-D__TIME__=\"redacted\"",
+ "-target",
+ target_system_name,
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ linker_param_file_feature = feature(
+ name = "linker_param_file",
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS + [
+ ACTION_NAMES.cpp_link_static_library,
+ _OBJC_ARCHIVE_ACTION_NAME,
+ ACTION_NAMES.objc_fully_link,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["@%{linker_param_file}"],
+ expand_if_available = "linker_param_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ relative_ast_path_feature = feature(
+ name = "relative_ast_path",
+ enabled = True,
+ env_sets = [
+ env_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ env_entries = [
+ env_entry(
+ key = "RELATIVE_AST_PATH",
+ value = "true",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ archiver_flags_feature = feature(
+ name = "archiver_flags",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.cpp_link_static_library],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-D",
+ "-no_warning_for_no_symbols",
+ "-static",
+ "-o",
+ "%{output_execpath}",
+ ],
+ expand_if_available = "output_execpath",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ fdo_optimize_feature = feature(
+ name = "fdo_optimize",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.c_compile, ACTION_NAMES.cpp_compile],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-fprofile-use=%{fdo_profile_path}",
+ "-Wno-profile-instr-unprofiled",
+ "-Wno-profile-instr-out-of-date",
+ "-fprofile-correction",
+ ],
+ expand_if_available = "fdo_profile_path",
+ ),
+ ],
+ ),
+ ],
+ provides = ["profile"],
+ )
+
+ no_objc_arc_feature = feature(
+ name = "no_objc_arc",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-fno-objc-arc"],
+ expand_if_available = "no_objc_arc",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ debug_prefix_map_pwd_is_dot_feature = feature(
+ name = "debug_prefix_map_pwd_is_dot",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [flag_group(flags = ["-fdebug-prefix-map=__BAZEL_EXECUTION_ROOT__=."])],
+ ),
+ ],
+ )
+
+ remap_xcode_path_feature = feature(
+ name = "remap_xcode_path",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [flag_group(flags = [
+ "-fdebug-prefix-map=__BAZEL_XCODE_DEVELOPER_DIR__=/PLACEHOLDER_DEVELOPER_DIR",
+ ])],
+ ),
+ ],
+ )
+
+ linkstamps_feature = feature(
+ name = "linkstamps",
+ flag_sets = [
+ flag_set(
+ actions = all_link_actions,
+ flag_groups = [
+ flag_group(
+ flags = ["%{linkstamp_paths}"],
+ iterate_over = "linkstamp_paths",
+ expand_if_available = "linkstamp_paths",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ include_paths_feature = feature(
+ name = "include_paths",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-iquote", "%{quote_include_paths}"],
+ iterate_over = "quote_include_paths",
+ ),
+ flag_group(
+ flags = ["-I%{include_paths}"],
+ iterate_over = "include_paths",
+ ),
+ flag_group(
+ flags = ["-isystem", "%{system_include_paths}"],
+ iterate_over = "system_include_paths",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ default_compile_flags_feature = feature(
+ name = "default_compile_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.lto_backend,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-D_FORTIFY_SOURCE=1",
+ ],
+ ),
+ ],
+ with_features = [with_feature_set(not_features = ["asan"])],
+ ),
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.lto_backend,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-fstack-protector",
+ "-fcolor-diagnostics",
+ "-Wall",
+ "-Wthread-safety",
+ "-Wself-assign",
+ "-fno-omit-frame-pointer",
+ ],
+ ),
+ ],
+ ),
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.lto_backend,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [flag_group(flags = ["-O0", "-DDEBUG"])],
+ with_features = [with_feature_set(features = ["fastbuild"])],
+ ),
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.lto_backend,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-g0",
+ "-O2",
+ "-DNDEBUG",
+ "-DNS_BLOCK_ASSERTIONS=1",
+ ],
+ ),
+ ],
+ with_features = [with_feature_set(features = ["opt"])],
+ ),
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.lto_backend,
+ ACTION_NAMES.clif_match,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [flag_group(flags = ["-g"])],
+ with_features = [with_feature_set(features = ["dbg"])],
+ ),
+ flag_set(
+ actions = [
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.lto_backend,
+ ACTION_NAMES.clif_match,
+ ],
+ flag_groups = [flag_group(flags = ["-std=c++14"])],
+ ),
+ ],
+ )
+
+ objcopy_embed_flags_feature = feature(
+ name = "objcopy_embed_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = ["objcopy_embed_data"],
+ flag_groups = [flag_group(flags = ["-I", "binary"])],
+ ),
+ ],
+ )
+
+ dead_strip_feature = feature(
+ name = "dead_strip",
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = ["-dead_strip"],
+ ),
+ ],
+ ),
+ ],
+ requires = [feature_set(features = ["opt"])],
+ )
+
+ oso_prefix_feature = feature(
+ name = "oso_prefix_is_pwd",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [flag_group(flags = ["-Wl,-oso_prefix,__BAZEL_EXECUTION_ROOT__/"])],
+ ),
+ ],
+ )
+
+ generate_dsym_file_feature = feature(
+ name = "generate_dsym_file",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ "objc-executable",
+ _OBJCPP_EXECUTABLE_ACTION_NAME,
+ ],
+ flag_groups = [flag_group(flags = ["-g"])],
+ ),
+ flag_set(
+ actions = ["objc-executable", _OBJCPP_EXECUTABLE_ACTION_NAME],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "DSYM_HINT_LINKED_BINARY=%{linked_binary}",
+ "DSYM_HINT_DSYM_PATH=%{dsym_path}",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ # Kernel extensions for Apple Silicon are arm64e.
+ if (ctx.attr.cpu == "darwin_x86_64" or
+ ctx.attr.cpu == "darwin_arm64e"):
+ kernel_extension_feature = feature(
+ name = "kernel_extension",
+ flag_sets = [
+ flag_set(
+ actions = ["objc-executable", _OBJCPP_EXECUTABLE_ACTION_NAME],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-nostdlib",
+ "-lkmod",
+ "-lkmodc++",
+ "-lcc_kext",
+ "-Xlinker",
+ "-kext",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+ else:
+ kernel_extension_feature = feature(name = "kernel_extension")
+
+ apply_default_warnings_feature = feature(
+ name = "apply_default_warnings",
+ flag_sets = [
+ flag_set(
+ actions = [ACTION_NAMES.objc_compile, ACTION_NAMES.objcpp_compile],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Werror=incompatible-sysroot",
+ "-Wshorten-64-to-32",
+ "-Wbool-conversion",
+ "-Wconstant-conversion",
+ "-Wduplicate-method-match",
+ "-Wempty-body",
+ "-Wenum-conversion",
+ "-Wint-conversion",
+ "-Wunreachable-code",
+ "-Wmismatched-return-types",
+ "-Wundeclared-selector",
+ "-Wuninitialized",
+ "-Wunused-function",
+ "-Wunused-variable",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ dependency_file_feature = feature(
+ name = "dependency_file",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-MD", "-MF", "%{dependency_file}"],
+ expand_if_available = "dependency_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ serialized_diagnostics_file_feature = feature(
+ name = "serialized_diagnostics_file",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["--serialize-diagnostics", "%{serialized_diagnostics_file}"],
+ expand_if_available = "serialized_diagnostics_file",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ preprocessor_defines_feature = feature(
+ name = "preprocessor_defines",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["-D%{preprocessor_defines}"],
+ iterate_over = "preprocessor_defines",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ fdo_instrument_feature = feature(
+ name = "fdo_instrument",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ACTION_NAMES.cpp_link_executable,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-fprofile-generate=%{fdo_instrument_path}",
+ "-fno-data-sections",
+ ],
+ expand_if_available = "fdo_instrument_path",
+ ),
+ ],
+ ),
+ ],
+ provides = ["profile"],
+ )
+
+ if (ctx.attr.cpu == "darwin_x86_64" or
+ ctx.attr.cpu == "darwin_arm64" or
+ ctx.attr.cpu == "darwin_arm64e"):
+ link_cocoa_feature = feature(
+ name = "link_cocoa",
+ flag_sets = [
+ flag_set(
+ actions = ["objc-executable", _OBJCPP_EXECUTABLE_ACTION_NAME],
+ flag_groups = [flag_group(flags = ["-framework", "Cocoa"])],
+ ),
+ ],
+ )
+ else:
+ link_cocoa_feature = feature(name = "link_cocoa")
+
+ user_compile_flags_feature = feature(
+ name = "user_compile_flags",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.assemble,
+ ACTION_NAMES.preprocess_assemble,
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.cpp_header_parsing,
+ ACTION_NAMES.cpp_module_compile,
+ ACTION_NAMES.cpp_module_codegen,
+ ACTION_NAMES.linkstamp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = ["%{user_compile_flags}"],
+ iterate_over = "user_compile_flags",
+ expand_if_available = "user_compile_flags",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ headerpad_feature = feature(
+ name = "headerpad",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [flag_group(flags = ["-headerpad_max_install_names"])],
+ ),
+ ],
+ )
+
+ generate_linkmap_feature = feature(
+ name = "generate_linkmap",
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Xlinker",
+ "-map",
+ "-Xlinker",
+ "%{linkmap_exec_path}",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ set_install_name = feature(
+ name = "set_install_name",
+ enabled = ctx.fragments.cpp.do_not_use_macos_set_install_name,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.cpp_link_dynamic_library,
+ ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Wl,-install_name,@rpath/%{runtime_solib_name}",
+ ],
+ expand_if_available = "runtime_solib_name",
+ ),
+ ],
+ ),
+ ],
+ )
+
+ asan_feature = feature(
+ name = "asan",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(flags = ["-fsanitize=address"]),
+ ],
+ with_features = [
+ with_feature_set(features = ["asan"]),
+ ],
+ ),
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(flags = ["-fsanitize=address"]),
+ ],
+ with_features = [
+ with_feature_set(features = ["asan"]),
+ ],
+ ),
+ ],
+ )
+
+ tsan_feature = feature(
+ name = "tsan",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(flags = ["-fsanitize=thread"]),
+ ],
+ with_features = [
+ with_feature_set(features = ["tsan"]),
+ ],
+ ),
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(flags = ["-fsanitize=thread"]),
+ ],
+ with_features = [
+ with_feature_set(features = ["tsan"]),
+ ],
+ ),
+ ],
+ )
+
+ ubsan_feature = feature(
+ name = "ubsan",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(flags = ["-fsanitize=undefined"]),
+ ],
+ with_features = [
+ with_feature_set(features = ["ubsan"]),
+ ],
+ ),
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(flags = ["-fsanitize=undefined"]),
+ ],
+ with_features = [
+ with_feature_set(features = ["ubsan"]),
+ ],
+ ),
+ ],
+ )
+
+ default_sanitizer_flags_feature = feature(
+ name = "default_sanitizer_flags",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-gline-tables-only",
+ "-fno-omit-frame-pointer",
+ "-fno-sanitize-recover=all",
+ ],
+ ),
+ ],
+ with_features = [
+ with_feature_set(features = ["asan"]),
+ with_feature_set(features = ["tsan"]),
+ with_feature_set(features = ["ubsan"]),
+ ],
+ ),
+ ],
+ )
+
+ treat_warnings_as_errors_feature = feature(
+ name = "treat_warnings_as_errors",
+ flag_sets = [
+ flag_set(
+ actions = [
+ ACTION_NAMES.c_compile,
+ ACTION_NAMES.cpp_compile,
+ ACTION_NAMES.objc_compile,
+ ACTION_NAMES.objcpp_compile,
+ ],
+ flag_groups = [flag_group(flags = ["-Werror"])],
+ ),
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [flag_group(flags = ["-Wl,-fatal_warnings"])],
+ ),
+ ],
+ )
+
+ # As of Xcode 15, linker warnings are emitted if duplicate `-l` options are
+ # present. Until such linkopts can be deduped by bazel itself, we disable
+ # these warnings.
+ no_warn_duplicate_libraries_feature = feature(
+ name = "no_warn_duplicate_libraries",
+ enabled = xcode_support.is_xcode_at_least_version(xcode_config, "15.0.0"),
+ flag_sets = [
+ flag_set(
+ actions = _DYNAMIC_LINK_ACTIONS,
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-Wl,-no_warn_duplicate_libraries",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ features = [
+ # Marker features
+ feature(name = "archive_param_file"),
+ feature(name = "compile_all_modules"),
+ feature(name = "coverage"),
+ feature(name = "dbg"),
+ feature(name = "exclude_private_headers_in_module_maps"),
+ feature(name = "fastbuild"),
+ feature(name = "has_configured_linker_path"),
+ feature(name = "module_maps", enabled = True),
+ feature(name = "no_legacy_features"),
+ feature(name = "only_doth_headers_in_module_maps"),
+ feature(name = "opt"),
+
+ # Features with more configuration
+ link_libcpp_feature,
+ default_compile_flags_feature,
+ debug_prefix_map_pwd_is_dot_feature,
+ remap_xcode_path_feature,
+ generate_dsym_file_feature,
+ generate_linkmap_feature,
+ oso_prefix_feature,
+ objc_actions_feature,
+ strip_debug_symbols_feature,
+ shared_flag_feature,
+ kernel_extension_feature,
+ linkstamps_feature,
+ output_execpath_flags_feature,
+ archiver_flags_feature,
+ runtime_root_flags_feature,
+ input_param_flags_feature,
+ objc_link_flag_feature,
+ force_pic_flags_feature,
+ pch_feature,
+ apply_default_warnings_feature,
+ includes_feature,
+ include_paths_feature,
+ sysroot_feature,
+ dependency_file_feature,
+ serialized_diagnostics_file_feature,
+ pic_feature,
+ per_object_debug_info_feature,
+ preprocessor_defines_feature,
+ framework_paths_feature,
+ random_seed_feature,
+ fdo_instrument_feature,
+ fdo_optimize_feature,
+ autofdo_feature,
+ lipo_feature,
+ llvm_coverage_map_format_feature,
+ gcc_coverage_map_format_feature,
+ coverage_prefix_map_feature,
+ apply_default_compiler_flags_feature,
+ include_system_dirs_feature,
+ headerpad_feature,
+ objc_arc_feature,
+ no_objc_arc_feature,
+ apple_env_feature,
+ relative_ast_path_feature,
+ user_link_flags_feature,
+ default_link_flags_feature,
+ no_deduplicate_feature,
+ dead_strip_feature,
+ apply_implicit_frameworks_feature,
+ link_cocoa_feature,
+ apply_simulator_compiler_flags_feature,
+ unfiltered_cxx_flags_feature,
+ user_compile_flags_feature,
+ unfiltered_compile_flags_feature,
+ linker_param_file_feature,
+ compiler_input_flags_feature,
+ compiler_output_flags_feature,
+ objcopy_embed_flags_feature,
+ set_install_name,
+ asan_feature,
+ tsan_feature,
+ ubsan_feature,
+ default_sanitizer_flags_feature,
+ treat_warnings_as_errors_feature,
+ no_warn_duplicate_libraries_feature,
+ ]
+
+ if (ctx.attr.cpu == "darwin_x86_64" or
+ ctx.attr.cpu == "darwin_arm64" or
+ ctx.attr.cpu == "darwin_arm64e"):
+ features.append(feature(name = "dynamic_linking_mode"))
+
+ # macOS artifact name patterns differ from the defaults only for dynamic
+ # libraries.
+ artifact_name_patterns = [
+ artifact_name_pattern(
+ category_name = "dynamic_library",
+ prefix = "lib",
+ extension = ".dylib",
+ ),
+ ]
+
+ make_variables = [
+ make_variable(
+ name = "STACK_FRAME_UNLIMITED",
+ value = "-Wframe-larger-than=100000000 -Wno-vla",
+ ),
+ ]
+
+ tool_paths = {
+ "ar": "libtool",
+ "cpp": "/usr/bin/cpp",
+ "dwp": "/usr/bin/dwp",
+ "gcc": "cc_wrapper.sh",
+ "gcov": "/usr/bin/gcov",
+ "ld": "/usr/bin/ld",
+ "nm": "/usr/bin/nm",
+ "objcopy": "/usr/bin/objcopy",
+ "objdump": "/usr/bin/objdump",
+ "strip": "/usr/bin/strip",
+ }
+
+ tool_paths.update(ctx.attr.tool_paths_overrides)
+
+ out = ctx.actions.declare_file(ctx.label.name)
+ ctx.actions.write(out, "Fake executable")
+ return [
+ cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ features = features,
+ action_configs = action_configs,
+ artifact_name_patterns = artifact_name_patterns,
+ cxx_builtin_include_directories = ctx.attr.cxx_builtin_include_directories,
+ toolchain_identifier = ctx.attr.cpu,
+ host_system_name = "x86_64-apple-macosx",
+ target_system_name = target_system_name,
+ target_cpu = ctx.attr.cpu,
+ target_libc = target_libc,
+ compiler = ctx.attr.compiler,
+ abi_version = abi_version,
+ abi_libc_version = abi_libc_version,
+ tool_paths = [tool_path(name = name, path = path) for (name, path) in tool_paths.items()],
+ make_variables = make_variables,
+ builtin_sysroot = None,
+ cc_target_os = "apple",
+ ),
+ DefaultInfo(
+ executable = out,
+ ),
+ ]
+
+cc_toolchain_config = rule(
+ implementation = _impl,
+ attrs = {
+ "cpu": attr.string(mandatory = True),
+ "compiler": attr.string(),
+ "cxx_builtin_include_directories": attr.string_list(),
+ "tool_paths_overrides": attr.string_dict(),
+ "extra_env": attr.string_dict(),
+ "_xcode_config": attr.label(default = configuration_field(
+ fragment = "apple",
+ name = "xcode_config_label",
+ )),
+ },
+ provides = [CcToolchainConfigInfo],
+ executable = True,
+ fragments = ["cpp"],
+)
diff --git a/crosstool/libtool.sh b/crosstool/libtool.sh
new file mode 100755
index 0000000..2b7282d
--- /dev/null
+++ b/crosstool/libtool.sh
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+#
+# libtool.sh runs the command passed to it using "xcrunwrapper libtool".
+#
+# It creates symbolic links for all input files with a path-hash appended
+# to their original name (foo.o becomes foo_{md5sum}.o). This is to circumvent
+# a bug in the original libtool that arises when two input files have the same
+# base name (even if they are in different directories).
+
+set -eu
+
+# A trick to allow invoking this script in multiple contexts.
+if [ -z ${MY_LOCATION+x} ]; then
+ if [ -d "$0.runfiles/" ]; then
+ MY_LOCATION="$0.runfiles/bazel_tools/tools/objc"
+ else
+ MY_LOCATION="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+ fi
+fi
+
+function invoke_libtool() {
+ # Just invoke libtool via xcrunwrapper
+ "${MY_LOCATION}/xcrunwrapper.sh" libtool "$@" \
+ 2> >(grep -v "the table of contents is empty (no object file members in the"`
+ `" library define global symbols)$" >&2)
+ # ^ Filtering a warning that's unlikely to indicate a real issue
+ # ...and not silencable via a flag.
+}
+
+if [ ! -f "${MY_LOCATION}"/libtool_check_unique ] ; then
+ echo "libtool_check_unique not found. Please file an issue at github.com/bazelbuild/bazel"
+ exit 1
+elif "${MY_LOCATION}"/libtool_check_unique "$@"; then
+ # If there are no duplicate .o basenames,
+ # libtool can be invoked with the original arguments.
+ invoke_libtool "$@"
+ exit
+fi
+
+TEMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/libtool.XXXXXXXX")"
+trap 'rm -rf "$TEMPDIR"' EXIT
+
+# Creates a symbolic link to the input argument file and returns the symlink
+# file path.
+function hash_objfile() {
+ ORIGINAL_NAME="$1"
+ ORIGINAL_HASH="$(/sbin/md5 -qs "${ORIGINAL_NAME}")"
+ SYMLINK_NAME="${TEMPDIR}/$(basename "${ORIGINAL_NAME%.o}_${ORIGINAL_HASH}.o")"
+ if [[ ! -e "$SYMLINK_NAME" ]]; then
+ case "${ORIGINAL_NAME}" in
+ /*) ln -sf "$ORIGINAL_NAME" "$SYMLINK_NAME" ;;
+ *) ln -sf "$(pwd)/$ORIGINAL_NAME" "$SYMLINK_NAME" ;;
+ esac
+ fi
+ echo "$SYMLINK_NAME"
+}
+
+python_executable=/usr/bin/python3
+if [[ ! -x "$python_executable" ]]; then
+ python_executable=python3
+fi
+
+ARGS=()
+handle_filelist=0
+keep_next=0
+
+function parse_option() {
+ local -r ARG="$1"
+ if [[ "$handle_filelist" == "1" ]]; then
+ handle_filelist=0
+ HASHED_FILELIST="${ARG%.objlist}_hashes.objlist"
+ rm -f "${HASHED_FILELIST}"
+ # Use python helper script for fast md5 calculation of many strings.
+ "$python_executable" "${MY_LOCATION}/make_hashed_objlist.py" \
+ "${ARG}" "${HASHED_FILELIST}" "${TEMPDIR}"
+ ARGS+=("${HASHED_FILELIST}")
+ elif [[ "$keep_next" == "1" ]]; then
+ keep_next=0
+ ARGS+=("$ARG")
+ else
+ case "${ARG}" in
+ # Filelist flag, need to symlink each input in the contents of file and
+ # pass a new filelist which contains the symlinks.
+ -filelist)
+ handle_filelist=1
+ ARGS+=("${ARG}")
+ ;;
+ @*)
+ path="${ARG:1}"
+ while IFS= read -r opt
+ do
+ parse_option "$opt"
+ done < "$path" || exit 1
+ ;;
+ # Flags with no args
+ -static|-s|-a|-c|-L|-T|-D|-v|-no_warning_for_no_symbols)
+ ARGS+=("${ARG}")
+ ;;
+ # Single-arg flags
+ -arch_only|-syslibroot|-o)
+ keep_next=1
+ ARGS+=("${ARG}")
+ ;;
+ # Any remaining flags are unexpected and may ruin flag parsing.
+ # Add any flags here to libtool_check_unique.cc as well
+ -*)
+ echo "Unrecognized libtool flag ${ARG}"
+ exit 1
+ ;;
+ # Archive inputs can remain untouched, as they come from other targets.
+ *.a)
+ ARGS+=("${ARG}")
+ ;;
+ # Remaining args are input objects
+ *)
+ ARGS+=("$(hash_objfile "${ARG}")")
+ ;;
+ esac
+ fi
+}
+
+for arg in "$@"; do
+ parse_option "$arg"
+done
+
+printf '%s\n' "${ARGS[@]}" > "$TEMPDIR/processed.params"
+invoke_libtool "@$TEMPDIR/processed.params"
diff --git a/crosstool/libtool_check_unique.cc b/crosstool/libtool_check_unique.cc
new file mode 100644
index 0000000..7ba32de
--- /dev/null
+++ b/crosstool/libtool_check_unique.cc
@@ -0,0 +1,109 @@
+// Copyright 2020 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.
+
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <regex> // NOLINT
+#include <unordered_set>
+
+using std::ifstream;
+using std::regex;
+using std::string;
+using std::unordered_set;
+using std::vector;
+
+const regex libRegex = regex(".*\\.a$");
+const regex noArgFlags =
+ regex("-static|-s|-a|-c|-L|-T|-D|-v|-no_warning_for_no_symbols");
+const regex singleArgFlags = regex("-arch_only|-syslibroot|-o");
+
+string getBasename(const string &path) {
+ // Assumes we're on an OS with "/" as the path separator
+ auto idx = path.find_last_of("/");
+ if (idx == string::npos) {
+ return path;
+ }
+ return path.substr(idx + 1);
+}
+
+vector<string> readFile(const string path) {
+ vector<string> lines;
+ ifstream file(path);
+ string line;
+ while (std::getline(file, line)) {
+ if (!line.empty()) {
+ lines.push_back(line);
+ }
+ }
+
+ return lines;
+}
+
+unordered_set<string> parseArgs(vector<string> args) {
+ unordered_set<string> basenames;
+ for (auto it = args.begin(); it != args.end(); ++it) {
+ const string arg = *it;
+ if (arg == "-filelist") {
+ ++it;
+ ifstream list(*it);
+ for (string line; getline(list, line);) {
+ const string basename = getBasename(line);
+ const auto pair = basenames.insert(basename);
+ if (!pair.second) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ list.close();
+ } else if (arg[0] == '@') {
+ string paramsFilePath(arg.substr(1));
+ auto newBasenames = parseArgs(readFile(paramsFilePath));
+ for (auto newBasename : newBasenames) {
+ const auto pair = basenames.insert(newBasename);
+ if (!pair.second) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else if (regex_match(arg, noArgFlags)) {
+ } else if (regex_match(arg, singleArgFlags)) {
+ ++it;
+ } else if (arg[0] == '-') {
+ exit(EXIT_FAILURE);
+ // Unrecognized flag, let the wrapper deal with it, any flags added to
+ // libtool.sh should also be added here.
+ } else if (regex_match(arg, libRegex)) {
+ // Archive inputs can remain untouched, as they come from other targets.
+ } else {
+ const string basename = getBasename(arg);
+ const auto pair = basenames.insert(basename);
+ if (!pair.second) {
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ return basenames;
+}
+
+// Returns 0 if there are no duplicate basenames in the object files (via
+// -filelist, params files, and shell args), 1 otherwise
+int main(int argc, const char *argv[]) {
+ vector<string> args;
+ // Set i to 1 to skip executable path
+ for (int i = 1; argv[i] != nullptr; i++) {
+ args.push_back(argv[i]);
+ }
+ parseArgs(args);
+ return EXIT_SUCCESS;
+}
diff --git a/crosstool/make_hashed_objlist.py b/crosstool/make_hashed_objlist.py
new file mode 100644
index 0000000..bb32642
--- /dev/null
+++ b/crosstool/make_hashed_objlist.py
@@ -0,0 +1,58 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2016 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.
+
+"""Creates symbolic links for .o files with hashcode.
+
+This script reads the file list containing the input files, creates
+symbolic links with a path-hash appended to their original name (foo.o
+becomes foo_{md5sum}.o), then saves the list of symbolic links to another
+file.
+
+The symbolic links are created into the given temporary directory. There is
+no guarantee that we can write to the directory that contained the inputs to
+this script.
+
+This is to circumvent a bug in the original libtool that arises when two
+input files have the same base name (even if they are in different
+directories).
+"""
+
+import hashlib
+import os
+import sys
+
+
+def main():
+ outdir = sys.argv[3]
+ with open(sys.argv[1]) as obj_file_list:
+ with open(sys.argv[2], 'w') as hashed_obj_file_list:
+ for line in obj_file_list:
+ obj_file_path = line.rstrip('\n')
+
+ hashed_obj_file_name = '%s_%s.o' % (
+ os.path.basename(os.path.splitext(obj_file_path)[0]),
+ hashlib.md5(obj_file_path.encode('utf-8')).hexdigest())
+ hashed_obj_file_path = os.path.join(outdir, hashed_obj_file_name)
+
+ hashed_obj_file_list.write(hashed_obj_file_path + '\n')
+
+ # Create symlink only if the symlink doesn't exist.
+ if not os.path.exists(hashed_obj_file_path):
+ os.symlink(os.path.abspath(obj_file_path),
+ hashed_obj_file_path)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/crosstool/osx_cc_configure.bzl b/crosstool/osx_cc_configure.bzl
new file mode 100644
index 0000000..94d4ca4
--- /dev/null
+++ b/crosstool/osx_cc_configure.bzl
@@ -0,0 +1,195 @@
+# pylint: disable=g-bad-file-header
+# Copyright 2016 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.
+"""Configuring the C++ toolchain on macOS."""
+
+load(
+ "@bazel_tools//tools/cpp:lib_cc_configure.bzl",
+ "escape_string",
+)
+load("@bazel_tools//tools/osx:xcode_configure.bzl", "run_xcode_locator")
+
+def _get_escaped_xcode_cxx_inc_directories(repository_ctx, xcode_toolchains):
+ """Compute the list of default C++ include paths on Xcode-enabled darwin.
+
+ Args:
+ repository_ctx: The repository context.
+ xcode_toolchains: A list containing the xcode toolchains available
+ Returns:
+ include_paths: A list of builtin include paths.
+ """
+
+ # Assume that everything is managed by Xcode / toolchain installations
+ include_dirs = [
+ "/Applications/",
+ "/Library/",
+ ]
+
+ user = repository_ctx.os.environ.get("USER")
+ if user:
+ include_dirs.extend([
+ "/Users/{}/Applications/".format(user),
+ "/Users/{}/Library/".format(user),
+ ])
+
+ # Include extra Xcode paths in case they're installed on other volumes
+ for toolchain in xcode_toolchains:
+ include_dirs.append(escape_string(toolchain.developer_dir))
+
+ return include_dirs
+
+def _compile_cc_file(repository_ctx, src_name, out_name):
+ env = repository_ctx.os.environ
+ xcrun_result = repository_ctx.execute([
+ "env",
+ "-i",
+ "DEVELOPER_DIR={}".format(env.get("DEVELOPER_DIR", default = "")),
+ "xcrun",
+ "--sdk",
+ "macosx",
+ "clang",
+ "-mmacosx-version-min=10.15",
+ "-std=c++17",
+ "-lc++",
+ "-arch",
+ "arm64",
+ "-arch",
+ "x86_64",
+ "-Wl,-no_adhoc_codesign",
+ "-Wl,-no_uuid",
+ "-O3",
+ "-o",
+ out_name,
+ src_name,
+ ])
+
+ if xcrun_result.return_code != 0:
+ error_msg = (
+ "return code {code}, stderr: {err}, stdout: {out}"
+ ).format(
+ code = xcrun_result.return_code,
+ err = xcrun_result.stderr,
+ out = xcrun_result.stdout,
+ )
+ fail(out_name + " failed to generate. Please file an issue at " +
+ "https://github.com/bazelbuild/apple_support/issues with the following:\n" +
+ error_msg)
+
+ xcrun_result = repository_ctx.execute([
+ "env",
+ "-i",
+ "codesign",
+ "--identifier", # Required to be reproducible across archs
+ out_name,
+ "--force",
+ "--sign",
+ "-",
+ out_name,
+ ])
+ if xcrun_result.return_code != 0:
+ error_msg = (
+ "codesign return code {code}, stderr: {err}, stdout: {out}"
+ ).format(
+ code = xcrun_result.return_code,
+ err = xcrun_result.stderr,
+ out = xcrun_result.stdout,
+ )
+ fail(out_name + " failed to generate. Please file an issue at " +
+ "https://github.com/bazelbuild/apple_support/issues with the following:\n" +
+ error_msg)
+
+def configure_osx_toolchain(repository_ctx):
+ """Configure C++ toolchain on macOS.
+
+ Args:
+ repository_ctx: The repository context.
+
+ Returns:
+ Whether or not configuration was successful
+ """
+
+ # All Label resolutions done at the top of the function to avoid issues
+ # with starlark function restarts, see this:
+ # https://github.com/bazelbuild/bazel/blob/ab71a1002c9c53a8061336e40f91204a2a32c38e/tools/cpp/lib_cc_configure.bzl#L17-L38
+ # for more info
+ xcode_locator = Label("@bazel_tools//tools/osx:xcode_locator.m")
+ osx_cc_wrapper = Label("@bazel_tools//tools/cpp:osx_cc_wrapper.sh.tpl")
+ xcrunwrapper = Label("@build_bazel_apple_support//crosstool:xcrunwrapper.sh")
+ libtool = Label("@build_bazel_apple_support//crosstool:libtool.sh")
+ make_hashed_objlist = Label("@build_bazel_apple_support//crosstool:make_hashed_objlist.py")
+ cc_toolchain_config = Label("@build_bazel_apple_support//crosstool:cc_toolchain_config.bzl")
+ build_template = Label("@build_bazel_apple_support//crosstool:BUILD.tpl")
+ libtool_check_unique_src_path = str(repository_ctx.path(
+ Label("@build_bazel_apple_support//crosstool:libtool_check_unique.cc"),
+ ))
+ wrapped_clang_src_path = str(repository_ctx.path(
+ Label("@build_bazel_apple_support//crosstool:wrapped_clang.cc"),
+ ))
+
+ xcode_toolchains = []
+ xcodeloc_err = ""
+ allow_non_applications_xcode = "BAZEL_ALLOW_NON_APPLICATIONS_XCODE" in repository_ctx.os.environ and repository_ctx.os.environ["BAZEL_ALLOW_NON_APPLICATIONS_XCODE"] == "1"
+ if allow_non_applications_xcode:
+ (xcode_toolchains, xcodeloc_err) = run_xcode_locator(repository_ctx, xcode_locator)
+ if not xcode_toolchains:
+ return False, xcodeloc_err
+
+ # For Xcode toolchains, there's no reason to use anything other than
+ # wrapped_clang, so that we still get the Bazel Xcode placeholder
+ # substitution and other behavior for actions that invoke this
+ # cc_wrapper.sh script. The wrapped_clang binary is already hardcoded
+ # into the Objective-C crosstool actions, anyway, so this ensures that
+ # the C++ actions behave consistently.
+ cc_path = '"$(/usr/bin/dirname "$0")"/wrapped_clang'
+ repository_ctx.template(
+ "cc_wrapper.sh",
+ osx_cc_wrapper,
+ {
+ "%{cc}": escape_string(cc_path),
+ "%{env}": "",
+ },
+ )
+ repository_ctx.symlink(xcrunwrapper, "xcrunwrapper.sh")
+ repository_ctx.symlink(libtool, "libtool")
+ repository_ctx.symlink(make_hashed_objlist, "make_hashed_objlist.py")
+ repository_ctx.symlink(cc_toolchain_config, "cc_toolchain_config.bzl")
+ _compile_cc_file(repository_ctx, libtool_check_unique_src_path, "libtool_check_unique")
+ _compile_cc_file(repository_ctx, wrapped_clang_src_path, "wrapped_clang")
+ repository_ctx.symlink("wrapped_clang", "wrapped_clang_pp")
+
+ tool_paths = {}
+ gcov_path = repository_ctx.os.environ.get("GCOV")
+ if gcov_path != None:
+ if not gcov_path.startswith("/"):
+ gcov_path = repository_ctx.which(gcov_path)
+ tool_paths["gcov"] = gcov_path
+
+ escaped_include_paths = _get_escaped_xcode_cxx_inc_directories(repository_ctx, xcode_toolchains)
+ escaped_cxx_include_directories = []
+ for path in escaped_include_paths:
+ escaped_cxx_include_directories.append((" \"%s\"," % path))
+ if xcodeloc_err:
+ escaped_cxx_include_directories.append(" # Error: " + xcodeloc_err)
+ repository_ctx.template(
+ "BUILD",
+ build_template,
+ {
+ "%{cxx_builtin_include_directories}": "\n".join(escaped_cxx_include_directories),
+ "%{tool_paths_overrides}": ",\n ".join(
+ ['"%s": "%s"' % (k, v) for k, v in tool_paths.items()],
+ ),
+ },
+ )
+
+ return True, ""
diff --git a/crosstool/setup.bzl b/crosstool/setup.bzl
new file mode 100644
index 0000000..669050c
--- /dev/null
+++ b/crosstool/setup.bzl
@@ -0,0 +1,71 @@
+"""Configure the Apple CC toolchain"""
+
+load("//crosstool:osx_cc_configure.bzl", "configure_osx_toolchain")
+
+_DISABLE_ENV_VAR = "BAZEL_NO_APPLE_CPP_TOOLCHAIN"
+
+def _apple_cc_autoconf_toolchains_impl(repository_ctx):
+ """Generate BUILD file with 'toolchain' targets for the local host C++ toolchain.
+
+ Args:
+ repository_ctx: repository context
+ """
+ env = repository_ctx.os.environ
+ should_disable = _DISABLE_ENV_VAR in env and env[_DISABLE_ENV_VAR] == "1"
+
+ if should_disable:
+ repository_ctx.file("BUILD", "# Apple CC toolchain autoconfiguration was disabled by {} env variable.".format(_DISABLE_ENV_VAR))
+ elif repository_ctx.os.name.startswith("mac os"):
+ repository_ctx.symlink(
+ repository_ctx.path(Label("@build_bazel_apple_support//crosstool:BUILD.toolchains")),
+ "BUILD",
+ )
+ else:
+ repository_ctx.file("BUILD", "# Apple CC toolchain autoconfiguration was disabled because you're not running on macOS")
+
+_apple_cc_autoconf_toolchains = repository_rule(
+ environ = [_DISABLE_ENV_VAR],
+ implementation = _apple_cc_autoconf_toolchains_impl,
+ configure = True,
+)
+
+def _apple_cc_autoconf_impl(repository_ctx):
+ env = repository_ctx.os.environ
+ should_disable = _DISABLE_ENV_VAR in env and env[_DISABLE_ENV_VAR] == "1"
+
+ if should_disable:
+ repository_ctx.file("BUILD", "# Apple CC autoconfiguration was disabled by {} env variable.".format(_DISABLE_ENV_VAR))
+ elif repository_ctx.os.name.startswith("mac os"):
+ success, error = configure_osx_toolchain(repository_ctx)
+ if not success:
+ fail("Failed to configure Apple CC toolchain, if you only have the command line tools installed and not Xcode, you cannot use this toolchain. You should either remove it or temporarily set '{}=1' in the environment: {}".format(_DISABLE_ENV_VAR, error))
+ else:
+ repository_ctx.file("BUILD", "# Apple CC autoconfiguration was disabled because you're not on macOS")
+
+_apple_cc_autoconf = repository_rule(
+ environ = [
+ _DISABLE_ENV_VAR,
+ "DEVELOPER_DIR", # Used for making sure we use the right Xcode for compiling toolchain binaries
+ "GCOV", # TODO: Remove this
+ "USE_CLANG_CL", # Kept as a hack for those who rely on this invaliding the toolchain
+ "USER", # Used to allow paths for custom toolchains to be used by C* compiles
+ "XCODE_VERSION", # Force re-computing the toolchain by including the current Xcode version info in an env var
+ ],
+ implementation = _apple_cc_autoconf_impl,
+ configure = True,
+)
+
+# buildifier: disable=unnamed-macro
+def apple_cc_configure():
+ _apple_cc_autoconf_toolchains(name = "local_config_apple_cc_toolchains")
+ _apple_cc_autoconf(name = "local_config_apple_cc")
+ native.register_toolchains(
+ # Use register_toolchain's target pattern expansion to register all toolchains in the package.
+ "@local_config_apple_cc_toolchains//:all",
+ )
+
+def _apple_cc_configure_extension_impl(_):
+ _apple_cc_autoconf_toolchains(name = "local_config_apple_cc_toolchains")
+ _apple_cc_autoconf(name = "local_config_apple_cc")
+
+apple_cc_configure_extension = module_extension(implementation = _apple_cc_configure_extension_impl)
diff --git a/crosstool/wrapped_clang.cc b/crosstool/wrapped_clang.cc
new file mode 100644
index 0000000..a71464d
--- /dev/null
+++ b/crosstool/wrapped_clang.cc
@@ -0,0 +1,472 @@
+// 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.
+//
+// wrapped_clang.cc: Pass args to 'xcrun clang' and zip dsym files.
+//
+// wrapped_clang passes its args to clang, but also supports a separate set of
+// invocations to generate dSYM files. If "DSYM_HINT" flags are passed in, they
+// are used to construct that separate set of invocations (instead of being
+// passed to clang).
+// The following "DSYM_HINT" flags control dsym generation. If any one if these
+// are passed in, then they all must be passed in.
+// "DSYM_HINT_LINKED_BINARY": Workspace-relative path to binary output of the
+// link action generating the dsym file.
+// "DSYM_HINT_DSYM_PATH": Workspace-relative path to dSYM dwarf file.
+
+#include <libgen.h>
+#include <spawn.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <array>
+#include <cerrno>
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+extern char **environ;
+
+namespace {
+
+constexpr char kAddASTPathPrefix[] = "-Wl,-add_ast_path,";
+
+// Returns the base name of the given filepath. For example, given
+// /foo/bar/baz.txt, returns 'baz.txt'.
+const char *Basename(const char *filepath) {
+ const char *base = strrchr(filepath, '/');
+ return base ? (base + 1) : filepath;
+}
+
+// Unescape and unquote an argument read from a line of a response file.
+static std::string Unescape(const std::string &arg) {
+ std::string result;
+ auto length = arg.size();
+ for (size_t i = 0; i < length; ++i) {
+ auto ch = arg[i];
+
+ // If it's a backslash, consume it and append the character that follows.
+ if (ch == '\\' && i + 1 < length) {
+ ++i;
+ result.push_back(arg[i]);
+ continue;
+ }
+
+ // If it's a quote, process everything up to the matching quote, unescaping
+ // backslashed characters as needed.
+ if (ch == '"' || ch == '\'') {
+ auto quote = ch;
+ ++i;
+ while (i != length && arg[i] != quote) {
+ if (arg[i] == '\\' && i + 1 < length) {
+ ++i;
+ }
+ result.push_back(arg[i]);
+ ++i;
+ }
+ if (i == length) {
+ break;
+ }
+ continue;
+ }
+
+ // It's a regular character.
+ result.push_back(ch);
+ }
+
+ return result;
+}
+
+// Converts an array of string arguments to char *arguments.
+// The first arg is reduced to its basename as per execve conventions.
+// Note that the lifetime of the char* arguments in the returned array
+// are controlled by the lifetime of the strings in args.
+std::vector<const char *> ConvertToCArgs(const std::vector<std::string> &args) {
+ std::vector<const char *> c_args;
+ c_args.push_back(Basename(args[0].c_str()));
+ for (int i = 1; i < args.size(); i++) {
+ c_args.push_back(args[i].c_str());
+ }
+ c_args.push_back(nullptr);
+ return c_args;
+}
+
+// Spawns a subprocess for given arguments args. The first argument is used
+// for the executable path.
+bool RunSubProcess(const std::vector<std::string> &args) {
+ std::vector<const char *> exec_argv = ConvertToCArgs(args);
+ pid_t pid;
+ int status = posix_spawn(&pid, args[0].c_str(), nullptr, nullptr,
+ const_cast<char **>(exec_argv.data()), environ);
+ if (status == 0) {
+ int wait_status;
+ do {
+ wait_status = waitpid(pid, &status, 0);
+ } while ((wait_status == -1) && (errno == EINTR));
+ if (wait_status < 0) {
+ std::cerr << "Error waiting on child process '" << args[0] << "'. "
+ << strerror(errno) << "\n";
+ return false;
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ std::cerr << "Error in child process '" << args[0] << "'. "
+ << WEXITSTATUS(status) << "\n";
+ return false;
+ } else if (WIFSIGNALED(status)) {
+ std::cerr << "Error in child process '" << args[0] << "'. "
+ << WTERMSIG(status) << "\n";
+ return false;
+ }
+ } else {
+ std::cerr << "Error forking process '" << args[0] << "'. "
+ << strerror(status) << "\n";
+ return false;
+ }
+
+ return true;
+}
+
+// Finds and replaces all instances of oldsub with newsub, in-place on str.
+void FindAndReplace(const std::string &oldsub, const std::string &newsub,
+ std::string *str) {
+ int start = 0;
+ while ((start = str->find(oldsub, start)) != std::string::npos) {
+ str->replace(start, oldsub.length(), newsub);
+ start += newsub.length();
+ }
+}
+
+// If arg is of the classic flag form "foo=bar", and flagname is 'foo', sets
+// str to point to a new std::string 'bar' and returns true.
+// Otherwise, returns false.
+bool SetArgIfFlagPresent(const std::string &arg, const std::string &flagname,
+ std::string *str) {
+ std::string prefix_string = flagname + "=";
+ if (arg.compare(0, prefix_string.length(), prefix_string) == 0) {
+ *str = arg.substr(prefix_string.length());
+ return true;
+ }
+ return false;
+}
+
+// Returns the DEVELOPER_DIR environment variable in the current process
+// environment. Aborts if this variable is unset.
+std::string GetMandatoryEnvVar(const std::string &var_name) {
+ char *env_value = getenv(var_name.c_str());
+ if (env_value == nullptr) {
+ std::cerr << "Error: " << var_name << " not set.\n";
+ exit(EXIT_FAILURE);
+ }
+ return env_value;
+}
+
+// Returns true if `str` starts with the specified `prefix`.
+bool StartsWith(const std::string &str, const std::string &prefix) {
+ return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// If *`str` begins `prefix`, strip it out and return true.
+// Otherwise leave *`str` unchanged and return false.
+bool StripPrefixStringIfPresent(std::string *str, const std::string &prefix) {
+ if (StartsWith(*str, prefix)) {
+ *str = str->substr(prefix.size());
+ return true;
+ }
+ return false;
+}
+
+// An RAII temporary file.
+class TempFile {
+ public:
+ // Create a new temporary file using the given path template string (the same
+ // form used by `mkstemp`). The file will automatically be deleted when the
+ // object goes out of scope.
+ static std::unique_ptr<TempFile> Create(const std::string &path_template) {
+ const char *tmpDir = getenv("TMPDIR");
+ if (!tmpDir) {
+ tmpDir = "/tmp";
+ }
+ size_t size = strlen(tmpDir) + path_template.size() + 2;
+ std::unique_ptr<char[]> path(new char[size]);
+ snprintf(path.get(), size, "%s/%s", tmpDir, path_template.c_str());
+
+ if (mkstemp(path.get()) == -1) {
+ std::cerr << "Failed to create temporary file '" << path.get()
+ << "': " << strerror(errno) << "\n";
+ return nullptr;
+ }
+ return std::unique_ptr<TempFile>(new TempFile(path.get()));
+ }
+
+ // Explicitly make TempFile non-copyable and movable.
+ TempFile(const TempFile &) = delete;
+ TempFile &operator=(const TempFile &) = delete;
+ TempFile(TempFile &&) = default;
+ TempFile &operator=(TempFile &&) = default;
+
+ ~TempFile() { remove(path_.c_str()); }
+
+ // Gets the path to the temporary file.
+ std::string GetPath() const { return path_; }
+
+ private:
+ explicit TempFile(const std::string &path) : path_(path) {}
+
+ std::string path_;
+};
+
+static std::unique_ptr<TempFile> WriteResponseFile(
+ const std::vector<std::string> &args) {
+ auto response_file = TempFile::Create("wrapped_clang_params.XXXXXX");
+ std::ofstream response_file_stream(response_file->GetPath());
+
+ for (const auto &arg : args) {
+ // When Clang/Swift write out a response file to communicate from driver to
+ // frontend, they just quote every argument to be safe; we duplicate that
+ // instead of trying to be "smarter" and only quoting when necessary.
+ response_file_stream << '"';
+ for (auto ch : arg) {
+ if (ch == '"' || ch == '\\') {
+ response_file_stream << '\\';
+ }
+ response_file_stream << ch;
+ }
+ response_file_stream << "\"\n";
+ }
+
+ response_file_stream.close();
+ return response_file;
+}
+
+void ProcessArgument(const std::string arg, const std::string developer_dir,
+ const std::string sdk_root, const std::string cwd,
+ bool relative_ast_path, std::string &linked_binary,
+ std::string &dsym_path, std::string toolchain_path,
+ std::function<void(const std::string &)> consumer);
+
+bool ProcessResponseFile(const std::string arg, const std::string developer_dir,
+ const std::string sdk_root, const std::string cwd,
+ bool relative_ast_path, std::string &linked_binary,
+ std::string &dsym_path, std::string toolchain_path,
+ std::function<void(const std::string &)> consumer) {
+ auto path = arg.substr(1);
+ std::ifstream original_file(path);
+ // Ignore non-file args such as '@loader_path/...'
+ if (!original_file.good()) {
+ return false;
+ }
+
+ std::string arg_from_file;
+ while (std::getline(original_file, arg_from_file)) {
+ // Arguments in response files might be quoted/escaped, so we need to
+ // unescape them ourselves.
+ ProcessArgument(Unescape(arg_from_file), developer_dir, sdk_root, cwd,
+ relative_ast_path, linked_binary, dsym_path,
+ toolchain_path, consumer);
+ }
+
+ return true;
+}
+
+std::string GetCurrentDirectory() {
+ // Passing null,0 causes getcwd to allocate the buffer of the correct size.
+ char *buffer = getcwd(nullptr, 0);
+ std::string cwd(buffer);
+ free(buffer);
+ return cwd;
+}
+
+std::string exec(std::string cmd) {
+ std::array<char, 128> buffer;
+ std::string result;
+ std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
+ if (!pipe) {
+ std::cerr << "Error: failed to open pipe to '" << cmd << "'" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
+ result += buffer.data();
+ }
+ return result;
+}
+
+std::string GetToolchainPath(const std::string &toolchain_id) {
+ // NOTE: This requires all toolchains to contain a 'clang' executable. This
+ // is true today for custom Swift toolchains, but could change in the future.
+ std::string output = exec("xcrun --find clang --toolchain " + toolchain_id);
+
+ if (output.empty()) {
+ std::cerr << "Error: TOOLCHAINS was set to '" << toolchain_id
+ << "' but no toolchain with that ID was found" << std::endl;
+ exit(EXIT_FAILURE);
+ } else if (output.find("XcodeDefault.xctoolchain") != std::string::npos) {
+ // NOTE: Ideally xcrun would fail if the toolchain we asked for didn't exist
+ // but it falls back to the DEVELOPER_DIR instead, so we have to check the
+ // output ourselves.
+ std::cerr << "Error: TOOLCHAINS was set to '" << toolchain_id
+ << "' but the default toolchain was found, that likely means a matching "
+ << "toolchain isn't installed" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ std::filesystem::path toolchain_path(output);
+ // Remove usr/bin/clang components to get the root of the custom toolchain
+ return toolchain_path.parent_path().parent_path().parent_path();
+}
+
+void ProcessArgument(const std::string arg, const std::string developer_dir,
+ const std::string sdk_root, const std::string cwd,
+ bool relative_ast_path, std::string &linked_binary,
+ std::string &dsym_path, std::string toolchain_path,
+ std::function<void(const std::string &)> consumer) {
+ auto new_arg = arg;
+ if (arg[0] == '@') {
+ if (ProcessResponseFile(arg, developer_dir, sdk_root, cwd,
+ relative_ast_path, linked_binary, dsym_path,
+ toolchain_path, consumer)) {
+ return;
+ }
+ }
+
+ if (SetArgIfFlagPresent(arg, "DSYM_HINT_LINKED_BINARY", &linked_binary)) {
+ return;
+ }
+ if (SetArgIfFlagPresent(arg, "DSYM_HINT_DSYM_PATH", &dsym_path)) {
+ return;
+ }
+
+ FindAndReplace("__BAZEL_EXECUTION_ROOT__", cwd, &new_arg);
+ FindAndReplace("__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir, &new_arg);
+ FindAndReplace("__BAZEL_XCODE_SDKROOT__", sdk_root, &new_arg);
+ if (!toolchain_path.empty()) {
+ FindAndReplace("__BAZEL_CUSTOM_XCODE_TOOLCHAIN_PATH__", toolchain_path, &new_arg);
+ }
+
+ // Make the `add_ast_path` options used to embed Swift module references
+ // absolute to enable Swift debugging without dSYMs: see
+ // https://forums.swift.org/t/improving-swift-lldb-support-for-path-remappings/22694
+ if (!relative_ast_path &&
+ StripPrefixStringIfPresent(&new_arg, kAddASTPathPrefix)) {
+ // Only modify relative paths.
+ if (!StartsWith(arg, "/")) {
+ new_arg = std::string(kAddASTPathPrefix) + cwd + "/" + new_arg;
+ } else {
+ new_arg = std::string(kAddASTPathPrefix) + new_arg;
+ }
+ }
+
+ consumer(new_arg);
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ std::string tool_name;
+
+ std::string binary_name = Basename(argv[0]);
+ if (binary_name == "wrapped_clang_pp") {
+ tool_name = "clang++";
+ } else if (binary_name == "wrapped_clang") {
+ tool_name = "clang";
+ } else {
+ std::cerr << "Binary must either be named 'wrapped_clang' or "
+ "'wrapped_clang_pp', not "
+ << binary_name << "\n";
+ return 1;
+ }
+
+ const char *toolchain_id = getenv("TOOLCHAINS");
+ std::string toolchain_path = "";
+ if (toolchain_id != nullptr) {
+ toolchain_path = GetToolchainPath(toolchain_id);
+ }
+
+ std::string developer_dir = GetMandatoryEnvVar("DEVELOPER_DIR");
+ std::string sdk_root = GetMandatoryEnvVar("SDKROOT");
+ std::string linked_binary, dsym_path;
+
+ const std::string cwd = GetCurrentDirectory();
+ std::vector<std::string> invocation_args = {"/usr/bin/xcrun", tool_name};
+ std::vector<std::string> processed_args = {};
+
+ bool relative_ast_path = getenv("RELATIVE_AST_PATH") != nullptr;
+ auto consumer = [&](const std::string &arg) {
+ processed_args.push_back(arg);
+ };
+ for (int i = 1; i < argc; i++) {
+ std::string arg(argv[i]);
+
+ ProcessArgument(arg, developer_dir, sdk_root, cwd, relative_ast_path,
+ linked_binary, dsym_path, toolchain_path, consumer);
+ }
+
+ // Special mode that only prints the command. Used for testing.
+ if (getenv("__WRAPPED_CLANG_LOG_ONLY")) {
+ for (const std::string &arg : invocation_args) std::cout << arg << ' ';
+ for (const std::string &arg : processed_args) std::cout << arg << ' ';
+ std::cout << "\n";
+ return 0;
+ }
+
+ auto response_file = WriteResponseFile(processed_args);
+ invocation_args.push_back("@" + response_file->GetPath());
+
+ // Check to see if we should postprocess with dsymutil.
+ bool postprocess = false;
+ if ((!linked_binary.empty()) || (!dsym_path.empty())) {
+ if ((linked_binary.empty()) || (dsym_path.empty())) {
+ const char *missing_dsym_flag;
+ if (linked_binary.empty()) {
+ missing_dsym_flag = "DSYM_HINT_LINKED_BINARY";
+ } else {
+ missing_dsym_flag = "DSYM_HINT_DSYM_PATH";
+ }
+ std::cerr << "Error in clang wrapper: If any dsym "
+ "hint is defined, then "
+ << missing_dsym_flag << " must be defined\n";
+ return 1;
+ } else {
+ postprocess = true;
+ }
+ }
+
+ if (!RunSubProcess(invocation_args)) {
+ return 1;
+ }
+
+ if (!postprocess) {
+ return 0;
+ }
+
+ std::vector<std::string> dsymutil_args = {"/usr/bin/xcrun",
+ "dsymutil",
+ linked_binary,
+ "-o",
+ dsym_path,
+ "--flat",
+ "--no-swiftmodule-timestamp"};
+ if (!RunSubProcess(dsymutil_args)) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/crosstool/xcrunwrapper.sh b/crosstool/xcrunwrapper.sh
new file mode 100755
index 0000000..143ff23
--- /dev/null
+++ b/crosstool/xcrunwrapper.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# xcrunwrapper runs the command passed to it using xcrun. The first arg
+# passed is the name of the tool to be invoked via xcrun. (For example, libtool
+# or clang).
+# xcrunwrapper replaces __BAZEL_XCODE_DEVELOPER_DIR__ with $DEVELOPER_DIR (or
+# reasonable default) and __BAZEL_XCODE_SDKROOT__ with a valid path based on
+# SDKROOT (or reasonable default).
+# These values (__BAZEL_XCODE_*) are a shared secret withIosSdkCommands.java.
+
+set -eu
+
+TOOLNAME=$1
+shift
+
+# Pick values for DEVELOPER_DIR and SDKROOT as appropriate (if they weren't set)
+WRAPPER_DEVDIR="${DEVELOPER_DIR:-}"
+if [[ -z "${WRAPPER_DEVDIR}" ]] ; then
+ WRAPPER_DEVDIR="$(xcode-select -p)"
+fi
+
+# Substitute toolkit path placeholders.
+UPDATEDARGS=()
+for ARG in "$@" ; do
+ ARG="${ARG//__BAZEL_XCODE_DEVELOPER_DIR__/${WRAPPER_DEVDIR}}"
+ ARG="${ARG//__BAZEL_XCODE_SDKROOT__/${SDKROOT}}"
+ UPDATEDARGS+=("${ARG}")
+done
+
+/usr/bin/xcrun "${TOOLNAME}" "${UPDATEDARGS[@]}"
diff --git a/doc/BUILD b/doc/BUILD
new file mode 100644
index 0000000..97b5593
--- /dev/null
+++ b/doc/BUILD
@@ -0,0 +1,71 @@
+load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc")
+
+_DOC_SRCS = {
+ "rules": [
+ "apple_genrule",
+ "toolchain_substitution",
+ "universal_binary",
+ ],
+}
+
+write_file(
+ name = "rules_header",
+ out = "rules_header.vm",
+ content = [
+ "<!-- Generated with Stardoc, Do Not Edit! -->",
+ "",
+ "${moduleDocstring}",
+ "On this page:",
+ "",
+ ] + [" * [{0}](#{0})".format(r) for r in _DOC_SRCS["rules"]] + [
+ "",
+ ],
+)
+
+[
+ stardoc(
+ name = file + "_doc",
+ out = file + ".md_",
+ header_template = file + "_header.vm",
+ input = "//rules:rules.doc.bzl",
+ symbol_names = symbols,
+ tags = ["no-sandbox"], # https://github.com/bazelbuild/stardoc/issues/112
+ deps = ["//rules"],
+ )
+ for [
+ file,
+ symbols,
+ ] in _DOC_SRCS.items()
+]
+
+[
+ diff_test(
+ name = "test_" + file,
+ failure_message = "\nPlease update the docs by running\n bazel run //doc:update",
+ file1 = file + ".md_",
+ file2 = file + ".md",
+ )
+ for file in _DOC_SRCS.keys()
+]
+
+write_file(
+ name = "gen_update",
+ out = "update.sh",
+ content = [
+ "#!/usr/bin/env bash",
+ "cd $BUILD_WORKSPACE_DIRECTORY",
+ ] + [
+ "cp -fv bazel-bin/doc/{0}.md_ doc/{0}.md".format(
+ file,
+ )
+ for file in _DOC_SRCS.keys()
+ ],
+)
+
+sh_binary(
+ name = "update",
+ srcs = ["update.sh"],
+ data = [file + ".md_" for file in _DOC_SRCS.keys()],
+)
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..b3e9618
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,50 @@
+# Apple Support Starlark Modules and Rules
+
+Apple Support provides a collection of Starlark helpers for rule authors
+targeting Apple Platforms (and Xcode) as well as some Rules directly.
+
+## Starlark Modules
+
+<table class="table table-condensed table-bordered table-params">
+ <thead>
+ <tr>
+ <th><code>.bzl</code> file</th>
+ <th>Module</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td valign="top"><code>@build_bazel_apple_support//lib:apple_support.bzl</code></td>
+ <td valign="top"><code><a href="apple_support.md">apple_support</a></code><br/></td>
+ </tr>
+ <tr>
+ <td valign="top"><code>@build_bazel_apple_support//lib:xcode_support.bzl</code></td>
+ <td valign="top"><code><a href="xcode_support.md">xcode_support</a></code><br/></td>
+ </tr>
+ </tbody>
+</table>
+
+## Rules
+
+<table class="table table-condensed table-bordered table-params">
+ <thead>
+ <tr>
+ <th><code>.bzl</code> file</th>
+ <th>Rules</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td valign="top"><code>@build_bazel_apple_support//rules:apple_genrule.bzl</code></td>
+ <td valign="top"><code><a href="rules.md#apple_genrule">apple_genrule</a></code><br/></td>
+ </tr>
+ <tr>
+ <td valign="top"><code>@build_bazel_apple_support//rules:toolchain_substitution.bzl</code></td>
+ <td valign="top"><code><a href="rules.md#toolchain_substitution">toolchain_substitution</a></code><br/></td>
+ </tr>
+ <tr>
+ <td valign="top"><code>@build_bazel_apple_support//rules:universal_binary.bzl</code></td>
+ <td valign="top"><code><a href="rules.md#universal_binary">universal_binary</a></code><br/></td>
+ </tr>
+ </tbody>
+</table>
diff --git a/doc/apple_support.md b/doc/apple_support.md
new file mode 100644
index 0000000..6be5706
--- /dev/null
+++ b/doc/apple_support.md
@@ -0,0 +1,395 @@
+# `apple_support` Starlark Module
+
+<!-- Generated file, do not edit directly. -->
+
+
+A module of helpers for rule authors to aid in writing actions that
+target Apple platforms.
+
+To use these in your Starlark code, simply load the module; for example:
+
+```build
+load("@build_bazel_apple_support//lib:apple_support.bzl", "apple_support")
+```
+
+<!-- BEGIN_TOC -->
+On this page:
+
+ * [apple_support.action_required_attrs](#apple_support.action_required_attrs)
+ * [apple_support.action_required_env](#apple_support.action_required_env)
+ * [apple_support.action_required_execution_requirements](#apple_support.action_required_execution_requirements)
+ * [apple_support.path_placeholders.platform_frameworks](#apple_support.path_placeholders.platform_frameworks)
+ * [apple_support.path_placeholders.sdkroot](#apple_support.path_placeholders.sdkroot)
+ * [apple_support.path_placeholders.xcode](#apple_support.path_placeholders.xcode)
+ * [apple_support.run](#apple_support.run)
+ * [apple_support.run_shell](#apple_support.run_shell)
+<!-- END_TOC -->
+
+
+<a name="apple_support.action_required_attrs"></a>
+## apple_support.action_required_attrs
+
+<pre style="white-space: normal">
+apple_support.action_required_attrs()
+</pre>
+
+Returns a dictionary with required attributes for registering actions on Apple platforms.
+
+This method adds private attributes which should not be used outside of the apple_support
+codebase. It also adds the following attributes which are considered to be public for rule
+maintainers to use:
+
+ * `_xcode_config`: Attribute that references a target containing the single
+ `apple_common.XcodeVersionConfig` provider. This provider can be used to inspect Xcode-related
+ properties about the Xcode being used for the build, as specified with the `--xcode_version`
+ Bazel flag. The most common way to retrieve this provider is:
+ `ctx.attr._xcode_config[apple_common.XcodeVersionConfig]`.
+
+The returned `dict` can be added to the rule's attributes using Skylib's `dicts.add()` method.
+
+<a name="apple_support.action_required_attrs.returns"></a>
+### Returns
+
+A `dict` object containing attributes to be added to rule implementations.
+
+<a name="apple_support.action_required_env"></a>
+## apple_support.action_required_env
+
+<pre style="white-space: normal">
+apple_support.action_required_env(<a href="#apple_support.action_required_env.ctx">ctx</a>, *,
+<a href="#apple_support.action_required_env.xcode_config">xcode_config</a>,
+<a href="#apple_support.action_required_env.apple_fragment">apple_fragment</a>)
+</pre>
+
+Returns a dictionary with the environment variables required for Xcode path resolution.
+
+In most cases, you should _not_ use this API. It exists solely for using it on test rules,
+where the test action registration API is not available in Starlark.
+
+To use these environment variables for a test, your test rule needs to propagate the
+`testing.TestEnvironment` provider, which takes a dictionary with environment variables to set
+during the test execution.
+
+<a name="apple_support.action_required_env.arguments"></a>
+### Arguments
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="apple_support.action_required_env.ctx">
+ <td><code>ctx</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The context of the rule registering the
+ action. Required if <code>xcode_config</code> and <code>apple_fragment</code> are not
+ provided. Deprecated.</p></td>
+ </tr>
+ <tr id="apple_support.action_required_env.xcode_config">
+ <td><code>xcode_config</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The
+ <code>apple_common.XcodeVersionConfig</code> provider as found in the current rule or
+ aspect's context. Typically from
+ <code>ctx.attr._xcode_config[apple_common.XcodeVersionConfig]</code>. Required if
+ <code>ctx</code> is not given.</p></td>
+ </tr>
+ <tr id="apple_support.action_required_env.apple_fragment">
+ <td><code>apple_fragment</code></td>
+ <td><p><code>Optional; default is None</code></p><p>A reference to the apple fragment.
+ Typically from <code>ctx.fragments.apple</code>. Required if <code>ctx</code> is not
+ given.</p></td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="apple_support.action_required_env.returns"></a>
+### Returns
+
+A dictionary with environment variables required for Xcode path resolution.
+
+<a name="apple_support.action_required_execution_requirements"></a>
+## apple_support.action_required_execution_requirements
+
+<pre style="white-space: normal">
+apple_support.action_required_execution_requirements(<a href="#apple_support.action_required_execution_requirements.ctx">ctx</a>, *, <a href="#apple_support.action_required_execution_requirements.xcode_config">xcode_config</a>)
+</pre>
+
+Returns a dictionary with the execution requirements for running actions on Apple platforms.
+
+In most cases, you should _not_ use this API. It exists solely for using it on test rules,
+where the test action registration API is not available in Starlark.
+
+To use these environment variables for a test, your test rule needs to propagate the
+`testing.TestExecution` provider, which takes a dictionary with execution requirements for the
+test action.
+
+<a name="apple_support.action_required_execution_requirements.arguments"></a>
+### Arguments
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="apple_support.action_required_execution_requirements.ctx">
+ <td><code>ctx</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The context of the rule registering the
+ action. Required if <code>xcode_config</code> is not provided. Deprecated.</p></td>
+ </tr>
+ <tr id="apple_support.action_required_execution_requirements.xcode_config">
+ <td><code>xcode_config</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The
+ <code>apple_common.XcodeVersionConfig</code> provider as found in the current rule or
+ aspect's context. Typically from
+ <code>ctx.attr._xcode_config[apple_common.XcodeVersionConfig]</code>. Required if
+ <code>ctx</code> is not given.</p></td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="apple_support.action_required_execution_requirements.returns"></a>
+### Returns
+
+A dictionary with execution requirements for running actions on Apple platforms.
+
+
+<a name="apple_support.path_placeholders.platform_frameworks"></a>
+## apple_support.path_placeholders.platform_frameworks
+
+<pre style="white-space: normal">
+apple_support.path_placeholders.platform_frameworks(<a href="#apple_support.path_placeholders.platform_frameworks.ctx">ctx</a>, *, <a href="#apple_support.path_placeholders.platform_frameworks.apple_fragment">apple_fragment</a>)
+</pre>
+
+Returns the platform's frameworks directory, anchored to the Xcode path placeholder.
+
+<a name="apple_support.path_placeholders.platform_frameworks.arguments"></a>
+### Arguments
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="apple_support.path_placeholders.platform_frameworks.ctx">
+ <td><code>ctx</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The context of the rule registering the
+ action. Required if <code>apple_fragment</code> is not provided. Deprecated.</p></td>
+ </tr>
+ <tr id="apple_support.path_placeholders.platform_frameworks.apple_fragment">
+ <td><code>apple_fragment</code></td>
+ <td><p><code>Optional; default is None</code></p><p>A reference to the apple fragment.
+ Typically from <code>ctx.fragments.apple</code>. Required if <code>ctx</code> is not
+ given.</p></td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="apple_support.path_placeholders.platform_frameworks.returns"></a>
+### Returns
+
+Returns a string with the platform's frameworks directory, anchored to the Xcode path
+placeholder.
+
+<a name="apple_support.path_placeholders.sdkroot"></a>
+## apple_support.path_placeholders.sdkroot
+
+<pre style="white-space: normal">
+apple_support.path_placeholders.sdkroot()
+</pre>
+
+Returns a placeholder value to be replaced with SDKROOT during action execution.
+
+In order to get this values replaced, you'll need to use the `apple_support.run()` API by
+setting the `xcode_path_resolve_level` argument to either the
+`apple_support.xcode_path_resolve_level.args` or
+`apple_support.xcode_path_resolve_level.args_and_files` value.
+
+<a name="apple_support.path_placeholders.sdkroot.returns"></a>
+### Returns
+
+Returns a placeholder value to be replaced with SDKROOT during action execution.
+
+<a name="apple_support.path_placeholders.xcode"></a>
+## apple_support.path_placeholders.xcode
+
+<pre style="white-space: normal">
+apple_support.path_placeholders.xcode()
+</pre>
+
+Returns a placeholder value to be replaced with DEVELOPER_DIR during action execution.
+
+In order to get this values replaced, you'll need to use the `apple_support.run()` API by
+setting the `xcode_path_resolve_level` argument to either the
+`apple_support.xcode_path_resolve_level.args` or
+`apple_support.xcode_path_resolve_level.args_and_files` value.
+
+<a name="apple_support.path_placeholders.xcode.returns"></a>
+### Returns
+
+Returns a placeholder value to be replaced with DEVELOPER_DIR during action execution.
+
+
+<a name="apple_support.run"></a>
+## apple_support.run
+
+<pre style="white-space: normal">
+apple_support.run(<a href="#apple_support.run.ctx">ctx</a>, <a href="#apple_support.run.xcode_path_resolve_level">xcode_path_resolve_level</a>, *, <a href="#apple_support.run.actions">actions</a>, <a href="#apple_support.run.xcode_config">xcode_config</a>, <a href="#apple_support.run.apple_fragment">apple_fragment</a>, <a href="#apple_support.run.xcode_path_wrapper">xcode_path_wrapper</a>, <a href="#apple_support.run.**kwargs">**kwargs</a>)
+</pre>
+
+Registers an action to run on an Apple machine.
+
+In order to use `apple_support.run()`, you'll need to modify your rule definition to add the
+following:
+
+ * `fragments = ["apple"]`
+ * Add the `apple_support.action_required_attrs()` attributes to the `attrs` dictionary. This
+ can be done using the `dicts.add()` method from Skylib.
+
+This method registers an action to run on an Apple machine, configuring it to ensure that the
+`DEVELOPER_DIR` and `SDKROOT` environment variables are set.
+
+If the `xcode_path_resolve_level` is enabled, this method will replace the given `executable`
+with a wrapper script that will replace all instances of the `__BAZEL_XCODE_DEVELOPER_DIR__` and
+`__BAZEL_XCODE_SDKROOT__` placeholders in the given arguments with the values of `DEVELOPER_DIR`
+and `SDKROOT`, respectively.
+
+In your rule implementation, you can use references to Xcode through the
+`apple_support.path_placeholders` API, which in turn uses the placeholder values as described
+above. The available APIs are:
+
+ * `apple_support.path_placeholders.xcode()`: Returns a reference to the Xcode.app
+ installation path.
+ * `apple_support.path_placeholders.sdkroot()`: Returns a reference to the SDK root path.
+ * `apple_support.path_placeholders.platform_frameworks(ctx)`: Returns the Frameworks path
+ within the Xcode installation, for the requested platform.
+
+If the `xcode_path_resolve_level` value is:
+
+ * `apple_support.xcode_path_resolve_level.none`: No processing will be done to the given
+ `arguments`.
+ * `apple_support.xcode_path_resolve_level.args`: Only instances of the placeholders in the
+ argument strings will be replaced.
+ * `apple_support.xcode_path_resolve_level.args_and_files`: Instances of the placeholders in
+ the arguments strings and instances of the placeholders within response files (i.e. any
+ path argument beginning with `@`) will be replaced.
+
+<a name="apple_support.run.arguments"></a>
+### Arguments
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="apple_support.run.ctx">
+ <td><code>ctx</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The context of the rule registering the
+ action. Required if <code>xcode_config</code> and <code>apple_fragment</code> are not
+ provided. Deprecated.</p></td>
+ </tr>
+ <tr id="apple_support.run.xcode_path_resolve_level">
+ <td><code>xcode_path_resolve_level</code></td>
+ <td><p><code>Optional; default is apple_support.xcode_path_resolve_level.none</code></p>
+ <p>The level of Xcode path replacement required for the action.</p></td>
+ </tr>
+ <tr id="apple_support.run.actions">
+ <td><code>actions</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The actions provider from
+ <code>ctx.actions</code>. Required if <code>ctx</code> is not given.</p></td>
+ </tr>
+ <tr id="apple_support.run.xcode_config">
+ <td><code>xcode_config</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The
+ <code>apple_common.XcodeVersionConfig</code> provider as found in the current rule or
+ aspect's context. Typically from
+ <code>ctx.attr._xcode_config[apple_common.XcodeVersionConfig]</code>. Required if
+ <code>ctx</code> is not given.</p></td>
+ </tr>
+ <tr id="apple_support.run.apple_fragment">
+ <td><code>apple_fragment</code></td>
+ <td><p><code>Optional; default is None</code></p><p>A reference to the apple fragment.
+ Typically from <code>ctx.fragments.apple</code>. Required if <code>ctx</code> is not
+ given.</p></td>
+ </tr>
+ <tr id="apple_support.run.xcode_path_wrapper">
+ <td><code>xcode_path_wrapper</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The Xcode path wrapper script. Required
+ if <code>ctx</code> is not given and <code>xcode_path_resolve_level</code> is not
+ <code>apple_support.xcode_path_resolve_level.none</code>.</p></td>
+ </tr>
+ <tr id="apple_support.run.**kwargs">
+ <td><code>**kwargs</code></td>
+ <td><p>See <code>ctx.actions.run</code> for the rest of the available arguments.</p></td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="apple_support.run_shell"></a>
+## apple_support.run_shell
+
+<pre style="white-space: normal">
+apple_support.run_shell(<a href="#apple_support.run_shell.ctx">ctx</a>, *, <a href="#apple_support.run_shell.actions">actions</a>, <a href="#apple_support.run_shell.xcode_config">xcode_config</a>, <a href="#apple_support.run_shell.apple_fragment">apple_fragment</a>, <a href="#apple_support.run_shell.**kwargs">**kwargs</a>)
+</pre>
+
+Registers a shell action to run on an Apple machine.
+
+In order to use `apple_support.run_shell()`, you'll need to modify your rule definition to add
+the following:
+
+ * `fragments = ["apple"]`
+ * Add the `apple_support.action_required_attrs()` attributes to the `attrs` dictionary. This
+ can be done using the `dicts.add()` method from Skylib.
+
+This method registers an action to run on an Apple machine, configuring it to ensure that the
+`DEVELOPER_DIR` and `SDKROOT` environment variables are set.
+
+`run_shell` does not support placeholder substitution. To achieve placeholder substitution,
+please use `run` instead.
+
+<a name="apple_support.run_shell.arguments"></a>
+### Arguments
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="apple_support.run_shell.ctx">
+ <td><code>ctx</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The context of the rule registering the
+ action. Required if <code>xcode_config</code> and <code>apple_fragment</code> are not
+ provided. Deprecated.</p></td>
+ </tr>
+ <tr id="apple_support.run_shell.actions">
+ <td><code>actions</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The actions provider from
+ <code>ctx.actions</code>. Required if <code>ctx</code> is not given.</p></td>
+ </tr>
+ <tr id="apple_support.run_shell.xcode_config">
+ <td><code>xcode_config</code></td>
+ <td><p><code>Optional; default is None</code></p><p>The
+ <code>apple_common.XcodeVersionConfig</code> provider as found in the current rule or
+ aspect's context. Typically from
+ <code>ctx.attr._xcode_config[apple_common.XcodeVersionConfig]</code>. Required if
+ <code>ctx</code> is not given.</p></td>
+ </tr>
+ <tr id="apple_support.run_shell.apple_fragment">
+ <td><code>apple_fragment</code></td>
+ <td><p><code>Optional; default is None</code></p><p>A reference to the apple fragment.
+ Typically from <code>ctx.fragments.apple</code>. Required if <code>ctx</code> is not
+ given.</p></td>
+ </tr>
+ <tr id="apple_support.run_shell.**kwargs">
+ <td><code>**kwargs</code></td>
+ <td><p>See <code>ctx.actions.run_shell</code> for the rest of the available arguments.</p></td>
+ </tr>
+ </tbody>
+</table>
+
+
+
+
diff --git a/doc/rules.md b/doc/rules.md
new file mode 100644
index 0000000..5b5064c
--- /dev/null
+++ b/doc/rules.md
@@ -0,0 +1,143 @@
+<!-- Generated with Stardoc, Do Not Edit! -->
+
+# Helper rules for Apple platforms
+On this page:
+
+ * [apple_genrule](#apple_genrule)
+ * [toolchain_substitution](#toolchain_substitution)
+ * [universal_binary](#universal_binary)
+
+<a id="apple_genrule"></a>
+
+## apple_genrule
+
+<pre>
+apple_genrule(<a href="#apple_genrule-name">name</a>, <a href="#apple_genrule-srcs">srcs</a>, <a href="#apple_genrule-outs">outs</a>, <a href="#apple_genrule-cmd">cmd</a>, <a href="#apple_genrule-executable">executable</a>, <a href="#apple_genrule-message">message</a>, <a href="#apple_genrule-no_sandbox">no_sandbox</a>, <a href="#apple_genrule-tools">tools</a>)
+</pre>
+
+Genrule which provides Apple specific environment and make variables.
+
+This mirrors the native genrule except that it provides a different set of
+make variables. This rule will only run on a Mac.
+
+Example of use:
+
+```
+load("@build_bazel_apple_support//rules:apple_genrule.bzl", "apple_genrule")
+
+apple_genrule(
+ name = "world",
+ outs = ["hi"],
+ cmd = "touch $@",
+)
+```
+
+This rule also does location expansion, much like the native genrule.
+For example, `$(location hi)` may be used to refer to the output in the
+above example.
+
+The set of make variables that are supported for this rule:
+
+* `OUTS`: The outs list. If you have only one output file, you can also use
+ `$@`.
+* `SRCS`: The srcs list (or more precisely, the pathnames of the files
+ corresponding to labels in the srcs list). If you have only one
+ source file, you can also use `$<`.
+* `<`: srcs, if it's a single file.
+* `@`: outs, if it's a single file.
+* `@D`: The output directory. If there is only one filename in outs, this
+ expands to the directory containing that file. If there are
+ multiple filenames, this variable instead expands to the package's
+ root directory in the genfiles tree, even if all the generated
+ files belong to the same subdirectory.
+
+The following environment variables are defined when the rule is executed:
+
+* `DEVELOPER_DIR`: The base developer directory as defined on Apple
+ architectures, most commonly used in invoking Apple
+ tools such as xcrun.
+* `SDKROOT`: The base SDK directory as defined on Apple architectures, most
+ commonly used in invoking Apple tools such as xcrun.
+
+NOTE: `DEVELOPER_DIR` and `SDKROOT` are environment variables and *not* make
+ variables. To refer to them in `cmd` you must use environment variable
+ syntax (i.e. using `$$`). Example: ```cmd = "xcrun --sdkroot $$SDKROOT clang...```
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="apple_genrule-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="apple_genrule-srcs"></a>srcs | A list of inputs for this rule, such as source files to process. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
+| <a id="apple_genrule-outs"></a>outs | A list of files generated by this rule. If the executable flag is set, outs must contain exactly one label. | List of labels | required | |
+| <a id="apple_genrule-cmd"></a>cmd | The command to run. Subject the variable substitution. | String | required | |
+| <a id="apple_genrule-executable"></a>executable | Declare output to be executable. Setting this flag to 1 means the output is an executable file and can be run using the run command. The genrule must produce exactly one output in this case. | Boolean | optional | `False` |
+| <a id="apple_genrule-message"></a>message | A progress message to be reported as the rule runs. | String | optional | `""` |
+| <a id="apple_genrule-no_sandbox"></a>no_sandbox | If the sandbox should be disabled when the action is run. | Boolean | optional | `False` |
+| <a id="apple_genrule-tools"></a>tools | A list of tool dependencies for this rule, they will be available when the action is run. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
+
+
+<a id="toolchain_substitution"></a>
+
+## toolchain_substitution
+
+<pre>
+toolchain_substitution(<a href="#toolchain_substitution-name">name</a>, <a href="#toolchain_substitution-src">src</a>, <a href="#toolchain_substitution-var_name">var_name</a>)
+</pre>
+
+Register a subsitution, from a variable to a file, for use in arguments in a
+target's attributes. Useful for passing custom tools to a compile or link.
+
+### Example:
+
+```bzl
+load("@build_bazel_apple_support//rules:toolchain_substitution.bzl", "toolchain_substitution")
+
+toolchain_substitution(
+ name = "resource_rules",
+ src = "resource_rules.plist",
+ var_name = "RULES",
+)
+
+ios_application(
+ ...
+ codesignopts = ["--resource-rules=$(RULES)"],
+ codesign_inputs = [":resource_rules"],
+ toolchains = [":resource_rules"],
+)
+```
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="toolchain_substitution-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="toolchain_substitution-src"></a>src | Source file to substitute | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+| <a id="toolchain_substitution-var_name"></a>var_name | Name of the variable to substitute, ex: FOO | String | required | |
+
+
+<a id="universal_binary"></a>
+
+## universal_binary
+
+<pre>
+universal_binary(<a href="#universal_binary-name">name</a>, <a href="#universal_binary-binary">binary</a>)
+</pre>
+
+This rule produces a multi-architecture ("fat") binary targeting Apple macOS
+platforms *regardless* of the architecture of the macOS host platform. The
+`lipo` tool is used to combine built binaries of multiple architectures. For
+non-macOS platforms, this simply just creates a symbolic link of the input
+binary.
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| <a id="universal_binary-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
+| <a id="universal_binary-binary"></a>binary | Target to generate a 'fat' binary from. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
+
+
diff --git a/doc/xcode_support.md b/doc/xcode_support.md
new file mode 100644
index 0000000..93603f5
--- /dev/null
+++ b/doc/xcode_support.md
@@ -0,0 +1,62 @@
+# `xcode_support` Starlark Module
+
+<!-- Generated file, do not edit directly. -->
+
+
+A modules of helpers for rule authors to aid in writing rules that
+need to change what they do based on attributes of the active Xcode.
+
+To use these in your Starlark code, simply load the module; for example:
+
+```build
+load("@build_bazel_apple_support//lib:xcode_support.bzl", "xcode_support")
+```
+
+<!-- BEGIN_TOC -->
+On this page:
+
+ * [xcode_support.is_xcode_at_least_version](#xcode_support.is_xcode_at_least_version)
+<!-- END_TOC -->
+
+
+<a name="xcode_support.is_xcode_at_least_version"></a>
+## xcode_support.is_xcode_at_least_version
+
+<pre style="white-space: normal">
+xcode_support.is_xcode_at_least_version(<a href="#xcode_support.is_xcode_at_least_version.xcode_config">xcode_config</a>, <a href="#xcode_support.is_xcode_at_least_version.version">version</a>)
+</pre>
+
+Returns True if Xcode version is at least a given version.
+
+This method takes as input an `XcodeVersionConfig` provider, which can be obtained from the
+`_xcode_config` attribute (e.g. `ctx.attr._xcode_config[apple_common.XcodeVersionConfig]`). This
+provider should contain the Xcode version parameters with which this rule is being built with.
+If you need to add this attribute to your rule implementation, please refer to
+`apple_support.action_required_attrs()`.
+
+<a name="xcode_support.is_xcode_at_least_version.arguments"></a>
+### Arguments
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="xcode_support.is_xcode_at_least_version.xcode_config">
+ <td><code>xcode_config</code></td>
+ <td><p><code>Required</code></p><p>The XcodeVersionConfig provider from the <code>_xcode_config</code> attribute's value.</p></td>
+ </tr>
+ <tr id="xcode_support.is_xcode_at_least_version.version">
+ <td><code>version</code></td>
+ <td><p><code>Required</code></p><p>The minimum desired Xcode version, as a dotted version string.</p></td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="xcode_support.is_xcode_at_least_version.returns"></a>
+### Returns
+
+True if the given `xcode_config` version at least as high as the requested version.
+
+
diff --git a/lib/BUILD b/lib/BUILD
new file mode 100644
index 0000000..3a8767a
--- /dev/null
+++ b/lib/BUILD
@@ -0,0 +1,72 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+licenses(["notice"])
+
+# This target is mainly for use by rules_apple and rules_swift.
+# When this target is linked into an XCTest binary, and the
+# `BUILD_WORKSPACE_DIRECTORY` environment variable is set, the relative paths of
+# `XCTSourceCodeLocation` are made absolute, fixing Test Issue navigation in
+# Xcode.
+objc_library(
+ name = "swizzle_absolute_xcttestsourcelocation",
+ testonly = True,
+ srcs = ["swizzle_absolute_xcttestsourcelocation.m"],
+ target_compatible_with = select({
+ "@platforms//os:ios": [],
+ "@platforms//os:macos": [],
+ "@platforms//os:tvos": [],
+ "@platforms//os:visionos": [],
+ "@platforms//os:watchos": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ }),
+ visibility = ["//visibility:public"],
+ alwayslink = True,
+)
+
+# Public bzl_library for anything that needs to depend on apple_support.bzl.
+bzl_library(
+ name = "apple_support",
+ srcs = ["apple_support.bzl"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "@bazel_skylib//lib:types",
+ ],
+)
+
+bzl_library(
+ name = "lipo",
+ srcs = ["lipo.bzl"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":apple_support",
+ "@bazel_skylib//lib:shell",
+ ],
+)
+
+bzl_library(
+ name = "transitions",
+ srcs = ["transitions.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+# Public bzl_library for anything that needs to depend on xcode_support.bzl.
+bzl_library(
+ name = "xcode_support",
+ srcs = ["xcode_support.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+# Public bzl_library for anything that needs to depend on repositories.bzl.
+bzl_library(
+ name = "repositories",
+ srcs = ["repositories.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = 1,
+ srcs = glob(["**"]),
+ visibility = ["//:__pkg__"],
+)
diff --git a/lib/apple_support.bzl b/lib/apple_support.bzl
new file mode 100644
index 0000000..94cdafa
--- /dev/null
+++ b/lib/apple_support.bzl
@@ -0,0 +1,625 @@
+# Copyright 2018 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.
+"""Definitions for registering actions on Apple platforms."""
+
+load("@bazel_skylib//lib:types.bzl", "types")
+
+# Options to declare the level of Xcode path resolving needed in an `apple_support.run()`
+# invocation.
+_XCODE_PATH_RESOLVE_LEVEL = struct(
+ none = None,
+ args = "args",
+ args_and_files = "args_and_files",
+)
+
+# This is a proxy for being on bazel 7.x which has
+# --incompatible_merge_fixed_and_default_shell_env enabled by default
+_USE_DEFAULT_SHELL_ENV = not hasattr(apple_common, "apple_crosstool_transition")
+
+_XCODE_PROCESSOR__ARGS = r"""#!/bin/bash
+
+set -eu
+
+# SYNOPSIS
+# Rewrites any Bazel placeholder strings in the given argument string,
+# echoing the result.
+#
+# USAGE
+# rewrite_argument <argument>
+function rewrite_argument {
+ ARG="$1"
+ ARG="${ARG//__BAZEL_XCODE_DEVELOPER_DIR__/$DEVELOPER_DIR}"
+ ARG="${ARG//__BAZEL_XCODE_SDKROOT__/$SDKROOT}"
+ echo "$ARG"
+}
+
+TOOLNAME="$1"
+shift
+
+ARGS=()
+
+for ARG in "$@" ; do
+ ARGS+=("$(rewrite_argument "$ARG")")
+done
+
+exec "$TOOLNAME" "${ARGS[@]}"
+"""
+
+_XCODE_PROCESSOR__ARGS_AND_FILES = r"""#!/bin/bash
+
+set -eu
+
+# SYNOPSIS
+# Rewrites any Bazel placeholder strings in the given argument string,
+# echoing the result.
+#
+# USAGE
+# rewrite_argument <argument>
+function rewrite_argument {
+ ARG="$1"
+ ARG="${ARG//__BAZEL_XCODE_DEVELOPER_DIR__/$DEVELOPER_DIR}"
+ ARG="${ARG//__BAZEL_XCODE_SDKROOT__/$SDKROOT}"
+ echo "$ARG"
+}
+
+# SYNOPSIS
+# Rewrites any Bazel placeholder strings in the given params file, if any.
+# If there were no substitutions to be made, the original path is echoed back
+# out; otherwise, this function echoes the path to a temporary file
+# containing the rewritten file.
+#
+# USAGE
+# rewrite_params_file <path>
+function rewrite_params_file {
+ PARAMSFILE="$1"
+ if grep -qe '__BAZEL_XCODE_\(DEVELOPER_DIR\|SDKROOT\)__' "$PARAMSFILE" ; then
+ NEWFILE="$(mktemp "${TMPDIR%/}/bazel_xcode_wrapper_params.XXXXXXXXXX")"
+ sed \
+ -e "s#__BAZEL_XCODE_DEVELOPER_DIR__#$DEVELOPER_DIR#g" \
+ -e "s#__BAZEL_XCODE_SDKROOT__#$SDKROOT#g" \
+ "$PARAMSFILE" > "$NEWFILE"
+ echo "$NEWFILE"
+ else
+ # There were no placeholders to substitute, so just return the original
+ # file.
+ echo "$PARAMSFILE"
+ fi
+}
+
+TOOLNAME="$1"
+shift
+
+ARGS=()
+
+# If any temporary files are created (like rewritten response files), clean
+# them up when the script exits.
+TEMPFILES=()
+trap '[[ ${#TEMPFILES[@]} -ne 0 ]] && rm "${TEMPFILES[@]}"' EXIT
+
+for ARG in "$@" ; do
+ case "$ARG" in
+ @*)
+ PARAMSFILE="${ARG:1}"
+ NEWFILE=$(rewrite_params_file "$PARAMSFILE")
+ if [[ "$PARAMSFILE" != "$NEWFILE" ]] ; then
+ TEMPFILES+=("$NEWFILE")
+ fi
+ ARG="@$NEWFILE"
+ ;;
+ *)
+ ARG=$(rewrite_argument "$ARG")
+ ;;
+ esac
+ ARGS+=("$ARG")
+done
+
+# We can't use `exec` here because we need to make sure the `trap` runs
+# afterward.
+"$TOOLNAME" "${ARGS[@]}"
+"""
+
+def _validate_ctx_xor_platform_requirements(*, ctx, actions, apple_fragment, xcode_config):
+ """Raises an error if there is overlap in platform requirements or if they are insufficent."""
+
+ if ctx != None and any([actions, xcode_config, apple_fragment]):
+ fail("Can't specific ctx along with actions, xcode_config, apple_fragment.")
+ if ctx == None and not all([actions, xcode_config, apple_fragment]):
+ fail("Must specify all of actions, xcode_config, and apple_fragment.")
+ if ctx != None:
+ _validate_ctx_attribute_present(ctx, "_xcode_config")
+
+def _platform_frameworks_path_placeholder(*, apple_fragment):
+ """Returns the platform's frameworks directory, anchored to the Xcode path placeholder.
+
+ Args:
+ apple_fragment: A reference to the apple fragment. Typically from `ctx.fragments.apple`.
+
+ Returns:
+ Returns a string with the platform's frameworks directory, anchored to the Xcode path
+ placeholder.
+ """
+ return "{xcode_path}/Platforms/{platform_name}.platform/Developer/Library/Frameworks".format(
+ platform_name = apple_fragment.single_arch_platform.name_in_plist,
+ xcode_path = _xcode_path_placeholder(),
+ )
+
+def _sdkroot_path_placeholder():
+ """Returns a placeholder value to be replaced with SDKROOT during action execution.
+
+ In order to get this values replaced, you'll need to use the `apple_support.run()` API by
+ setting the `xcode_path_resolve_level` argument to either the
+ `apple_support.xcode_path_resolve_level.args` or
+ `apple_support.xcode_path_resolve_level.args_and_files` value.
+
+ Returns:
+ Returns a placeholder value to be replaced with SDKROOT during action execution.
+ """
+ return "__BAZEL_XCODE_SDKROOT__"
+
+def _xcode_path_placeholder():
+ """Returns a placeholder value to be replaced with DEVELOPER_DIR during action execution.
+
+ In order to get this values replaced, you'll need to use the `apple_support.run()` API by
+ setting the `xcode_path_resolve_level` argument to either the
+ `apple_support.xcode_path_resolve_level.args` or
+ `apple_support.xcode_path_resolve_level.args_and_files` value.
+
+ Returns:
+ Returns a placeholder value to be replaced with DEVELOPER_DIR during action execution.
+ """
+ return "__BAZEL_XCODE_DEVELOPER_DIR__"
+
+def _kwargs_for_apple_platform(
+ *,
+ apple_fragment,
+ xcode_config,
+ **kwargs):
+ """Returns a modified dictionary with required arguments to run on Apple platforms."""
+ processed_args = dict(kwargs)
+
+ merged_env = {}
+ original_env = processed_args.get("env")
+ if original_env:
+ merged_env.update(original_env)
+
+ if "use_default_shell_env" not in processed_args:
+ processed_args["use_default_shell_env"] = _USE_DEFAULT_SHELL_ENV
+
+ # Add the environment variables required for DEVELOPER_DIR and SDKROOT last to avoid clients
+ # overriding these values.
+ merged_env.update(apple_common.apple_host_system_env(xcode_config))
+ merged_env.update(
+ apple_common.target_apple_env(xcode_config, apple_fragment.single_arch_platform),
+ )
+
+ merged_execution_requirements = {}
+ original_execution_requirements = processed_args.get("execution_requirements")
+ if original_execution_requirements:
+ merged_execution_requirements.update(original_execution_requirements)
+
+ # Add the Xcode execution requirements last to avoid clients overriding these values.
+ merged_execution_requirements.update(xcode_config.execution_info())
+
+ processed_args["env"] = merged_env
+ processed_args["execution_requirements"] = merged_execution_requirements
+ return processed_args
+
+def _validate_ctx_attribute_present(ctx, attribute_name):
+ """Validates that the given attribute is present for the rule, failing otherwise."""
+ if not hasattr(ctx.attr, attribute_name):
+ fail("\n".join([
+ "",
+ "ERROR: This rule requires the '{}' attribute to be present. ".format(attribute_name),
+ "To add this attribute, modify your rule definition like this:",
+ "",
+ "load(\"@bazel_skylib//lib:dicts.bzl\", \"dicts\")",
+ "load(",
+ " \"@build_bazel_apple_support//lib:apple_support.bzl\",",
+ " \"apple_support\",",
+ ")",
+ "",
+ "your_rule_name = rule(",
+ " attrs = dicts.add(apple_support.action_required_attrs(), {",
+ " # other attributes",
+ " }),",
+ " # other rule arguments",
+ ")",
+ "",
+ ]))
+
+def _action_required_attrs():
+ """Returns a dictionary with required attributes for registering actions on Apple platforms.
+
+ This method adds private attributes which should not be used outside of the apple_support
+ codebase. It also adds the following attributes which are considered to be public for rule
+ maintainers to use:
+
+ * `_xcode_config`: Attribute that references a target containing the single
+ `apple_common.XcodeVersionConfig` provider. This provider can be used to inspect Xcode-related
+ properties about the Xcode being used for the build, as specified with the `--xcode_version`
+ Bazel flag. The most common way to retrieve this provider is:
+ `ctx.attr._xcode_config[apple_common.XcodeVersionConfig]`.
+
+ The returned `dict` can be added to the rule's attributes using Skylib's `dicts.add()` method.
+
+ Returns:
+ A `dict` object containing attributes to be added to rule implementations.
+ """
+ return {
+ "_xcode_config": attr.label(
+ default = configuration_field(
+ name = "xcode_config_label",
+ fragment = "apple",
+ ),
+ ),
+ }
+
+def _platform_constraint_attrs():
+ """Returns a dictionary of all known Apple platform constraints that can be resolved.
+
+ The returned `dict` can be added to the rule's attributes using Skylib's `dicts.add()` method.
+
+ Returns:
+ A `dict` object containing attributes to be added to rule implementations.
+ """
+ return {
+ "_ios_constraint": attr.label(
+ default = Label("@platforms//os:ios"),
+ ),
+ "_macos_constraint": attr.label(
+ default = Label("@platforms//os:macos"),
+ ),
+ "_tvos_constraint": attr.label(
+ default = Label("@platforms//os:tvos"),
+ ),
+ "_visionos_constraint": attr.label(
+ default = Label("@platforms//os:visionos"),
+ ),
+ "_watchos_constraint": attr.label(
+ default = Label("@platforms//os:watchos"),
+ ),
+ "_arm64_constraint": attr.label(
+ default = Label("@platforms//cpu:arm64"),
+ ),
+ "_arm64e_constraint": attr.label(
+ default = Label("@platforms//cpu:arm64e"),
+ ),
+ "_arm64_32_constraint": attr.label(
+ default = Label("@platforms//cpu:arm64_32"),
+ ),
+ "_armv7k_constraint": attr.label(
+ default = Label("@platforms//cpu:armv7k"),
+ ),
+ "_x86_64_constraint": attr.label(
+ default = Label("@platforms//cpu:x86_64"),
+ ),
+ "_apple_device_constraint": attr.label(
+ default = Label("//constraints:device"),
+ ),
+ "_apple_simulator_constraint": attr.label(
+ default = Label("//constraints:simulator"),
+ ),
+ }
+
+def _run(
+ ctx = None,
+ xcode_path_resolve_level = _XCODE_PATH_RESOLVE_LEVEL.none,
+ *,
+ actions = None,
+ xcode_config = None,
+ apple_fragment = None,
+ **kwargs):
+ """Registers an action to run on an Apple machine.
+
+ In order to use `apple_support.run()`, you'll need to modify your rule definition to add the
+ following:
+
+ * `fragments = ["apple"]`
+ * Add the `apple_support.action_required_attrs()` attributes to the `attrs` dictionary. This
+ can be done using the `dicts.add()` method from Skylib.
+
+ This method registers an action to run on an Apple machine, configuring it to ensure that the
+ `DEVELOPER_DIR` and `SDKROOT` environment variables are set.
+
+ If the `xcode_path_resolve_level` is enabled, this method will replace the given `executable`
+ with a wrapper script that will replace all instances of the `__BAZEL_XCODE_DEVELOPER_DIR__` and
+ `__BAZEL_XCODE_SDKROOT__` placeholders in the given arguments with the values of `DEVELOPER_DIR`
+ and `SDKROOT`, respectively.
+
+ In your rule implementation, you can use references to Xcode through the
+ `apple_support.path_placeholders` API, which in turn uses the placeholder values as described
+ above. The available APIs are:
+
+ * `apple_support.path_placeholders.xcode()`: Returns a reference to the Xcode.app
+ installation path.
+ * `apple_support.path_placeholders.sdkroot()`: Returns a reference to the SDK root path.
+ * `apple_support.path_placeholders.platform_frameworks(ctx)`: Returns the Frameworks path
+ within the Xcode installation, for the requested platform.
+
+ If the `xcode_path_resolve_level` value is:
+
+ * `apple_support.xcode_path_resolve_level.none`: No processing will be done to the given
+ `arguments`.
+ * `apple_support.xcode_path_resolve_level.args`: Only instances of the placeholders in the
+ argument strings will be replaced.
+ * `apple_support.xcode_path_resolve_level.args_and_files`: Instances of the placeholders in
+ the arguments strings and instances of the placeholders within response files (i.e. any
+ path argument beginning with `@`) will be replaced.
+
+ Args:
+ ctx: The context of the rule registering the action. Deprecated.
+ xcode_path_resolve_level: The level of Xcode path replacement required for the action.
+ actions: The actions provider from ctx.actions. Required if ctx is not given.
+ xcode_config: The xcode_config as found in the current rule or aspect's
+ context. Typically from `ctx.attr._xcode_config[apple_common.XcodeVersionConfig]`.
+ Required if ctx is not given.
+ apple_fragment: A reference to the apple fragment. Typically from `ctx.fragments.apple`.
+ Required if ctx is not given.
+ **kwargs: See `ctx.actions.run` for the rest of the available arguments.
+ """
+ _validate_ctx_xor_platform_requirements(
+ ctx = ctx,
+ actions = actions,
+ apple_fragment = apple_fragment,
+ xcode_config = xcode_config,
+ )
+
+ if not actions:
+ actions = ctx.actions
+ if not xcode_config:
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ if not apple_fragment:
+ apple_fragment = ctx.fragments.apple
+
+ if xcode_path_resolve_level == _XCODE_PATH_RESOLVE_LEVEL.none:
+ actions.run(**_kwargs_for_apple_platform(
+ xcode_config = xcode_config,
+ apple_fragment = apple_fragment,
+ **kwargs
+ ))
+ return
+
+ # Since a label/name isn't passed in, use the first output to derive a name
+ # that will hopefully be unique. For added attempt at safety, name the file
+ # based on the content, so any dup actions might still merge.
+ base = kwargs.get("outputs")[0].short_path.replace("/", "_")
+ if xcode_path_resolve_level == _XCODE_PATH_RESOLVE_LEVEL.args:
+ script = _XCODE_PROCESSOR__ARGS
+ suffix = "args"
+ else:
+ script = _XCODE_PROCESSOR__ARGS_AND_FILES
+ suffix = "args_and_files"
+ processor_script = actions.declare_file("{}_processor_script_{}.sh".format(base, suffix))
+ actions.write(processor_script, script, is_executable = True)
+
+ processed_kwargs = _kwargs_for_apple_platform(
+ xcode_config = xcode_config,
+ apple_fragment = apple_fragment,
+ **kwargs
+ )
+
+ all_arguments = []
+
+ # If the client requires Xcode path resolving, push the original executable to be the first
+ # argument, as the executable will be set to be the xcode_path_wrapper script.
+ # Note: Bounce through an actions.args() incase the executable was a `File`, this allows it
+ # to then work within the arguments list.
+ executable_args = actions.args()
+ original_executable = processed_kwargs.pop("executable")
+
+ # actions.run supports multiple executable types. (File; or string; or FilesToRunProvider)
+ # If the passed in executable is a FilesToRunProvider, only add the main executable to the
+ # should be added to the executable args.
+ if type(original_executable) == "FilesToRunProvider":
+ executable_args.add(original_executable.executable)
+ else:
+ executable_args.add(original_executable)
+ all_arguments.append(executable_args)
+
+ # Append the original arguments to the full list of arguments, after the original executable.
+ original_args_list = processed_kwargs.pop("arguments", [])
+ if not original_args_list:
+ fail("Error: Does not make sense to request args processing without any `arguments`.")
+ all_arguments.extend(original_args_list)
+
+ # We also need to include the user executable in the "tools" argument of the action, since it
+ # won't be referenced by "executable" anymore.
+ original_tools = processed_kwargs.pop("tools", None)
+ if types.is_list(original_tools):
+ all_tools = [original_executable] + original_tools
+ elif types.is_depset(original_tools):
+ all_tools = depset([original_executable], transitive = [original_tools])
+ elif original_tools:
+ fail("'tools' argument must be a sequence or depset.")
+ elif not types.is_string(original_executable):
+ # Only add the user_executable to the "tools" list if it's a File, not a string.
+ all_tools = [original_executable]
+ else:
+ all_tools = []
+
+ actions.run(
+ executable = processor_script,
+ arguments = all_arguments,
+ tools = all_tools,
+ **processed_kwargs
+ )
+
+def _run_shell(
+ ctx = None,
+ *,
+ actions = None,
+ xcode_config = None,
+ apple_fragment = None,
+ **kwargs):
+ """Registers a shell action to run on an Apple machine.
+
+ In order to use `apple_support.run_shell()`, you'll need to modify your rule definition to add
+ the following:
+
+ * `fragments = ["apple"]`
+ * Add the `apple_support.action_required_attrs()` attributes to the `attrs` dictionary. This
+ can be done using the `dicts.add()` method from Skylib.
+
+ This method registers an action to run on an Apple machine, configuring it to ensure that the
+ `DEVELOPER_DIR` and `SDKROOT` environment variables are set.
+
+ `run_shell` does not support placeholder substitution. To achieve placeholder substitution,
+ please use `run` instead.
+
+ Args:
+ ctx: The context of the rule registering the action. Deprecated.
+ actions: The actions provider from ctx.actions.
+ xcode_config: The xcode_config as found in the current rule or aspect's
+ context. Typically from `ctx.attr._xcode_config[apple_common.XcodeVersionConfig]`.
+ Required if ctx is not given.
+ apple_fragment: A reference to the apple fragment. Typically from `ctx.fragments.apple`.
+ Required if ctx is not given.
+ **kwargs: See `ctx.actions.run_shell` for the rest of the available arguments.
+ """
+ _validate_ctx_xor_platform_requirements(
+ ctx = ctx,
+ actions = actions,
+ apple_fragment = apple_fragment,
+ xcode_config = xcode_config,
+ )
+
+ if not actions:
+ actions = ctx.actions
+ if not xcode_config:
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ if not apple_fragment:
+ apple_fragment = ctx.fragments.apple
+
+ actions.run_shell(**_kwargs_for_apple_platform(
+ xcode_config = xcode_config,
+ apple_fragment = apple_fragment,
+ **kwargs
+ ))
+
+def _target_arch_from_rule_ctx(ctx):
+ """Returns a `String` representing the target architecture based on constraints.
+
+ The returned `String` will represent a cpu architecture, such as `arm64` or `arm64e`.
+
+ In order to use `apple_support.target_arch_from_rule_ctx()`, you'll need to modify your rule
+ definition to add the following:
+
+ * Add the `apple_support.platform_constraint_attrs()` attributes to the `attrs` dictionary.
+ This can be done using the `dicts.add()` method from Skylib.
+
+ Args:
+ ctx: The context of the rule that has Apple platform constraint attributes.
+
+ Returns:
+ A `String` representing the selected target architecture or cpu type (e.g. `arm64`,
+ `arm64e`).
+ """
+ arm64_constraint = ctx.attr._arm64_constraint[platform_common.ConstraintValueInfo]
+ arm64e_constraint = ctx.attr._arm64e_constraint[platform_common.ConstraintValueInfo]
+ arm64_32_constraint = ctx.attr._arm64_32_constraint[platform_common.ConstraintValueInfo]
+ armv7k_constraint = ctx.attr._armv7k_constraint[platform_common.ConstraintValueInfo]
+ x86_64_constraint = ctx.attr._x86_64_constraint[platform_common.ConstraintValueInfo]
+
+ if ctx.target_platform_has_constraint(arm64_constraint):
+ return "arm64"
+ elif ctx.target_platform_has_constraint(arm64e_constraint):
+ return "arm64e"
+ elif ctx.target_platform_has_constraint(arm64_32_constraint):
+ return "arm64_32"
+ elif ctx.target_platform_has_constraint(armv7k_constraint):
+ return "armv7k"
+ elif ctx.target_platform_has_constraint(x86_64_constraint):
+ return "x86_64"
+ fail("ERROR: A valid Apple cpu constraint could not be found from the resolved toolchain.")
+
+def _target_environment_from_rule_ctx(ctx):
+ """Returns a `String` representing the target environment based on constraints.
+
+ The returned `String` will represent an environment, such as `device` or `simulator`.
+
+ For consistency with other Apple platforms, `macos` is considered to be a `device`.
+
+ In order to use `apple_support.target_environment_from_rule_ctx()`, you'll need to modify your
+ rule definition to add the following:
+
+ * Add the `apple_support.platform_constraint_attrs()` attributes to the `attrs` dictionary.
+ This can be done using the `dicts.add()` method from Skylib.
+
+ Args:
+ ctx: The context of the rule that has Apple platform constraint attributes.
+
+ Returns:
+ A `String` representing the selected environment (e.g. `device`, `simulator`).
+ """
+ device_constraint = ctx.attr._apple_device_constraint[platform_common.ConstraintValueInfo]
+ simulator_constraint = ctx.attr._apple_simulator_constraint[platform_common.ConstraintValueInfo]
+
+ if ctx.target_platform_has_constraint(device_constraint):
+ return "device"
+ elif ctx.target_platform_has_constraint(simulator_constraint):
+ return "simulator"
+ fail("ERROR: A valid Apple environment (device, simulator) constraint could not be found from" +
+ " the resolved toolchain.")
+
+def _target_os_from_rule_ctx(ctx):
+ """Returns a `String` representing the target OS based on constraints.
+
+ The returned `String` will match an equivalent value from one of the platform definitions in
+ `apple_common.platform_type`, such as `ios` or `macos`.
+
+ In order to use `apple_support.target_os_from_rule_ctx()`, you'll need to modify your rule
+ definition to add the following:
+
+ * Add the `apple_support.platform_constraint_attrs()` attributes to the `attrs` dictionary.
+ This can be done using the `dicts.add()` method from Skylib.
+
+ Args:
+ ctx: The context of the rule that has Apple platform constraint attributes.
+
+ Returns:
+ A `String` representing the selected Apple OS.
+ """
+ ios_constraint = ctx.attr._ios_constraint[platform_common.ConstraintValueInfo]
+ macos_constraint = ctx.attr._macos_constraint[platform_common.ConstraintValueInfo]
+ tvos_constraint = ctx.attr._tvos_constraint[platform_common.ConstraintValueInfo]
+ visionos_constraint = ctx.attr._visionos_constraint[platform_common.ConstraintValueInfo]
+ watchos_constraint = ctx.attr._watchos_constraint[platform_common.ConstraintValueInfo]
+
+ if ctx.target_platform_has_constraint(ios_constraint):
+ return str(apple_common.platform_type.ios)
+ elif ctx.target_platform_has_constraint(macos_constraint):
+ return str(apple_common.platform_type.macos)
+ elif ctx.target_platform_has_constraint(tvos_constraint):
+ return str(apple_common.platform_type.tvos)
+ elif ctx.target_platform_has_constraint(visionos_constraint):
+ return str(apple_common.platform_type.visionos)
+ elif ctx.target_platform_has_constraint(watchos_constraint):
+ return str(apple_common.platform_type.watchos)
+ fail("ERROR: A valid Apple platform constraint could not be found from the resolved toolchain.")
+
+apple_support = struct(
+ action_required_attrs = _action_required_attrs,
+ path_placeholders = struct(
+ platform_frameworks = _platform_frameworks_path_placeholder,
+ sdkroot = _sdkroot_path_placeholder,
+ xcode = _xcode_path_placeholder,
+ ),
+ platform_constraint_attrs = _platform_constraint_attrs,
+ run = _run,
+ run_shell = _run_shell,
+ target_arch_from_rule_ctx = _target_arch_from_rule_ctx,
+ target_environment_from_rule_ctx = _target_environment_from_rule_ctx,
+ target_os_from_rule_ctx = _target_os_from_rule_ctx,
+ xcode_path_resolve_level = _XCODE_PATH_RESOLVE_LEVEL,
+)
diff --git a/lib/lipo.bzl b/lib/lipo.bzl
new file mode 100644
index 0000000..8bf6b54
--- /dev/null
+++ b/lib/lipo.bzl
@@ -0,0 +1,146 @@
+# Copyright 2021 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.
+
+"""APIs for operating on universal binaries with `lipo`."""
+
+load(
+ "@bazel_skylib//lib:shell.bzl",
+ "shell",
+)
+load(":apple_support.bzl", "apple_support")
+
+def _create(
+ *,
+ actions,
+ inputs,
+ output,
+ apple_fragment,
+ xcode_config):
+ """Creates a universal binary by combining other binaries.
+
+ Args:
+ actions: The `Actions` object used to register actions.
+ inputs: A sequence or `depset` of `File`s that represent binaries to
+ be combined. As with the `lipo` tool's `-create` command (when
+ invoked without the `-arch` option) all of the architectures in
+ each input file will be copied into the output file (so the inputs
+ may be either single-architecture binaries or universal binaries).
+ output: A `File` representing the universal binary that will be the
+ output of the action.
+ apple_fragment: The `apple` configuration fragment used to configure
+ the action environment.
+ xcode_config: The `apple_common.XcodeVersionConfig` provider used to
+ configure the action environment.
+ """
+ if not inputs:
+ fail("lipo.create requires at least one input file.")
+
+ # Explicitly create the containing directory to avoid an occasional error
+ # from lipo; "can't create temporary output file [...] (Permission denied)"
+ command = [
+ "mkdir -p {} &&".format(shell.quote(output.dirname)),
+ "/usr/bin/lipo",
+ "-create",
+ ]
+ command.extend([
+ shell.quote(input_file.path)
+ for input_file in inputs
+ ])
+ command.extend(["-output", shell.quote(output.path)])
+
+ apple_support.run_shell(
+ actions = actions,
+ command = " ".join(command),
+ mnemonic = "AppleLipo",
+ inputs = inputs,
+ outputs = [output],
+ apple_fragment = apple_fragment,
+ xcode_config = xcode_config,
+ )
+
+def _extract_or_thin(
+ *,
+ actions,
+ apple_fragment,
+ archs,
+ input_shell_expression = None,
+ input_file = None,
+ output,
+ xcode_config):
+ """Extracts a subset of an existing universal binary based on the set of incoming architectures.
+
+ Args:
+ actions: The `Actions` object used to register actions.
+ apple_fragment: The `apple` configuration fragment used to configure the action environment.
+ archs: A list of strings that indicates the exact set of architectures we need to create the
+ output binary. As with the `lipo` tool's `-extract` command, all of the selected
+ architectures indicated by `archs` in the universal binary will be copied into the
+ output file. If only one architecture is selected, this will be rely on the `lipo`
+ tool's `-thin` command instead.
+ input_shell_expression: A string that represent the universal binary to be extracted. Use
+ only if the input cannot be represented adequately as a file, i.e. if `$SDKROOT` is
+ required to get at its path. Required if the `input_file` is not set.
+ input_file: A `File` that represents the universal binary to be extracted. Required if the
+ `input_command` is not set.
+ output: A `File` representing the universal binary that will be the output of the action.
+ xcode_config: The `apple_common.XcodeVersionConfig` provider used to configure the action
+ environment.
+ """
+ if not input_file and not input_shell_expression:
+ fail("lipo.extract_or_thin needs a fat binary in `input_file` or `input_shell_expression`.")
+ if input_file and input_shell_expression:
+ fail("""
+lipo.extract_or_thin cannot use `input_file` along with `input_shell_expression` simultaneously.
+
+Please use only `input_file` or `input_shell_expression` to express the intended input.
+""")
+ if not archs:
+ fail("lipo.extract_or_thin requires at least one arch.")
+
+ # Explicitly create the containing directory if it doesn't exist, otherwise the following error
+ # can come from lipo; "can't create temporary output file [...] (Permission denied)"
+ command = [
+ "mkdir -p {} &&".format(shell.quote(output.dirname)),
+ "/usr/bin/lipo",
+ ]
+ if input_file:
+ command.append(shell.quote(input_file.path))
+ else:
+ command.append(input_shell_expression)
+ if len(archs) == 1:
+ command.extend(["-thin", archs[0]])
+ else:
+ for arch in archs:
+ command.extend(["-extract", arch])
+ command.extend(["-output", shell.quote(output.path)])
+
+ extra_args = {}
+ if input_file:
+ extra_args["inputs"] = [input_file]
+ apple_support.run_shell(
+ actions = actions,
+ command = " ".join(command),
+ mnemonic = "AppleLipoExtract",
+ outputs = [output],
+ apple_fragment = apple_fragment,
+ xcode_config = xcode_config,
+ **extra_args
+ )
+
+# TODO(apple-rules-team): Add support for other mutating operations here if
+# there is a need: extract_family, remove, replace.
+lipo = struct(
+ create = _create,
+ extract_or_thin = _extract_or_thin,
+)
diff --git a/lib/repositories.bzl b/lib/repositories.bzl
new file mode 100644
index 0000000..cad0083
--- /dev/null
+++ b/lib/repositories.bzl
@@ -0,0 +1,59 @@
+# Copyright 2018 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.
+
+"""Definitions for handling Bazel repositories used by apple_support."""
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("//crosstool:setup.bzl", "apple_cc_configure")
+
+def _maybe(repo_rule, name, **kwargs):
+ """Executes the given repository rule if it hasn't been executed already.
+
+ Args:
+ repo_rule: The repository rule to be executed (e.g., `git_repository`.)
+ name: The name of the repository to be defined by the rule.
+ **kwargs: Additional arguments passed directly to the repository rule.
+ """
+ if not native.existing_rule(name):
+ repo_rule(name = name, **kwargs)
+
+# buildifier: disable=unnamed-macro
+def apple_support_dependencies():
+ """Fetches repository dependencies of the `apple_support` workspace.
+
+ Users should call this macro in their `WORKSPACE` to ensure that all of the
+ dependencies of the Swift rules are downloaded and that they are isolated from
+ changes to those dependencies.
+ """
+ _maybe(
+ http_archive,
+ name = "bazel_skylib",
+ urls = [
+ "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
+ "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
+ ],
+ sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
+ )
+
+ _maybe(
+ http_archive,
+ name = "platforms",
+ urls = [
+ "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.7/platforms-0.0.7.tar.gz",
+ "https://github.com/bazelbuild/platforms/releases/download/0.0.7/platforms-0.0.7.tar.gz",
+ ],
+ sha256 = "3a561c99e7bdbe9173aa653fd579fe849f1d8d67395780ab4770b1f381431d51",
+ )
+
+ apple_cc_configure()
diff --git a/lib/swizzle_absolute_xcttestsourcelocation.m b/lib/swizzle_absolute_xcttestsourcelocation.m
new file mode 100644
index 0000000..4f3af8d
--- /dev/null
+++ b/lib/swizzle_absolute_xcttestsourcelocation.m
@@ -0,0 +1,55 @@
+#import <objc/runtime.h>
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+static NSString *kSourceRoot;
+
+NSURL* remapFileUrl (NSURL *fileURL) {
+ if ([fileURL.path hasPrefix:kSourceRoot]) {
+ return fileURL;
+ }
+
+ return [NSURL fileURLWithPath:
+ [NSString stringWithFormat:@"%@/%@", kSourceRoot, fileURL.relativePath]
+ ];
+}
+
+@implementation XCTSourceCodeLocation (FixRelativePaths)
+
++ (void)load {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ kSourceRoot = [NSProcessInfo processInfo]
+ .environment[@"BUILD_WORKSPACE_DIRECTORY"];
+ if (!kSourceRoot) {
+ NSLog(@"warning: The 'BUILD_WORKSPACE_DIRECTORY' environment "
+ "variable was not set. Test issue navigation might not work.");
+ return;
+ }
+ if (![kSourceRoot hasPrefix:@"/"]) {
+ NSLog(@"warning: The 'BUILD_WORKSPACE_DIRECTORY' was not an "
+ "absolute path. Test issue navigation might not work.");
+ return;
+ }
+
+ Class class = [XCTSourceCodeLocation class];
+
+ SEL originalSelector = @selector(initWithFileURL:lineNumber:);
+ SEL swizzledSelector = @selector(xxx_initWithRelativeFileURL:lineNumber:);
+
+ Method originalMethod = class_getInstanceMethod(class, originalSelector);
+ Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
+
+ method_exchangeImplementations(originalMethod, swizzledMethod);
+ });
+}
+
+- (instancetype)xxx_initWithRelativeFileURL:(NSURL *)fileURL
+ lineNumber:(NSInteger)lineNumber
+{
+ // Not recursive because of swizzling
+ return [self xxx_initWithRelativeFileURL:remapFileUrl(fileURL)
+ lineNumber:lineNumber];
+}
+
+@end
diff --git a/lib/transitions.bzl b/lib/transitions.bzl
new file mode 100644
index 0000000..2bcfabc
--- /dev/null
+++ b/lib/transitions.bzl
@@ -0,0 +1,49 @@
+# Copyright 2021 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.
+
+"""Transition support."""
+
+def _macos_universal_transition_impl(settings, _attr):
+ # Create a split transition from any macOS cpu to a list of all macOS cpus
+ if settings["//command_line_option:cpu"].startswith("darwin"):
+ return [
+ {
+ "//command_line_option:cpu": "darwin_arm64",
+ "//command_line_option:platforms": [
+ settings["//platforms:macos_arm64_platform"],
+ ],
+ },
+ {
+ "//command_line_option:cpu": "darwin_x86_64",
+ "//command_line_option:platforms": [
+ settings["//platforms:macos_x86_64_platform"],
+ ],
+ },
+ ]
+ else:
+ return {
+ "//command_line_option:cpu": settings["//command_line_option:cpu"],
+ "//command_line_option:platforms": settings["//command_line_option:platforms"],
+ }
+
+macos_universal_transition = transition(
+ implementation = _macos_universal_transition_impl,
+ inputs = [
+ "//command_line_option:cpu",
+ "//command_line_option:platforms",
+ "//platforms:macos_x86_64_platform",
+ "//platforms:macos_arm64_platform",
+ ],
+ outputs = ["//command_line_option:cpu", "//command_line_option:platforms"],
+)
diff --git a/lib/xcode_support.bzl b/lib/xcode_support.bzl
new file mode 100644
index 0000000..c7a2531
--- /dev/null
+++ b/lib/xcode_support.bzl
@@ -0,0 +1,44 @@
+# Copyright 2019 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.
+
+"""Support functions for working with Xcode configurations."""
+
+def _is_xcode_at_least_version(xcode_config, version):
+ """Returns True if Xcode version is at least a given version.
+
+ This method takes as input an `XcodeVersionConfig` provider, which can be obtained from the
+ `_xcode_config` attribute (e.g. `ctx.attr._xcode_config[apple_common.XcodeVersionConfig]`). This
+ provider should contain the Xcode version parameters with which this rule is being built with.
+ If you need to add this attribute to your rule implementation, please refer to
+ `apple_support.action_required_attrs()`.
+
+ Args:
+ xcode_config: The XcodeVersionConfig provider from the `_xcode_config` attribute's value.
+ version: The minimum desired Xcode version, as a dotted version string.
+
+ Returns:
+ True if the given `xcode_config` version at least as high as the requested version.
+ """
+ current_version = xcode_config.xcode_version()
+ if not current_version:
+ fail("Could not determine Xcode version at all. This likely means Xcode isn't available; " +
+ "if you think this is a mistake, please file an issue.")
+
+ desired_version = apple_common.dotted_version(version)
+ return current_version >= desired_version
+
+# Define the loadable module that lists the exported symbols in this file.
+xcode_support = struct(
+ is_xcode_at_least_version = _is_xcode_at_least_version,
+)
diff --git a/platform_mappings b/platform_mappings
new file mode 100644
index 0000000..a8a9fd9
--- /dev/null
+++ b/platform_mappings
@@ -0,0 +1,137 @@
+platforms:
+ //platforms:macos_x86_64
+ --apple_platform_type=macos
+ --cpu=darwin_x86_64
+
+ //platforms:macos_arm64
+ --apple_platform_type=macos
+ --cpu=darwin_arm64
+
+ //platforms:darwin_arm64e
+ --apple_platform_type=macos
+ --cpu=darwin_arm64e
+
+ //platforms:ios_i386
+ --apple_platform_type=ios
+ --cpu=ios_i386
+
+ //platforms:ios_x86_64
+ --apple_platform_type=ios
+ --cpu=ios_x86_64
+
+ //platforms:ios_sim_arm64
+ --apple_platform_type=ios
+ --cpu=ios_sim_arm64
+
+ //platforms:ios_armv7
+ --apple_platform_type=ios
+ --cpu=ios_armv7
+
+ //platforms:ios_arm64
+ --apple_platform_type=ios
+ --cpu=ios_arm64
+
+ //platforms:ios_arm64e
+ --apple_platform_type=ios
+ --cpu=ios_arm64e
+
+ //platforms:tvos_x86_64
+ --apple_platform_type=tvos
+ --cpu=tvos_x86_64
+
+ //platforms:tvos_sim_arm64
+ --apple_platform_type=tvos
+ --cpu=tvos_sim_arm64
+
+ //platforms:tvos_arm64
+ --apple_platform_type=tvos
+ --cpu=tvos_arm64
+
+ //platforms:watchos_i386
+ --apple_platform_type=watchos
+ --cpu=watchos_i386
+
+ //platforms:watchos_x86_64
+ --apple_platform_type=watchos
+ --cpu=watchos_x86_64
+
+ //platforms:watchos_arm64
+ --apple_platform_type=watchos
+ --cpu=watchos_arm64
+
+ //platforms:watchos_armv7k
+ --apple_platform_type=watchos
+ --cpu=watchos_armv7k
+
+ //platforms:watchos_arm64_32
+ --apple_platform_type=watchos
+ --cpu=watchos_arm64_32
+
+flags:
+ --cpu=darwin_x86_64
+ --apple_platform_type=macos
+ //platforms:macos_x86_64
+
+ --cpu=darwin_arm64
+ --apple_platform_type=macos
+ //platforms:macos_arm64
+
+ --cpu=darwin_arm64e
+ --apple_platform_type=macos
+ //platforms:darwin_arm64e
+
+ --cpu=ios_i386
+ --apple_platform_type=ios
+ //platforms:ios_i386
+
+ --cpu=ios_x86_64
+ --apple_platform_type=ios
+ //platforms:ios_x86_64
+
+ --cpu=ios_sim_arm64
+ --apple_platform_type=ios
+ //platforms:ios_sim_arm64
+
+ --cpu=ios_armv7
+ --apple_platform_type=ios
+ //platforms:ios_armv7
+
+ --cpu=ios_arm64
+ --apple_platform_type=ios
+ //platforms:ios_arm64
+
+ --cpu=ios_arm64e
+ --apple_platform_type=ios
+ //platforms:ios_arm64e
+
+ --cpu=tvos_x86_64
+ --apple_platform_type=tvos
+ //platforms:tvos_x86_64
+
+ --cpu=tvos_sim_arm64
+ --apple_platform_type=tvos
+ //platforms:tvos_sim_arm64
+
+ --cpu=tvos_arm64
+ --apple_platform_type=tvos
+ //platforms:tvos_arm64
+
+ --cpu=watchos_i386
+ --apple_platform_type=watchos
+ //platforms:watchos_i386
+
+ --cpu=watchos_x86_64
+ --apple_platform_type=watchos
+ //platforms:watchos_x86_64
+
+ --cpu=watchos_arm64
+ --apple_platform_type=watchos
+ //platforms:watchos_arm64
+
+ --cpu=watchos_armv7k
+ --apple_platform_type=watchos
+ //platforms:watchos_armv7k
+
+ --cpu=watchos_arm64_32
+ --apple_platform_type=watchos
+ //platforms:watchos_arm64_32
diff --git a/platforms/BUILD b/platforms/BUILD
new file mode 100644
index 0000000..86f8b5c
--- /dev/null
+++ b/platforms/BUILD
@@ -0,0 +1,46 @@
+load(
+ "//configs:platforms.bzl",
+ "APPLE_PLATFORMS_CONSTRAINTS",
+ "CPU_TO_DEFAULT_PLATFORM_NAME",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+[
+ platform(
+ name = cpu,
+ constraint_values = constraints,
+ )
+ for cpu, constraints in APPLE_PLATFORMS_CONSTRAINTS.items()
+]
+
+alias(
+ name = "macos_x86_64",
+ actual = "darwin_x86_64",
+)
+
+alias(
+ name = "macos_arm64",
+ actual = "darwin_arm64",
+)
+
+alias(
+ name = "macos_arm64e",
+ actual = "darwin_arm64e",
+)
+
+[
+ label_flag(
+ name = platform + "_platform",
+ build_setting_default = platform,
+ )
+ for platform in CPU_TO_DEFAULT_PLATFORM_NAME.values()
+]
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = True,
+ srcs = glob(["**"]),
+ visibility = ["//:__pkg__"],
+)
diff --git a/rules/BUILD b/rules/BUILD
new file mode 100644
index 0000000..7d3cc29
--- /dev/null
+++ b/rules/BUILD
@@ -0,0 +1,54 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+licenses(["notice"])
+
+exports_files(["rules.doc.bzl"])
+
+# Public bzl_library for anything that needs to depend on apple_genrule.bzl.
+bzl_library(
+ name = "apple_genrule",
+ srcs = ["apple_genrule.bzl"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//rules/private:apple_genrule",
+ ],
+)
+
+bzl_library(
+ name = "toolchain_substitution",
+ srcs = ["toolchain_substitution.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+bzl_library(
+ name = "universal_binary",
+ srcs = ["universal_binary.bzl"],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//lib:apple_support",
+ "//lib:lipo",
+ "//lib:transitions",
+ "@bazel_skylib//lib:dicts",
+ ],
+)
+
+bzl_library(
+ name = "rules",
+ srcs = ["rules.doc.bzl"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":toolchain_substitution",
+ ":universal_binary",
+ "//rules/private:apple_genrule",
+ ],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = 1,
+ srcs = glob(["**"]) + [
+ "//rules/private:for_bazel_tests",
+ ],
+ visibility = ["//:__pkg__"],
+)
diff --git a/rules/apple_genrule.bzl b/rules/apple_genrule.bzl
new file mode 100644
index 0000000..cb9ddc2
--- /dev/null
+++ b/rules/apple_genrule.bzl
@@ -0,0 +1,64 @@
+# 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.
+
+"""Genrule which provides Apple's Xcode environment."""
+
+load(
+ "//rules/private:apple_genrule.bzl",
+ _apple_genrule_inner = "apple_genrule",
+)
+
+# buildozer: disable=function-docstring-args
+def apple_genrule(name, **kwargs):
+ """Genrule which provides Apple specific environment and make variables."""
+
+ # This split/indirection traces back to cl/128714692 and b/30413353.
+ if kwargs.get("executable", False):
+ outs = kwargs.pop("outs", [])
+ if len(outs) != 1:
+ fail("apple_genrule, if executable, must have exactly one output")
+
+ intermediate_out = outs[0] + ".nonexecutable"
+
+ # Remove any visibility and make this sub rule private since it is an
+ # implementation detail.
+ sub_kwargs = dict(kwargs)
+ sub_kwargs.pop("visibility", None)
+ _apple_genrule_inner(
+ name = name + "_nonexecutable",
+ outs = [intermediate_out],
+ visibility = ["//visibility:private"],
+ **sub_kwargs
+ )
+
+ # Remove anything from kwargs that might have a meaning that isn't wanted
+ # on the genrule that does the copy. Generally, we are just trying to
+ # keep things like testonly, visibility, etc.
+ kwargs.pop("stamp", None)
+ kwargs.pop("cmd", None)
+ kwargs.pop("executable", None)
+ kwargs.pop("srcs", None)
+ kwargs.pop("message", None)
+ kwargs.pop("tools", None)
+ kwargs.pop("no_sandbox", None)
+ native.genrule(
+ name = name,
+ outs = outs,
+ srcs = [intermediate_out],
+ cmd = "cp $< $@",
+ executable = True,
+ **kwargs
+ )
+ else:
+ _apple_genrule_inner(name = name, **kwargs)
diff --git a/rules/private/BUILD b/rules/private/BUILD
new file mode 100644
index 0000000..9e7c6d7
--- /dev/null
+++ b/rules/private/BUILD
@@ -0,0 +1,24 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+licenses(["notice"])
+
+# Public bzl_library for anything that needs to depend on apple_genrule.bzl.
+bzl_library(
+ name = "apple_genrule",
+ srcs = ["apple_genrule.bzl"],
+ visibility = [
+ "//rules:__pkg__",
+ ],
+ deps = [
+ "//lib:apple_support",
+ "@bazel_skylib//lib:dicts",
+ ],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = 1,
+ srcs = glob(["**"]),
+ visibility = ["//rules:__pkg__"],
+)
diff --git a/rules/private/apple_genrule.bzl b/rules/private/apple_genrule.bzl
new file mode 100644
index 0000000..b00395e
--- /dev/null
+++ b/rules/private/apple_genrule.bzl
@@ -0,0 +1,198 @@
+# 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.
+
+"""Genrule which provides Apple's Xcode environment."""
+
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
+load("//lib:apple_support.bzl", "apple_support")
+
+def _compute_make_variables(
+ genfiles_dir,
+ label,
+ resolved_srcs,
+ files_to_build):
+ resolved_srcs_list = resolved_srcs.to_list()
+ variables = {
+ "OUTS": " ".join([x.path for x in files_to_build]),
+ "SRCS": " ".join([x.path for x in resolved_srcs_list]),
+ }
+ if len(resolved_srcs_list) == 1:
+ variables["<"] = resolved_srcs_list[0].path
+ if len(files_to_build) == 1:
+ variables["@"] = files_to_build[0].path
+ variables["@D"] = files_to_build[0].dirname
+ else:
+ variables["@D"] = genfiles_dir.path + "/" + label.package
+ return variables
+
+def _apple_genrule_impl(ctx):
+ if not ctx.outputs.outs:
+ fail("apple_genrule must have one or more outputs", attr = "outs")
+ files_to_build = ctx.outputs.outs
+
+ if ctx.attr.executable and len(files_to_build) > 1:
+ fail(
+ "if genrules produce executables, they are allowed only one output. " +
+ "If you need the executable=1 argument, then you should split this " +
+ "genrule into genrules producing single outputs",
+ attr = "executable",
+ )
+
+ resolved_srcs = depset(transitive = [dep.files for dep in ctx.attr.srcs])
+ label_dict = {dep.label: dep.files.to_list() for dep in ctx.attr.srcs}
+
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+
+ resolved_inputs, argv, runfiles_manifests = ctx.resolve_command(
+ command = ctx.attr.cmd,
+ attribute = "cmd",
+ expand_locations = True,
+ make_variables = _compute_make_variables(
+ ctx.genfiles_dir,
+ ctx.label,
+ resolved_srcs,
+ files_to_build,
+ ),
+ tools = ctx.attr.tools,
+ label_dict = label_dict,
+ execution_requirements = xcode_config.execution_info(),
+ )
+
+ message = ctx.attr.message or "Executing apple_genrule"
+
+ extra_args = {}
+ if ctx.attr.no_sandbox:
+ extra_args["execution_requirements"] = {"no-sandbox": "1"}
+
+ apple_support.run(
+ actions = ctx.actions,
+ xcode_config = xcode_config,
+ apple_fragment = ctx.fragments.apple,
+ executable = argv[0],
+ arguments = argv[1:],
+ inputs = depset(resolved_inputs, transitive = [resolved_srcs]),
+ outputs = files_to_build,
+ env = ctx.configuration.default_shell_env,
+ progress_message = "%s %s" % (message, ctx.label),
+ mnemonic = "Genrule",
+ input_manifests = runfiles_manifests,
+ **extra_args
+ )
+
+ return [
+ DefaultInfo(
+ files = depset(files_to_build),
+ data_runfiles = ctx.runfiles(files = files_to_build),
+ ),
+ ]
+
+# NOTE: This likely could have used `name = "apple_genrule"` and stayed in the
+# other bzl file, but this way something like a stardoc shim can directly load
+# it and generate from here instead of the macro.
+
+apple_genrule = rule(
+ implementation = _apple_genrule_impl,
+ attrs = dicts.add(apple_support.action_required_attrs(), {
+ "cmd": attr.string(
+ mandatory = True,
+ doc = "The command to run. Subject the variable substitution.",
+ ),
+ "executable": attr.bool(
+ default = False,
+ doc = """\
+Declare output to be executable. Setting this flag to 1 means the output is an
+executable file and can be run using the run command. The genrule must produce
+exactly one output in this case.
+""",
+ ),
+ "outs": attr.output_list(
+ allow_empty = False,
+ mandatory = True,
+ doc = """\
+A list of files generated by this rule. If the executable flag is set, outs must
+contain exactly one label.
+""",
+ ),
+ "srcs": attr.label_list(
+ allow_files = True,
+ doc = "A list of inputs for this rule, such as source files to process.",
+ ),
+ "message": attr.string(
+ doc = "A progress message to be reported as the rule runs.",
+ ),
+ "tools": attr.label_list(
+ cfg = "exec",
+ allow_files = True,
+ doc = """\
+A list of tool dependencies for this rule, they will be available when the
+action is run.
+""",
+ ),
+ "no_sandbox": attr.bool(
+ doc = "If the sandbox should be disabled when the action is run.",
+ ),
+ }),
+ doc = """\
+Genrule which provides Apple specific environment and make variables.
+
+This mirrors the native genrule except that it provides a different set of
+make variables. This rule will only run on a Mac.
+
+Example of use:
+
+```
+load("@build_bazel_apple_support//rules:apple_genrule.bzl", "apple_genrule")
+
+apple_genrule(
+ name = "world",
+ outs = ["hi"],
+ cmd = "touch $@",
+)
+```
+
+This rule also does location expansion, much like the native genrule.
+For example, `$(location hi)` may be used to refer to the output in the
+above example.
+
+The set of make variables that are supported for this rule:
+
+* `OUTS`: The outs list. If you have only one output file, you can also use
+ `$@`.
+* `SRCS`: The srcs list (or more precisely, the pathnames of the files
+ corresponding to labels in the srcs list). If you have only one
+ source file, you can also use `$<`.
+* `<`: srcs, if it's a single file.
+* `@`: outs, if it's a single file.
+* `@D`: The output directory. If there is only one filename in outs, this
+ expands to the directory containing that file. If there are
+ multiple filenames, this variable instead expands to the package's
+ root directory in the genfiles tree, even if all the generated
+ files belong to the same subdirectory.
+
+The following environment variables are defined when the rule is executed:
+
+* `DEVELOPER_DIR`: The base developer directory as defined on Apple
+ architectures, most commonly used in invoking Apple
+ tools such as xcrun.
+* `SDKROOT`: The base SDK directory as defined on Apple architectures, most
+ commonly used in invoking Apple tools such as xcrun.
+
+NOTE: `DEVELOPER_DIR` and `SDKROOT` are environment variables and *not* make
+ variables. To refer to them in `cmd` you must use environment variable
+ syntax (i.e. using `$$`). Example: ```cmd = "xcrun --sdkroot $$SDKROOT clang...```
+""",
+ exec_compatible_with = ["@platforms//os:macos"],
+ output_to_genfiles = True,
+ fragments = ["apple"],
+)
diff --git a/rules/rules.doc.bzl b/rules/rules.doc.bzl
new file mode 100644
index 0000000..dbf7cf4
--- /dev/null
+++ b/rules/rules.doc.bzl
@@ -0,0 +1,11 @@
+"""
+# Helper rules for Apple platforms
+"""
+
+load("//rules:toolchain_substitution.bzl", _toolchain_substitution = "toolchain_substitution")
+load("//rules:universal_binary.bzl", _universal_binary = "universal_binary")
+load("//rules/private:apple_genrule.bzl", _apple_genrule = "apple_genrule")
+
+apple_genrule = _apple_genrule
+toolchain_substitution = _toolchain_substitution
+universal_binary = _universal_binary
diff --git a/rules/toolchain_substitution.bzl b/rules/toolchain_substitution.bzl
new file mode 100644
index 0000000..fac803f
--- /dev/null
+++ b/rules/toolchain_substitution.bzl
@@ -0,0 +1,52 @@
+"""
+Register a subsitution, from a variable to a file, for use in arguments in a
+target's attributes. Useful for passing custom tools to a compile or link
+"""
+
+def _toolchain_substitution(ctx):
+ return [
+ DefaultInfo(
+ files = depset([ctx.file.src]),
+ ),
+ platform_common.TemplateVariableInfo({
+ ctx.attr.var_name: ctx.file.src.path,
+ }),
+ ]
+
+toolchain_substitution = rule(
+ attrs = {
+ "var_name": attr.string(
+ mandatory = True,
+ doc = "Name of the variable to substitute, ex: FOO",
+ ),
+ "src": attr.label(
+ mandatory = True,
+ allow_single_file = True,
+ doc = "Source file to substitute",
+ ),
+ },
+ implementation = _toolchain_substitution,
+ doc = """
+Register a subsitution, from a variable to a file, for use in arguments in a
+target's attributes. Useful for passing custom tools to a compile or link.
+
+### Example:
+
+```bzl
+load("@build_bazel_apple_support//rules:toolchain_substitution.bzl", "toolchain_substitution")
+
+toolchain_substitution(
+ name = "resource_rules",
+ src = "resource_rules.plist",
+ var_name = "RULES",
+)
+
+ios_application(
+ ...
+ codesignopts = ["--resource-rules=$(RULES)"],
+ codesign_inputs = [":resource_rules"],
+ toolchains = [":resource_rules"],
+)
+```
+""",
+)
diff --git a/rules/universal_binary.bzl b/rules/universal_binary.bzl
new file mode 100644
index 0000000..5c0eca5
--- /dev/null
+++ b/rules/universal_binary.bzl
@@ -0,0 +1,87 @@
+# Copyright 2021 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.
+
+"""Implementation for macOS universal binary rule."""
+
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
+load("//lib:apple_support.bzl", "apple_support")
+load("//lib:lipo.bzl", "lipo")
+load("//lib:transitions.bzl", "macos_universal_transition")
+
+def _universal_binary_impl(ctx):
+ inputs = [
+ binary.files.to_list()[0]
+ for binary in ctx.split_attr.binary.values()
+ ]
+
+ if not inputs:
+ fail("Target (%s) `binary` label ('%s') does not provide any " +
+ "file for universal binary" % (ctx.attr.name, ctx.attr.binary))
+
+ output = ctx.actions.declare_file(ctx.label.name)
+
+ if len(inputs) > 1:
+ lipo.create(
+ actions = ctx.actions,
+ apple_fragment = ctx.fragments.apple,
+ inputs = inputs,
+ output = output,
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
+ )
+
+ else:
+ # If the transition doesn't split, this is building for a non-macOS
+ # target, so just create a symbolic link of the input binary.
+ ctx.actions.symlink(target_file = inputs[0], output = output)
+
+ runfiles = ctx.runfiles(files = ctx.files.binary)
+ transitive_runfiles = [
+ binary[DefaultInfo].default_runfiles
+ for binary in ctx.split_attr.binary.values()
+ ]
+ runfiles = runfiles.merge_all(transitive_runfiles)
+
+ return [
+ DefaultInfo(
+ executable = output,
+ files = depset([output]),
+ runfiles = runfiles,
+ ),
+ ]
+
+universal_binary = rule(
+ attrs = dicts.add(
+ apple_support.action_required_attrs(),
+ {
+ "binary": attr.label(
+ cfg = macos_universal_transition,
+ doc = "Target to generate a 'fat' binary from.",
+ mandatory = True,
+ ),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ ),
+ doc = """
+This rule produces a multi-architecture ("fat") binary targeting Apple macOS
+platforms *regardless* of the architecture of the macOS host platform. The
+`lipo` tool is used to combine built binaries of multiple architectures. For
+non-macOS platforms, this simply just creates a symbolic link of the input
+binary.
+""",
+ executable = True,
+ fragments = ["apple"],
+ implementation = _universal_binary_impl,
+)
diff --git a/test/BUILD b/test/BUILD
new file mode 100644
index 0000000..a5a0914
--- /dev/null
+++ b/test/BUILD
@@ -0,0 +1,126 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("//rules:apple_genrule.bzl", "apple_genrule")
+load(":apple_support_test.bzl", "apple_support_test")
+load(":binary_tests.bzl", "binary_test_suite")
+load(":linking_tests.bzl", "linking_test_suite")
+load(":starlark_apple_binary.bzl", "starlark_apple_binary")
+load(":universal_binary_test.bzl", "universal_binary_test")
+load(":xcode_support_test.bzl", "xcode_support_test")
+
+licenses(["notice"])
+
+# Custom rules that test rule-context APIs. Check their implementations for more details.
+apple_support_test(name = "apple_support_test")
+
+xcode_support_test(name = "xcode_support_test")
+
+binary_test_suite(name = "binary")
+
+linking_test_suite(name = "linking")
+
+# Test to ensure the environment variable contract of apple_genrule.
+sh_test(
+ name = "apple_genrule_test",
+ size = "small",
+ srcs = ["apple_genrule_test.sh"],
+ args = ["$(location simple_genrule.txt)"],
+ data = ["simple_genrule.txt"],
+)
+
+build_test(
+ name = "touched_test",
+ targets = [":touched"],
+)
+
+apple_genrule(
+ name = "simple_genrule",
+ outs = ["simple_genrule.txt"],
+ cmd = "printenv | grep \"^\\(DEVELOPER_DIR\\|SDKROOT\\)\" > $(@)",
+)
+
+apple_genrule(
+ name = "touched",
+ outs = ["genrule_touched.txt"],
+ cmd = "touch $(OUTS)",
+)
+
+bzl_library(
+ name = "starlark_tests_bzls",
+ srcs = glob(["*.bzl"]),
+ deps = [
+ "//lib:apple_support",
+ "//lib:xcode_support",
+ ],
+)
+
+universal_binary_test(
+ name = "universal_binary_test_x86_64",
+ binary_contains_symbols = [
+ "__Z19function_for_x86_64v",
+ "__Z19function_for_arch64v",
+ ],
+ cpu = "darwin_x86_64",
+ target_under_test = "//test/test_data:multi_arch_cc_binary",
+)
+
+universal_binary_test(
+ name = "universal_binary_test_arm64",
+ binary_contains_symbols = [
+ "__Z19function_for_x86_64v",
+ "__Z19function_for_arch64v",
+ ],
+ cpu = "darwin_arm64",
+ target_under_test = "//test/test_data:multi_arch_cc_binary",
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = True,
+ srcs = glob(["**"]),
+ visibility = ["//test/shell:__pkg__"],
+)
+
+cc_binary(
+ name = "main_c",
+ srcs = ["main.c"],
+)
+
+cc_binary(
+ name = "main_cpp",
+ srcs = ["main.cc"],
+)
+
+objc_library(
+ name = "main_objc",
+ srcs = ["main.m"],
+)
+
+starlark_apple_binary(
+ name = "main_apple",
+ minimum_os_version = "13.0",
+ platform_type = "macos",
+ deps = ["main_objc"],
+)
+
+cc_library(
+ name = "underlying_lib",
+ hdrs = ["underlying_lib.h"],
+ tags = ["manual"],
+)
+
+objc_library(
+ name = "objc_lib",
+ srcs = ["objc_lib.m"],
+ hdrs = ["objc_lib.h"],
+ tags = ["manual"],
+ deps = [":underlying_lib"],
+ alwayslink = True,
+)
+
+cc_test(
+ name = "cc_test_with_objc_deps",
+ srcs = ["cc_test_with_objc_deps.cc"],
+ deps = [":objc_lib"],
+)
diff --git a/test/apple_genrule_test.sh b/test/apple_genrule_test.sh
new file mode 100755
index 0000000..34d6523
--- /dev/null
+++ b/test/apple_genrule_test.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# Copyright 2019 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.
+
+set -eu
+
+# Integration test for apple_genrule.
+
+# This test checks that the first argument contains a file that contains
+# Xcode environment variables. Since the file was generated using an
+# apple_genrule using `printenv`, we enforce that the contract of providing the
+# DEVELOPER_DIR and SDKROOT environment variables is maintained.
+
+INPUT_FILE="$1"
+
+if ! grep -Fq DEVELOPER_DIR "$INPUT_FILE"; then
+ echo "FAILURE: DEVELOPER_DIR not found."
+ exit 1
+fi
+
+if ! grep -Fq SDKROOT "$INPUT_FILE"; then
+ echo "FAILURE: SDKROOT not found."
+ exit 1
+fi
diff --git a/test/apple_support_test.bzl b/test/apple_support_test.bzl
new file mode 100644
index 0000000..22e45d7
--- /dev/null
+++ b/test/apple_support_test.bzl
@@ -0,0 +1,281 @@
+# Copyright 2018 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.
+"""Definition of a test rule to test apple_support."""
+
+load(
+ "//lib:apple_support.bzl",
+ "apple_support",
+)
+
+# Contents of the tool that writes the state of the action into a file. The first argument to this
+# script is the path to the output file.
+_TEST_TOOL_CONTENTS = """#!/bin/bash
+
+set -eu
+
+OUTPUT_FILE="$1" ; shift
+
+echo "XCODE_PATH_ENV=$DEVELOPER_DIR" > "$OUTPUT_FILE"
+echo "SDKROOT_PATH_ENV=$SDKROOT" >> "$OUTPUT_FILE"
+
+for arg in "$@"; do
+ if [[ "$arg" == @* ]]; then
+ cat "${arg:1}" >> "$OUTPUT_FILE"
+ else
+ echo "$arg" >> "$OUTPUT_FILE"
+ fi
+done
+"""
+
+# Template for the test script used to validate that the action outputs contain the expected
+# values.
+_TEST_SCRIPT_CONTENTS = """
+#!/bin/bash
+
+set -eu
+
+FILES=(
+ {file_paths}
+)
+
+function assert_contains_line() {{
+ file="$1"
+ contents="$2"
+
+ if [[ -f "$file" ]]; then
+ grep -Fxq "$contents" "$file" || \
+ (echo "In file: $file"; \
+ echo "Expected contents not found: $contents"; \
+ echo "File contents:"; \
+ cat "$file"; \
+ exit 1)
+ else
+ echo "$file doesn't exist"
+ exit 1
+ fi
+}}
+
+function assert_not_contains() {{
+ file="$1"
+ contents="$2"
+
+ if [[ -f "$file" ]]; then
+ grep -Fq "$contents" "$file" || return 0
+ else
+ echo "$file doesn't exist"
+ exit 1
+ fi
+
+ echo "In file: $file"
+ echo "Contents found but not expected: $contents"
+ echo "File contents:"
+ cat "$file"
+ exit 1
+}}
+
+XCODE_PATH_ENV="$DEVELOPER_DIR"
+SDKROOT_PATH_ENV="$SDKROOT"
+
+for file in "${{FILES[@]}}"; do
+ assert_contains_line "$file" "XCODE_PATH_ENV=$XCODE_PATH_ENV"
+ assert_contains_line "$file" "SDKROOT_PATH_ENV=$SDKROOT_PATH_ENV"
+ assert_not_contains "$file" "{xcode_path_placeholder}"
+ assert_not_contains "$file" "{sdkroot_path_placeholder}"
+done
+
+echo "Test passed"
+
+exit 0
+"""
+
+def _apple_support_test_impl(ctx):
+ """Implementation of the apple_support_test rule."""
+
+ # Declare all the action outputs
+ run_output = ctx.actions.declare_file(
+ "{}_run_output".format(ctx.label.name),
+ )
+ run_output_xcode_path_in_args = ctx.actions.declare_file(
+ "{}_run_output_xcode_path_in_args".format(ctx.label.name),
+ )
+ run_output_xcode_path_in_file = ctx.actions.declare_file(
+ "{}_run_output_xcode_path_in_file".format(ctx.label.name),
+ )
+ run_shell_output = ctx.actions.declare_file(
+ "{}_run_shell_output".format(ctx.label.name),
+ )
+ run_output_ctx = ctx.actions.declare_file(
+ "{}_run_output_ctx".format(ctx.label.name),
+ )
+ run_output_xcode_path_in_args_ctx = ctx.actions.declare_file(
+ "{}_run_output_xcode_path_in_args_ctx".format(ctx.label.name),
+ )
+ run_output_xcode_path_in_file_ctx = ctx.actions.declare_file(
+ "{}_run_output_xcode_path_in_file_ctx".format(ctx.label.name),
+ )
+ run_shell_output_ctx = ctx.actions.declare_file(
+ "{}_run_shell_output_ctx".format(ctx.label.name),
+ )
+
+ test_tool = ctx.actions.declare_file("{}_test_tool".format(ctx.label.name))
+ ctx.actions.write(test_tool, _TEST_TOOL_CONTENTS, is_executable = True)
+
+ # Create one action per possible combination of inputs to the apple_support.run and
+ # apple_support.run_shell helper methods.
+ apple_support.run(
+ actions = ctx.actions,
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
+ apple_fragment = ctx.fragments.apple,
+ outputs = [run_output],
+ executable = test_tool,
+ arguments = [run_output.path],
+ )
+ apple_support.run(
+ ctx,
+ outputs = [run_output_ctx],
+ executable = test_tool,
+ arguments = [run_output_ctx.path],
+ )
+
+ platform_frameworks = apple_support.path_placeholders.platform_frameworks(
+ apple_fragment = ctx.fragments.apple,
+ )
+
+ apple_support.run(
+ actions = ctx.actions,
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
+ apple_fragment = ctx.fragments.apple,
+ outputs = [run_output_xcode_path_in_args],
+ executable = test_tool,
+ arguments = [
+ run_output_xcode_path_in_args.path,
+ "XCODE_PATH_ARG={}".format(apple_support.path_placeholders.xcode()),
+ "FRAMEWORKS_PATH_ARG={}".format(platform_frameworks),
+ "SDKROOT_PATH_ARG={}".format(apple_support.path_placeholders.sdkroot()),
+ ],
+ xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args,
+ )
+ apple_support.run(
+ ctx,
+ outputs = [run_output_xcode_path_in_args_ctx],
+ executable = test_tool,
+ arguments = [
+ run_output_xcode_path_in_args_ctx.path,
+ "XCODE_PATH_ARG={}".format(apple_support.path_placeholders.xcode()),
+ "FRAMEWORKS_PATH_ARG={}".format(platform_frameworks),
+ "SDKROOT_PATH_ARG={}".format(apple_support.path_placeholders.sdkroot()),
+ ],
+ xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args,
+ )
+
+ action_args = ctx.actions.args()
+ action_args.add(
+ "XCODE_PATH_ARG={}".format(apple_support.path_placeholders.xcode()),
+ )
+ action_args.add(
+ "FRAMEWORKS_PATH_ARG={}".format(platform_frameworks),
+ )
+ action_args.add(
+ "SDKROOT_PATH_ARG={}".format(apple_support.path_placeholders.sdkroot()),
+ )
+ action_args.set_param_file_format("multiline")
+ action_args.use_param_file("@%s", use_always = True)
+
+ apple_support.run(
+ actions = ctx.actions,
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
+ apple_fragment = ctx.fragments.apple,
+ outputs = [run_output_xcode_path_in_file],
+ executable = test_tool,
+ arguments = [
+ run_output_xcode_path_in_file.path,
+ action_args,
+ ],
+ xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args_and_files,
+ )
+ apple_support.run(
+ ctx,
+ outputs = [run_output_xcode_path_in_file_ctx],
+ executable = test_tool,
+ arguments = [
+ run_output_xcode_path_in_file_ctx.path,
+ action_args,
+ ],
+ xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args_and_files,
+ )
+
+ apple_support.run_shell(
+ actions = ctx.actions,
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
+ apple_fragment = ctx.fragments.apple,
+ outputs = [run_shell_output],
+ tools = [test_tool],
+ command = "{tool} {output}".format(
+ output = run_shell_output.path,
+ tool = test_tool.path,
+ ),
+ )
+ apple_support.run_shell(
+ ctx,
+ outputs = [run_shell_output_ctx],
+ tools = [test_tool],
+ command = "{tool} {output}".format(
+ output = run_shell_output_ctx.path,
+ tool = test_tool.path,
+ ),
+ )
+
+ test_files = [
+ run_output,
+ run_output_ctx,
+ run_output_xcode_path_in_args,
+ run_output_xcode_path_in_args_ctx,
+ run_output_xcode_path_in_file,
+ run_output_xcode_path_in_file_ctx,
+ run_shell_output,
+ run_shell_output_ctx,
+ ]
+
+ test_script = ctx.actions.declare_file("{}_test_script".format(ctx.label.name))
+ ctx.actions.write(test_script, _TEST_SCRIPT_CONTENTS.format(
+ file_paths = "\n ".join([x.short_path for x in test_files]),
+ sdkroot_path_placeholder = apple_support.path_placeholders.sdkroot(),
+ xcode_path_placeholder = apple_support.path_placeholders.xcode(),
+ ), is_executable = True)
+
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ apple_platform = ctx.fragments.apple.single_arch_platform
+
+ test_env = {}
+ test_env.update(apple_common.apple_host_system_env(xcode_config))
+ test_env.update(apple_common.target_apple_env(xcode_config, apple_platform))
+
+ return [
+ testing.ExecutionInfo(xcode_config.execution_info()),
+ testing.TestEnvironment(test_env),
+ DefaultInfo(
+ executable = test_script,
+ files = depset([run_output_xcode_path_in_args]),
+ runfiles = ctx.runfiles(
+ files = test_files,
+ ),
+ ),
+ ]
+
+apple_support_test = rule(
+ implementation = _apple_support_test_impl,
+ attrs = apple_support.action_required_attrs(),
+ fragments = ["apple"],
+ test = True,
+)
diff --git a/test/binary_tests.bzl b/test/binary_tests.bzl
new file mode 100644
index 0000000..db44ee2
--- /dev/null
+++ b/test/binary_tests.bzl
@@ -0,0 +1,145 @@
+"""Tests verifying produced binaries"""
+
+load(
+ "//test/rules:apple_verification_test.bzl",
+ "apple_verification_test",
+)
+
+def binary_test_suite(name):
+ """Test various aspects of binary generation
+
+ Args:
+ name: The prefix of each test name
+ """
+
+ apple_verification_test(
+ name = "{}_macos_binary_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"macos_cpus": "x86_64"},
+ expected_platform_type = "macos",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:macos_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_macos_binary_with_spaces_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"macos_cpus": "x86_64"},
+ expected_platform_type = "macos",
+ generate_dsym = True,
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:macos_binary_with_spaces",
+ )
+
+ apple_verification_test(
+ name = "{}_visionos_device_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"visionos_cpus": "arm64"},
+ expected_platform_type = "visionos",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:visionos_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_visionos_arm64_simulator_test".format(name),
+ tags = [name],
+ build_type = "simulator",
+ cpus = {"visionos_cpus": "sim_arm64"},
+ expected_platform_type = "visionos",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:visionos_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_visionos_x86_64_simulator_test".format(name),
+ tags = [name],
+ build_type = "simulator",
+ cpus = {"visionos_cpus": "x86_64"},
+ expected_platform_type = "visionos",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:visionos_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_unused_symbol_is_kept_by_default".format(name),
+ build_type = "simulator",
+ cpus = {"ios_multi_cpus": "x86_64"},
+ compilation_mode = "fastbuild",
+ objc_enable_binary_stripping = False,
+ verifier_script = "//test:verify_unused_symbol_exists.sh",
+ target_under_test = "//test/test_data:ios_app_with_unused_symbol",
+ tags = [name],
+ )
+
+ apple_verification_test(
+ name = "{}_unused_symbol_is_stripped".format(name),
+ build_type = "simulator",
+ cpus = {"ios_multi_cpus": "x86_64"},
+ compilation_mode = "opt",
+ objc_enable_binary_stripping = True,
+ verifier_script = "//test:verify_stripped_symbols.sh",
+ target_under_test = "//test/test_data:ios_app_with_unused_symbol",
+ tags = [name],
+ )
+
+ apple_verification_test(
+ name = "{}_archive_timestamps".format(name),
+ build_type = "simulator",
+ cpus = {"ios_multi_cpus": "x86_64"},
+ verifier_script = "//test:verify_archive_timestamps.sh",
+ target_under_test = "//test/test_data:static_lib",
+ tags = [name],
+ )
+
+ apple_verification_test(
+ name = "{}_fat_static_lib".format(name),
+ build_type = "simulator",
+ cpus = {"ios_multi_cpus": "x86_64,sim_arm64"},
+ expected_platform_type = "ios",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:static_lib",
+ tags = [name],
+ )
+
+ apple_verification_test(
+ name = "{}_watchos_device_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"watchos_cpus": "x86_64"},
+ expected_platform_type = "watchos",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:watch_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_watchos_simulator_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"watchos_cpus": "arm64"},
+ expected_platform_type = "watchos",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:watch_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_ios_device_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"ios_multi_cpus": "x86_64,sim_arm64"},
+ expected_platform_type = "ios",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:ios_binary",
+ )
+
+ apple_verification_test(
+ name = "{}_ios_simulator_test".format(name),
+ tags = [name],
+ build_type = "device",
+ cpus = {"ios_multi_cpus": "arm64,arm64e"},
+ expected_platform_type = "ios",
+ verifier_script = "//test/shell:verify_binary.sh",
+ target_under_test = "//test/test_data:ios_binary",
+ )
diff --git a/test/cc_test_with_objc_deps.cc b/test/cc_test_with_objc_deps.cc
new file mode 100644
index 0000000..f89e27e
--- /dev/null
+++ b/test/cc_test_with_objc_deps.cc
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test/objc_lib.h"
+
+int main() {
+ if (from_objc_library() == 10) {
+ printf("SUCCESS: from_objc_library() = %d\n", from_objc_library());
+ return EXIT_SUCCESS;
+ } else {
+ printf("FAILED: from_objc_library() = %d, expected 10\n", from_objc_library());
+ return EXIT_FAILURE;
+ }
+}
diff --git a/test/fixtures/linking/BUILD b/test/fixtures/linking/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/fixtures/linking/BUILD
diff --git a/test/linking_tests.bzl b/test/linking_tests.bzl
new file mode 100644
index 0000000..ed1032f
--- /dev/null
+++ b/test/linking_tests.bzl
@@ -0,0 +1,66 @@
+"""Tests for linking behavior."""
+
+load(
+ "//test/rules:action_command_line_test.bzl",
+ "make_action_command_line_test_rule",
+)
+
+default_test = make_action_command_line_test_rule()
+
+disable_objc_test = make_action_command_line_test_rule(
+ config_settings = {
+ "//command_line_option:features": [
+ "-objc_link_flag",
+ ],
+ },
+)
+
+dsym_test = make_action_command_line_test_rule(
+ config_settings = {
+ "//command_line_option:apple_generate_dsym": True,
+ },
+)
+
+def linking_test_suite(name):
+ default_test(
+ name = "{}_default_apple_link_test".format(name),
+ tags = [name],
+ expected_argv = [
+ "-Xlinker",
+ "-objc_abi_version",
+ "-Xlinker",
+ "2",
+ "-ObjC",
+ ],
+ not_expected_argv = [
+ "-g",
+ "DSYM_HINT_LINKED_BINARY",
+ ],
+ mnemonic = "ObjcLink",
+ target_under_test = "//test/test_data:macos_binary",
+ )
+
+ disable_objc_test(
+ name = "{}_disable_objc_apple_link_test".format(name),
+ tags = [name],
+ expected_argv = [
+ "-Xlinker",
+ "-objc_abi_version",
+ "-Xlinker",
+ "2",
+ ],
+ not_expected_argv = ["-ObjC"],
+ mnemonic = "ObjcLink",
+ target_under_test = "//test/test_data:macos_binary",
+ )
+
+ dsym_test(
+ name = "{}_generate_dsym_test".format(name),
+ tags = [name],
+ expected_argv = [
+ "-g",
+ "DSYM_HINT_LINKED_BINARY",
+ ],
+ mnemonic = "ObjcLink",
+ target_under_test = "//test/test_data:macos_binary",
+ )
diff --git a/test/main.c b/test/main.c
new file mode 100644
index 0000000..4cce7f6
--- /dev/null
+++ b/test/main.c
@@ -0,0 +1,3 @@
+int main() {
+ return 0;
+}
diff --git a/test/main.cc b/test/main.cc
new file mode 100644
index 0000000..4cce7f6
--- /dev/null
+++ b/test/main.cc
@@ -0,0 +1,3 @@
+int main() {
+ return 0;
+}
diff --git a/test/main.m b/test/main.m
new file mode 100644
index 0000000..33c14ce
--- /dev/null
+++ b/test/main.m
@@ -0,0 +1,3 @@
+int main() {
+ return 0;
+}
diff --git a/test/objc_lib.h b/test/objc_lib.h
new file mode 100644
index 0000000..d1969cd
--- /dev/null
+++ b/test/objc_lib.h
@@ -0,0 +1,3 @@
+extern "C" {
+ int from_objc_library();
+}
diff --git a/test/objc_lib.m b/test/objc_lib.m
new file mode 100644
index 0000000..22d0dc8
--- /dev/null
+++ b/test/objc_lib.m
@@ -0,0 +1,5 @@
+#include "test/underlying_lib.h"
+
+int from_objc_library() {
+ return from_cc_library + 5;
+}
diff --git a/test/rules/BUILD b/test/rules/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/rules/BUILD
diff --git a/test/rules/action_command_line_test.bzl b/test/rules/action_command_line_test.bzl
new file mode 100644
index 0000000..83852cd
--- /dev/null
+++ b/test/rules/action_command_line_test.bzl
@@ -0,0 +1,149 @@
+# Copyright 2020 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 for testing the contents of action command lines."""
+
+load("@bazel_skylib//lib:collections.bzl", "collections")
+load("@bazel_skylib//lib:unittest.bzl", "analysistest", "unittest")
+
+def _action_command_line_test_impl(ctx):
+ env = analysistest.begin(ctx)
+ target_under_test = analysistest.target_under_test(env)
+
+ # Find the desired action and verify that there is exactly one.
+ actions = analysistest.target_actions(env)
+ mnemonic = ctx.attr.mnemonic
+ matching_actions = [
+ action
+ for action in actions
+ if action.mnemonic == mnemonic
+ ]
+ if not matching_actions:
+ actual_mnemonics = collections.uniq(
+ [action.mnemonic for action in actions],
+ )
+ unittest.fail(
+ env,
+ ("Target '{}' registered no actions with the mnemonic '{}' " +
+ "(it had {}).").format(
+ str(target_under_test.label),
+ mnemonic,
+ actual_mnemonics,
+ ),
+ )
+ return analysistest.end(env)
+ if len(matching_actions) != 1:
+ # This is a hack to avoid CppLink meaning binary linking and static
+ # library archiving https://github.com/bazelbuild/bazel/pull/15060
+ if mnemonic == "CppLink" and len(matching_actions) == 2:
+ matching_actions = [
+ action
+ for action in matching_actions
+ if action.argv[0] not in [
+ "/usr/bin/ar",
+ "external/local_config_cc/libtool",
+ "external/local_config_apple_cc/libtool",
+ ]
+ ]
+ if len(matching_actions) != 1:
+ unittest.fail(
+ env,
+ ("Expected exactly one action with the mnemonic '{}', " +
+ "but found {}.").format(
+ mnemonic,
+ len(matching_actions),
+ ),
+ )
+ return analysistest.end(env)
+
+ action = matching_actions[0]
+ message_prefix = "In {} action for target '{}', ".format(
+ mnemonic,
+ str(target_under_test.label),
+ )
+
+ # Concatenate the arguments into a single string so that we can easily look
+ # for subsequences of arguments. Note that we append an extra space to the
+ # end and look for arguments followed by a trailing space so that having
+ # `-foo` in the expected list doesn't match `-foobar`, for example.
+ concatenated_args = " ".join(action.argv) + " "
+ bin_dir = analysistest.target_bin_dir_path(env)
+ for expected in ctx.attr.expected_argv:
+ expected = expected.replace("$(BIN_DIR)", bin_dir).replace("$(WORKSPACE_NAME)", ctx.workspace_name)
+ if expected + " " not in concatenated_args and expected + "=" not in concatenated_args:
+ unittest.fail(
+ env,
+ "{}expected argv to contain '{}', but it did not: {}".format(
+ message_prefix,
+ expected,
+ concatenated_args,
+ ),
+ )
+ for not_expected in ctx.attr.not_expected_argv:
+ not_expected = not_expected.replace("$(BIN_DIR)", bin_dir).replace("$(WORKSPACE_NAME)", ctx.workspace_name)
+ if not_expected + " " in concatenated_args or not_expected + "=" in concatenated_args:
+ unittest.fail(
+ env,
+ "{}expected argv to not contain '{}', but it did: {}".format(
+ message_prefix,
+ not_expected,
+ concatenated_args,
+ ),
+ )
+
+ return analysistest.end(env)
+
+def make_action_command_line_test_rule(config_settings = {}):
+ """Returns a new `action_command_line_test`-like rule with custom configs.
+
+ Args:
+ config_settings: A dictionary of configuration settings and their values
+ that should be applied during tests.
+
+ Returns:
+ A rule returned by `analysistest.make` that has the
+ `action_command_line_test` interface and the given config settings.
+ """
+ return analysistest.make(
+ _action_command_line_test_impl,
+ attrs = {
+ "expected_argv": attr.string_list(
+ mandatory = False,
+ doc = """\
+A list of strings representing substrings expected to appear in the action
+command line, after concatenating all command line arguments into a single
+space-delimited string.
+""",
+ ),
+ "not_expected_argv": attr.string_list(
+ mandatory = False,
+ doc = """\
+A list of strings representing substrings expected not to appear in the action
+command line, after concatenating all command line arguments into a single
+space-delimited string.
+""",
+ ),
+ "mnemonic": attr.string(
+ mandatory = True,
+ doc = """\
+The mnemonic of the action to be inspected on the target under test. It is
+expected that there will be exactly one of these.
+""",
+ ),
+ },
+ config_settings = config_settings,
+ )
+
+# A default instantiation of the rule when no custom config settings are needed.
+action_command_line_test = make_action_command_line_test_rule()
diff --git a/test/rules/apple_verification_test.bzl b/test/rules/apple_verification_test.bzl
new file mode 100644
index 0000000..4735682
--- /dev/null
+++ b/test/rules/apple_verification_test.bzl
@@ -0,0 +1,180 @@
+# Copyright 2019 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.
+
+"""Test rule to perform generic bundle verification tests."""
+
+load("@bazel_skylib//lib:dicts.bzl", "dicts")
+
+_supports_visionos = hasattr(apple_common.platform_type, "visionos")
+
+def _transition_impl(_, attr):
+ output_dictionary = {
+ "//command_line_option:apple_generate_dsym": attr.generate_dsym,
+ "//command_line_option:compilation_mode": attr.compilation_mode,
+ "//command_line_option:cpu": "darwin_x86_64",
+ "//command_line_option:ios_signing_cert_name": "-",
+ "//command_line_option:macos_cpus": "x86_64",
+ "//command_line_option:objc_enable_binary_stripping": attr.objc_enable_binary_stripping,
+ }
+ if attr.build_type == "simulator":
+ output_dictionary.update({
+ "//command_line_option:ios_multi_cpus": "x86_64",
+ "//command_line_option:tvos_cpus": "x86_64",
+ "//command_line_option:visionos_cpus": "x86_64",
+ "//command_line_option:watchos_cpus": "x86_64",
+ })
+
+ else:
+ output_dictionary.update({
+ "//command_line_option:ios_multi_cpus": "arm64",
+ "//command_line_option:tvos_cpus": "arm64",
+ "//command_line_option:visionos_cpus": "arm64",
+ "//command_line_option:watchos_cpus": "arm64_32,armv7k",
+ })
+
+ if hasattr(attr, "cpus"):
+ for cpu_option, cpu in attr.cpus.items():
+ command_line_option = "//command_line_option:%s" % cpu_option
+ output_dictionary.update({command_line_option: cpu})
+
+ if not _supports_visionos:
+ output_dictionary.pop("//command_line_option:visionos_cpus", None)
+
+ return output_dictionary
+
+_transition = transition(
+ implementation = _transition_impl,
+ inputs = [],
+ outputs = [
+ "//command_line_option:apple_generate_dsym",
+ "//command_line_option:compilation_mode",
+ "//command_line_option:cpu",
+ "//command_line_option:ios_multi_cpus",
+ "//command_line_option:ios_signing_cert_name",
+ "//command_line_option:macos_cpus",
+ "//command_line_option:objc_enable_binary_stripping",
+ "//command_line_option:tvos_cpus",
+ "//command_line_option:watchos_cpus",
+ ] + (["//command_line_option:visionos_cpus"] if _supports_visionos else []),
+)
+
+def _apple_verification_test_impl(ctx):
+ binary = ctx.attr.target_under_test[0].files.to_list()[0]
+ output_script = ctx.actions.declare_file("{}_test_script".format(ctx.label.name))
+ ctx.actions.expand_template(
+ template = ctx.file.verifier_script,
+ output = output_script,
+ substitutions = {
+ "%{binary}s": binary.short_path,
+ },
+ is_executable = True,
+ )
+
+ # Extra test environment to set during the test.
+ test_env = {
+ "BUILD_TYPE": ctx.attr.build_type,
+ "PLATFORM_TYPE": ctx.attr.expected_platform_type,
+ }
+
+ if ctx.attr.cpus:
+ cpu = ctx.attr.cpus.values()[0]
+ if cpu.startswith("sim_"):
+ cpu = cpu[4:]
+ test_env["CPU"] = cpu
+
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+
+ return [
+ testing.ExecutionInfo(xcode_config.execution_info()),
+ testing.TestEnvironment(dicts.add(
+ apple_common.apple_host_system_env(xcode_config),
+ test_env,
+ )),
+ DefaultInfo(
+ executable = output_script,
+ runfiles = ctx.runfiles(
+ files = [binary],
+ ),
+ ),
+ ]
+
+apple_verification_test = rule(
+ implementation = _apple_verification_test_impl,
+ attrs = {
+ "generate_dsym": attr.bool(
+ default = False,
+ doc = """
+Whether to generate a dSYM file for the binary under test.
+""",
+ ),
+ "build_type": attr.string(
+ mandatory = True,
+ values = ["simulator", "device"],
+ doc = """
+Type of build for the target under test. Possible values are `simulator` or `device`.
+""",
+ ),
+ "compilation_mode": attr.string(
+ values = ["fastbuild", "opt", "dbg"],
+ doc = """
+Possible values are `fastbuild`, `dbg` or `opt`. Defaults to `fastbuild`.
+https://docs.bazel.build/versions/master/user-manual.html#flag--compilation_mode
+""",
+ default = "fastbuild",
+ ),
+ "cpus": attr.string_dict(
+ doc = """
+Dictionary of command line options cpu flags and the list of
+cpu's to use for test under target (e.g. {'ios_multi_cpus': ['arm64', 'x86_64']})
+""",
+ ),
+ "expected_platform_type": attr.string(
+ default = "",
+ doc = """
+The apple_platform_type the binary should have been built for.
+""",
+ ),
+ "objc_enable_binary_stripping": attr.bool(
+ default = False,
+ doc = """
+Whether to perform symbol and dead-code strippings on linked binaries. Binary
+strippings will be performed if both this flag and --compilation_mode=opt are
+specified.
+""",
+ ),
+ "target_under_test": attr.label(
+ mandatory = True,
+ doc = "The binary being verified.",
+ cfg = _transition,
+ ),
+ "verifier_script": attr.label(
+ mandatory = True,
+ allow_single_file = [".sh"],
+ doc = """
+Script containing the verification code.
+""",
+ ),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ "_xcode_config": attr.label(
+ default = configuration_field(
+ name = "xcode_config_label",
+ fragment = "apple",
+ ),
+ ),
+ },
+ test = True,
+ fragments = ["apple"],
+)
diff --git a/test/shell/BUILD b/test/shell/BUILD
new file mode 100644
index 0000000..32a3f1c
--- /dev/null
+++ b/test/shell/BUILD
@@ -0,0 +1,35 @@
+exports_files(["verify_binary.sh"])
+
+filegroup(
+ name = "for_bazel_tests",
+ testonly = True,
+ srcs = [
+ "//test:for_bazel_tests",
+ "@build_bazel_apple_support//:for_bazel_tests",
+ ],
+)
+
+sh_library(
+ name = "bashunit",
+ testonly = True,
+ srcs = [
+ "integration_test_setup.sh",
+ "testenv.sh",
+ "unittest.bash",
+ "unittest_utils.sh",
+ ],
+ deps = [
+ "@bazel_tools//tools/bash/runfiles",
+ ],
+)
+
+sh_test(
+ name = "wrapped_clang_test",
+ size = "small",
+ srcs = ["wrapped_clang_test.sh"],
+ data = [
+ ":bashunit",
+ "//crosstool:wrapped_clang",
+ "@bazel_tools//tools/bash/runfiles",
+ ],
+)
diff --git a/test/shell/integration_test_setup.sh b/test/shell/integration_test_setup.sh
new file mode 100755
index 0000000..a16ba90
--- /dev/null
+++ b/test/shell/integration_test_setup.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+#
+
+function print_message_and_exit() {
+ echo "$1" >&2; exit 1;
+}
+
+source "$(rlocation build_bazel_apple_support/test/shell/unittest.bash)" \
+ || print_message_and_exit "unittest.bash not found!"
+source "$(rlocation build_bazel_apple_support/test/shell/testenv.sh)" \
+ || print_message_and_exit "testenv.sh not found!"
diff --git a/test/shell/testenv.sh b/test/shell/testenv.sh
new file mode 100755
index 0000000..7ab9da3
--- /dev/null
+++ b/test/shell/testenv.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+function setup_objc_test_support() {
+ cat > WORKSPACE.bazel <<EOF
+local_repository(
+ name = 'build_bazel_apple_support',
+ path = '$(rlocation build_bazel_apple_support)',
+)
+
+load(
+ "@build_bazel_apple_support//lib:repositories.bzl",
+ "apple_support_dependencies",
+)
+
+apple_support_dependencies()
+EOF
+
+ cat > .bazelrc <<EOF
+build --apple_crosstool_top=@local_config_apple_cc//:toolchain
+build --crosstool_top=@local_config_apple_cc//:toolchain
+build --host_crosstool_top=@local_config_apple_cc//:toolchain
+EOF
+}
diff --git a/test/shell/unittest.bash b/test/shell/unittest.bash
new file mode 100644
index 0000000..b54fa63
--- /dev/null
+++ b/test/shell/unittest.bash
@@ -0,0 +1,846 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Common utility file for Bazel shell tests
+#
+# unittest.bash: a unit test framework in Bash.
+#
+# A typical test suite looks like so:
+#
+# ------------------------------------------------------------------------
+# #!/bin/bash
+#
+# source path/to/unittest.bash || exit 1
+#
+# # Test that foo works.
+# function test_foo() {
+# foo >$TEST_log || fail "foo failed";
+# expect_log "blah" "Expected to see 'blah' in output of 'foo'."
+# }
+#
+# # Test that bar works.
+# function test_bar() {
+# bar 2>$TEST_log || fail "bar failed";
+# expect_not_log "ERROR" "Unexpected error from 'bar'."
+# ...
+# assert_equals $x $y
+# }
+#
+# run_suite "Test suite for blah"
+# ------------------------------------------------------------------------
+#
+# Each test function is considered to pass iff fail() is not called
+# while it is active. fail() may be called directly, or indirectly
+# via other assertions such as expect_log(). run_suite must be called
+# at the very end.
+#
+# A test suite may redefine functions "set_up" and/or "tear_down";
+# these functions are executed before and after each test function,
+# respectively. Similarly, "cleanup" and "timeout" may be redefined,
+# and these function are called upon exit (of any kind) or a timeout.
+#
+# The user can pass --test_arg to blaze test to select specific tests
+# to run. Specifying --test_arg multiple times allows to select several
+# tests to be run in the given order. Additionally the user may define
+# TESTS=(test_foo test_bar ...) to specify a subset of test functions to
+# execute, for example, a working set during debugging. By default, all
+# functions called test_* will be executed.
+#
+# This file provides utilities for assertions over the output of a
+# command. The output of the command under test is directed to the
+# file $TEST_log, and then the expect_log* assertions can be used to
+# test for the presence of certain regular expressions in that file.
+#
+# The test framework is responsible for restoring the original working
+# directory before each test.
+#
+# The order in which test functions are run is not defined, so it is
+# important that tests clean up after themselves.
+#
+# Each test will be run in a new subshell.
+#
+# Functions named __* are not intended for use by clients.
+#
+# This framework implements the "test sharding protocol".
+#
+
+[[ -n "$BASH_VERSION" ]] ||
+ { echo "unittest.bash only works with bash!" >&2; exit 1; }
+
+export BAZEL_SHELL_TEST=1
+
+DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+
+# Load the environment support utilities.
+source "${DIR}/unittest_utils.sh" || { echo "unittest_utils.sh not found" >&2; exit 1; }
+
+#### Global variables:
+
+TEST_name="" # The name of the current test.
+
+TEST_log=$TEST_TMPDIR/log # The log file over which the
+ # expect_log* assertions work. Must
+ # be absolute to be robust against
+ # tests invoking 'cd'!
+
+TEST_passed="true" # The result of the current test;
+ # failed assertions cause this to
+ # become false.
+
+# These variables may be overridden by the test suite:
+
+TESTS=() # A subset or "working set" of test
+ # functions that should be run. By
+ # default, all tests called test_* are
+ # run.
+
+_TEST_FILTERS=() # List of globs to use to filter the tests.
+ # If non-empty, all tests matching at least one
+ # of the globs are run and test list provided in
+ # the arguments is ignored if present.
+
+__in_tear_down=0 # Indicates whether we are in `tear_down` phase
+ # of test. Used to avoid re-entering `tear_down`
+ # on failures within it.
+
+if (( $# > 0 )); then
+ (
+ IFS=':'
+ echo "WARNING: Passing test names in arguments (--test_arg) is deprecated, please use --test_filter='$*' instead." >&2
+ )
+
+ # Legacy behavior is to ignore missing regexp, but with errexit
+ # the following line fails without || true.
+ # TODO(dmarting): maybe we should revisit the way of selecting
+ # test with that framework (use Bazel's environment variable instead).
+ TESTS=($(for i in "$@"; do echo $i; done | grep ^test_ || true))
+ if (( ${#TESTS[@]} == 0 )); then
+ echo "WARNING: Arguments do not specify tests!" >&2
+ fi
+fi
+# TESTBRIDGE_TEST_ONLY contains the value of --test_filter, if any. We want to
+# preferentially use that instead of $@ to determine which tests to run.
+if [[ ${TESTBRIDGE_TEST_ONLY:-} != "" ]]; then
+ if (( ${#TESTS[@]} != 0 )); then
+ echo "WARNING: Both --test_arg and --test_filter specified, ignoring --test_arg" >&2
+ TESTS=()
+ fi
+ # Split TESTBRIDGE_TEST_ONLY on colon and store it in `_TEST_FILTERS` array.
+ IFS=':' read -r -a _TEST_FILTERS <<< "$TESTBRIDGE_TEST_ONLY"
+fi
+
+TEST_verbose="true" # Whether or not to be verbose. A
+ # command; "true" or "false" are
+ # acceptable. The default is: true.
+
+TEST_script="$0" # Full path to test script
+# Check if the script path is absolute, if not prefix the PWD.
+if [[ ! "$TEST_script" = /* ]]; then
+ TEST_script="${PWD}/$0"
+fi
+
+
+#### Internal functions
+
+function __show_log() {
+ echo "-- Test log: -----------------------------------------------------------"
+ [[ -e $TEST_log ]] && cat "$TEST_log" || echo "(Log file did not exist.)"
+ echo "------------------------------------------------------------------------"
+}
+
+# Usage: __pad <title> <pad-char>
+# Print $title padded to 80 columns with $pad_char.
+function __pad() {
+ local title=$1
+ local pad=$2
+ # Ignore the subshell error -- `head` closes the fd before reading to the
+ # end, therefore the subshell will get SIGPIPE while stuck in `write`.
+ {
+ echo -n "${pad}${pad} ${title} "
+ printf "%80s" " " | tr ' ' "$pad"
+ } | head -c 80 || true
+ echo
+}
+
+#### Exported functions
+
+# Usage: init_test ...
+# Deprecated. Has no effect.
+function init_test() {
+ :
+}
+
+
+# Usage: set_up
+# Called before every test function. May be redefined by the test suite.
+function set_up() {
+ :
+}
+
+# Usage: tear_down
+# Called after every test function. May be redefined by the test suite.
+function tear_down() {
+ :
+}
+
+# Usage: cleanup
+# Called upon eventual exit of the test suite. May be redefined by
+# the test suite.
+function cleanup() {
+ :
+}
+
+# Usage: timeout
+# Called upon early exit from a test due to timeout.
+function timeout() {
+ :
+}
+
+# Usage: testenv_set_up
+# Called prior to set_up. For use by testenv.sh.
+function testenv_set_up() {
+ :
+}
+
+# Usage: testenv_tear_down
+# Called after tear_down. For use by testenv.sh.
+function testenv_tear_down() {
+ :
+}
+
+# Usage: fail <message> [<message> ...]
+# Print failure message with context information, and mark the test as
+# a failure. The context includes a stacktrace including the longest sequence
+# of calls outside this module. (We exclude the top and bottom portions of
+# the stack because they just add noise.) Also prints the contents of
+# $TEST_log.
+function fail() {
+ __show_log >&2
+ echo "${TEST_name} FAILED: $*." >&2
+ # Keep the original error message if we fail in `tear_down` after a failure.
+ [[ "${TEST_passed}" == "true" ]] && echo "$@" >"$TEST_TMPDIR"/__fail
+ TEST_passed="false"
+ __show_stack
+ # Cleanup as we are leaving the subshell now
+ __run_tear_down_after_failure
+ exit 1
+}
+
+function __run_tear_down_after_failure() {
+ # Skip `tear_down` after a failure in `tear_down` to prevent infinite
+ # recursion.
+ (( __in_tear_down )) && return
+ __in_tear_down=1
+ echo -e "\nTear down:\n" >&2
+ tear_down
+ testenv_tear_down
+}
+
+# Usage: warn <message>
+# Print a test warning with context information.
+# The context includes a stacktrace including the longest sequence
+# of calls outside this module. (We exclude the top and bottom portions of
+# the stack because they just add noise.)
+function warn() {
+ __show_log >&2
+ echo "${TEST_name} WARNING: $1." >&2
+ __show_stack
+
+ if [[ -n "${TEST_WARNINGS_OUTPUT_FILE:-}" ]]; then
+ echo "${TEST_name} WARNING: $1." >> "$TEST_WARNINGS_OUTPUT_FILE"
+ fi
+}
+
+# Usage: show_stack
+# Prints the portion of the stack that does not belong to this module,
+# i.e. the user's code that called a failing assertion. Stack may not
+# be available if Bash is reading commands from stdin; an error is
+# printed in that case.
+__show_stack() {
+ local i=0
+ local trace_found=0
+
+ # Skip over active calls within this module:
+ while (( i < ${#FUNCNAME[@]} )) && [[ ${BASH_SOURCE[i]:-} == "${BASH_SOURCE[0]}" ]]; do
+ (( ++i ))
+ done
+
+ # Show all calls until the next one within this module (typically run_suite):
+ while (( i < ${#FUNCNAME[@]} )) && [[ ${BASH_SOURCE[i]:-} != "${BASH_SOURCE[0]}" ]]; do
+ # Read online docs for BASH_LINENO to understand the strange offset.
+ # Undefined can occur in the BASH_SOURCE stack apparently when one exits from a subshell
+ echo "${BASH_SOURCE[i]:-"Unknown"}:${BASH_LINENO[i - 1]:-"Unknown"}: in call to ${FUNCNAME[i]:-"Unknown"}" >&2
+ (( ++i ))
+ trace_found=1
+ done
+
+ (( trace_found )) || echo "[Stack trace not available]" >&2
+}
+
+# Usage: expect_log <regexp> [error-message]
+# Asserts that $TEST_log matches regexp. Prints the contents of
+# $TEST_log and the specified (optional) error message otherwise, and
+# returns non-zero.
+function expect_log() {
+ local pattern=$1
+ local message=${2:-Expected regexp "$pattern" not found}
+ grep -sq -- "$pattern" $TEST_log && return 0
+
+ fail "$message"
+ return 1
+}
+
+# Usage: expect_log_warn <regexp> [error-message]
+# Warns if $TEST_log does not match regexp. Prints the contents of
+# $TEST_log and the specified (optional) error message on mismatch.
+function expect_log_warn() {
+ local pattern=$1
+ local message=${2:-Expected regexp "$pattern" not found}
+ grep -sq -- "$pattern" $TEST_log && return 0
+
+ warn "$message"
+ return 1
+}
+
+# Usage: expect_log_once <regexp> [error-message]
+# Asserts that $TEST_log contains one line matching <regexp>.
+# Prints the contents of $TEST_log and the specified (optional)
+# error message otherwise, and returns non-zero.
+function expect_log_once() {
+ local pattern=$1
+ local message=${2:-Expected regexp "$pattern" not found exactly once}
+ expect_log_n "$pattern" 1 "$message"
+}
+
+# Usage: expect_log_n <regexp> <count> [error-message]
+# Asserts that $TEST_log contains <count> lines matching <regexp>.
+# Prints the contents of $TEST_log and the specified (optional)
+# error message otherwise, and returns non-zero.
+function expect_log_n() {
+ local pattern=$1
+ local expectednum=${2:-1}
+ local message=${3:-Expected regexp "$pattern" not found exactly $expectednum times}
+ local count=$(grep -sc -- "$pattern" $TEST_log)
+ (( count == expectednum )) && return 0
+ fail "$message"
+ return 1
+}
+
+# Usage: expect_not_log <regexp> [error-message]
+# Asserts that $TEST_log does not match regexp. Prints the contents
+# of $TEST_log and the specified (optional) error message otherwise, and
+# returns non-zero.
+function expect_not_log() {
+ local pattern=$1
+ local message=${2:-Unexpected regexp "$pattern" found}
+ grep -sq -- "$pattern" $TEST_log || return 0
+
+ fail "$message"
+ return 1
+}
+
+# Usage: expect_query_targets <arguments>
+# Checks that log file contains exactly the targets in the argument list.
+function expect_query_targets() {
+ for arg in "$@"; do
+ expect_log_once "^$arg$"
+ done
+
+# Checks that the number of lines started with '//' equals to the number of
+# arguments provided.
+ expect_log_n "^//[^ ]*$" $#
+}
+
+# Usage: expect_log_with_timeout <regexp> <timeout> [error-message]
+# Waits for the given regexp in the $TEST_log for up to timeout seconds.
+# Prints the contents of $TEST_log and the specified (optional)
+# error message otherwise, and returns non-zero.
+function expect_log_with_timeout() {
+ local pattern=$1
+ local timeout=$2
+ local message=${3:-Regexp "$pattern" not found in "$timeout" seconds}
+ local count=0
+ while (( count < timeout )); do
+ grep -sq -- "$pattern" "$TEST_log" && return 0
+ let count=count+1
+ sleep 1
+ done
+
+ grep -sq -- "$pattern" "$TEST_log" && return 0
+ fail "$message"
+ return 1
+}
+
+# Usage: expect_cmd_with_timeout <expected> <cmd> [timeout]
+# Repeats the command once a second for up to timeout seconds (10s by default),
+# until the output matches the expected value. Fails and returns 1 if
+# the command does not return the expected value in the end.
+function expect_cmd_with_timeout() {
+ local expected="$1"
+ local cmd="$2"
+ local timeout=${3:-10}
+ local count=0
+ while (( count < timeout )); do
+ local actual="$($cmd)"
+ [[ "$expected" == "$actual" ]] && return 0
+ (( ++count ))
+ sleep 1
+ done
+
+ [[ "$expected" == "$actual" ]] && return 0
+ fail "Expected '${expected}' within ${timeout}s, was '${actual}'"
+ return 1
+}
+
+# Usage: assert_one_of <expected_list>... <actual>
+# Asserts that actual is one of the items in expected_list
+#
+# Example:
+# local expected=( "foo", "bar", "baz" )
+# assert_one_of $expected $actual
+function assert_one_of() {
+ local args=("$@")
+ local last_arg_index=$((${#args[@]} - 1))
+ local actual=${args[last_arg_index]}
+ unset args[last_arg_index]
+ for expected_item in "${args[@]}"; do
+ [[ "$expected_item" == "$actual" ]] && return 0
+ done;
+
+ fail "Expected one of '${args[*]}', was '$actual'"
+ return 1
+}
+
+# Usage: assert_not_one_of <expected_list>... <actual>
+# Asserts that actual is not one of the items in expected_list
+#
+# Example:
+# local unexpected=( "foo", "bar", "baz" )
+# assert_not_one_of $unexpected $actual
+function assert_not_one_of() {
+ local args=("$@")
+ local last_arg_index=$((${#args[@]} - 1))
+ local actual=${args[last_arg_index]}
+ unset args[last_arg_index]
+ for expected_item in "${args[@]}"; do
+ if [[ "$expected_item" == "$actual" ]]; then
+ fail "'${args[*]}' contains '$actual'"
+ return 1
+ fi
+ done;
+
+ return 0
+}
+
+# Usage: assert_equals <expected> <actual>
+# Asserts [[ expected == actual ]].
+function assert_equals() {
+ local expected=$1 actual=$2
+ [[ "$expected" == "$actual" ]] && return 0
+
+ fail "Expected '$expected', was '$actual'"
+ return 1
+}
+
+# Usage: assert_not_equals <unexpected> <actual>
+# Asserts [[ unexpected != actual ]].
+function assert_not_equals() {
+ local unexpected=$1 actual=$2
+ [[ "$unexpected" != "$actual" ]] && return 0;
+
+ fail "Expected not '${unexpected}', was '${actual}'"
+ return 1
+}
+
+# Usage: assert_contains <regexp> <file> [error-message]
+# Asserts that file matches regexp. Prints the contents of
+# file and the specified (optional) error message otherwise, and
+# returns non-zero.
+function assert_contains() {
+ local pattern=$1
+ local file=$2
+ local message=${3:-Expected regexp "$pattern" not found in "$file"}
+ grep -sq -- "$pattern" "$file" && return 0
+
+ cat "$file" >&2
+ fail "$message"
+ return 1
+}
+
+# Usage: assert_not_contains <regexp> <file> [error-message]
+# Asserts that file does not match regexp. Prints the contents of
+# file and the specified (optional) error message otherwise, and
+# returns non-zero.
+function assert_not_contains() {
+ local pattern=$1
+ local file=$2
+ local message=${3:-Expected regexp "$pattern" found in "$file"}
+
+ if [[ -f "$file" ]]; then
+ grep -sq -- "$pattern" "$file" || return 0
+ else
+ fail "$file is not a file: $message"
+ return 1
+ fi
+
+ cat "$file" >&2
+ fail "$message"
+ return 1
+}
+
+function assert_contains_n() {
+ local pattern=$1
+ local expectednum=${2:-1}
+ local file=$3
+ local message=${4:-Expected regexp "$pattern" not found exactly $expectednum times}
+ local count
+ if [[ -f "$file" ]]; then
+ count=$(grep -sc -- "$pattern" "$file")
+ else
+ fail "$file is not a file: $message"
+ return 1
+ fi
+ (( count == expectednum )) && return 0
+
+ cat "$file" >&2
+ fail "$message"
+ return 1
+}
+
+# Updates the global variables TESTS if
+# sharding is enabled, i.e. ($TEST_TOTAL_SHARDS > 0).
+function __update_shards() {
+ [[ -z "${TEST_TOTAL_SHARDS-}" ]] && return 0
+
+ (( TEST_TOTAL_SHARDS > 0 )) ||
+ { echo "Invalid total shards ${TEST_TOTAL_SHARDS}" >&2; exit 1; }
+
+ (( TEST_SHARD_INDEX < 0 || TEST_SHARD_INDEX >= TEST_TOTAL_SHARDS )) &&
+ { echo "Invalid shard ${TEST_SHARD_INDEX}" >&2; exit 1; }
+
+ IFS=$'\n' read -rd $'\0' -a TESTS < <(
+ for test in "${TESTS[@]}"; do echo "$test"; done |
+ awk "NR % ${TEST_TOTAL_SHARDS} == ${TEST_SHARD_INDEX}" &&
+ echo -en '\0')
+
+ [[ -z "${TEST_SHARD_STATUS_FILE-}" ]] || touch "$TEST_SHARD_STATUS_FILE"
+}
+
+# Usage: __test_terminated <signal-number>
+# Handler that is called when the test terminated unexpectedly
+function __test_terminated() {
+ __show_log >&2
+ echo "$TEST_name FAILED: terminated by signal $1." >&2
+ TEST_passed="false"
+ __show_stack
+ timeout
+ exit 1
+}
+
+# Usage: __test_terminated_err
+# Handler that is called when the test terminated unexpectedly due to "errexit".
+function __test_terminated_err() {
+ # When a subshell exits due to signal ERR, its parent shell also exits,
+ # thus the signal handler is called recursively and we print out the
+ # error message and stack trace multiple times. We're only interested
+ # in the first one though, as it contains the most information, so ignore
+ # all following.
+ if [[ -f $TEST_TMPDIR/__err_handled ]]; then
+ exit 1
+ fi
+ __show_log >&2
+ if [[ ! -z "$TEST_name" ]]; then
+ echo -n "$TEST_name " >&2
+ fi
+ echo "FAILED: terminated because this command returned a non-zero status:" >&2
+ touch $TEST_TMPDIR/__err_handled
+ TEST_passed="false"
+ __show_stack
+ # If $TEST_name is still empty, the test suite failed before we even started
+ # to run tests, so we shouldn't call tear_down.
+ if [[ -n "$TEST_name" ]]; then
+ __run_tear_down_after_failure
+ fi
+ exit 1
+}
+
+# Usage: __trap_with_arg <handler> <signals ...>
+# Helper to install a trap handler for several signals preserving the signal
+# number, so that the signal number is available to the trap handler.
+function __trap_with_arg() {
+ func="$1" ; shift
+ for sig ; do
+ trap "$func $sig" "$sig"
+ done
+}
+
+# Usage: <node> <block>
+# Adds the block to the given node in the report file. Quotes in the in
+# arguments need to be escaped.
+function __log_to_test_report() {
+ local node="$1"
+ local block="$2"
+ if [[ ! -e "$XML_OUTPUT_FILE" ]]; then
+ local xml_header='<?xml version="1.0" encoding="UTF-8"?>'
+ echo "${xml_header}<testsuites></testsuites>" > "$XML_OUTPUT_FILE"
+ fi
+
+ # replace match on node with block and match
+ # replacement expression only needs escaping for quotes
+ perl -e "\
+\$input = @ARGV[0]; \
+\$/=undef; \
+open FILE, '+<$XML_OUTPUT_FILE'; \
+\$content = <FILE>; \
+if (\$content =~ /($node.*)\$/) { \
+ seek FILE, 0, 0; \
+ print FILE \$\` . \$input . \$1; \
+}; \
+close FILE" "$block"
+}
+
+# Usage: <total> <passed>
+# Adds the test summaries to the xml nodes.
+function __finish_test_report() {
+ local suite_name="$1"
+ local total="$2"
+ local passed="$3"
+ local failed=$((total - passed))
+
+ # Update the xml output with the suite name and total number of
+ # passed/failed tests.
+ cat "$XML_OUTPUT_FILE" | \
+ sed \
+ "s/<testsuites>/<testsuites tests=\"$total\" failures=\"0\" errors=\"$failed\">/" | \
+ sed \
+ "s/<testsuite>/<testsuite name=\"${suite_name}\" tests=\"$total\" failures=\"0\" errors=\"$failed\">/" \
+ > "${XML_OUTPUT_FILE}.bak"
+
+ rm -f "$XML_OUTPUT_FILE"
+ mv "${XML_OUTPUT_FILE}.bak" "$XML_OUTPUT_FILE"
+}
+
+# Multi-platform timestamp function
+UNAME=$(uname -s | tr 'A-Z' 'a-z')
+if [[ "$UNAME" == "linux" ]] || [[ "$UNAME" =~ msys_nt* ]]; then
+ function timestamp() {
+ echo $(($(date +%s%N)/1000000))
+ }
+else
+ function timestamp() {
+ # macOS and BSDs do not have %N, so Python is the best we can do.
+ # LC_ALL=C works around python 3.8 and 3.9 crash on macOS when the
+ # filesystem encoding is unspecified (e.g. when LANG=en_US).
+ local PYTHON=python
+ command -v python3 &> /dev/null && PYTHON=python3
+ LC_ALL=C "${PYTHON}" -c 'import time; print(int(round(time.time() * 1000)))'
+ }
+fi
+
+function get_run_time() {
+ local ts_start=$1
+ local ts_end=$2
+ run_time_ms=$((ts_end - ts_start))
+ echo $((run_time_ms / 1000)).${run_time_ms: -3}
+}
+
+# Usage: run_tests <suite-comment>
+# Must be called from the end of the user's test suite.
+# Calls exit with zero on success, non-zero otherwise.
+function run_suite() {
+ local message="$1"
+ # The name of the suite should be the script being run, which
+ # will be the filename with the ".sh" extension removed.
+ local suite_name="$(basename "$0")"
+
+ echo >&2
+ echo "$message" >&2
+ echo >&2
+
+ __log_to_test_report "<\/testsuites>" "<testsuite></testsuite>"
+
+ local total=0
+ local passed=0
+
+ atexit "cleanup"
+
+ # If the user didn't specify an explicit list of tests (e.g. a
+ # working set), use them all.
+ if (( ${#TESTS[@]} == 0 )); then
+ # Even if there aren't any tests, this needs to succeed.
+ local all_tests=()
+ IFS=$'\n' read -d $'\0' -ra all_tests < <(
+ declare -F | awk '{print $3}' | grep ^test_ || true; echo -en '\0')
+
+ if (( "${#_TEST_FILTERS[@]}" == 0 )); then
+ # Use ${array[@]+"${array[@]}"} idiom to avoid errors when running with
+ # Bash version <= 4.4 with `nounset` when `all_tests` is empty (
+ # https://github.com/bminor/bash/blob/a0c0a00fc419b7bc08202a79134fcd5bc0427071/CHANGES#L62-L63).
+ TESTS=("${all_tests[@]+${all_tests[@]}}")
+ else
+ for t in "${all_tests[@]+${all_tests[@]}}"; do
+ local matches=0
+ for f in "${_TEST_FILTERS[@]}"; do
+ # We purposely want to glob match.
+ # shellcheck disable=SC2053
+ [[ "$t" = $f ]] && matches=1 && break
+ done
+ if (( matches )); then
+ TESTS+=("$t")
+ fi
+ done
+ fi
+
+ elif [[ -n "${TEST_WARNINGS_OUTPUT_FILE:-}" ]]; then
+ if grep -q "TESTS=" "$TEST_script" ; then
+ echo "TESTS variable overridden in sh_test. Please remove before submitting" \
+ >> "$TEST_WARNINGS_OUTPUT_FILE"
+ fi
+ fi
+
+ # Reset TESTS in the common case where it contains a single empty string.
+ if [[ -z "${TESTS[*]-}" ]]; then
+ TESTS=()
+ fi
+ local original_tests_size=${#TESTS[@]}
+
+ __update_shards
+
+ if [[ "${#TESTS[@]}" -ne 0 ]]; then
+ for TEST_name in "${TESTS[@]}"; do
+ >"$TEST_log" # Reset the log.
+ TEST_passed="true"
+
+ (( ++total ))
+ if [[ "$TEST_verbose" == "true" ]]; then
+ date >&2
+ __pad "$TEST_name" '*' >&2
+ fi
+
+ local run_time="0.0"
+ rm -f "${TEST_TMPDIR}"/{__ts_start,__ts_end}
+
+ if [[ "$(type -t "$TEST_name")" == function ]]; then
+ # Save exit handlers eventually set.
+ local SAVED_ATEXIT="$ATEXIT";
+ ATEXIT=
+
+ # Run test in a subshell.
+ rm -f "${TEST_TMPDIR}"/__err_handled
+ __trap_with_arg __test_terminated INT KILL PIPE TERM ABRT FPE ILL QUIT SEGV
+
+ # Remember -o pipefail value and disable it for the subshell result
+ # collection.
+ if [[ "${SHELLOPTS}" =~ (^|:)pipefail(:|$) ]]; then
+ local __opt_switch=-o
+ else
+ local __opt_switch=+o
+ fi
+ set +o pipefail
+ (
+ set "${__opt_switch}" pipefail
+ # if errexit is enabled, make sure we run cleanup and collect the log.
+ if [[ "$-" = *e* ]]; then
+ set -E
+ trap __test_terminated_err ERR
+ fi
+ timestamp >"${TEST_TMPDIR}"/__ts_start
+ testenv_set_up
+ set_up
+ eval "$TEST_name"
+ __in_tear_down=1
+ tear_down
+ testenv_tear_down
+ timestamp >"${TEST_TMPDIR}"/__ts_end
+ test "$TEST_passed" == "true"
+ ) 2>&1 | tee "${TEST_TMPDIR}"/__log
+ # Note that tee will prevent the control flow continuing if the test
+ # spawned any processes which are still running and have not closed
+ # their stdout.
+
+ test_subshell_status=${PIPESTATUS[0]}
+ set "${__opt_switch}" pipefail
+ if (( test_subshell_status != 0 )); then
+ TEST_passed="false"
+ # Ensure that an end time is recorded in case the test subshell
+ # terminated prematurely.
+ [[ -f "$TEST_TMPDIR"/__ts_end ]] || timestamp >"$TEST_TMPDIR"/__ts_end
+ fi
+
+ # Calculate run time for the testcase.
+ local ts_start
+ ts_start=$(<"${TEST_TMPDIR}"/__ts_start)
+ local ts_end
+ ts_end=$(<"${TEST_TMPDIR}"/__ts_end)
+ run_time=$(get_run_time $ts_start $ts_end)
+
+ # Eventually restore exit handlers.
+ if [[ -n "$SAVED_ATEXIT" ]]; then
+ ATEXIT="$SAVED_ATEXIT"
+ trap "$ATEXIT" EXIT
+ fi
+ else # Bad test explicitly specified in $TESTS.
+ fail "Not a function: '$TEST_name'"
+ fi
+
+ local testcase_tag=""
+
+ local red='\033[0;31m'
+ local green='\033[0;32m'
+ local no_color='\033[0m'
+
+ if [[ "$TEST_verbose" == "true" ]]; then
+ echo >&2
+ fi
+
+ if [[ "$TEST_passed" == "true" ]]; then
+ if [[ "$TEST_verbose" == "true" ]]; then
+ echo -e "${green}PASSED${no_color}: ${TEST_name}" >&2
+ fi
+ (( ++passed ))
+ testcase_tag="<testcase name=\"${TEST_name}\" status=\"run\" time=\"${run_time}\" classname=\"\"></testcase>"
+ else
+ echo -e "${red}FAILED${no_color}: ${TEST_name}" >&2
+ # end marker in CDATA cannot be escaped, we need to split the CDATA sections
+ log=$(sed 's/]]>/]]>]]&gt;<![CDATA[/g' "${TEST_TMPDIR}"/__log)
+ fail_msg=$(cat "${TEST_TMPDIR}"/__fail 2> /dev/null || echo "No failure message")
+ # Replacing '&' with '&amp;', '<' with '&lt;', '>' with '&gt;', and '"' with '&quot;'
+ escaped_fail_msg=$(echo "$fail_msg" | sed 's/&/\&amp;/g' | sed 's/</\&lt;/g' | sed 's/>/\&gt;/g' | sed 's/"/\&quot;/g')
+ testcase_tag="<testcase name=\"${TEST_name}\" status=\"run\" time=\"${run_time}\" classname=\"\"><error message=\"${escaped_fail_msg}\"><![CDATA[${log}]]></error></testcase>"
+ fi
+
+ if [[ "$TEST_verbose" == "true" ]]; then
+ echo >&2
+ fi
+ __log_to_test_report "<\/testsuite>" "$testcase_tag"
+ done
+ fi
+
+ __finish_test_report "$suite_name" $total $passed
+ __pad "${passed} / ${total} tests passed." '*' >&2
+ if (( original_tests_size == 0 )); then
+ __pad "No tests found." '*'
+ exit 1
+ elif (( total != passed )); then
+ __pad "There were errors." '*' >&2
+ exit 1
+ elif (( total == 0 )); then
+ __pad "No tests executed due to sharding. Check your test's shard_count." '*'
+ __pad "Succeeding anyway." '*'
+ fi
+
+ exit 0
+}
diff --git a/test/shell/unittest_utils.sh b/test/shell/unittest_utils.sh
new file mode 100644
index 0000000..be3409e
--- /dev/null
+++ b/test/shell/unittest_utils.sh
@@ -0,0 +1,181 @@
+# Copyright 2020 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.
+
+# Support for unittest.bash
+
+#### Set up the test environment.
+
+set -euo pipefail
+
+cat_jvm_log () {
+ if [[ "$log_content" =~ \
+ "(error code:".*", error message: '".*"', log file: '"(.*)"')" ]]; then
+ echo >&2
+ echo "Content of ${BASH_REMATCH[1]}:" >&2
+ cat "${BASH_REMATCH[1]}" >&2
+ fi
+}
+
+# Print message in "$1" then exit with status "$2"
+die () {
+ # second argument is optional, defaulting to 1
+ local status_code=${2:-1}
+ # Stop capturing stdout/stderr, and dump captured output
+ if [[ "$CAPTURED_STD_ERR" -ne 0 || "$CAPTURED_STD_OUT" -ne 0 ]]; then
+ restore_outputs
+ if [[ "$CAPTURED_STD_OUT" -ne 0 ]]; then
+ cat "${TEST_TMPDIR}/captured.out"
+ CAPTURED_STD_OUT=0
+ fi
+ if [[ "$CAPTURED_STD_ERR" -ne 0 ]]; then
+ cat "${TEST_TMPDIR}/captured.err" 1>&2
+ cat_jvm_log "$(cat "${TEST_TMPDIR}/captured.err")"
+ CAPTURED_STD_ERR=0
+ fi
+ fi
+
+ if [[ -n "${1-}" ]] ; then
+ echo "$1" 1>&2
+ fi
+ if [[ -n "${BASH-}" ]]; then
+ local caller_n=0
+ while [[ $caller_n -lt 4 ]] && \
+ caller_out=$(caller $caller_n 2>/dev/null); do
+ test $caller_n -eq 0 && echo "CALLER stack (max 4):"
+ echo " $caller_out"
+ let caller_n=caller_n+1
+ done 1>&2
+ fi
+ if [[ -n "${status_code}" && "${status_code}" -ne 0 ]]; then
+ exit "$status_code"
+ else
+ exit 1
+ fi
+}
+
+# Print message in "$1" then record that a non-fatal error occurred in
+# ERROR_COUNT
+ERROR_COUNT="${ERROR_COUNT:-0}"
+error () {
+ if [[ -n "$1" ]] ; then
+ echo "$1" 1>&2
+ fi
+ ERROR_COUNT=$(($ERROR_COUNT + 1))
+}
+
+# Die if "$1" != "$2", print $3 as death reason
+check_eq () {
+ [[ "$1" = "$2" ]] || die "Check failed: '$1' == '$2' ${3:+ ($3)}"
+}
+
+# Die if "$1" == "$2", print $3 as death reason
+check_ne () {
+ [[ "$1" != "$2" ]] || die "Check failed: '$1' != '$2' ${3:+ ($3)}"
+}
+
+# The structure of the following if statements is such that if '[[' fails
+# (e.g., a non-number was passed in) then the check will fail.
+
+# Die if "$1" > "$2", print $3 as death reason
+check_le () {
+ [[ "$1" -gt "$2" ]] || die "Check failed: '$1' <= '$2' ${3:+ ($3)}"
+}
+
+# Die if "$1" >= "$2", print $3 as death reason
+check_lt () {
+ [[ "$1" -lt "$2" ]] || die "Check failed: '$1' < '$2' ${3:+ ($3)}"
+}
+
+# Die if "$1" < "$2", print $3 as death reason
+check_ge () {
+ [[ "$1" -ge "$2" ]] || die "Check failed: '$1' >= '$2' ${3:+ ($3)}"
+}
+
+# Die if "$1" <= "$2", print $3 as death reason
+check_gt () {
+ [[ "$1" -gt "$2" ]] || die "Check failed: '$1' > '$2' ${3:+ ($3)}"
+}
+
+# Die if $2 !~ $1; print $3 as death reason
+check_match ()
+{
+ expr match "$2" "$1" >/dev/null || \
+ die "Check failed: '$2' does not match regex '$1' ${3:+ ($3)}"
+}
+
+# Run command "$1" at exit. Like "trap" but multiple atexits don't
+# overwrite each other. Will break if someone does call trap
+# directly. So, don't do that.
+ATEXIT="${ATEXIT-}"
+atexit () {
+ if [[ -z "$ATEXIT" ]]; then
+ ATEXIT="$1"
+ else
+ ATEXIT="$1 ; $ATEXIT"
+ fi
+ trap "$ATEXIT" EXIT
+}
+
+## TEST_TMPDIR
+if [[ -z "${TEST_TMPDIR:-}" ]]; then
+ export TEST_TMPDIR="$(mktemp -d ${TMPDIR:-/tmp}/bazel-test.XXXXXXXX)"
+fi
+if [[ ! -e "${TEST_TMPDIR}" ]]; then
+ mkdir -p -m 0700 "${TEST_TMPDIR}"
+ # Clean TEST_TMPDIR on exit
+ atexit "rm -fr ${TEST_TMPDIR}"
+fi
+
+# Functions to compare the actual output of a test to the expected
+# (golden) output.
+#
+# Usage:
+# capture_test_stdout
+# ... do something ...
+# diff_test_stdout "$TEST_SRCDIR/path/to/golden.out"
+
+# Redirect a file descriptor to a file.
+CAPTURED_STD_OUT="${CAPTURED_STD_OUT:-0}"
+CAPTURED_STD_ERR="${CAPTURED_STD_ERR:-0}"
+
+capture_test_stdout () {
+ exec 3>&1 # Save stdout as fd 3
+ exec 4>"${TEST_TMPDIR}/captured.out"
+ exec 1>&4
+ CAPTURED_STD_OUT=1
+}
+
+capture_test_stderr () {
+ exec 6>&2 # Save stderr as fd 6
+ exec 7>"${TEST_TMPDIR}/captured.err"
+ exec 2>&7
+ CAPTURED_STD_ERR=1
+}
+
+# Force XML_OUTPUT_FILE to an existing path
+if [[ -z "${XML_OUTPUT_FILE:-}" ]]; then
+ XML_OUTPUT_FILE=${TEST_TMPDIR}/output.xml
+fi
+
+# Functions to provide easy access to external repository outputs in the sibling
+# repository layout.
+#
+# Usage:
+# bin_dir <repository name>
+# genfiles_dir <repository name>
+# testlogs_dir <repository name>
+
+testlogs_dir() {
+ echo $(bazel info bazel-testlogs | sed "s|bazel-out|bazel-out/$1|")
+}
diff --git a/test/shell/verify_binary.sh b/test/shell/verify_binary.sh
new file mode 100755
index 0000000..f73943a
--- /dev/null
+++ b/test/shell/verify_binary.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+readonly binary="%{binary}s"
+expected_platform="MACOS"
+if [[ "$PLATFORM_TYPE" == "ios" && "$BUILD_TYPE" == "device" ]]; then
+ expected_platform="IOS"
+elif [[ "$PLATFORM_TYPE" == "ios" && "$BUILD_TYPE" == "simulator" ]]; then
+ expected_platform="IOSSIMULATOR"
+elif [[ "$PLATFORM_TYPE" == "visionos" && "$BUILD_TYPE" == "device" ]]; then
+ expected_platform="XROS"
+elif [[ "$PLATFORM_TYPE" == "visionos" && "$BUILD_TYPE" == "simulator" ]]; then
+ expected_platform="XROSSIMULATOR"
+elif [[ "$PLATFORM_TYPE" == "watchos" && "$BUILD_TYPE" == "device" ]]; then
+ expected_platform="WATCHOS"
+elif [[ "$PLATFORM_TYPE" == "watchos" && "$BUILD_TYPE" == "simulator" ]]; then
+ expected_platform="WATCHSIMULATOR"
+fi
+
+platforms=$(otool -lv "$binary" | grep "platform " | sort -u || true)
+if ! echo "$platforms" | grep -q "platform $expected_platform"; then
+ echo "error: binary $binary does not contain platform $expected_platform, got: '$platforms'" >&2
+ exit 1
+fi
+
+lipo_output=$(lipo -info "$binary")
+expected_cpus=${CPU//,/ }
+expected_cpus=${expected_cpus//sim_/}
+if ! echo "$lipo_output" | grep -q "$expected_cpus"; then
+ echo "error: binary $binary does not contain CPU $CPU, got: '$lipo_output"
+ exit 1
+fi
diff --git a/test/shell/wrapped_clang_test.sh b/test/shell/wrapped_clang_test.sh
new file mode 100755
index 0000000..19ab9a3
--- /dev/null
+++ b/test/shell/wrapped_clang_test.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 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.
+
+# Unit tests for wrapped_clang.
+
+# --- begin runfiles.bash initialization ---
+# Copy-pasted from Bazel's Bash runfiles library (tools/bash/runfiles/runfiles.bash).
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ if [[ -f "$0.runfiles_manifest" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+ elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+ export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+ elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ export RUNFILES_DIR="$0.runfiles"
+ fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+ source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+ source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+ echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+ exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+
+# Load test environment
+source "$(rlocation "build_bazel_apple_support/test/shell/unittest.bash")" \
+ || { echo "unittest.bash not found!" >&2; exit 1; }
+WRAPPED_CLANG=$(rlocation "build_bazel_apple_support/crosstool/wrapped_clang")
+
+
+# This env var tells wrapped_clang to log its command instead of running.
+export __WRAPPED_CLANG_LOG_ONLY=1
+
+
+# Test that add_ast_path is remapped properly.
+function test_add_ast_path_remapping() {
+ env DEVELOPER_DIR=dummy SDKROOT=a \
+ "${WRAPPED_CLANG}" "-Wl,-add_ast_path,foo" >$TEST_log || fail "wrapped_clang failed";
+ expect_log "-Wl,-add_ast_path,${PWD}/foo" "Expected add_ast_path to be remapped."
+}
+
+function test_disable_add_ast_path_remapping() {
+ env RELATIVE_AST_PATH=isset DEVELOPER_DIR=dummy SDKROOT=a \
+ "${WRAPPED_CLANG}" "-Wl,-add_ast_path,relative/foo" >$TEST_log || fail "wrapped_clang failed";
+ expect_log "-Wl,-add_ast_path,relative/foo" "Expected add_ast_path to not be remapped."
+}
+
+# Test that __BAZEL_XCODE_DEVELOPER_DIR__ is remapped properly.
+function test_developer_dir_remapping() {
+ env DEVELOPER_DIR=mydir SDKROOT=a \
+ "${WRAPPED_CLANG}" "developer_dir=__BAZEL_XCODE_DEVELOPER_DIR__" \
+ >$TEST_log || fail "wrapped_clang failed";
+ expect_log "developer_dir=mydir" "Expected developer dir to be remapped."
+}
+
+# Test that __BAZEL_XCODE_SDKROOT__ is remapped properly.
+function test_sdkroot_remapping() {
+ env DEVELOPER_DIR=dummy SDKROOT=mysdkroot \
+ "${WRAPPED_CLANG}" "sdkroot=__BAZEL_XCODE_SDKROOT__" \
+ >$TEST_log || fail "wrapped_clang failed";
+ expect_log "sdkroot=mysdkroot" "Expected sdkroot to be remapped."
+}
+
+function test_execroot_remapped() {
+ env DEVELOPER_DIR=dummy SDKROOT=mysdkroot \
+ "${WRAPPED_CLANG}" "-fdebug-prefix-map=__BAZEL_EXECUTION_ROOT__=." \
+ >$TEST_log || fail "wrapped_clang failed";
+ expect_log "-fdebug-prefix-map=/" "Expected execroot to be remapped."
+}
+
+function test_params_expansion() {
+ params=$(mktemp)
+ {
+ echo "first"
+ echo "-rpath"
+ echo "@loader_path"
+ echo "sdkroot=__BAZEL_XCODE_SDKROOT__"
+ echo "developer_dir=__BAZEL_XCODE_DEVELOPER_DIR__"
+ } > "$params"
+
+ env DEVELOPER_DIR=dummy SDKROOT=mysdkroot \
+ "${WRAPPED_CLANG}" "@$params" \
+ >"$TEST_log" || fail "wrapped_clang failed";
+ expect_log "/usr/bin/xcrun clang first -rpath @loader_path sdkroot=mysdkroot developer_dir=dummy"
+}
+
+run_suite "Wrapped clang tests"
diff --git a/test/starlark_apple_binary.bzl b/test/starlark_apple_binary.bzl
new file mode 100644
index 0000000..f9437c5
--- /dev/null
+++ b/test/starlark_apple_binary.bzl
@@ -0,0 +1,84 @@
+"""Test rule for linking with bazel's builtin Apple logic"""
+
+load("//test:transitions.bzl", "apple_platform_split_transition")
+
+def _starlark_apple_binary_impl(ctx):
+ link_result = apple_common.link_multi_arch_binary(
+ ctx = ctx,
+ stamp = ctx.attr.stamp,
+ )
+ processed_binary = ctx.actions.declare_file(
+ "{}_lipobin".format(ctx.label.name),
+ )
+ lipo_inputs = [output.binary for output in link_result.outputs]
+ if len(lipo_inputs) > 1:
+ apple_env = {}
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ apple_env.update(apple_common.apple_host_system_env(xcode_config))
+ apple_env.update(
+ apple_common.target_apple_env(
+ xcode_config,
+ ctx.fragments.apple.single_arch_platform,
+ ),
+ )
+ args = ctx.actions.args()
+ args.add("-create")
+ args.add_all(lipo_inputs)
+ args.add("-output", processed_binary)
+ ctx.actions.run(
+ arguments = [args],
+ env = apple_env,
+ executable = "/usr/bin/lipo",
+ execution_requirements = xcode_config.execution_info(),
+ inputs = lipo_inputs,
+ outputs = [processed_binary],
+ )
+ else:
+ ctx.actions.symlink(
+ target_file = lipo_inputs[0],
+ output = processed_binary,
+ )
+ return [
+ DefaultInfo(files = depset([processed_binary])),
+ OutputGroupInfo(**link_result.output_groups),
+ link_result.debug_outputs_provider,
+ ]
+
+# All of the attributes below, except for `stamp`, are required as part of the
+# implied contract of `apple_common.link_multi_arch_binary` since it asks for
+# attributes directly from the rule context. As these requirements are changed
+# from implied attributes to function arguments, they can be removed.
+starlark_apple_binary = rule(
+ attrs = {
+ "_child_configuration_dummy": attr.label(
+ cfg = apple_platform_split_transition,
+ default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+ ),
+ "_xcode_config": attr.label(
+ default = configuration_field(
+ fragment = "apple",
+ name = "xcode_config_label",
+ ),
+ ),
+ "_xcrunwrapper": attr.label(
+ cfg = "exec",
+ default = Label("@bazel_tools//tools/objc:xcrunwrapper"),
+ executable = True,
+ ),
+ "binary_type": attr.string(default = "executable"),
+ "bundle_loader": attr.label(),
+ "deps": attr.label_list(
+ cfg = apple_platform_split_transition,
+ ),
+ "dylibs": attr.label_list(),
+ "linkopts": attr.string_list(),
+ "minimum_os_version": attr.string(mandatory = True),
+ "platform_type": attr.string(mandatory = True),
+ "stamp": attr.int(default = -1, values = [-1, 0, 1]),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ fragments = ["apple", "objc", "cpp"],
+ implementation = _starlark_apple_binary_impl,
+)
diff --git a/test/starlark_apple_static_library.bzl b/test/starlark_apple_static_library.bzl
new file mode 100644
index 0000000..85e28f0
--- /dev/null
+++ b/test/starlark_apple_static_library.bzl
@@ -0,0 +1,90 @@
+"""Test rule for static linking with bazel's builtin Apple logic"""
+
+load("//test:transitions.bzl", "apple_platform_split_transition")
+
+def _starlark_apple_static_library_impl(ctx):
+ if not hasattr(apple_common.platform_type, ctx.attr.platform_type):
+ fail('Unsupported platform type \"{}\"'.format(ctx.attr.platform_type))
+ link_result = apple_common.link_multi_arch_static_library(ctx = ctx)
+ processed_library = ctx.actions.declare_file(
+ "{}_lipo.a".format(ctx.label.name),
+ )
+ files_to_build = [processed_library]
+ runfiles = ctx.runfiles(
+ files = files_to_build,
+ collect_default = True,
+ collect_data = True,
+ )
+ lipo_inputs = [output.library for output in link_result.outputs]
+ if len(lipo_inputs) > 1:
+ apple_env = {}
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ apple_env.update(apple_common.apple_host_system_env(xcode_config))
+ apple_env.update(
+ apple_common.target_apple_env(
+ xcode_config,
+ ctx.fragments.apple.single_arch_platform,
+ ),
+ )
+ args = ctx.actions.args()
+ args.add("-create")
+ args.add_all(lipo_inputs)
+ args.add("-output", processed_library)
+ ctx.actions.run(
+ arguments = [args],
+ env = apple_env,
+ executable = "/usr/bin/lipo",
+ execution_requirements = xcode_config.execution_info(),
+ inputs = lipo_inputs,
+ outputs = [processed_library],
+ )
+ else:
+ ctx.actions.symlink(
+ target_file = lipo_inputs[0],
+ output = processed_library,
+ )
+ providers = [
+ DefaultInfo(files = depset(files_to_build), runfiles = runfiles),
+ link_result.output_groups,
+ ]
+ if getattr(link_result, "objc", None):
+ providers.append(link_result.objc)
+ return providers
+
+starlark_apple_static_library = rule(
+ _starlark_apple_static_library_impl,
+ attrs = {
+ "_child_configuration_dummy": attr.label(
+ cfg = apple_platform_split_transition,
+ default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+ ),
+ "_xcode_config": attr.label(
+ default = configuration_field(
+ fragment = "apple",
+ name = "xcode_config_label",
+ ),
+ ),
+ "_xcrunwrapper": attr.label(
+ executable = True,
+ cfg = "exec",
+ default = Label("@bazel_tools//tools/objc:xcrunwrapper"),
+ ),
+ "additional_linker_inputs": attr.label_list(
+ allow_files = True,
+ ),
+ "avoid_deps": attr.label_list(
+ cfg = apple_platform_split_transition,
+ default = [],
+ ),
+ "deps": attr.label_list(
+ cfg = apple_platform_split_transition,
+ ),
+ "linkopts": attr.string_list(),
+ "platform_type": attr.string(mandatory = True),
+ "minimum_os_version": attr.string(mandatory = True),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ fragments = ["apple", "objc", "cpp"],
+)
diff --git a/test/test_data/BUILD b/test/test_data/BUILD
new file mode 100644
index 0000000..26b9bb4
--- /dev/null
+++ b/test/test_data/BUILD
@@ -0,0 +1,141 @@
+load("//rules:universal_binary.bzl", "universal_binary")
+load("//test:starlark_apple_binary.bzl", "starlark_apple_binary")
+load("//test:starlark_apple_static_library.bzl", "starlark_apple_static_library")
+
+package(
+ default_testonly = 1,
+ default_visibility = ["//test:__subpackages__"],
+)
+
+TARGETS_UNDER_TEST_TAGS = [
+ "manual",
+ "notap",
+]
+
+cc_binary(
+ name = "cc_test_binary",
+ srcs = ["main.cc"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+)
+
+cc_library(
+ name = "cc_main",
+ srcs = ["main.cc"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+)
+
+universal_binary(
+ name = "multi_arch_cc_binary",
+ binary = ":cc_test_binary",
+ tags = TARGETS_UNDER_TEST_TAGS,
+)
+
+starlark_apple_binary(
+ name = "macos_binary",
+ minimum_os_version = "13.0",
+ platform_type = "macos",
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":cc_main"],
+)
+
+starlark_apple_binary(
+ name = "macos_binary_with_spaces",
+ minimum_os_version = "13.0",
+ platform_type = "macos",
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":objc_lib_with_spaces"],
+)
+
+cc_library(
+ name = "cc_lib",
+ srcs = ["cc_lib.cc"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+)
+
+objc_library(
+ name = "objc_lib",
+ srcs = ["objc_lib.m"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = ["cc_lib"],
+)
+
+objc_library(
+ name = "objc_lib_with_spaces",
+ srcs = ["objc lib with spaces.m"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+)
+
+starlark_apple_static_library(
+ name = "static_lib",
+ minimum_os_version = "15.0",
+ platform_type = "ios",
+ deps = [":objc_lib"],
+)
+
+objc_library(
+ name = "objc_main",
+ srcs = ["main.m"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = ["objc_lib"],
+)
+
+objc_library(
+ name = "objc_lib_with_unused_symbol",
+ srcs = ["objc_lib_with_unused_symbol.m"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+)
+
+starlark_apple_binary(
+ name = "ios_app_with_unused_symbol",
+ minimum_os_version = "13.0",
+ platform_type = "ios",
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":objc_lib_with_unused_symbol"],
+)
+
+config_setting(
+ name = "supports_visionos",
+ values = {"define": "supports_visionos=1"},
+)
+
+starlark_apple_binary(
+ name = "visionos_binary",
+ minimum_os_version = "1.0",
+ platform_type = "visionos",
+ tags = TARGETS_UNDER_TEST_TAGS,
+ target_compatible_with = select({
+ ":supports_visionos": [],
+ "//conditions:default": ["@platforms//:incompatible"],
+ }),
+ deps = [":objc_main"],
+)
+
+objc_library(
+ name = "watch_main",
+ srcs = ["watch_main.m"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":cc_lib"],
+)
+
+starlark_apple_binary(
+ name = "watch_binary",
+ minimum_os_version = "8.0",
+ platform_type = "watchos",
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":watch_main"],
+)
+
+objc_library(
+ name = "ios_main",
+ srcs = ["ios_main.m"],
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":cc_lib"],
+)
+
+starlark_apple_binary(
+ name = "ios_binary",
+ minimum_os_version = "15.0",
+ platform_type = "ios",
+ tags = TARGETS_UNDER_TEST_TAGS,
+ deps = [":ios_main"],
+)
diff --git a/test/test_data/cc_lib.cc b/test/test_data/cc_lib.cc
new file mode 100644
index 0000000..55c772a
--- /dev/null
+++ b/test/test_data/cc_lib.cc
@@ -0,0 +1,3 @@
+int cc_function() {
+ return 2;
+}
diff --git a/test/test_data/ios_main.m b/test/test_data/ios_main.m
new file mode 100644
index 0000000..a3d2c3a
--- /dev/null
+++ b/test/test_data/ios_main.m
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+int main(int argc, char *argv[]) {
+ return UIApplicationMain(argc, argv, nil, nil);
+}
diff --git a/test/test_data/main.cc b/test/test_data/main.cc
new file mode 100644
index 0000000..fad64bc
--- /dev/null
+++ b/test/test_data/main.cc
@@ -0,0 +1,32 @@
+// Copyright 2021 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.
+
+// C++ binary with basic hello world for universal Apple binary tests.
+
+#include <iostream>
+
+
+#if defined(__x86_64__)
+void function_for_x86_64() {
+ std::cout << "Compiled for x86_64" << std::endl;
+}
+#elif defined(__aarch64__)
+void function_for_arch64() {
+ std::cout << "Compiled for arm64" << std::endl;
+}
+#endif
+
+int main(int argc, char** argv) {
+ return 0;
+}
diff --git a/test/test_data/main.m b/test/test_data/main.m
new file mode 100644
index 0000000..9238e52
--- /dev/null
+++ b/test/test_data/main.m
@@ -0,0 +1,3 @@
+int main() {
+ return 42;
+}
diff --git a/test/test_data/objc lib with spaces.m b/test/test_data/objc lib with spaces.m
new file mode 100644
index 0000000..33c14ce
--- /dev/null
+++ b/test/test_data/objc lib with spaces.m
@@ -0,0 +1,3 @@
+int main() {
+ return 0;
+}
diff --git a/test/test_data/objc_lib.m b/test/test_data/objc_lib.m
new file mode 100644
index 0000000..2f11078
--- /dev/null
+++ b/test/test_data/objc_lib.m
@@ -0,0 +1,3 @@
+int objc_function() {
+ return 1;
+}
diff --git a/test/test_data/objc_lib_with_unused_symbol.m b/test/test_data/objc_lib_with_unused_symbol.m
new file mode 100644
index 0000000..c91cad8
--- /dev/null
+++ b/test/test_data/objc_lib_with_unused_symbol.m
@@ -0,0 +1,11 @@
+#import <UIKit/UIKit.h>
+
+// This is untentionally unused
+int addOne(int num);
+int addOne(int num) {
+ return num + 1;
+}
+
+int main(int argc, char *argv[]) {
+ return UIApplicationMain(argc, argv, nil, nil);
+}
diff --git a/test/test_data/watch_main.m b/test/test_data/watch_main.m
new file mode 100644
index 0000000..7477162
--- /dev/null
+++ b/test/test_data/watch_main.m
@@ -0,0 +1,9 @@
+#import <WatchKit/WatchKit.h>
+
+// Note that WKExtensionDelegate is only available in Watch SDK.
+@interface TestInterfaceMain : NSObject <WKExtensionDelegate>
+@end
+
+int main() {
+ return 0;
+}
diff --git a/test/transitions.bzl b/test/transitions.bzl
new file mode 100644
index 0000000..4d4b382
--- /dev/null
+++ b/test/transitions.bzl
@@ -0,0 +1,214 @@
+"""Dummy transitions for testing basic behavior"""
+
+load("//configs:platforms.bzl", "CPU_TO_DEFAULT_PLATFORM_NAME")
+
+_PLATFORM_TYPE_TO_CPUS_FLAG = {
+ "ios": "//command_line_option:ios_multi_cpus",
+ "macos": "//command_line_option:macos_cpus",
+ "tvos": "//command_line_option:tvos_cpus",
+ "visionos": "//command_line_option:visionos_cpus",
+ "watchos": "//command_line_option:watchos_cpus",
+}
+
+_PLATFORM_TYPE_TO_DEFAULT_ARCH = {
+ "ios": "x86_64",
+ "macos": "x86_64",
+ "tvos": "x86_64",
+ "visionos": "x86_64",
+ "watchos": "x86_64",
+}
+
+_CPU_TO_DEFAULT_PLATFORM_FLAG = {
+ cpu: "//platforms:{}_platform".format(platform_name)
+ for cpu, platform_name in CPU_TO_DEFAULT_PLATFORM_NAME.items()
+}
+
+_supports_visionos = hasattr(apple_common.platform_type, "visionos")
+
+def _cpu_string(*, environment_arch, platform_type, settings = {}):
+ if platform_type == "ios":
+ if environment_arch:
+ return "ios_{}".format(environment_arch)
+ ios_cpus = settings["//command_line_option:ios_multi_cpus"]
+ if ios_cpus:
+ return "ios_{}".format(ios_cpus[0])
+ cpu_value = settings["//command_line_option:cpu"]
+ if cpu_value.startswith("ios_"):
+ return cpu_value
+ if cpu_value == "darwin_arm64":
+ return "ios_sim_arm64"
+ return "ios_x86_64"
+ if platform_type == "macos":
+ if environment_arch:
+ return "darwin_{}".format(environment_arch)
+ macos_cpus = settings["//command_line_option:macos_cpus"]
+ if macos_cpus:
+ return "darwin_{}".format(macos_cpus[0])
+ cpu_value = settings["//command_line_option:cpu"]
+ if cpu_value.startswith("darwin_"):
+ return cpu_value
+ return "darwin_x86_64"
+ if platform_type == "tvos":
+ if environment_arch:
+ return "tvos_{}".format(environment_arch)
+ tvos_cpus = settings["//command_line_option:tvos_cpus"]
+ if tvos_cpus:
+ return "tvos_{}".format(tvos_cpus[0])
+ return "tvos_x86_64"
+ if platform_type == "watchos":
+ if environment_arch:
+ return "watchos_{}".format(environment_arch)
+ watchos_cpus = settings["//command_line_option:watchos_cpus"]
+ if watchos_cpus:
+ return "watchos_{}".format(watchos_cpus[0])
+ return "watchos_x86_64"
+ if platform_type == "visionos":
+ if environment_arch:
+ return "visionos_{}".format(environment_arch)
+ visionos_cpus = settings["//command_line_option:visionos_cpus"]
+ if visionos_cpus:
+ return "visionos_{}".format(visionos_cpus[0])
+ return "visionos_x86_64"
+
+ fail("ERROR: Unknown platform type: {}".format(platform_type))
+
+def _min_os_version_or_none(*, minimum_os_version, platform, platform_type):
+ if platform_type == platform:
+ return minimum_os_version
+ return None
+
+def _command_line_options(*, apple_platforms = [], environment_arch = None, minimum_os_version, platform_type, settings):
+ cpu = _cpu_string(
+ environment_arch = environment_arch,
+ platform_type = platform_type,
+ settings = settings,
+ )
+
+ output_dictionary = {
+ "//command_line_option:apple configuration distinguisher": "applebin_" + platform_type,
+ "//command_line_option:apple_platform_type": platform_type,
+ "//command_line_option:apple_platforms": apple_platforms,
+ # `apple_split_cpu` is used by the Bazel Apple configuration distinguisher to distinguish
+ # architecture and environment, therefore we set `environment_arch` when it is available.
+ "//command_line_option:apple_split_cpu": environment_arch if environment_arch else "",
+ "//command_line_option:compiler": None,
+ "//command_line_option:cpu": cpu,
+ "//command_line_option:crosstool_top": (
+ settings["//command_line_option:apple_crosstool_top"]
+ ),
+ "//command_line_option:fission": [],
+ "//command_line_option:grte_top": None,
+ "//command_line_option:platforms": (
+ [apple_platforms[0]] if apple_platforms else [settings[_CPU_TO_DEFAULT_PLATFORM_FLAG[cpu]]]
+ ),
+ "//command_line_option:ios_minimum_os": _min_os_version_or_none(
+ minimum_os_version = minimum_os_version,
+ platform = "ios",
+ platform_type = platform_type,
+ ),
+ "//command_line_option:macos_minimum_os": _min_os_version_or_none(
+ minimum_os_version = minimum_os_version,
+ platform = "macos",
+ platform_type = platform_type,
+ ),
+ "//command_line_option:tvos_minimum_os": _min_os_version_or_none(
+ minimum_os_version = minimum_os_version,
+ platform = "tvos",
+ platform_type = platform_type,
+ ),
+ "//command_line_option:watchos_minimum_os": _min_os_version_or_none(
+ minimum_os_version = minimum_os_version,
+ platform = "watchos",
+ platform_type = platform_type,
+ ),
+ }
+
+ return output_dictionary
+
+_apple_platform_transition_inputs = [
+ "//command_line_option:apple_crosstool_top",
+ "//command_line_option:apple_platforms",
+ "//command_line_option:cpu",
+ "//command_line_option:incompatible_enable_apple_toolchain_resolution",
+ "//command_line_option:ios_multi_cpus",
+ "//command_line_option:macos_cpus",
+ "//command_line_option:platforms",
+ "//command_line_option:tvos_cpus",
+ "//command_line_option:watchos_cpus",
+] + _CPU_TO_DEFAULT_PLATFORM_FLAG.values() + (
+ ["//command_line_option:visionos_cpus"] if _supports_visionos else []
+)
+
+_apple_rule_base_transition_outputs = [
+ "//command_line_option:apple configuration distinguisher",
+ "//command_line_option:apple_platform_type",
+ "//command_line_option:apple_platforms",
+ "//command_line_option:apple_split_cpu",
+ "//command_line_option:compiler",
+ "//command_line_option:cpu",
+ "//command_line_option:crosstool_top",
+ "//command_line_option:fission",
+ "//command_line_option:grte_top",
+ "//command_line_option:ios_minimum_os",
+ "//command_line_option:macos_minimum_os",
+ "//command_line_option:platforms",
+ "//command_line_option:tvos_minimum_os",
+ "//command_line_option:watchos_minimum_os",
+]
+
+def _apple_platform_split_transition_impl(settings, attr):
+ output_dictionary = {}
+ if settings["//command_line_option:incompatible_enable_apple_toolchain_resolution"]:
+ platforms = (
+ settings["//command_line_option:apple_platforms"] or
+ settings["//command_line_option:platforms"]
+ )
+ # Currently there is no "default" platform for Apple-based platforms. If necessary, a
+ # default platform could be generated for the rule's underlying platform_type, but for now
+ # we work with the assumption that all users of the rules should set an appropriate set of
+ # platforms when building Apple targets with `apple_platforms`.
+
+ for index, platform in enumerate(platforms):
+ # Create a new, reordered list so that the platform we need to resolve is always first,
+ # and the other platforms will follow.
+ apple_platforms = list(platforms)
+ platform_to_resolve = apple_platforms.pop(index)
+ apple_platforms.insert(0, platform_to_resolve)
+
+ if str(platform) not in output_dictionary:
+ output_dictionary[str(platform)] = _command_line_options(
+ apple_platforms = apple_platforms,
+ minimum_os_version = attr.minimum_os_version,
+ platform_type = attr.platform_type,
+ settings = settings,
+ )
+
+ else:
+ platform_type = attr.platform_type
+ environment_archs = settings[_PLATFORM_TYPE_TO_CPUS_FLAG[platform_type]]
+ if not environment_archs:
+ environment_archs = [_PLATFORM_TYPE_TO_DEFAULT_ARCH[platform_type]]
+ for environment_arch in environment_archs:
+ found_cpu = _cpu_string(
+ environment_arch = environment_arch,
+ platform_type = platform_type,
+ settings = settings,
+ )
+ if found_cpu in output_dictionary:
+ continue
+
+ minimum_os_version = attr.minimum_os_version
+ output_dictionary[found_cpu] = _command_line_options(
+ environment_arch = environment_arch,
+ minimum_os_version = minimum_os_version,
+ platform_type = platform_type,
+ settings = settings,
+ )
+
+ return output_dictionary
+
+apple_platform_split_transition = transition(
+ implementation = _apple_platform_split_transition_impl,
+ inputs = _apple_platform_transition_inputs,
+ outputs = _apple_rule_base_transition_outputs,
+)
diff --git a/test/underlying_lib.h b/test/underlying_lib.h
new file mode 100644
index 0000000..cedf546
--- /dev/null
+++ b/test/underlying_lib.h
@@ -0,0 +1 @@
+int from_cc_library = 5;
diff --git a/test/universal_binary_test.bzl b/test/universal_binary_test.bzl
new file mode 100644
index 0000000..611130c
--- /dev/null
+++ b/test/universal_binary_test.bzl
@@ -0,0 +1,114 @@
+# Copyright 2021 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.
+
+"""Definition of a test rule to test universal_binary."""
+
+# Template for the test script used to validate that the action outputs contain
+# the expected values.
+_TEST_SCRIPT_CONTENTS = """\
+#!/bin/bash
+
+set -euo pipefail
+
+newline=$'\n'
+
+SYMBOLS=(
+ {binary_contains_symbols}
+)
+
+if [[ ! -f "{file}" ]]; then
+ echo "{file} doesn't exist"
+ exit 1
+fi
+
+actual_symbols=$(nm -Uj -arch x86_64 -arch arm64 {file})
+for symbol in "${{SYMBOLS[@]}}"; do
+ echo "$actual_symbols" | grep -Fxq "$symbol" || \
+ (echo "In file: {file}"; \
+ echo "Expected symbol \"$symbol\" was not found. The " \
+ "symbols in the binary were:$newline$actual_symbols"; \
+ exit 1)
+done
+
+echo "Test passed"
+
+exit 0
+"""
+
+def _universal_binary_test_transition(_settings, attr):
+ """Implementation of the universal_binary_test_transition transition."""
+
+ return {
+ "//command_line_option:cpu": attr.cpu,
+ }
+
+universal_binary_test_transition = transition(
+ implementation = _universal_binary_test_transition,
+ inputs = [],
+ outputs = ["//command_line_option:cpu"],
+)
+
+def _universal_binary_test_impl(ctx):
+ """Implementation of the universal_binary_test rule."""
+
+ target_under_test = ctx.split_attr.target_under_test.values()[0]
+ output_to_verify = target_under_test[DefaultInfo].files.to_list()[0]
+
+ test_script = ctx.actions.declare_file("{}_test_script".format(ctx.label.name))
+ test_script_contents = _TEST_SCRIPT_CONTENTS.format(
+ binary_contains_symbols = "\n ".join([
+ x
+ for x in ctx.attr.binary_contains_symbols
+ ]),
+ file = output_to_verify.short_path,
+ )
+
+ ctx.actions.write(
+ content = test_script_contents,
+ is_executable = True,
+ output = test_script,
+ )
+
+ return [
+ DefaultInfo(
+ executable = test_script,
+ runfiles = ctx.runfiles(files = [output_to_verify]),
+ ),
+ ]
+
+universal_binary_test = rule(
+ attrs = {
+ "binary_contains_symbols": attr.string_list(
+ doc = """\
+A list of symbols that should appear in the binary file specified in
+`target_under_test`.""",
+ mandatory = True,
+ ),
+ "cpu": attr.string(
+ doc = "CPU to use for test under target.",
+ ),
+ "target_under_test": attr.label(
+ mandatory = True,
+ providers = [DefaultInfo],
+ doc = "The binary whose contents are to be verified.",
+ cfg = universal_binary_test_transition,
+ ),
+ "_allowlist_function_transition": attr.label(
+ default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+ ),
+ },
+ fragments = ["apple"],
+ implementation = _universal_binary_test_impl,
+ test = True,
+)
diff --git a/test/verify_archive_timestamps.sh b/test/verify_archive_timestamps.sh
new file mode 100755
index 0000000..b6d26fa
--- /dev/null
+++ b/test/verify_archive_timestamps.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+readonly binary="%{binary}s"
+
+if [[ "$binary" != *_lipo.a ]]; then
+ echo "$binary: error: binary name does not end in _lipo.a" >&2
+ exit 1
+fi
+
+ar -tv "$binary"
+
+# Based on timezones, ar -tv may show the timestamp of the contents as either
+# Dec 31 1969 or Jan 1 1970 -- either is fine.
+# We would use 'date' here, but the format is slightly different (Jan 1 vs.
+# Jan 01).
+if ! ar -tv "$binary" | grep "objc_lib" | grep "Dec 31" | grep "1969" \
+ && ! ar -tv "$binary" | grep "objc_lib" | grep "Jan 1" | grep "1970"
+then
+ echo "error: timestamp of contents of archive file should be zero" >&2
+ exit 1
+fi
diff --git a/test/verify_stripped_symbols.sh b/test/verify_stripped_symbols.sh
new file mode 100755
index 0000000..2049535
--- /dev/null
+++ b/test/verify_stripped_symbols.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+readonly binary="%{binary}s"
+
+! nm "$binary" | grep addOne \
+ || (echo "should fail to find symbol addOne" >&2 && exit 1)
diff --git a/test/verify_unused_symbol_exists.sh b/test/verify_unused_symbol_exists.sh
new file mode 100755
index 0000000..92928d2
--- /dev/null
+++ b/test/verify_unused_symbol_exists.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+readonly binary="%{binary}s"
+
+nm "$binary" | grep addOne \
+ || (echo "should find symbol addOne" >&2 && exit 1)
diff --git a/test/xcode_support_test.bzl b/test/xcode_support_test.bzl
new file mode 100644
index 0000000..e10a949
--- /dev/null
+++ b/test/xcode_support_test.bzl
@@ -0,0 +1,59 @@
+# Copyright 2019 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.
+"""Definition of a test rule to test xcode_support."""
+
+load(
+ "//lib:apple_support.bzl",
+ "apple_support",
+)
+load(
+ "//lib:xcode_support.bzl",
+ "xcode_support",
+)
+
+# Template for the test script used to validate that the action outputs contain the expected
+# values.
+_TEST_SCRIPT_CONTENTS = """
+#!/bin/bash
+
+set -eu
+
+if [[ "{past_version_is_true}" != "True" ]]; then
+ echo "FAILURE: 'past_version_is_true' should be True, but it's {past_version_is_true}."
+ exit 1
+fi
+
+if [[ "{future_version_is_false}" != "False" ]]; then
+ echo "FAILURE: 'future_version_is_false' should be False, but it's {future_version_is_false}."
+ exit 1
+fi
+"""
+
+def _xcode_support_test_impl(ctx):
+ """Implementation of the xcode_support_test rule."""
+
+ test_script = ctx.actions.declare_file("{}_test_script".format(ctx.label.name))
+ xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
+ ctx.actions.write(test_script, _TEST_SCRIPT_CONTENTS.format(
+ past_version_is_true = str(xcode_support.is_xcode_at_least_version(xcode_config, "1.0")),
+ future_version_is_false = str(xcode_support.is_xcode_at_least_version(xcode_config, "999")),
+ ), is_executable = True)
+
+ return [DefaultInfo(executable = test_script)]
+
+xcode_support_test = rule(
+ implementation = _xcode_support_test_impl,
+ attrs = apple_support.action_required_attrs(),
+ test = True,
+)
diff --git a/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..ccd26e1
--- /dev/null
+++ b/tools/BUILD
@@ -0,0 +1,17 @@
+licenses(["notice"])
+
+# Additional files, such as tools, that should be present in a test's runfiles
+# during coverage collection.
+filegroup(
+ name = "coverage_support",
+ # Public so other rules can add the dependencies via this group.
+ visibility = ["//visibility:public"],
+)
+
+# Consumed by bazel tests.
+filegroup(
+ name = "for_bazel_tests",
+ testonly = 1,
+ srcs = glob(["**"]),
+ visibility = ["//:__pkg__"],
+)