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};