From 3c402ffa0f2d53c64c40e3bc7a00ebbb01618825 Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Tue, 25 Nov 2025 21:47:56 -0500 Subject: [PATCH 1/5] feat!: better path deps and move rustify out of project --- Makefile | 4 +- cabin.toml | 2 +- lib/Manifest.cc | 185 ++++++++++- rustify/cabin.toml | 8 + .../include}/Rustify/Result.hpp | 0 .../include}/Rustify/Tests.hpp | 0 tests/deps_path.cc | 306 ++++++++++++++++++ tests/new.cc | 19 ++ 8 files changed, 508 insertions(+), 16 deletions(-) create mode 100644 rustify/cabin.toml rename {include => rustify/include}/Rustify/Result.hpp (100%) rename {include => rustify/include}/Rustify/Tests.hpp (100%) create mode 100644 tests/deps_path.cc diff --git a/Makefile b/Makefile index 4bf6dee03..dc8525a45 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CUSTOM_CXXFLAGS := $(shell grep -m1 cxxflags cabin.toml | sed 's/cxxflags = \[// # Git dependency versions TOML11_VER := $(shell grep -m1 toml11 cabin.toml | sed 's/.*tag = \(.*\)}/\1/' | tr -d '"') -RESULT_VER := $(shell grep -m1 cpp-result cabin.toml | sed 's/.*tag = \(.*\)}/\1/' | tr -d '"') +RESULT_VER := $(shell grep -m1 cpp-result rustify/cabin.toml | sed 's/.*tag = \(.*\)}/\1/' | tr -d '"') GIT_DEPS := $(O)/DEPS/toml11 $(O)/DEPS/mitama-cpp-result @@ -44,7 +44,7 @@ DEFINES := -DCABIN_CABIN_PKG_VERSION='"$(VERSION)"' \ -DCABIN_CABIN_COMMIT_HASH='"$(COMMIT_HASH)"' \ -DCABIN_CABIN_COMMIT_SHORT_HASH='"$(COMMIT_SHORT_HASH)"' \ -DCABIN_CABIN_COMMIT_DATE='"$(COMMIT_DATE)"' -INCLUDES := -Iinclude -Isrc -isystem $(O)/DEPS/toml11/include \ +INCLUDES := -Iinclude -Isrc -Irustify/include -isystem $(O)/DEPS/toml11/include \ -isystem $(O)/DEPS/mitama-cpp-result/include CXXFLAGS := -std=c++$(EDITION) -fdiagnostics-color $(CUSTOM_CXXFLAGS) \ diff --git a/cabin.toml b/cabin.toml index a7020f432..75d664bf5 100644 --- a/cabin.toml +++ b/cabin.toml @@ -12,13 +12,13 @@ version = "0.13.0" [dependencies] toml11 = {git = "https://github.com/ToruNiina/toml11.git", tag = "v4.4.0"} -mitama-cpp-result = {git = "https://github.com/loliGothicK/mitama-cpp-result.git", tag = "v11.0.0"} fmt = {version = ">=9 && <13", system = true} spdlog = {version = ">=1.8 && <2", system = true} libcurl = {version = ">=7.79.1 && <9", system = true} libgit2 = {version = ">=1.7 && <1.10", system = true} nlohmann_json = {version = "3.10.5", system = true} tbb = {version = ">=2021.5.0 && <2023.0.0", system = true} +rustify = {path = "rustify"} [dev-dependencies] boost-ut = {git = "https://github.com/boost-ext/ut.git", tag = "v2.3.1"} diff --git a/lib/Manifest.cc b/lib/Manifest.cc index 9fc9fce6f..2bc47efd4 100644 --- a/lib/Manifest.cc +++ b/lib/Manifest.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,174 @@ static const std::unordered_set ALLOWED_CHARS = { '-', '_', '/', '.', '+' // allowed in the dependency name }; +template +struct Overloaded : Ts... { + using Ts::operator()...; +}; +template +Overloaded(Ts...) -> Overloaded; + +enum class DepKind : std::uint8_t { Git, Path, System }; + +struct DepKey { + DepKind kind; + std::string detail; + + bool operator==(const DepKey& other) const = default; +}; + +static fs::path resolveIncludeDir(const fs::path& installDir) { + fs::path includeDir = installDir / "include"; + if (fs::exists(includeDir) && fs::is_directory(includeDir) + && !fs::is_empty(includeDir)) { + return includeDir; + } + return installDir; +} + +static fs::path canonicalizePathDep(const fs::path& baseDir, + const std::string& relPath) { + std::error_code ec; + fs::path depRoot = fs::weakly_canonical(baseDir / relPath, ec); + if (ec) { + depRoot = fs::absolute(baseDir / relPath); + } + return depRoot; +} + +static Result +installDependencies(const Manifest& manifest, bool includeDevDeps, + std::unordered_map& seenDeps, + std::unordered_set& visited, + std::vector& installed); + +static Result +installPathDependency(const Manifest& manifest, const PathDependency& pathDep, + bool includeDevDeps, + std::unordered_map& seenDeps, + std::unordered_set& visited, + std::vector& installed) { + const fs::path basePath = manifest.path.parent_path(); + const fs::path depRoot = canonicalizePathDep(basePath, pathDep.path); + + Ensure(fs::exists(depRoot) && fs::is_directory(depRoot), + "{} can't be accessible as directory", depRoot.string()); + if (!visited.insert(depRoot).second) { + return Ok(); + } + + CompilerOpts pathOpts; + pathOpts.cFlags.includeDirs.emplace_back(resolveIncludeDir(depRoot), + /*isSystem=*/false); + + const fs::path depManifestPath = depRoot / Manifest::FILE_NAME; + Ensure(fs::exists(depManifestPath), "missing `{}` in path dependency {}", + Manifest::FILE_NAME, depRoot.string()); + const Manifest depManifest = Try(Manifest::tryParse(depManifestPath, false)); + + std::vector nestedDeps; + Try(installDependencies(depManifest, includeDevDeps, seenDeps, visited, + nestedDeps)); + for (const CompilerOpts& opts : nestedDeps) { + pathOpts.merge(opts); + } + + installed.emplace_back(std::move(pathOpts)); + return Ok(); +} + +static DepKey makeDepKey(const Manifest& manifest, const Dependency& dep) { + const fs::path basePath = manifest.path.parent_path(); + return std::visit( + Overloaded{ + [&](const GitDependency& gitDep) -> DepKey { + std::string detail = gitDep.url; + if (gitDep.target.has_value()) { + detail.push_back('#'); + detail.append(gitDep.target.value()); + } + return DepKey{ .kind = DepKind::Git, .detail = std::move(detail) }; + }, + [&](const SystemDependency& sysDep) -> DepKey { + return DepKey{ .kind = DepKind::System, + .detail = sysDep.versionReq.toString() }; + }, + [&](const PathDependency& pathDep) -> DepKey { + const fs::path canon = canonicalizePathDep(basePath, pathDep.path); + return DepKey{ .kind = DepKind::Path, + .detail = canon.generic_string() }; + }, + }, + dep); +} + +static const std::string& depName(const Dependency& dep) { + return std::visit( + Overloaded{ + [](const GitDependency& gitDep) -> const std::string& { + return gitDep.name; + }, + [](const SystemDependency& sysDep) -> const std::string& { + return sysDep.name; + }, + [](const PathDependency& pathDep) -> const std::string& { + return pathDep.name; + }, + }, + dep); +} + +static Result rememberDep(const Manifest& manifest, const Dependency& dep, + std::unordered_map& seen) { + const DepKey key = makeDepKey(manifest, dep); + const std::string& name = depName(dep); + const auto [it, inserted] = seen.emplace(name, key); + if (inserted) { + return Ok(); + } + if (it->second == key) { + return Ok(); + } + Bail("dependency `{}` conflicts across manifests", name); +} + +static Result +installDependencies(const Manifest& manifest, const bool includeDevDeps, + std::unordered_map& seenDeps, + std::unordered_set& visited, + std::vector& installed) { + const auto installOne = [&](const Dependency& dep) -> Result { + Try(rememberDep(manifest, dep, seenDeps)); + return std::visit(Overloaded{ + [&](const GitDependency& gitDep) -> Result { + installed.emplace_back(Try(gitDep.install())); + return Ok(); + }, + [&](const SystemDependency& sysDep) -> Result { + installed.emplace_back(Try(sysDep.install())); + return Ok(); + }, + [&](const PathDependency& pathDep) -> Result { + return installPathDependency( + manifest, pathDep, includeDevDeps, seenDeps, + visited, installed); + }, + }, + dep); + }; + + for (const auto& dep : manifest.dependencies) { + Try(installOne(dep)); + } + if (includeDevDeps) { + for (const auto& dep : manifest.devDependencies) { + Try(installOne(dep)); + } + } + + return Ok(); +} + Result Edition::tryFromString(std::string str) noexcept { if (str == "98") { return Ok(Edition(Edition::Cpp98, std::move(str))); @@ -432,20 +601,10 @@ Result Manifest::findPath(fs::path candidateDir) noexcept { Result> Manifest::installDeps(const bool includeDevDeps) const { + std::unordered_map seenDeps; + std::unordered_set visited; std::vector installed; - const auto install = [&](const auto& arg) -> Result { - installed.emplace_back(Try(arg.install())); - return Ok(); - }; - - for (const auto& dep : dependencies) { - Try(std::visit(install, dep)); - } - if (includeDevDeps) { - for (const auto& dep : devDependencies) { - Try(std::visit(install, dep)); - } - } + Try(installDependencies(*this, includeDevDeps, seenDeps, visited, installed)); return Ok(installed); } diff --git a/rustify/cabin.toml b/rustify/cabin.toml new file mode 100644 index 000000000..436093dfe --- /dev/null +++ b/rustify/cabin.toml @@ -0,0 +1,8 @@ +[package] +name = "rustify" +version = "0.1.0" +edition = "23" + +[dependencies] +fmt = {version = ">=9 && <13", system = true} +mitama-cpp-result = {git = "https://github.com/loliGothicK/mitama-cpp-result.git", tag = "v11.0.0"} diff --git a/include/Rustify/Result.hpp b/rustify/include/Rustify/Result.hpp similarity index 100% rename from include/Rustify/Result.hpp rename to rustify/include/Rustify/Result.hpp diff --git a/include/Rustify/Tests.hpp b/rustify/include/Rustify/Tests.hpp similarity index 100% rename from include/Rustify/Tests.hpp rename to rustify/include/Rustify/Tests.hpp diff --git a/tests/deps_path.cc b/tests/deps_path.cc new file mode 100644 index 000000000..751f7d5f5 --- /dev/null +++ b/tests/deps_path.cc @@ -0,0 +1,306 @@ +#include "helpers.hpp" + +#include +#include +#include + +int main() { + using boost::ut::expect; + using boost::ut::operator""_test; + namespace fs = std::filesystem; + + "path dependency installs transitive deps"_test = [] { + const tests::TempDir tmp; + + const fs::path innerRoot = tmp.path / "inner"; + fs::create_directories(innerRoot / "include" / "inner"); + tests::writeFile(innerRoot / "cabin.toml", + R"([package] +name = "inner" +version = "0.1.0" +edition = "23" +)"); + tests::writeFile(innerRoot / "include" / "inner" / "inner.hpp", + R"(#pragma once + +inline int inner_value() { return 5; } +)"); + + const fs::path depRoot = tmp.path / "dep"; + fs::create_directories(depRoot / "include" / "dep"); + tests::writeFile(depRoot / "cabin.toml", + R"([package] +name = "dep" +version = "0.1.0" +edition = "23" + +[dependencies] +inner = {path = "../inner"} +)"); + tests::writeFile(depRoot / "include" / "dep" / "dep.hpp", + R"(#pragma once + +#include "inner/inner.hpp" + +inline int dep_value() { return inner_value(); } +)"); + + const fs::path appRoot = tmp.path / "app"; + fs::create_directories(appRoot / "src"); + tests::writeFile(appRoot / "cabin.toml", + R"([package] +name = "app" +version = "0.1.0" +edition = "23" + +[dependencies] +dep = {path = "../dep"} +)"); + tests::writeFile(appRoot / "src" / "main.cc", + R"(#include "dep/dep.hpp" + +int main() { + return dep_value() == 5 ? 0 : 1; +} +)"); + + const auto result = tests::runCabin({ "build" }, appRoot).unwrap(); + + expect(result.status.success()) << result.status.toString(); + }; + + "path dependency can depend on another path dependency"_test = [] { + const tests::TempDir tmp; + + const fs::path utilRoot = tmp.path / "util"; + fs::create_directories(utilRoot / "include" / "util"); + tests::writeFile(utilRoot / "cabin.toml", + R"([package] +name = "util" +version = "0.1.0" +edition = "23" +)"); + tests::writeFile(utilRoot / "include" / "util" / "util.hpp", + R"(#pragma once + +inline int util_value() { return 42; } +)"); + + const fs::path depRoot = tmp.path / "dep"; + fs::create_directories(depRoot / "include" / "dep"); + tests::writeFile(depRoot / "cabin.toml", + R"([package] +name = "dep" +version = "0.1.0" +edition = "23" + +[dependencies] +util = {path = "../util"} +)"); + tests::writeFile(depRoot / "include" / "dep" / "dep.hpp", + R"(#pragma once + +#include "util/util.hpp" + +inline int dep_value() { return util_value(); } +)"); + + const fs::path appRoot = tmp.path / "app"; + fs::create_directories(appRoot / "src"); + tests::writeFile(appRoot / "cabin.toml", + R"([package] +name = "app" +version = "0.1.0" +edition = "23" + +[dependencies] +dep = {path = "../dep"} +)"); + tests::writeFile(appRoot / "src" / "main.cc", + R"(#include "dep/dep.hpp" + +int main() { + return dep_value() == 42 ? 0 : 1; +} +)"); + + const auto result = tests::runCabin({ "build" }, appRoot).unwrap(); + expect(result.status.success()) << result.status.toString(); + }; + + "path dependency uses root when include/ is absent"_test = [] { + const tests::TempDir tmp; + + const fs::path depRoot = tmp.path / "dep"; + fs::create_directories(depRoot); + tests::writeFile(depRoot / "cabin.toml", + R"([package] +name = "dep" +version = "0.1.0" +edition = "23" +)"); + tests::writeFile(depRoot / "dep.hpp", + R"(#pragma once + +inline int dep_value() { return 7; } +)"); + + const fs::path appRoot = tmp.path / "app"; + fs::create_directories(appRoot / "src"); + tests::writeFile(appRoot / "cabin.toml", + R"([package] +name = "app" +version = "0.1.0" +edition = "23" + +[dependencies] +dep = {path = "../dep"} +)"); + tests::writeFile(appRoot / "src" / "main.cc", + R"(#include "dep.hpp" + +int main() { return dep_value() == 7 ? 0 : 1; } +)"); + + const auto result = tests::runCabin({ "build" }, appRoot).unwrap(); + expect(result.status.success()) << result.status.toString(); + }; + + "root and dep agree on shared dep"_test = [] { + const tests::TempDir tmp; + + const fs::path sharedRoot = tmp.path / "shared"; + fs::create_directories(sharedRoot / "include" / "shared"); + tests::writeFile(sharedRoot / "cabin.toml", + R"([package] +name = "shared" +version = "0.1.0" +edition = "23" +)"); + tests::writeFile(sharedRoot / "include" / "shared" / "shared.hpp", + R"(#pragma once + +inline int shared_value() { return 11; } +)"); + + const fs::path depRoot = tmp.path / "dep"; + fs::create_directories(depRoot / "include" / "dep"); + tests::writeFile(depRoot / "cabin.toml", + R"([package] +name = "dep" +version = "0.1.0" +edition = "23" + +[dependencies] +fmt = {path = "../shared"} +)"); + tests::writeFile(depRoot / "include" / "dep" / "dep.hpp", + R"(#pragma once + +#include "shared/shared.hpp" + +inline int dep_value() { return shared_value(); } +)"); + + const fs::path appRoot = tmp.path / "app"; + fs::create_directories(appRoot / "src"); + tests::writeFile(appRoot / "cabin.toml", + R"([package] +name = "app" +version = "0.1.0" +edition = "23" + +[dependencies] +dep = {path = "../dep"} +fmt = {path = "../shared"} +)"); + tests::writeFile(appRoot / "src" / "main.cc", + R"(#include "dep/dep.hpp" +#include "shared/shared.hpp" + +int main() { + return dep_value() == shared_value() ? 0 : 1; +} +)"); + + const auto result = tests::runCabin({ "build" }, appRoot).unwrap(); + expect(result.status.success()) << result.status.toString(); + }; + + "root and dep conflict on shared dep"_test = [] { + const tests::TempDir tmp; + + const fs::path sharedRoot = tmp.path / "shared"; + fs::create_directories(sharedRoot / "include" / "shared"); + tests::writeFile(sharedRoot / "cabin.toml", + R"([package] +name = "shared" +version = "0.1.0" +edition = "23" +)"); + tests::writeFile(sharedRoot / "include" / "shared" / "shared.hpp", + R"(#pragma once + +inline int shared_value() { return 11; } +)"); + + const fs::path otherRoot = tmp.path / "other"; + fs::create_directories(otherRoot / "include" / "other"); + tests::writeFile(otherRoot / "cabin.toml", + R"([package] +name = "other" +version = "0.1.0" +edition = "23" +)"); + tests::writeFile(otherRoot / "include" / "other" / "other.hpp", + R"(#pragma once + +inline int other_value() { return 22; } +)"); + + const fs::path depRoot = tmp.path / "dep"; + fs::create_directories(depRoot / "include" / "dep"); + tests::writeFile(depRoot / "cabin.toml", + R"([package] +name = "dep" +version = "0.1.0" +edition = "23" + +[dependencies] +fmt = {path = "../other"} +)"); + tests::writeFile(depRoot / "include" / "dep" / "dep.hpp", + R"(#pragma once + +#include "other/other.hpp" + +inline int dep_value() { return other_value(); } +)"); + + const fs::path appRoot = tmp.path / "app"; + fs::create_directories(appRoot / "src"); + tests::writeFile(appRoot / "cabin.toml", + R"([package] +name = "app" +version = "0.1.0" +edition = "23" + +[dependencies] +dep = {path = "../dep"} +fmt = {path = "../shared"} +)"); + tests::writeFile(appRoot / "src" / "main.cc", + R"(#include "dep/dep.hpp" +#include "shared/shared.hpp" + +int main() { + return dep_value() == shared_value() ? 0 : 1; +} +)"); + + const auto result = tests::runCabin({ "build" }, appRoot).unwrap(); + expect(!result.status.success()); + const auto err = tests::sanitizeOutput(result.err); + expect(err.contains("dependency `fmt` conflicts across manifests")); + }; +} diff --git a/tests/new.cc b/tests/new.cc index 0643bcc47..25bb59a6d 100644 --- a/tests/new.cc +++ b/tests/new.cc @@ -57,6 +57,25 @@ int main() { expect(sanitizedErr == expectedErr); }; + "cabin new hyphenated library"_test = [] { + const tests::TempDir tmp; + const auto result = + tests::runCabin({ "new", "--lib", "my-lib" }, tmp.path).unwrap(); + + expect(result.status.success()) << result.status.toString(); + + const auto project = tmp.path / "my-lib"; + const auto header = project / "include" / "my-lib" / "my-lib.hpp"; + const auto impl = project / "lib" / "my-lib.cc"; + expect(tests::fs::is_regular_file(header)); + expect(tests::fs::is_regular_file(impl)); + + const auto headerContent = tests::readFile(header); + expect(headerContent.contains("namespace my_lib")); + const auto implContent = tests::readFile(impl); + expect(implContent.contains("namespace my_lib")); + }; + "cabin new requires name"_test = [] { const tests::TempDir tmp; const auto result = tests::runCabin({ "new" }, tmp.path).unwrap(); From b00ecbff6a93ea44ee04e08cd2aad9e98e62f18d Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Tue, 25 Nov 2025 21:52:34 -0500 Subject: [PATCH 2/5] fix: docker build --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 4bca8b659..93912e40e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY .git . COPY Makefile . COPY include ./include/ COPY lib ./lib/ +COPY rustify ./rustify/ COPY src ./src/ RUN make BUILD=release install From 93656612f09a2deaf55b3378bc32f40b6e0c0cc6 Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Tue, 25 Nov 2025 22:51:27 -0500 Subject: [PATCH 3/5] fix: ignore path deps --- src/Cmd/Fmt.cc | 20 ++++++++++++++++++++ src/Cmd/Lint.cc | 29 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/Cmd/Fmt.cc b/src/Cmd/Fmt.cc index 448451c1f..80b3078ec 100644 --- a/src/Cmd/Fmt.cc +++ b/src/Cmd/Fmt.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,19 @@ struct TargetFile { : path(std::move(path)), modTime(fs::last_write_time(this->path)) {} }; +static bool isWithin(const fs::path& base, const fs::path& candidate) { + std::error_code ec; + const fs::path rel = fs::relative(candidate, base, ec); + if (ec) { + return false; + } + if (rel.empty()) { + return true; + } + const auto first = rel.begin(); + return first != rel.end() && *first != ".."; +} + static std::vector collectFormatTargets(const fs::path& manifestDir, const std::vector& excludes, @@ -76,6 +90,12 @@ collectFormatTargets(const fs::path& manifestDir, if (entry->is_directory()) { const std::string path = fs::relative(entry->path(), manifestDir).string(); + if (fs::exists(entry->path() / Manifest::FILE_NAME) + && !isWithin(manifestDir, entry->path())) { + spdlog::debug("Ignore nested project: {}", path); + entry.disable_recursion_pending(); + continue; + } if ((hasGitRepo && repo.isIgnored(path)) || isExcluded(path)) { spdlog::debug("Ignore: {}", path); entry.disable_recursion_pending(); diff --git a/src/Cmd/Lint.cc b/src/Cmd/Lint.cc index 5da9d0ca2..49487716d 100644 --- a/src/Cmd/Lint.cc +++ b/src/Cmd/Lint.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,29 @@ struct LintArgs { std::vector excludes; }; +static std::vector +collectNestedProjectExcludes(const fs::path& manifestDir) { + std::vector excludes; + for (auto entry = fs::recursive_directory_iterator(manifestDir); + entry != fs::recursive_directory_iterator(); ++entry) { + if (!entry->is_directory()) { + continue; + } + if (entry->path() == manifestDir) { + continue; + } + if (fs::exists(entry->path() / Manifest::FILE_NAME)) { + std::error_code ec; + const fs::path rel = fs::relative(entry->path(), manifestDir, ec); + if (!ec) { + excludes.emplace_back("--exclude=" + rel.generic_string()); + } + entry.disable_recursion_pending(); + } + } + return excludes; +} + static Result lint(const std::string_view name, const std::vector& cpplintArgs, bool useVcsIgnoreFiles) { @@ -100,6 +124,11 @@ static Result lintMain(const CliArgsView args) { const auto manifest = Try(Manifest::tryParse()); std::vector cpplintArgs = std::move(lintArgs.excludes); + const fs::path projectDir = manifest.path.parent_path(); + const std::vector nestedExcludes = + collectNestedProjectExcludes(projectDir); + cpplintArgs.insert(cpplintArgs.end(), nestedExcludes.begin(), + nestedExcludes.end()); if (fs::exists("CPPLINT.cfg")) { spdlog::debug("Using CPPLINT.cfg for lint ..."); return lint(manifest.package.name, cpplintArgs, useVcsIgnoreFiles); From d4b09358228f14f6bcee72680f5d25b4cc68359c Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:01:26 -0500 Subject: [PATCH 4/5] fix: address reviews --- src/Cmd/Fmt.cc | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/Cmd/Fmt.cc b/src/Cmd/Fmt.cc index 80b3078ec..0a7331052 100644 --- a/src/Cmd/Fmt.cc +++ b/src/Cmd/Fmt.cc @@ -45,19 +45,6 @@ struct TargetFile { : path(std::move(path)), modTime(fs::last_write_time(this->path)) {} }; -static bool isWithin(const fs::path& base, const fs::path& candidate) { - std::error_code ec; - const fs::path rel = fs::relative(candidate, base, ec); - if (ec) { - return false; - } - if (rel.empty()) { - return true; - } - const auto first = rel.begin(); - return first != rel.end() && *first != ".."; -} - static std::vector collectFormatTargets(const fs::path& manifestDir, const std::vector& excludes, @@ -90,8 +77,8 @@ collectFormatTargets(const fs::path& manifestDir, if (entry->is_directory()) { const std::string path = fs::relative(entry->path(), manifestDir).string(); - if (fs::exists(entry->path() / Manifest::FILE_NAME) - && !isWithin(manifestDir, entry->path())) { + if (entry->path() != manifestDir + && fs::exists(entry->path() / Manifest::FILE_NAME)) { spdlog::debug("Ignore nested project: {}", path); entry.disable_recursion_pending(); continue; From a65a85e1ba94df1f0dcf58f26e08359c6a54c147 Mon Sep 17 00:00:00 2001 From: Ken Matsui <26405363+ken-matsui@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:16:06 -0500 Subject: [PATCH 5/5] fix: address reviews --- lib/Manifest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Manifest.cc b/lib/Manifest.cc index 2bc47efd4..482718593 100644 --- a/lib/Manifest.cc +++ b/lib/Manifest.cc @@ -188,7 +188,7 @@ installDependencies(const Manifest& manifest, const bool includeDevDeps, for (const auto& dep : manifest.dependencies) { Try(installOne(dep)); } - if (includeDevDeps) { + if (includeDevDeps && manifest.path == Manifest::tryParse().unwrap().path) { for (const auto& dep : manifest.devDependencies) { Try(installOne(dep)); }