Skip to content
This repository has been archived by the owner on Jan 25, 2024. It is now read-only.

Commit

Permalink
Factor out Bazel placeholder substitution functionality into a separa…
Browse files Browse the repository at this point in the history
…te reusable class.

PiperOrigin-RevId: 366461152
  • Loading branch information
allevato authored and swiple-rules-gardener committed Apr 2, 2021
1 parent aeb56dc commit 89c6ab2
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 105 deletions.
12 changes: 6 additions & 6 deletions tools/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ package(

licenses(["notice"])

cc_library(
name = "bazel_substitutions",
srcs = ["bazel_substitutions.cc"],
hdrs = ["bazel_substitutions.h"],
)

cc_library(
name = "file_system",
srcs = ["file_system.cc"],
Expand All @@ -30,12 +36,6 @@ cc_library(
],
)

cc_library(
name = "string_utils",
srcs = ["string_utils.cc"],
hdrs = ["string_utils.h"],
)

cc_library(
name = "temp_file",
hdrs = ["temp_file.h"],
Expand Down
90 changes: 90 additions & 0 deletions tools/common/bazel_substitutions.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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.

#include "tools/common/bazel_substitutions.h"

#include <cstdlib>
#include <iostream>
#include <map>
#include <string>

namespace bazel_rules_swift {
namespace {

// Returns the value of the given environment variable, or the empty string if
// it wasn't set.
std::string GetEnvironmentVariable(const char *name) {
char *env_value = getenv(name);
if (env_value == nullptr) {
return "";
}
return env_value;
}

} // namespace

BazelPlaceholderSubstitutions::BazelPlaceholderSubstitutions() {
// When targeting Apple platforms, replace the magic Bazel placeholders with
// the path in the corresponding environment variable. These should be set by
// the build rules; only attempt to retrieve them if they're actually seen in
// the argument list.
placeholder_resolvers_ = {
{kBazelXcodeDeveloperDir, PlaceholderResolver([]() {
return GetEnvironmentVariable("DEVELOPER_DIR");
})},
{kBazelXcodeSdkRoot,
PlaceholderResolver([]() { return GetEnvironmentVariable("SDKROOT"); })},
};
}

BazelPlaceholderSubstitutions::BazelPlaceholderSubstitutions(
const std::string &developer_dir, const std::string &sdk_root) {
placeholder_resolvers_ = {
{kBazelXcodeDeveloperDir,
PlaceholderResolver([=]() { return developer_dir; })},
{kBazelXcodeSdkRoot,
PlaceholderResolver([=]() { return sdk_root; })},
};
}

bool BazelPlaceholderSubstitutions::Apply(std::string &arg) {
bool changed = false;

// Replace placeholders in the string with their actual values.
for (auto &[placeholder, env_var] : placeholder_resolvers_) {
changed |= FindAndReplace(placeholder, env_var, arg);
}

return changed;
}

bool BazelPlaceholderSubstitutions::FindAndReplace(
const std::string &placeholder,
BazelPlaceholderSubstitutions::PlaceholderResolver &resolver,
std::string &str) {
int start = 0;
bool changed = false;
while ((start = str.find(placeholder, start)) != std::string::npos) {
std::string resolved_value = resolver.get();
if (resolved_value.empty()) {
return false;
}
changed = true;
str.replace(start, placeholder.length(), resolved_value);
start += resolved_value.length();
}
return changed;
}

} // namespace bazel_rules_swift
93 changes: 93 additions & 0 deletions tools/common/bazel_substitutions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// 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.

#ifndef BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_BAZEL_SUBSTITUTIONS_H_
#define BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_BAZEL_SUBSTITUTIONS_H_

#include <map>
#include <string>

namespace bazel_rules_swift {

// Manages the substitution of special Bazel placeholder strings in command line
// arguments that are used to defer the determination of Apple developer and SDK
// paths until execution time.
class BazelPlaceholderSubstitutions {
public:
// Initializes the substitutions by looking them up in the process's
// environment when they are first requested.
BazelPlaceholderSubstitutions();

// Initializes the substitutions with the given fixed strings. Intended to be
// used for testing.
BazelPlaceholderSubstitutions(const std::string &developer_dir,
const std::string &sdk_root);

// Applies any necessary substitutions to `arg` and returns true if this
// caused the string to change.
bool Apply(std::string &arg);

// The placeholder string used by Bazel that should be replaced by
// `DEVELOPER_DIR` at runtime.
static constexpr const char kBazelXcodeDeveloperDir[] =
"__BAZEL_XCODE_DEVELOPER_DIR__";

// The placeholder string used by Bazel that should be replaced by `SDKROOT`
// at runtime.
static constexpr const char kBazelXcodeSdkRoot[] = "__BAZEL_XCODE_SDKROOT__";

private:
// A resolver for a Bazel placeholder string that retrieves and caches the
// value the first time it is requested.
class PlaceholderResolver {
public:
explicit PlaceholderResolver(std::function<std::string()> fn)
: function_(fn), initialized_(false) {}

// Returns the requested placeholder value, caching it for future
// retrievals.
std::string get() {
if (!initialized_) {
value_ = function_();
initialized_ = true;
}
return value_;
}

private:
// The function that returns the value of the placeholder, or the empty
// string if the placeholder should not be replaced.
std::function<std::string()> function_;

// Indicates whether the value of the placeholder has been requested yet and
// and is therefore initialized.
bool initialized_;

// The cached value of the placeholder if `initialized_` is true.
std::string value_;
};

// Finds and replaces all instances of `placeholder` with the value provided
// by `resolver`, in-place on `str`. Returns true if the string was changed.
bool FindAndReplace(const std::string &placeholder,
PlaceholderResolver &resolver, std::string &str);

// A mapping from Bazel placeholder strings to resolvers that provide their
// values.
std::map<std::string, PlaceholderResolver> placeholder_resolvers_;
};

} // namespace bazel_rules_swift

#endif // BUILD_BAZEL_RULES_SWIFT_TOOLS_COMMON_BAZEL_SUBSTITUTIONS_H_
42 changes: 0 additions & 42 deletions tools/common/string_utils.cc

This file was deleted.

25 changes: 0 additions & 25 deletions tools/common/string_utils.h

This file was deleted.

3 changes: 1 addition & 2 deletions tools/worker/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ cc_library(
"//third_party/bazel_protos:worker_protocol_cc_proto",
"//tools/common:file_system",
"//tools/common:path_utils",
"//tools/common:string_utils",
"//tools/common:temp_file",
"@com_github_nlohmann_json//:json",
"@com_google_protobuf//:protobuf",
Expand All @@ -52,9 +51,9 @@ cc_library(
srcs = ["swift_runner.cc"],
hdrs = ["swift_runner.h"],
deps = [
"//tools/common:bazel_substitutions",
"//tools/common:file_system",
"//tools/common:process",
"//tools/common:string_utils",
"//tools/common:temp_file",
],
)
Expand Down
30 changes: 2 additions & 28 deletions tools/worker/swift_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,13 @@

#include <fstream>

#include "tools/common/bazel_substitutions.h"
#include "tools/common/file_system.h"
#include "tools/common/process.h"
#include "tools/common/string_utils.h"
#include "tools/common/temp_file.h"

namespace {

#if __APPLE__
// Returns the requested environment variable in the current process's
// environment. Aborts if this variable is unset.
static 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;
}
#endif

// Creates a temporary file and writes the given arguments to it, one per line.
static std::unique_ptr<TempFile> WriteResponseFile(
const std::vector<std::string> &args) {
Expand Down Expand Up @@ -104,19 +91,6 @@ static std::string Unescape(const std::string &arg) {
SwiftRunner::SwiftRunner(const std::vector<std::string> &args,
bool force_response_file)
: force_response_file_(force_response_file) {
#if __APPLE__
// On Apple platforms, replace the magic Bazel placeholders with the path
// in the corresponding environment variable.
std::string developer_dir = GetMandatoryEnvVar("DEVELOPER_DIR");
std::string sdk_root = GetMandatoryEnvVar("SDKROOT");

bazel_placeholder_substitutions_ = {
{"__BAZEL_XCODE_DEVELOPER_DIR__", developer_dir},
{"__BAZEL_XCODE_SDKROOT__", sdk_root},
};
#else
// We don't have these placeholder strings on non-Apple platforms.
#endif
args_ = ProcessArguments(args);
}

Expand Down Expand Up @@ -206,7 +180,7 @@ bool SwiftRunner::ProcessArgument(
// Bazel doesn't quote arguments in multi-line params files, so we need to
// ensure that our defensive quoting kicks in if an argument contains a
// space, even if no other changes would have been made.
changed = MakeSubstitutions(&new_arg, bazel_placeholder_substitutions_) ||
changed = bazel_placeholder_substitutions_.Apply(new_arg) ||
new_arg.find_first_of(' ') != std::string::npos;
consumer(new_arg);
}
Expand Down
4 changes: 3 additions & 1 deletion tools/worker/swift_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <string>
#include <vector>

#include "tools/common/bazel_substitutions.h"
#include "tools/common/temp_file.h"

// Handles spawning the Swift compiler driver, making any required substitutions
Expand Down Expand Up @@ -106,7 +107,8 @@ class SwiftRunner {

// A mapping of Bazel placeholder strings to the actual paths that should be
// substituted for them. Supports Xcode resolution on Apple OSes.
std::map<std::string, std::string> bazel_placeholder_substitutions_;
bazel_rules_swift::BazelPlaceholderSubstitutions
bazel_placeholder_substitutions_;

// The arguments, post-substitution, passed to the spawner.
std::vector<std::string> args_;
Expand Down
1 change: 0 additions & 1 deletion tools/worker/work_processor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <google/protobuf/text_format.h>
#include "tools/common/file_system.h"
#include "tools/common/path_utils.h"
#include "tools/common/string_utils.h"
#include "tools/common/temp_file.h"
#include "tools/worker/output_file_map.h"
#include "tools/worker/swift_runner.h"
Expand Down

0 comments on commit 89c6ab2

Please sign in to comment.