diff --git a/toolchain/BUILD.toolchain.tpl b/toolchain/BUILD.toolchain.tpl index 9a7a75b0..2d337000 100644 --- a/toolchain/BUILD.toolchain.tpl +++ b/toolchain/BUILD.toolchain.tpl @@ -35,6 +35,7 @@ filegroup( name = "internal-use-wrapped-tools", srcs = [ "%{wrapper_bin_prefix}cc_wrapper.sh", + "%{wrapper_bin_prefix}cc_wrapper_inner.sh", ], visibility = ["//visibility:private"], ) diff --git a/toolchain/cc_wrapper.sh.tpl b/toolchain/cc_wrapper.sh.tpl index f08eef40..727e437a 100644 --- a/toolchain/cc_wrapper.sh.tpl +++ b/toolchain/cc_wrapper.sh.tpl @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # # Copyright 2021 The Bazel Authors. All rights reserved. # @@ -14,137 +14,32 @@ # See the License for the specific language governing permissions and # limitations under the License. -# shellcheck disable=SC1083 +SCRIPT_DIR=$(dirname "$0") -set -euo pipefail +# Search for `bash` on the system, and then execute cc_wrapper_inner.sh with +# it. -CLEANUP_FILES=() - -function cleanup() { - if [[ ${#CLEANUP_FILES[@]} -gt 0 ]]; then - rm -f "${CLEANUP_FILES[@]}" - fi -} - -trap cleanup EXIT - -# See note in toolchain/internal/configure.bzl where we define -# `wrapper_bin_prefix` for why this wrapper is needed. - -# this script is located at either -# - /external//bin/cc_wrapper.sh -# - //bin/cc_wrapper.sh -# The clang is located at -# - /external//bin/clang -# - //bin/clang -# -# In both cases, getting to clang can be done via -# Finding the current dir of this script, -# - /external//bin/ -# - //bin/ -# going back 2 directories -# - /external -# - -# -# Going into %{toolchain_path_prefix} without the `external/` prefix + `bin/clang` -# - -dirname_shim() { - local path="$1" - - # Remove trailing slashes - path="${path%/}" - - # If there's no slash, return "." - if [[ "${path}" != */* ]]; then - echo "." - return - fi - - # Remove the last component after the final slash - path="${path%/*}" - - # If it becomes empty, it means root "/" - echo "${path:-/}" -} - -if [[ "${BASH_SOURCE[0]}" == "/"* ]]; then - bash_source_abs="$(realpath "${BASH_SOURCE[0]}")" - pwd_abs="$(realpath ".")" - bash_source_rel=${bash_source_abs#"${pwd_abs}/"} -else - bash_source_rel="${BASH_SOURCE[0]}" +# Attempt #1: /bin/bash -- present on FHS-compliant systems, but notably absent +# on others, including NixOS. +if /bin/bash true; then + /bin/bash "${SCRIPT_DIR}"/cc_wrapper_inner.sh "$@" + exit $? fi -script_dir=$(dirname_shim "${bash_source_rel}") -toolchain_path_prefix="%{toolchain_path_prefix}" -# Sometimes this path may be an absolute path in which case we dont do anything because -# This is using the host toolchain to build. -if [[ ${toolchain_path_prefix} != /* ]]; then - # shellcheck disable=SC2312 - toolchain_path_prefix="$(dirname_shim "$(dirname_shim "${script_dir}")")/${toolchain_path_prefix#external/}" +# Attempt #2: /usr/bin/env bash -- /usr/bin/env is required by POSIX, but some +# callers to the LLVM toolchain, such as rules_rust, clear $PATH and leave +# nothing for /usr/bin/env to search for. +# if /usr/bin/env bash true; then +# /usr/bin/env bash "${SCRIPT_DIR}"/cc_wrapper_inner.sh "$@" +# exit $? +# fi + +# Attempt #3: Try `command -v`. +if command -v bash; then + CMD=$(command -v bash) + "${CMD}" "${SCRIPT_DIR}"/cc_wrapper_inner.sh "$@" + exit $? fi -if [[ ! -f ${toolchain_path_prefix}bin/clang ]]; then - echo >&2 "ERROR: could not find clang; PWD=\"${PWD}\"; PATH=\"${PATH}\"; toolchain_path_prefix=${toolchain_path_prefix}." - exit 5 -fi - -OUTPUT= - -function parse_option() { - local -r opt="$1" - if [[ "${OUTPUT}" = "1" ]]; then - OUTPUT=${opt} - elif [[ "${opt}" = "-o" ]]; then - # output is coming - OUTPUT=1 - fi -} - -function sanitize_option() { - local -r opt=$1 - if [[ ${opt} == */cc_wrapper.sh ]]; then - printf "%s" "${toolchain_path_prefix}bin/clang" - elif [[ ${opt} =~ ^-fsanitize-(ignore|black)list=[^/] ]] && [[ ${script_dir} == /* ]]; then - # shellcheck disable=SC2206 - parts=(${opt/=/ }) # Split flag name and value into array. - # shellcheck disable=SC2312 - printf "%s" "${parts[0]}=$(dirname_shim "$(dirname_shim "$(dirname_shim "${script_dir}")")")/${parts[1]}" - else - printf "%s" "${opt}" - fi -} - -cmd=() -for ((i = 0; i <= $#; i++)); do - if [[ ${!i} == @* && -r "${i:1}" ]]; then - # Create a new, sanitized file. - tmpfile=$(mktemp) - CLEANUP_FILES+=("${tmpfile}") - while IFS= read -r opt; do - opt="$( - set -e - sanitize_option "${opt}" - )" - parse_option "${opt}" - echo "${opt}" >>"${tmpfile}" - done <"${!i:1}" - cmd+=("@${tmpfile}") - else - opt="$( - set -e - sanitize_option "${!i}" - )" - parse_option "${opt}" - cmd+=("${opt}") - fi -done - -# Call the C++ compiler. -"${cmd[@]}" - -# Generate an empty file if header processing succeeded. -if [[ "${OUTPUT}" == *.h.processed ]]; then - echo -n >"${OUTPUT}" -fi +echo >&2 'Failed to find bash at /bin/bash or in PATH.' +exit 1 diff --git a/toolchain/cc_wrapper_inner.sh.tpl b/toolchain/cc_wrapper_inner.sh.tpl new file mode 100644 index 00000000..3038b5bc --- /dev/null +++ b/toolchain/cc_wrapper_inner.sh.tpl @@ -0,0 +1,148 @@ +# 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. + +# shellcheck disable=SC1083 + +set -euo pipefail + +CLEANUP_FILES=() + +function cleanup() { + if [[ ${#CLEANUP_FILES[@]} -gt 0 ]]; then + rm -f "${CLEANUP_FILES[@]}" + fi +} + +trap cleanup EXIT + +# See note in toolchain/internal/configure.bzl where we define +# `wrapper_bin_prefix` for why this wrapper is needed. + +# this script is located at either +# - /external//bin/cc_wrapper_innerr.sh +# - //bin/cc_wrapper_inner.sh +# The clang is located at +# - /external//bin/clang +# - //bin/clang +# +# In both cases, getting to clang can be done via +# Finding the current dir of this script, +# - /external//bin/ +# - //bin/ +# going back 2 directories +# - /external +# - +# +# Going into %{toolchain_path_prefix} without the `external/` prefix + `bin/clang` +# + +dirname_shim() { + local path="$1" + + # Remove trailing slashes + path="${path%/}" + + # If there's no slash, return "." + if [[ "${path}" != */* ]]; then + echo "." + return + fi + + # Remove the last component after the final slash + path="${path%/*}" + + # If it becomes empty, it means root "/" + echo "${path:-/}" +} + +if [[ "${BASH_SOURCE[0]}" == "/"* ]]; then + bash_source_abs="$(realpath "${BASH_SOURCE[0]}")" + pwd_abs="$(realpath ".")" + bash_source_rel=${bash_source_abs#"${pwd_abs}/"} +else + bash_source_rel="${BASH_SOURCE[0]}" +fi +script_dir=$(dirname_shim "${bash_source_rel}") +toolchain_path_prefix="%{toolchain_path_prefix}" + +# Sometimes this path may be an absolute path in which case we dont do anything because +# This is using the host toolchain to build. +if [[ ${toolchain_path_prefix} != /* ]]; then + # shellcheck disable=SC2312 + toolchain_path_prefix="$(dirname_shim "$(dirname_shim "${script_dir}")")/${toolchain_path_prefix#external/}" +fi + +if [[ ! -f ${toolchain_path_prefix}bin/clang ]]; then + echo >&2 "ERROR: could not find clang; PWD=\"${PWD}\"; PATH=\"${PATH}\"; toolchain_path_prefix=${toolchain_path_prefix}." + exit 5 +fi + +OUTPUT= + +function parse_option() { + local -r opt="$1" + if [[ "${OUTPUT}" = "1" ]]; then + OUTPUT=${opt} + elif [[ "${opt}" = "-o" ]]; then + # output is coming + OUTPUT=1 + fi +} + +function sanitize_option() { + local -r opt=$1 + if [[ ${opt} == */cc_wrapper.sh ]]; then + printf "%s" "${toolchain_path_prefix}bin/clang" + elif [[ ${opt} =~ ^-fsanitize-(ignore|black)list=[^/] ]] && [[ ${script_dir} == /* ]]; then + # shellcheck disable=SC2206 + parts=(${opt/=/ }) # Split flag name and value into array. + # shellcheck disable=SC2312 + printf "%s" "${parts[0]}=$(dirname_shim "$(dirname_shim "$(dirname_shim "${script_dir}")")")/${parts[1]}" + else + printf "%s" "${opt}" + fi +} + +cmd=() +for ((i = 0; i <= $#; i++)); do + if [[ ${!i} == @* && -r "${i:1}" ]]; then + # Create a new, sanitized file. + tmpfile=$(mktemp) + CLEANUP_FILES+=("${tmpfile}") + while IFS= read -r opt; do + opt="$( + set -e + sanitize_option "${opt}" + )" + parse_option "${opt}" + echo "${opt}" >>"${tmpfile}" + done <"${!i:1}" + cmd+=("@${tmpfile}") + else + opt="$( + set -e + sanitize_option "${!i}" + )" + parse_option "${opt}" + cmd+=("${opt}") + fi +done + +# Call the C++ compiler. +"${cmd[@]}" + +# Generate an empty file if header processing succeeded. +if [[ "${OUTPUT}" == *.h.processed ]]; then + echo -n >"${OUTPUT}" +fi diff --git a/toolchain/internal/configure.bzl b/toolchain/internal/configure.bzl index fba75541..99d887bc 100644 --- a/toolchain/internal/configure.bzl +++ b/toolchain/internal/configure.bzl @@ -236,19 +236,37 @@ def llvm_config_impl(rctx): }, ) - # CC wrapper script; see comments near the definition of `wrapper_bin_prefix`. + # CC wrapper scripts; see comments near the definition of `wrapper_bin_prefix`. + rctx.template( + "bin/cc_wrapper.sh", + rctx.attr._cc_wrapper_sh_tpl, + { + "%{toolchain_path_prefix}": llvm_dist_path_prefix, + }, + ) if os == "darwin": - cc_wrapper_tpl = rctx.attr._darwin_cc_wrapper_sh_tpl + cc_wrapper_inner_tpl = rctx.attr._darwin_cc_wrapper_inner_sh_tpl else: - cc_wrapper_tpl = rctx.attr._cc_wrapper_sh_tpl + cc_wrapper_inner_tpl = rctx.attr._cc_wrapper_inner_sh_tpl rctx.template( - "bin/cc_wrapper.sh", - cc_wrapper_tpl, + "bin/cc_wrapper_inner.sh", + cc_wrapper_inner_tpl, { "%{toolchain_path_prefix}": llvm_dist_path_prefix, }, ) + # Inner CC wrapper script (redirect used for shell compatibility on Linux + # platforms). + if os != "darwin": + rctx.template( + "bin/cc_wrapper_inner.sh", + rctx.attr._cc_wrapper_inner_sh_tpl, + { + "%{toolchain_path_prefix}": llvm_dist_path_prefix, + }, + ) + if hasattr(rctx, "repo_metadata"): return rctx.repo_metadata(reproducible = True) else: diff --git a/toolchain/internal/repo.bzl b/toolchain/internal/repo.bzl index a0da2bda..0ab82ed2 100644 --- a/toolchain/internal/repo.bzl +++ b/toolchain/internal/repo.bzl @@ -408,12 +408,15 @@ llvm_config_attrs.update({ "_build_toolchain_tpl": attr.label( default = "//toolchain:BUILD.toolchain.tpl", ), - "_darwin_cc_wrapper_sh_tpl": attr.label( - default = "//toolchain:osx_cc_wrapper.sh.tpl", - ), "_cc_wrapper_sh_tpl": attr.label( default = "//toolchain:cc_wrapper.sh.tpl", ), + "_darwin_cc_wrapper_inner_sh_tpl": attr.label( + default = "//toolchain:osx_cc_wrapper_inner.sh.tpl", + ), + "_cc_wrapper_inner_sh_tpl": attr.label( + default = "//toolchain:cc_wrapper_inner.sh.tpl", + ), }) def llvm_repo_impl(rctx): diff --git a/toolchain/osx_cc_wrapper.sh.tpl b/toolchain/osx_cc_wrapper_inner.sh.tpl similarity index 98% rename from toolchain/osx_cc_wrapper.sh.tpl rename to toolchain/osx_cc_wrapper_inner.sh.tpl index 293f007e..f84144f2 100755 --- a/toolchain/osx_cc_wrapper.sh.tpl +++ b/toolchain/osx_cc_wrapper_inner.sh.tpl @@ -1,5 +1,3 @@ -#!/bin/bash -# # Copyright 2015 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,8 +65,8 @@ function parse_option() { # `wrapper_bin_prefix` for why this wrapper is needed. # this script is located at either -# - /external//bin/cc_wrapper.sh -# - //bin/cc_wrapper.sh +# - /external//bin/cc_wrapper_inner.sh +# - //bin/cc_wrapper_inner.sh # The clang is located at # - /external//bin/clang # - //bin/clang