diff options
author | kaipi <kaipi@google.com> | 2018-02-06 08:56:05 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-02-06 08:58:12 -0800 |
commit | 149e95b37954684678830038b19a6cf323ae308e (patch) | |
tree | bbc1a786723f5168474d32fd52a1dbf0e3e557c1 /tools/osx | |
parent | f0544d71599039b50151ac47d060b680288379ab (diff) |
Converts wrapped_clang from a bash script to a C++ source file which is compiled as part of bazel's repository bootstrap. This should make crosstool's clang invocations faster. An added benefit of this is that wrapped_clang.cc supports the "DSYM_HINT" flags specified through the CROSSTOOL, so with this change, apple_binary gets support for the --apple_generate_dsym flag.
The dSYM generation issue has been flagged multiple times:
https://github.com/bazelbuild/bazel/issues/4312
https://github.com/bazelbuild/bazel/issues/3940
https://github.com/bazelbuild/bazel/issues/3372
RELNOTES: apple_binary can now generate dSYM outputs with the --apple_generate_dsym=true flag.
PiperOrigin-RevId: 184688215
Diffstat (limited to 'tools/osx')
-rw-r--r-- | tools/osx/crosstool/CROSSTOOL.tpl | 230 | ||||
-rw-r--r-- | tools/osx/crosstool/wrapped_clang.cc | 268 |
2 files changed, 498 insertions, 0 deletions
diff --git a/tools/osx/crosstool/CROSSTOOL.tpl b/tools/osx/crosstool/CROSSTOOL.tpl index 79861cd321..af544e9dee 100644 --- a/tools/osx/crosstool/CROSSTOOL.tpl +++ b/tools/osx/crosstool/CROSSTOOL.tpl @@ -203,6 +203,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -1806,6 +1829,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -3425,6 +3471,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -5047,6 +5116,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -6696,6 +6788,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -8315,6 +8430,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -9922,6 +10060,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -11532,6 +11693,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -13169,6 +13353,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" @@ -14779,6 +14986,29 @@ toolchain { } } feature { + name: "generate_dsym_file" + flag_set { + action: "c-compile" + action: "c++-compile" + action: "objc-compile" + action: "objc++-compile" + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "-g" + } + } + flag_set { + action: "objc-executable" + action: "objc++-executable" + flag_group { + flag: "DSYM_HINT_LINKED_BINARY=%{linked_binary}" + flag: "DSYM_HINT_DSYM_PATH=%{dsym_path}" + flag: "DSYM_HINT_DSYM_BUNDLE_ZIP=%{dsym_bundle_zip}" + } + } + } + feature { name: "contains_objc_source" flag_set { action: "c++-link-interface-dynamic-library" diff --git a/tools/osx/crosstool/wrapped_clang.cc b/tools/osx/crosstool/wrapped_clang.cc new file mode 100644 index 0000000000..0f92db9b3c --- /dev/null +++ b/tools/osx/crosstool/wrapped_clang.cc @@ -0,0 +1,268 @@ +// 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 directory. +// "DSYM_HINT_DSYM_BUNDLE_ZIP": Workspace-relative path to dSYM zip. +// +// Likewise, this wrapper also contains a workaround for a bug in ld that causes +// flaky builds when using Bitcode symbol maps. ld allows the +// -bitcode_symbol_map to be either a directory (into which the file will be +// written) or a file, but the return value of the call to ::stat is never +// checked so examining the S_ISDIR bit of the struct afterwards returns +// true/false randomly depending on what data happened to be in memory at the +// time it was called: +// https://github.com/michaelweiser/ld64/blob/9c3700b64ed03e2d55ba094176bf6a172bf2bc6b/src/ld/Options.cpp#L3261 +// To address this, we prepend a special "BITCODE_TOUCH_SYMBOL_MAP=" flag to the +// symbol map filename and touch it before passing it along to clang, forcing +// the file to exist. + +#include <libgen.h> +#include <spawn.h> +#include <sys/wait.h> +#include <unistd.h> +#include <cerrno> +#include <climits> +#include <cstdio> +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <map> +#include <memory> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +extern char **environ; + +namespace { + +// 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; +} + +// 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; +} + +// Turn our current process into a new process. Avoids fork overhead. +// Never returns. +void ExecProcess(const std::vector<std::string> &args) { + std::vector<const char *> exec_argv = ConvertToCArgs(args); + execv(args[0].c_str(), const_cast<char **>(exec_argv.data())); + std::cerr << "Error executing child process.'" << args[0] << "'. " + << strerror(errno) << "\n"; + abort(); +} + +// Spawns a subprocess for given arguments args. The first argument is used +// for the executable path. +void 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(), NULL, NULL, + 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"; + abort(); + } + if (WEXITSTATUS(status) != 0) { + std::cerr << "Error in child process '" << args[0] << "'. " + << WEXITSTATUS(status) << "\n"; + abort(); + } + } else { + std::cerr << "Error forking process '" << args[0] << "'. " + << strerror(status) << "\n"; + abort(); + } +} + +// Splits txt using whitespace delimiter, pushing each substring into strs. +void SplitAndAdd(const std::string &txt, std::vector<std::string> &strs) { + for (std::istringstream stream(txt); !stream.eof();) { + std::string substring; + stream >> substring; + if (!substring.empty()) { + strs.push_back(substring); + } + } +} + +// 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"; + abort(); + } + return env_value; +} + +} // 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"; + abort(); + } + + std::string developer_dir = GetMandatoryEnvVar("DEVELOPER_DIR"); + std::string sdk_root = GetMandatoryEnvVar("SDKROOT"); + + std::vector<std::string> processed_args = {"/usr/bin/xcrun", tool_name}; + + std::string linked_binary, dsym_path, dsym_bundle_zip, bitcode_symbol_map; + for (int i = 1; i < argc; i++) { + std::string arg(argv[i]); + + if (SetArgIfFlagPresent(arg, "DSYM_HINT_LINKED_BINARY", &linked_binary)) { + continue; + } + if (SetArgIfFlagPresent(arg, "DSYM_HINT_DSYM_PATH", &dsym_path)) { + continue; + } + if (SetArgIfFlagPresent(arg, "DSYM_HINT_DSYM_BUNDLE_ZIP", + &dsym_bundle_zip)) { + continue; + } + if (SetArgIfFlagPresent(arg, "BITCODE_TOUCH_SYMBOL_MAP", + &bitcode_symbol_map)) { + // Touch bitcode_symbol_map. + std::ofstream bitcode_symbol_map_file(bitcode_symbol_map); + arg = bitcode_symbol_map; + } + FindAndReplace("__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir, &arg); + FindAndReplace("__BAZEL_XCODE_SDKROOT__", sdk_root, &arg); + SplitAndAdd(arg, processed_args); + } + + // Check to see if we should postprocess with dsymutil. + bool postprocess = false; + if ((!linked_binary.empty()) || (!dsym_path.empty()) || + (!dsym_bundle_zip.empty())) { + if ((linked_binary.empty()) || (dsym_path.empty()) || + (dsym_bundle_zip.empty())) { + const char *missing_dsym_flag; + if (linked_binary.empty()) { + missing_dsym_flag = "DSYM_HINT_LINKED_BINARY"; + } else if (dsym_path.empty()) { + missing_dsym_flag = "DSYM_HINT_DSYM_PATH"; + } else { + missing_dsym_flag = "DSYM_HINT_DSYM_BUNDLE_ZIP"; + } + std::cerr << "Error in clang wrapper: If any dsym " + "hint is defined, then " + << missing_dsym_flag << " must be defined\n"; + abort(); + } else { + postprocess = true; + } + } + + if (!postprocess) { + ExecProcess(processed_args); + std::cerr << "ExecProcess should not return. Please fix!\n"; + abort(); + } + + RunSubProcess(processed_args); + + std::vector<std::string> dsymutil_args = {"/usr/bin/xcrun", "dsymutil", + linked_binary, "-o", dsym_path}; + + RunSubProcess(dsymutil_args); + + std::unique_ptr<char, decltype(std::free) *> cwd{getcwd(nullptr, 0), + std::free}; + if (cwd == nullptr) { + std::cerr << "Error determining current working directory\n"; + abort(); + } + std::vector<std::string> zip_args = { + "/usr/bin/zip", "-q", "-r", + std::string(cwd.get()) + "/" + dsym_bundle_zip, "."}; + if (chdir(dsym_path.c_str()) < 0) { + std::cerr << "Error changing directory to '" << dsym_path << "'\n"; + abort(); + } + + ExecProcess(zip_args); + + std::cerr << "Should never get to end of wrapped_clang. Please fix!\n"; + abort(); + return 0; +} |