|
15 | 15 | #include "tools/common/bazel_substitutions.h" |
16 | 16 |
|
17 | 17 | #include <cstdlib> |
18 | | -#include <filesystem> |
19 | 18 | #include <iostream> |
20 | | -#include <map> |
21 | | -#include <sstream> |
22 | 19 | #include <string> |
23 | 20 |
|
24 | | -#include "tools/common/process.h" |
| 21 | +#include "absl/container/flat_hash_map.h" |
| 22 | +#include "absl/strings/str_replace.h" |
| 23 | +#include "absl/strings/string_view.h" |
25 | 24 |
|
26 | 25 | namespace bazel_rules_swift { |
27 | 26 | namespace { |
28 | 27 |
|
29 | | -// The placeholder string used by Bazel that should be replaced by |
30 | | -// `DEVELOPER_DIR` at runtime. |
31 | | -static const char kBazelXcodeDeveloperDir[] = "__BAZEL_XCODE_DEVELOPER_DIR__"; |
32 | | - |
33 | | -// The placeholder string used by Bazel that should be replaced by `SDKROOT` |
34 | | -// at runtime. |
35 | | -static const char kBazelXcodeSdkRoot[] = "__BAZEL_XCODE_SDKROOT__"; |
36 | | - |
37 | | -// The placeholder string used by the Apple and Swift rules to be replaced with |
38 | | -// the absolute path to the custom toolchain being used |
39 | | -static const char kBazelToolchainPath[] = |
40 | | - "__BAZEL_CUSTOM_XCODE_TOOLCHAIN_PATH__"; |
41 | | - |
42 | 28 | // Returns the value of the given environment variable, or the empty string if |
43 | 29 | // it wasn't set. |
44 | | -std::string GetAppleEnvironmentVariable(const char *name) { |
45 | | -#if !defined(__APPLE__) |
46 | | - return ""; |
47 | | -#endif |
48 | | - |
49 | | - char *env_value = getenv(name); |
| 30 | +std::string GetEnvironmentVariable(absl::string_view name) { |
| 31 | + std::string null_terminated_name(name.data(), name.length()); |
| 32 | + char *env_value = getenv(null_terminated_name.c_str()); |
50 | 33 | if (env_value == nullptr) { |
51 | | - std::cerr << "error: required Apple environment variable '" << name << "' was not set. Please file an issue on bazelbuild/rules_swift.\n"; |
52 | | - exit(EXIT_FAILURE); |
53 | | - } |
54 | | - return env_value; |
55 | | -} |
56 | | - |
57 | | -std::string GetToolchainPath() { |
58 | | -#if !defined(__APPLE__) |
59 | | - return ""; |
60 | | -#endif |
61 | | - |
62 | | - char *toolchain_id = getenv("TOOLCHAINS"); |
63 | | - if (toolchain_id == nullptr) { |
64 | 34 | return ""; |
65 | 35 | } |
66 | | - |
67 | | - std::ostringstream output_stream; |
68 | | - int exit_code = |
69 | | - RunSubProcess({"/usr/bin/xcrun", "--find", "clang", "--toolchain", toolchain_id}, |
70 | | - /*env=*/nullptr, &output_stream, /*stdout_to_stderr=*/true); |
71 | | - if (exit_code != 0) { |
72 | | - std::cerr << output_stream.str() << "Error: TOOLCHAINS was set to '" |
73 | | - << toolchain_id << "' but xcrun failed when searching for that ID" |
74 | | - << std::endl; |
75 | | - exit(EXIT_FAILURE); |
76 | | - } |
77 | | - |
78 | | - if (output_stream.str().empty()) { |
79 | | - std::cerr << "Error: TOOLCHAINS was set to '" << toolchain_id |
80 | | - << "' but no toolchain with that ID was found" << std::endl; |
81 | | - exit(EXIT_FAILURE); |
82 | | - } else if (output_stream.str().find("XcodeDefault.xctoolchain") != |
83 | | - std::string::npos) { |
84 | | - // NOTE: Ideally xcrun would fail if the toolchain we asked for didn't exist |
85 | | - // but it falls back to the DEVELOPER_DIR instead, so we have to check the |
86 | | - // output ourselves. |
87 | | - std::cerr << "Error: TOOLCHAINS was set to '" << toolchain_id |
88 | | - << "' but the default toolchain was found, that likely means a " |
89 | | - "matching " |
90 | | - << "toolchain isn't installed" << std::endl; |
91 | | - exit(EXIT_FAILURE); |
92 | | - } |
93 | | - |
94 | | - std::filesystem::path toolchain_path(output_stream.str()); |
95 | | - // Remove usr/bin/clang components to get the root of the custom toolchain |
96 | | - return toolchain_path.parent_path().parent_path().parent_path().string(); |
| 36 | + return env_value; |
97 | 37 | } |
98 | 38 |
|
99 | 39 | } // namespace |
100 | 40 |
|
101 | 41 | BazelPlaceholderSubstitutions::BazelPlaceholderSubstitutions() { |
102 | 42 | // When targeting Apple platforms, replace the magic Bazel placeholders with |
103 | | - // the path in the corresponding environment variable. These should be set by |
104 | | - // the build rules; only attempt to retrieve them if they're actually seen in |
105 | | - // the argument list. |
106 | | - placeholder_resolvers_ = { |
107 | | - {kBazelXcodeDeveloperDir, PlaceholderResolver([]() { |
108 | | - return GetAppleEnvironmentVariable("DEVELOPER_DIR"); |
109 | | - })}, |
110 | | - {kBazelXcodeSdkRoot, PlaceholderResolver([]() { |
111 | | - return GetAppleEnvironmentVariable("SDKROOT"); |
112 | | - })}, |
113 | | - {kBazelToolchainPath, |
114 | | - PlaceholderResolver([]() { return GetToolchainPath(); })}, |
115 | | - }; |
116 | | -} |
117 | | - |
118 | | -bool BazelPlaceholderSubstitutions::Apply(std::string &arg) { |
119 | | - bool changed = false; |
120 | | - |
121 | | - // Replace placeholders in the string with their actual values. |
122 | | - for (auto &pair : placeholder_resolvers_) { |
123 | | - changed |= FindAndReplace(pair.first, pair.second, arg); |
| 43 | + // the path in the corresponding environment variable, which should be set by |
| 44 | + // the build rules. If the variable isn't set, we don't store a substitution; |
| 45 | + // if it was needed then the eventual replacement will be a no-op and the |
| 46 | + // command will presumably fail later. |
| 47 | + if (std::string developer_dir = GetEnvironmentVariable("DEVELOPER_DIR"); |
| 48 | + !developer_dir.empty()) { |
| 49 | + substitutions_[kBazelXcodeDeveloperDir] = developer_dir; |
| 50 | + } |
| 51 | + if (std::string sdk_root = GetEnvironmentVariable("SDKROOT"); |
| 52 | + !sdk_root.empty()) { |
| 53 | + substitutions_[kBazelXcodeSdkRoot] = sdk_root; |
124 | 54 | } |
| 55 | +} |
125 | 56 |
|
126 | | - return changed; |
| 57 | +BazelPlaceholderSubstitutions::BazelPlaceholderSubstitutions( |
| 58 | + absl::string_view developer_dir, absl::string_view sdk_root) { |
| 59 | + substitutions_[kBazelXcodeDeveloperDir] = std::string(developer_dir); |
| 60 | + substitutions_[kBazelXcodeSdkRoot] = std::string(sdk_root); |
127 | 61 | } |
128 | 62 |
|
129 | | -bool BazelPlaceholderSubstitutions::FindAndReplace( |
130 | | - const std::string &placeholder, |
131 | | - BazelPlaceholderSubstitutions::PlaceholderResolver &resolver, |
132 | | - std::string &str) { |
133 | | - int start = 0; |
134 | | - bool changed = false; |
135 | | - while ((start = str.find(placeholder, start)) != std::string::npos) { |
136 | | - std::string resolved_value = resolver.get(); |
137 | | - if (resolved_value.empty()) { |
138 | | - return false; |
139 | | - } |
140 | | - changed = true; |
141 | | - str.replace(start, placeholder.length(), resolved_value); |
142 | | - start += resolved_value.length(); |
143 | | - } |
144 | | - return changed; |
| 63 | +bool BazelPlaceholderSubstitutions::Apply(std::string &arg) { |
| 64 | + return absl::StrReplaceAll(substitutions_, &arg) > 0; |
145 | 65 | } |
146 | 66 |
|
147 | 67 | } // namespace bazel_rules_swift |
0 commit comments