Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple --bazelrc on command line #12740

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a89e22a
nary
wisechengyi Dec 21, 2020
bda828c
more
wisechengyi Dec 21, 2020
e8b8931
no print
wisechengyi Dec 21, 2020
50d2b04
no dedup
wisechengyi Dec 21, 2020
13ae538
minor
wisechengyi Dec 21, 2020
421de4f
cmt
wisechengyi Dec 21, 2020
2f08fa8
initial tests
wisechengyi Dec 22, 2020
b52aa55
more
wisechengyi Dec 22, 2020
6b8b397
rm unused
wisechengyi Dec 22, 2020
d38eca0
rename
wisechengyi Dec 22, 2020
fd7540b
size_type
wisechengyi Dec 22, 2020
17e8b19
fmt
wisechengyi Dec 22, 2020
e1b2a07
one more size_t
wisechengyi Dec 22, 2020
3aa941e
auto
wisechengyi Dec 22, 2020
d72e7bc
potential index out of bound
wisechengyi Dec 22, 2020
2888227
nullptr
wisechengyi Dec 22, 2020
dda34c2
use vector<string>
wisechengyi Dec 22, 2020
b39c124
algo
wisechengyi Dec 22, 2020
71c781a
address comments
wisechengyi Jan 18, 2021
f690f2c
simplify
wisechengyi Jan 18, 2021
420d88a
integration tests for multirc
wisechengyi Jan 19, 2021
eb1894b
rm
wisechengyi Jan 19, 2021
dd78bd3
improve --bazelrc help message on multivalue
wisechengyi Jan 19, 2021
01d15ee
address comment
wisechengyi Jan 19, 2021
d73606d
rename
wisechengyi Jan 20, 2021
5fa3ff4
address comments
wisechengyi Jan 20, 2021
5099242
address comment
wisechengyi Jan 25, 2021
1d4ad3c
Use different options and PRODUCT_NAME
wisechengyi Jan 26, 2021
9307713
add doc string
wisechengyi Feb 1, 2021
35a899e
use info
wisechengyi Feb 4, 2021
438fd20
Merge branch 'master' into multi_rc
wisechengyi Feb 5, 2021
ed75ed9
fix tests
wisechengyi Feb 6, 2021
d7dc7e5
Ignore values after /dev/null (restart ci)
wisechengyi Mar 1, 2021
a2a7142
Merge branch 'master' into multi_rc
wisechengyi Mar 1, 2021
658a9bd
impl
wisechengyi Mar 5, 2021
23cdaf7
help msg
wisechengyi Mar 9, 2021
2a5ef01
add integ test
wisechengyi Mar 9, 2021
cf7f538
strcmp
wisechengyi Mar 9, 2021
f8f5b4b
help msg tweaks and fix format (retrigger ci x2)
wisechengyi Mar 10, 2021
af82fc1
grammar
wisechengyi Mar 11, 2021
c5350b6
use info
wisechengyi Mar 12, 2021
c8d093a
doc update
wisechengyi Mar 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/main/cpp/blaze_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "src/main/cpp/blaze_util.h"

#include <algorithm>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
Expand All @@ -35,6 +36,7 @@
namespace blaze {

using std::map;
using std::min;
using std::string;
using std::vector;

Expand Down Expand Up @@ -74,6 +76,49 @@ bool GetNullaryOption(const char *arg, const char *key) {
return true;
}

std::vector<std::string> GetAllUnaryOptionValues(const vector<string>& args,
const char *key,
const char *ignore_after_value) {
vector<std::string> values;
for (vector<string>::size_type i = 0; i < args.size(); ++i) {
wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
if (args[i] == "--") {
wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
// "--" means all remaining args aren't options
return values;
}

const char* next_arg = args[std::min(i + 1, args.size() - 1)].c_str();
const char* result = GetUnaryOption(args[i].c_str(),
next_arg,
key);
if (result != nullptr) {
// 'key' was found and 'result' has its value.
values.push_back(result);
}
// This is a pointer comparison, so equality means that the result must be from the next arg
// instead of happening to match the value from "--key=<value>" string, in which case
// we need to advance the index to skip the next arg for later iterations.
if (result == next_arg) {
i++;
}
}

if (ignore_after_value == nullptr) {
return values;
}
else {
vector<std::string> new_values;
wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
std::string ignore_after_value_str = std::string(ignore_after_value);
for (vector<string>::size_type i = 0; i < values.size(); ++i) {
std::string curr_val = values[i];
new_values.push_back(curr_val);
if (curr_val == ignore_after_value_str) {
break;
}
}
return new_values;
}
}

const char* SearchUnaryOption(const vector<string>& args,
const char *key, bool warn_if_dupe) {
if (args.empty()) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/cpp/blaze_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ bool GetNullaryOption(const char *arg, const char *key);
const char* SearchUnaryOption(const std::vector<std::string>& args,
const char* key, bool warn_if_dupe);

// Searches for 'key' in 'args' using GetUnaryOption. Arguments found after '--'
// are omitted from the search.
// If 'ignore_after_value' is not nullptr, all values matching the 'key' following
// 'ignore_after_value' will be ignored.
// Returns the values of the 'key' flag iff it occurs in args.
// Returns empty vector otherwise.
std::vector<std::string> GetAllUnaryOptionValues(const std::vector<std::string>& args,
const char* key,
const char* ignore_after_value=nullptr);

// Searches for '--flag_name' and '--noflag_name' in 'args' using
// GetNullaryOption. Arguments found after '--' are omitted from the search.
// Returns true if '--flag_name' is a flag in args and '--noflag_name' does not
Expand Down
48 changes: 29 additions & 19 deletions src/main/cpp/option_processor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,22 @@ std::set<std::string> GetOldRcPaths(
internal::FindRcAlongsideBinary(cwd, path_to_binary);
candidate_bazelrc_paths = {workspace_rc, binary_rc, system_bazelrc_path};
}
string user_bazelrc_path = internal::FindLegacyUserBazelrc(
SearchUnaryOption(startup_args, "--bazelrc", /* warn_if_dupe */ true),
workspace);
if (!user_bazelrc_path.empty()) {
candidate_bazelrc_paths.push_back(user_bazelrc_path);
vector<std::string> cmd_line_rc_files =
GetAllUnaryOptionValues(startup_args, "--bazelrc", "/dev/null");
// Divide the cases where the vector is empty vs not, as `FindLegacyUserBazelrc`
// has a case for rc_file to be a nullptr.
if (cmd_line_rc_files.empty()){
string user_bazelrc_path = internal::FindLegacyUserBazelrc(nullptr, workspace);
if (!user_bazelrc_path.empty()) {
candidate_bazelrc_paths.push_back(user_bazelrc_path);
}
} else {
for (auto& rc_file : cmd_line_rc_files) {
string user_bazelrc_path = internal::FindLegacyUserBazelrc(rc_file.c_str(), workspace);
if (!user_bazelrc_path.empty()) {
candidate_bazelrc_paths.push_back(user_bazelrc_path);
}
}
}
// DedupeBlazercPaths returns paths whose canonical path could be computed,
// therefore these paths must exist.
Expand Down Expand Up @@ -370,20 +381,19 @@ blaze_exit_code::ExitCode OptionProcessor::GetRcFiles(

// Get the command-line provided rc, passed as --bazelrc or nothing if the
// flag is absent.
const char* cmd_line_rc_file =
SearchUnaryOption(cmd_line->startup_args, "--bazelrc",
/* warn_if_dupe */ true);
if (cmd_line_rc_file != nullptr) {
string absolute_cmd_line_rc = blaze::AbsolutePathFromFlag(cmd_line_rc_file);
// Unlike the previous 3 paths, where we ignore it if the file does not
// exist or is unreadable, since this path is explicitly passed, this is an
// error. Check this condition here.
if (!blaze_util::CanReadFile(absolute_cmd_line_rc)) {
BAZEL_LOG(ERROR) << "Error: Unable to read .bazelrc file '"
<< absolute_cmd_line_rc << "'.";
return blaze_exit_code::BAD_ARGV;
}
rc_files.push_back(absolute_cmd_line_rc);
vector<std::string> cmd_line_rc_files =
GetAllUnaryOptionValues(cmd_line->startup_args, "--bazelrc", "/dev/null");
for (auto& rc_file : cmd_line_rc_files) {
string absolute_cmd_line_rc = blaze::AbsolutePathFromFlag(rc_file);
// Unlike the previous 3 paths, where we ignore it if the file does not
// exist or is unreadable, since this path is explicitly passed, this is an
// error. Check this condition here.
if (!blaze_util::CanReadFile(absolute_cmd_line_rc)) {
BAZEL_LOG(ERROR) << "Error: Unable to read .bazelrc file '"
<< absolute_cmd_line_rc << "'.";
return blaze_exit_code::BAD_ARGV;
}
rc_files.push_back(absolute_cmd_line_rc);
}

// Log which files we're looking for before removing duplicates and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@ public static final class Options extends OptionsBase {
valueHelp = "<path>",
help =
"The location of the user .bazelrc file containing default values of "
+ "Bazel options. If unspecified, Bazel uses the first .bazelrc file it finds in "
+ "Bazel options. This option can also be specified multiple times.\n"
+ "E.g. with `--bazelrc=x.rc --bazelrc=y.rc`, options in both RCs will be read.\n"
+ "--bazelrc values that come after `/dev/null` will be ignored to maintain "
wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
+ "backward compatibility.\nE.g. with "
+ "`--bazelrc=x.rc --bazelrc=y.rc --bazelrc=/dev/null --bazelrc=z.rc`, "
+ "only x.rc and y.rc will be ready, and z.rc will be ignored.\n"
+ "If unspecified, Bazel uses the first .bazelrc file it finds in "
+ "the following two locations: the workspace directory, then the user's home "
+ "directory. Use /dev/null to disable the search for a user rc file, e.g. in "
+ "release builds.")
+ "release builds.\n"
+ "Note: command line options will always supersede any option in bazelrc.")
public String blazerc;

// TODO(b/36168162): Remove this after the transition period is ower. This now only serves to
Expand Down
84 changes: 84 additions & 0 deletions src/test/cpp/blaze_util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,88 @@ TEST_F(BlazeUtilTest, TestSearchUnaryCommandOptionWarnsAboutDuplicates) {
}
}

void assert_equal_vector_char_pointer(std::vector<std::string> expected, std::vector<std::string> actual) {
ASSERT_EQ(actual.size(), expected.size()) << "Vectors expected and actual are of unequal length";

for (int i = 0; i < actual.size(); ++i) {
ASSERT_EQ(actual[i], expected[i]) << "Vectors expected and actual differ at index " << i;
}
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryForEmpty) {
assert_equal_vector_char_pointer({}, GetAllUnaryOptionValues({"bazel", "build", ":target"}, ""));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryFlagNotPresent) {
assert_equal_vector_char_pointer({}, GetAllUnaryOptionValues({"bazel", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithEquals) {
assert_equal_vector_char_pointer({"value"}, GetAllUnaryOptionValues({"bazel", "--flag=value", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithEquals2) {
assert_equal_vector_char_pointer({"value1", "value2"}, GetAllUnaryOptionValues({"bazel", "--flag=value1", "--flag=value2", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithRepeatingFlag) {
assert_equal_vector_char_pointer({"--flag"}, GetAllUnaryOptionValues({"bazel", "--flag", "--flag", "value1", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithRepeatingFlagOptionsOnly) {
assert_equal_vector_char_pointer({"--flag"}, GetAllUnaryOptionValues({"bazel", "--flag", "--flag", "value1"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithRepeatingFlagOptionsOnlyMulti) {
assert_equal_vector_char_pointer({"--flag", "value1"}, GetAllUnaryOptionValues({"bazel", "--flag", "--flag", "--flag", "value1"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithRepeatingFlagOptionsOnlyMultiWithEquals) {
wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
assert_equal_vector_char_pointer({"--flag", "value1"}, GetAllUnaryOptionValues({"bazel", "--flag=--flag", "--flag", "value1"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithEquals3) {
assert_equal_vector_char_pointer({"value1", "value2", "value3"}, GetAllUnaryOptionValues({"bazel", "--flag=value1", "--flag=value2", "--flag=value3", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithoutEquals) {
assert_equal_vector_char_pointer({"value"}, GetAllUnaryOptionValues({"bazel", "--flag", "value", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryStartupOptionWithoutEquals2) {
assert_equal_vector_char_pointer({"value1", "value2"}, GetAllUnaryOptionValues({"bazel", "--flag", "value1", "--flag", "value2", "build", ":target"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryCommandOptionWithEquals) {
assert_equal_vector_char_pointer({"value"}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--flag", "value"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryCommandOptionWithEquals2) {
assert_equal_vector_char_pointer({"value1","value2"}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--flag", "value1", "--flag", "value2"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryCommandOptionWithoutEquals) {
assert_equal_vector_char_pointer({"value"}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--flag=value"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryCommandOptionWithoutEquals2) {
assert_equal_vector_char_pointer({"value1", "value2"}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--flag=value1", "--flag=value2"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnarySkipsAfterDashDashWithEquals) {
assert_equal_vector_char_pointer({}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--", "--flag", "value"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnarySkipsAfterDashDashWithoutEquals) {
assert_equal_vector_char_pointer({}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--", "--flag=value"}, "--flag"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryCommandOptionWithIgnoreAfter) {
assert_equal_vector_char_pointer({"value1","/dev/null"}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--flag", "value1", "--flag", "/dev/null", "--flag", "value3"}, "--flag", "/dev/null"));
}

TEST_F(BlazeUtilTest, TestSearchAllUnaryCommandOptionWithIgnoreAfterDevNull) {
assert_equal_vector_char_pointer({"/dev/null"}, GetAllUnaryOptionValues({"bazel", "build", ":target", "--flag", "/dev/null", "--flag", "value2", "--flag", "value3"}, "--flag", "/dev/null"));
}

} // namespace blaze
24 changes: 24 additions & 0 deletions src/test/shell/integration/startup_options_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,28 @@ function test_autodetect_server_javabase() {
bazel --noautodetect_server_javabase version &> $TEST_log || fail "Should pass"
}

# Below are the regression tests for Issue #7489
function test_multiple_bazelrc_later_overwrites_earlier() {
# Help message only visible with --help_verbosity=medium
help_message_in_description="--${PRODUCT_NAME}rc (a string; default: see description)"

echo "help --help_verbosity=short" > 1.rc
echo "help --help_verbosity=medium" > 2.rc
bazel "--${PRODUCT_NAME}rc=1.rc" "--${PRODUCT_NAME}rc=2.rc" help startup_options &> $TEST_log || fail "Should pass"
expect_log "$help_message_in_description"

echo "help --help_verbosity=medium" > 1.rc
echo "help --help_verbosity=short" > 2.rc
bazel "--${PRODUCT_NAME}rc=1.rc" "--${PRODUCT_NAME}rc=2.rc" help startup_options &> $TEST_log || fail "Should pass"
expect_not_log "$help_message_in_description"
}

function test_multiple_bazelrc_set_different_options() {
echo "common --verbose_failures" > 1.rc
echo "common --test_output=all" > 2.rc
bazel "--${PRODUCT_NAME}rc=1.rc" "--${PRODUCT_NAME}rc=2.rc" build --announce_rc &> $TEST_log || fail "Should pass"
wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
expect_log "Inherited 'common' options: --verbose_failures"
expect_log "Inherited 'common' options: --test_output=all"
}

wisechengyi marked this conversation as resolved.
Show resolved Hide resolved
run_suite "${PRODUCT_NAME} startup options test"