diff --git a/dist-clang.files b/dist-clang.files
index 4ac8eb01..abd91f71 100644
--- a/dist-clang.files
+++ b/dist-clang.files
@@ -2283,6 +2283,8 @@ src/client/clean_command.hh
src/client/command.cc
src/client/command.hh
src/client/command_test.cc
+src/client/configuration.cc
+src/client/configuration.hh
src/client/configuration.proto
src/client/driver_command.cc
src/client/driver_command.hh
diff --git a/src/base/file_utils.h b/src/base/file_utils.h
index 30e4ee38..ae3e7814 100644
--- a/src/base/file_utils.h
+++ b/src/base/file_utils.h
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include
@@ -66,5 +67,7 @@ inline Path GetCurrentDir(String* error = nullptr) {
return current_dir;
}
+String GetRelativePath(const String& current_dir, const String& path);
+
} // namespace base
} // namespace dist_clang
diff --git a/src/base/file_utils_posix.cc b/src/base/file_utils_posix.cc
index e34624a8..27d82171 100644
--- a/src/base/file_utils_posix.cc
+++ b/src/base/file_utils_posix.cc
@@ -95,5 +95,36 @@ Pair GetModificationTime(const String& path, String* error) {
return {time_spec.tv_sec, time_spec.tv_nsec};
}
+String GetRelativePath(const String& current_dir, const String& path) {
+ // Remove dot directories from inputs to make behavior more consistent.
+ std::regex dot_pattern("\\/./");
+ List current_dir_parts, path_parts;
+ base::SplitString<'/'>(
+ std::regex_replace(current_dir, dot_pattern, "/"), current_dir_parts);
+ base::SplitString<'/'>(
+ std::regex_replace(path, dot_pattern, "/"), path_parts);
+
+ auto current_dir_part = current_dir_parts.begin();
+ auto path_part = path_parts.begin();
+ while (current_dir_part != current_dir_parts.end() &&
+ path_part != path_parts.end() &&
+ *current_dir_part == *path_part) {
+ ++current_dir_part;
+ ++path_part;
+ }
+
+ String result(".");
+ while (current_dir_part != current_dir_parts.end()) {
+ result += "/..";
+ ++current_dir_part;
+ }
+
+ while (path_part != path_parts.end()) {
+ result += "/" + *path_part;
+ ++path_part;
+ }
+ return result;
+}
+
} // namespace base
} // namespace dist_clang
diff --git a/src/base/file_utils_test.cc b/src/base/file_utils_test.cc
index 0250ebf3..5db6f064 100644
--- a/src/base/file_utils_test.cc
+++ b/src/base/file_utils_test.cc
@@ -71,5 +71,19 @@ TEST(FileUtilsTest, CreateDirectory) {
}
}
+TEST(FileUtilsTest, GetRelativeDirectory) {
+ const String cur_dir = "/path/to/current/dir";
+ EXPECT_EQ("./out/bin/../lib/1.0.0",
+ base::GetRelativePath(
+ cur_dir, "/path/to/current/dir/./out/bin/../lib/1.0.0"));
+ EXPECT_EQ("./and/more",
+ base::GetRelativePath(cur_dir, "/path/to/current/dir/and/more"));
+ EXPECT_EQ("./../../other/dir",
+ base::GetRelativePath(cur_dir, "/path/to/other/dir"));
+ EXPECT_EQ("./../../../../absolute/path/to/other/dir",
+ base::GetRelativePath(cur_dir, "/absolute/path/to/other/dir"));
+ EXPECT_EQ("./.", base::GetRelativePath("/", "."));
+}
+
} // namespace base
} // namespace dist_clang
diff --git a/src/client/clang_command.cc b/src/client/clang_command.cc
index 9109f9fd..8acb3c98 100644
--- a/src/client/clang_command.cc
+++ b/src/client/clang_command.cc
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#include
#include
@@ -64,6 +65,8 @@ bool ClangCommand::FillFlags(base::proto::Flags* flags,
return true;
}
+ const String current_dir = base::GetCurrentDir();
+
flags->Clear();
llvm::opt::ArgStringList non_direct_list, non_cached_list, other_list;
@@ -158,8 +161,10 @@ bool ClangCommand::FillFlags(base::proto::Flags* flags,
replaced_command.replace(
pos, self_path.size(),
clang_path.substr(0, clang_path.find_last_of('/')));
+ const String relative_command =
+ base::GetRelativePath(current_dir, replaced_command);
non_direct_list.push_back(arg->getSpelling().data());
- non_direct_list.push_back(tmp_list.MakeArgString(replaced_command));
+ non_direct_list.push_back(tmp_list.MakeArgString(relative_command));
LOG(VERBOSE) << "Replaced command: " << non_direct_list.back();
} else {
non_cached_list.push_back(arg->getSpelling().data());
diff --git a/src/client/command_test.cc b/src/client/command_test.cc
index 09854886..5ab031ed 100644
--- a/src/client/command_test.cc
+++ b/src/client/command_test.cc
@@ -264,6 +264,64 @@ TEST(CommandTest, FillFlagsAppendsRewriteIncludes) {
}
}
+class ScopedCurrentDir {
+ public:
+ explicit ScopedCurrentDir(const Path& path)
+ : initial_dir_(base::GetCurrentDir()) {
+ base::ChangeCurrentDir(path);
+ }
+ ~ScopedCurrentDir() {
+ base::ChangeCurrentDir(initial_dir_);
+ }
+ private:
+ const Path initial_dir_;
+};
+
+TEST(CommandTest, RelativeResourceDir) {
+ String self_path = base::GetCurrentDir();
+ ASSERT_TRUE(base::GetSelfPath(self_path));
+ const String input = "/test_file.cc";
+ const String output = "/tmp/output.o";
+ const String clang_path = self_path + "/path/to/llvm-build/bin/clang";
+ const String resource_dir = self_path + "/../lib/1.0.0";
+ const char* argv[] = {"clang++",
+ "-Xclang",
+ "-resource-dir",
+ "-Xclang",
+ resource_dir.c_str(),
+ "-c",
+ input.c_str(),
+ "-o",
+ output.c_str(),
+ nullptr};
+ const int argc = 9;
+
+ ScopedCurrentDir tests_binary_dir(std::move(self_path));
+
+ Command::List commands;
+ ASSERT_TRUE(Command::GenerateFromArgs(argc, argv, commands));
+ ASSERT_EQ(1u, commands.size());
+
+ auto& command = commands.front();
+ base::proto::Flags flags;
+ ASSERT_TRUE(command->CanFillFlags());
+ ASSERT_TRUE(command->FillFlags(&flags, clang_path, "1.0.0", false));
+
+ EXPECT_EQ(input, flags.input());
+ EXPECT_EQ(output, flags.output());
+ EXPECT_EQ("-emit-obj", flags.action());
+ EXPECT_EQ("c++", flags.language());
+ EXPECT_EQ("-cc1", *flags.other().begin());
+
+ EXPECT_NE(flags.non_direct().end(),
+ std::find(flags.non_direct().begin(), flags.non_direct().end(),
+ "./path/to/llvm-build/bin/../lib/1.0.0"));
+
+ if (HasNonfatalFailure()) {
+ FAIL() << command->RenderAllArgs();
+ }
+}
+
TEST(CommandTest, AppendCleanTempFilesCommand) {
const String temp_input = base::CreateTempFile(".cc");
const char* argv[] = {"clang++", temp_input.c_str(), nullptr};