-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix up busybox detection for relative symlinks (#4522)
Handling for relative symlinks is new (comment talks about relative symlinks). Relocated to add tests though, to do extra checking of logic.
- Loading branch information
Showing
4 changed files
with
231 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM | ||
// Exceptions. See /LICENSE for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#ifndef CARBON_TOOLCHAIN_INSTALL_BUSYBOX_INFO_H_ | ||
#define CARBON_TOOLCHAIN_INSTALL_BUSYBOX_INFO_H_ | ||
|
||
#include <filesystem> | ||
#include <optional> | ||
#include <string> | ||
|
||
#include "common/error.h" | ||
#include "llvm/ADT/StringRef.h" | ||
|
||
namespace Carbon { | ||
|
||
struct BusyboxInfo { | ||
// The path to `carbon-busybox`. | ||
std::filesystem::path bin_path; | ||
// The mode, such as `carbon` or `clang`. | ||
std::optional<std::string> mode; | ||
}; | ||
|
||
// Returns the busybox information, given argv[0]. This primarily handles | ||
// resolving symlinks that point at the busybox. | ||
inline auto GetBusyboxInfo(llvm::StringRef argv0) -> ErrorOr<BusyboxInfo> { | ||
BusyboxInfo info = BusyboxInfo{argv0.str(), std::nullopt}; | ||
while (true) { | ||
std::string filename = info.bin_path.filename(); | ||
if (filename == "carbon-busybox") { | ||
return info; | ||
} | ||
std::error_code ec; | ||
auto symlink_target = std::filesystem::read_symlink(info.bin_path, ec); | ||
if (ec) { | ||
return ErrorBuilder() | ||
<< "expected carbon-busybox symlink at `" << info.bin_path << "`"; | ||
} | ||
info.mode = filename; | ||
// Do a path join, to handle relative symlinks. | ||
info.bin_path = info.bin_path.parent_path() / symlink_target; | ||
} | ||
} | ||
|
||
} // namespace Carbon | ||
|
||
#endif // CARBON_TOOLCHAIN_INSTALL_BUSYBOX_INFO_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM | ||
// Exceptions. See /LICENSE for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#include "toolchain/install/busybox_info.h" | ||
|
||
#include <gmock/gmock.h> | ||
#include <gtest/gtest.h> | ||
|
||
#include <cstdlib> | ||
#include <fstream> | ||
|
||
#include "common/check.h" | ||
|
||
namespace Carbon { | ||
namespace { | ||
|
||
using ::testing::Eq; | ||
|
||
class BusyboxInfoTest : public ::testing::Test { | ||
protected: | ||
// Set up a temp directory for the test case. | ||
explicit BusyboxInfoTest() { | ||
const char* tmpdir = std::getenv("TEST_TMPDIR"); | ||
CARBON_CHECK(tmpdir); | ||
dir_ = MakeDir( | ||
std::filesystem::path(tmpdir) / | ||
::testing::UnitTest::GetInstance()->current_test_info()->name()); | ||
} | ||
|
||
// Delete the test case's temp directory. | ||
~BusyboxInfoTest() override { | ||
std::error_code ec; | ||
std::filesystem::remove_all(dir_, ec); | ||
CARBON_CHECK(!ec, "error removing {0}: {1}", dir_, ec.message()); | ||
} | ||
|
||
// Creates a stub file. Returns the input file for easier use. | ||
auto MakeFile(std::filesystem::path file) -> std::filesystem::path { | ||
std::ofstream out(file.c_str()); | ||
out << "stub"; | ||
CARBON_CHECK(out, "error creating {0}", file); | ||
return file; | ||
} | ||
|
||
// Creates a symlink to the target. Returns the input file for easier use. | ||
auto MakeSymlink(std::filesystem::path file, auto target) | ||
-> std::filesystem::path { | ||
std::error_code ec; | ||
std::filesystem::create_symlink(target, file, ec); | ||
CARBON_CHECK(!ec, "error creating {0}: {1}", file, ec.message()); | ||
return file; | ||
} | ||
|
||
// Creates a directory. Returns the input file for easier use. | ||
auto MakeDir(std::filesystem::path dir) -> std::filesystem::path { | ||
std::error_code ec; | ||
std::filesystem::create_directory(dir, ec); | ||
CARBON_CHECK(!ec, "error creating {0}: {1}", dir, ec.message()); | ||
return dir; | ||
} | ||
|
||
// The test's temp directory, deleted on destruction. | ||
std::filesystem::path dir_; | ||
}; | ||
|
||
TEST_F(BusyboxInfoTest, Direct) { | ||
auto busybox = MakeFile(dir_ / "carbon-busybox"); | ||
|
||
auto info = GetBusyboxInfo(busybox.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(busybox)); | ||
EXPECT_THAT(info->mode, Eq(std::nullopt)); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, SymlinkInCurrentDirectory) { | ||
MakeFile(dir_ / "carbon-busybox"); | ||
auto target = MakeSymlink(dir_ / "carbon", "carbon-busybox"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(dir_ / "carbon-busybox")); | ||
EXPECT_THAT(info->mode, Eq("carbon")); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, SymlinkInCurrentDirectoryWithDot) { | ||
MakeFile(dir_ / "carbon-busybox"); | ||
auto target = MakeSymlink(dir_ / "carbon", "./carbon-busybox"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(dir_ / "./carbon-busybox")); | ||
EXPECT_THAT(info->mode, Eq("carbon")); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, ExtraSymlink) { | ||
MakeFile(dir_ / "carbon-busybox"); | ||
MakeSymlink(dir_ / "carbon", "carbon-busybox"); | ||
auto target = MakeSymlink(dir_ / "c", "carbon"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(dir_ / "carbon-busybox")); | ||
EXPECT_THAT(info->mode, Eq("carbon")); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, BusyboxIsSymlink) { | ||
MakeFile(dir_ / "actual-busybox"); | ||
auto target = MakeSymlink(dir_ / "carbon-busybox", "actual-busybox"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(target)); | ||
EXPECT_THAT(info->mode, Eq(std::nullopt)); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, BusyboxIsSymlinkToNowhere) { | ||
auto target = MakeSymlink(dir_ / "carbon-busybox", "nonexistent"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(dir_ / "carbon-busybox")); | ||
EXPECT_THAT(info->mode, Eq(std::nullopt)); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, RelativeSymlink) { | ||
MakeDir(dir_ / "lib"); | ||
MakeDir(dir_ / "lib/carbon"); | ||
MakeFile(dir_ / "lib/carbon/carbon-busybox"); | ||
MakeDir(dir_ / "bin"); | ||
auto target = | ||
MakeSymlink(dir_ / "bin/carbon", "../lib/carbon/carbon-busybox"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
ASSERT_TRUE(info.ok()) << info.error(); | ||
EXPECT_THAT(info->bin_path, Eq(dir_ / "bin/../lib/carbon/carbon-busybox")); | ||
EXPECT_THAT(info->mode, Eq("carbon")); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, NotBusyboxFile) { | ||
auto target = MakeFile(dir_ / "file"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
EXPECT_FALSE(info.ok()); | ||
} | ||
|
||
TEST_F(BusyboxInfoTest, NotBusyboxSymlink) { | ||
MakeFile(dir_ / "file"); | ||
auto target = MakeSymlink(dir_ / "carbon", "file"); | ||
|
||
auto info = GetBusyboxInfo(target.string()); | ||
EXPECT_FALSE(info.ok()); | ||
} | ||
|
||
} // namespace | ||
} // namespace Carbon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters