Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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) \
Expand Down
2 changes: 1 addition & 1 deletion cabin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down
185 changes: 172 additions & 13 deletions lib/Manifest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <spdlog/spdlog.h>
#include <string>
#include <string_view>
#include <system_error>
#include <toml.hpp>
#include <unordered_map>
#include <unordered_set>
Expand All @@ -28,6 +29,174 @@ static const std::unordered_set<char> ALLOWED_CHARS = {
'-', '_', '/', '.', '+' // allowed in the dependency name
};

template <typename... Ts>
struct Overloaded : Ts... {
using Ts::operator()...;
};
template <typename... Ts>
Overloaded(Ts...) -> Overloaded<Ts...>;

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<void>
installDependencies(const Manifest& manifest, bool includeDevDeps,
std::unordered_map<std::string, DepKey>& seenDeps,
std::unordered_set<fs::path>& visited,
std::vector<CompilerOpts>& installed);

static Result<void>
installPathDependency(const Manifest& manifest, const PathDependency& pathDep,
bool includeDevDeps,
std::unordered_map<std::string, DepKey>& seenDeps,
std::unordered_set<fs::path>& visited,
std::vector<CompilerOpts>& 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<CompilerOpts> 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<void> rememberDep(const Manifest& manifest, const Dependency& dep,
std::unordered_map<std::string, DepKey>& 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<void>
installDependencies(const Manifest& manifest, const bool includeDevDeps,
std::unordered_map<std::string, DepKey>& seenDeps,
std::unordered_set<fs::path>& visited,
std::vector<CompilerOpts>& installed) {
const auto installOne = [&](const Dependency& dep) -> Result<void> {
Try(rememberDep(manifest, dep, seenDeps));
return std::visit(Overloaded{
[&](const GitDependency& gitDep) -> Result<void> {
installed.emplace_back(Try(gitDep.install()));
return Ok();
},
[&](const SystemDependency& sysDep) -> Result<void> {
installed.emplace_back(Try(sysDep.install()));
return Ok();
},
[&](const PathDependency& pathDep) -> Result<void> {
return installPathDependency(
manifest, pathDep, includeDevDeps, seenDeps,
visited, installed);
},
},
dep);
};

for (const auto& dep : manifest.dependencies) {
Try(installOne(dep));
}
if (includeDevDeps && manifest.path == Manifest::tryParse().unwrap().path) {
for (const auto& dep : manifest.devDependencies) {
Try(installOne(dep));
}
}

return Ok();
}

Result<Edition> Edition::tryFromString(std::string str) noexcept {
if (str == "98") {
return Ok(Edition(Edition::Cpp98, std::move(str)));
Expand Down Expand Up @@ -432,20 +601,10 @@ Result<fs::path> Manifest::findPath(fs::path candidateDir) noexcept {

Result<std::vector<CompilerOpts>>
Manifest::installDeps(const bool includeDevDeps) const {
std::unordered_map<std::string, DepKey> seenDeps;
std::unordered_set<fs::path> visited;
std::vector<CompilerOpts> installed;
const auto install = [&](const auto& arg) -> Result<void> {
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);
}

Expand Down
8 changes: 8 additions & 0 deletions rustify/cabin.toml
Original file line number Diff line number Diff line change
@@ -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"}
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions src/Cmd/Fmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <spdlog/spdlog.h>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -76,6 +77,12 @@ collectFormatTargets(const fs::path& manifestDir,
if (entry->is_directory()) {
const std::string path =
fs::relative(entry->path(), manifestDir).string();
if (entry->path() != manifestDir
&& fs::exists(entry->path() / Manifest::FILE_NAME)) {
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();
Expand Down
29 changes: 29 additions & 0 deletions src/Cmd/Lint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <spdlog/spdlog.h>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>

Expand All @@ -34,6 +35,29 @@ struct LintArgs {
std::vector<std::string> excludes;
};

static std::vector<std::string>
collectNestedProjectExcludes(const fs::path& manifestDir) {
std::vector<std::string> 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<void> lint(const std::string_view name,
const std::vector<std::string>& cpplintArgs,
bool useVcsIgnoreFiles) {
Expand Down Expand Up @@ -100,6 +124,11 @@ static Result<void> lintMain(const CliArgsView args) {
const auto manifest = Try(Manifest::tryParse());

std::vector<std::string> cpplintArgs = std::move(lintArgs.excludes);
const fs::path projectDir = manifest.path.parent_path();
const std::vector<std::string> 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);
Expand Down
Loading
Loading