Skip to content
Open
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
6 changes: 2 additions & 4 deletions include/Builder/DepGraph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@ namespace fs = std::filesystem;

class DepGraph {
public:
explicit DepGraph(fs::path rootPath) : rootPath(std::move(rootPath)) {}
explicit DepGraph(Manifest manifest) : rootManifest(std::move(manifest)) {}

rs::Result<void> resolve();
rs::Result<BuildGraph>
computeBuildGraph(const BuildProfile& buildProfile) const;

private:
fs::path rootPath;
std::optional<Manifest> rootManifest;
Manifest rootManifest;
};

} // namespace cabin
10 changes: 7 additions & 3 deletions include/Manifest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class Manifest {
const std::vector<Dependency> devDependencies;
const std::unordered_map<BuildProfile, Profile> profiles;
const Lint lint;
const fs::path targetDir;

static rs::Result<Manifest> tryParse(fs::path path = fs::current_path()
/ FILE_NAME,
Expand All @@ -133,15 +134,18 @@ class Manifest {
installDeps(bool includeDevDeps, const BuildProfile& buildProfile,
bool suppressDepDiag = false) const;

Manifest withTargetDir(fs::path targetDir) const;

private:
Manifest(fs::path path, Package package, std::vector<Dependency> dependencies,
std::vector<Dependency> devDependencies,
std::unordered_map<BuildProfile, Profile> profiles,
Lint lint) noexcept
std::unordered_map<BuildProfile, Profile> profiles, Lint lint,
fs::path targetDir) noexcept
: path(std::move(path)), package(std::move(package)),
dependencies(std::move(dependencies)),
devDependencies(std::move(devDependencies)),
profiles(std::move(profiles)), lint(std::move(lint)) {}
profiles(std::move(profiles)), lint(std::move(lint)),
targetDir(std::move(targetDir)) {}
};

rs::Result<void> validatePackageName(std::string_view name) noexcept;
Expand Down
9 changes: 1 addition & 8 deletions lib/Builder/DepGraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,9 @@

namespace cabin {

rs::Result<void> DepGraph::resolve() {
rootManifest.emplace(
rs_try(Manifest::tryParse(rootPath / Manifest::FILE_NAME)));
return rs::Ok();
}

rs::Result<BuildGraph>
DepGraph::computeBuildGraph(const BuildProfile& buildProfile) const {
rs_ensure(rootManifest.has_value(), "dependency graph not resolved");
return BuildGraph::create(*rootManifest, buildProfile);
return BuildGraph::create(rootManifest, buildProfile);
}

} // namespace cabin
25 changes: 23 additions & 2 deletions lib/Manifest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ installPathDependency(const Manifest& manifest, const PathDependency& pathDep,
depRoot.string());
}

Builder depBuilder(depRoot, buildProfile);
Builder depBuilder(depManifest, buildProfile);
ScheduleOptions depOptions;
depOptions.includeDevDeps = includeDevDeps;
depOptions.suppressAnalysisLog = true;
Expand Down Expand Up @@ -628,6 +628,19 @@ parseSystemDep(const std::string& name, const toml::table& info) noexcept {
return rs::Ok(SystemDependency(name, rs_try(VersionReq::parse(versionReq))));
}

static rs::Result<fs::path> parseTargetDir(const toml::value& val,
const char* key) noexcept {
const auto targetDir = toml::try_find<std::string>(val, key, "target-dir");
if (!targetDir.is_err()) {
spdlog::debug("[{}] not found or not a table", key);
return rs::Ok(targetDir.unwrap());
}
if (const char* env = std::getenv("CABIN_TARGET_DIR")) {
return rs::Ok(std::string(env));
}
return rs::Ok(fs::path("cabin-out"));
}

static rs::Result<std::vector<Dependency>>
parseDependencies(const toml::value& val, const char* key) noexcept {
const auto tomlDeps = toml::try_find<toml::table>(val, key);
Expand Down Expand Up @@ -667,6 +680,11 @@ rs::Result<Manifest> Manifest::tryParse(fs::path path,
return tryFromToml(toml::parse(path), path);
}

Manifest Manifest::withTargetDir(fs::path targetDir) const {
return Manifest{ path, package, dependencies, devDependencies,
profiles, lint, std::move(targetDir) };
}

rs::Result<Manifest> Manifest::tryFromToml(const toml::value& data,
fs::path path) noexcept {
auto package = rs_try(Package::tryFromToml(data));
Expand All @@ -678,9 +696,12 @@ rs::Result<Manifest> Manifest::tryFromToml(const toml::value& data,
rs_try(parseProfiles(data));
auto lint = rs_try(Lint::tryFromToml(data));

auto targetDir = rs_try(parseTargetDir(data, "build"));

return rs::Ok(Manifest(std::move(path), std::move(package),
std::move(dependencies), std::move(devDependencies),
std::move(profiles), std::move(lint)));
std::move(profiles), std::move(lint),
std::move(targetDir)));
}

rs::Result<fs::path> Manifest::findPath(fs::path candidateDir) noexcept {
Expand Down
8 changes: 4 additions & 4 deletions src/Builder/Builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@

namespace cabin {

Builder::Builder(fs::path rootPath, BuildProfile buildProfile)
: basePath(std::move(rootPath)), buildProfile(std::move(buildProfile)),
depGraph(basePath) {}
Builder::Builder(Manifest rootManifest, BuildProfile buildProfile)
: basePath(rootManifest.path.parent_path()),
buildProfile(std::move(buildProfile)), depGraph(std::move(rootManifest)) {
}

rs::Result<void> Builder::schedule(const ScheduleOptions& options) {
this->options = options;

rs_try(depGraph.resolve());
graphState.emplace(rs_try(depGraph.computeBuildGraph(buildProfile)));

const bool logAnalysis = !options.suppressAnalysisLog;
Expand Down
2 changes: 1 addition & 1 deletion src/Builder/Builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct ScheduleOptions {

class Builder {
public:
Builder(fs::path rootPath, BuildProfile buildProfile);
Builder(Manifest rootManifest, BuildProfile buildProfile);

rs::Result<void> schedule(const ScheduleOptions& options = {});
rs::Result<void> build();
Expand Down
3 changes: 2 additions & 1 deletion src/Builder/Project.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "TermColor.hpp"

#include <filesystem>
#include <ranges>
#include <rs/result.hpp>
#include <spdlog/spdlog.h>
#include <string>
Expand Down Expand Up @@ -78,7 +79,7 @@ static std::vector<std::string> getEnvFlags(const char* name) {
Project::Project(const BuildProfile& buildProfile, Manifest m,
CompilerOpts opts)
: rootPath(m.path.parent_path()),
outBasePath(rootPath / "cabin-out" / fmt::format("{}", buildProfile)),
outBasePath(rootPath / m.targetDir / fmt::format("{}", buildProfile)),
buildOutPath(outBasePath / (m.package.name + ".d")),
unittestOutPath(outBasePath / "unit"),
integrationTestOutPath(outBasePath / "intg"), manifest(std::move(m)),
Expand Down
14 changes: 12 additions & 2 deletions src/Cmd/Build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>

namespace cabin {
Expand All @@ -33,12 +34,14 @@ const Subcmd BUILD_CMD =
.addOpt(Opt{ "--compdb" }.setDesc(
"Generate compilation database instead of building"))
.addOpt(OPT_JOBS)
.addOpt(OPT_TARGET_DIR)
.setMainFn(buildMain);

static rs::Result<void> buildMain(const CliArgsView args) {
// Parse args
BuildProfile buildProfile = BuildProfile::Dev;
bool buildCompdb = false;
std::optional<std::string_view> targetDir;
for (auto itr = args.begin(); itr != args.end(); ++itr) {
const std::string_view arg = *itr;

Expand All @@ -63,13 +66,20 @@ static rs::Result<void> buildMain(const CliArgsView args) {
std::from_chars(nextArg.begin(), nextArg.end(), numThreads);
rs_ensure(ec == std::errc(), "invalid number of threads: {}", nextArg);
setParallelism(numThreads);
} else if (arg == "--target-dir") {
if (itr + 1 == args.end()) {
return Subcmd::missingOptArgumentFor(arg);
}
targetDir.emplace(*++itr);
} else {
return BUILD_CMD.noSuchArg(arg);
}
}

const Manifest manifest = rs_try(Manifest::tryParse());
Builder builder(manifest.path.parent_path(), buildProfile);
const Manifest manifest = [targetDir](const Manifest& m) -> Manifest {
return targetDir.has_value() ? m.withTargetDir(targetDir.value()) : m;
}(rs_try(Manifest::tryParse()));
Builder builder(manifest, buildProfile);
rs_try(builder.schedule());

if (buildCompdb) {
Expand Down
6 changes: 6 additions & 0 deletions src/Cmd/Common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@ inline const Opt OPT_JOBS =
.setPlaceholder("<NUM>")
.setDefault(NUM_DEFAULT_THREADS);

inline const Opt OPT_TARGET_DIR =
Opt{ "--target-dir" }
.setDesc(
"Set directory for all generated artifacts and intermediate files")
.setPlaceholder("<DIRECTORY>");

} // namespace cabin
14 changes: 12 additions & 2 deletions src/Cmd/Run.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>

namespace cabin {
Expand All @@ -30,6 +31,7 @@ const Subcmd RUN_CMD =
.setDesc("Build and execute src/main.cc")
.addOpt(OPT_RELEASE)
.addOpt(OPT_JOBS)
.addOpt(OPT_TARGET_DIR)
.setArg(Arg{ "args" }
.setDesc("Arguments passed to the program")
.setVariadic(true)
Expand All @@ -40,6 +42,7 @@ static rs::Result<void> runMain(const CliArgsView args) {
// Parse args
BuildProfile buildProfile = BuildProfile::Dev;
auto itr = args.begin();
std::optional<std::string_view> targetDir;
for (; itr != args.end(); ++itr) {
const std::string_view arg = *itr;

Expand All @@ -61,6 +64,11 @@ static rs::Result<void> runMain(const CliArgsView args) {
std::from_chars(nextArg.begin(), nextArg.end(), numThreads);
rs_ensure(ec == std::errc(), "invalid number of threads: {}", nextArg);
setParallelism(numThreads);
} else if (arg == "--target-dir") {
if (itr + 1 == args.end()) {
return Subcmd::missingOptArgumentFor(arg);
}
targetDir.emplace(*++itr);
} else {
// Unknown argument is the start of the program arguments.
break;
Expand All @@ -72,8 +80,10 @@ static rs::Result<void> runMain(const CliArgsView args) {
runArgs.emplace_back(*itr);
}

const auto manifest = rs_try(Manifest::tryParse());
Builder builder(manifest.path.parent_path(), buildProfile);
const Manifest manifest = [targetDir](const Manifest& m) -> Manifest {
return targetDir.has_value() ? m.withTargetDir(targetDir.value()) : m;
}(rs_try(Manifest::tryParse()));
Builder builder(manifest, buildProfile);
rs_try(builder.schedule());
rs_try(builder.build());

Expand Down
13 changes: 11 additions & 2 deletions src/Cmd/Test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const Subcmd TEST_CMD = //
.setShort("t")
.setDesc("Run the tests of a local package")
.addOpt(OPT_JOBS)
.addOpt(OPT_TARGET_DIR)
.addOpt(Opt{ "--coverage" }.setDesc("Enable code coverage analysis"))
.setArg(
Arg{ "TESTNAME" }.setRequired(false).setDesc("Test name to launch"))
Expand All @@ -34,6 +35,7 @@ const Subcmd TEST_CMD = //
static rs::Result<void> testMain(const CliArgsView args) {
bool enableCoverage = false;
std::optional<std::string> testName;
std::optional<std::string_view> targetDir;

for (auto itr = args.begin(); itr != args.end(); ++itr) {
const std::string_view arg = *itr;
Expand All @@ -58,15 +60,22 @@ static rs::Result<void> testMain(const CliArgsView args) {
setParallelism(numThreads);
} else if (arg == "--coverage") {
enableCoverage = true;
} else if (arg == "--target-dir") {
if (itr + 1 == args.end()) {
return Subcmd::missingOptArgumentFor(arg);
}
targetDir.emplace(*++itr);
} else if (!testName) {
testName = arg;
} else {
return TEST_CMD.noSuchArg(arg);
}
}

const Manifest manifest = rs_try(Manifest::tryParse());
Builder builder(manifest.path.parent_path(), BuildProfile::Test);
const Manifest manifest = [targetDir](const Manifest& m) -> Manifest {
return targetDir.has_value() ? m.withTargetDir(targetDir.value()) : m;
}(rs_try(Manifest::tryParse()));
Builder builder(manifest, BuildProfile::Test);
rs_try(builder.schedule(ScheduleOptions{ .includeDevDeps = true,
.enableCoverage = enableCoverage }));
return builder.test(std::move(testName));
Expand Down
2 changes: 1 addition & 1 deletion src/Cmd/Tidy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ static rs::Result<void> tidyMain(const CliArgsView args) {
BuildProfile::Test };
bool isFirstProfile = true;
for (const BuildProfile& profile : profiles) {
Builder builder(projectRoot, profile);
Builder builder(manifest, profile);
const bool includeDevDeps = (profile == BuildProfile::Test);
rs_try(builder.schedule(ScheduleOptions{
.includeDevDeps = includeDevDeps,
Expand Down
9 changes: 9 additions & 0 deletions tests/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ int main() {
using boost::ut::expect;
using boost::ut::operator""_test;

"cabin build uses cli target-dir"_test = [] {
const tests::TempDir tmp;
tests::runCabin({ "new", "hello_world" }, tmp.path).unwrap();
const auto project = tmp.path / "hello_world";

tests::runCabin({ "build", "--target-dir", "tmpdir" }, project).unwrap();
expect(tests::fs::is_directory(project / "tmpdir" / "dev"));
};

"cabin build emits ninja"_test = [] {
const tests::TempDir tmp;
tests::runCabin({ "new", "ninja_project" }, tmp.path).unwrap();
Expand Down
9 changes: 9 additions & 0 deletions tests/run.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ int main() {
using boost::ut::expect;
using boost::ut::operator""_test;

"cabin run uses cli target-dir"_test = [] {
const tests::TempDir tmp;
tests::runCabin({ "new", "hello_world" }, tmp.path).unwrap();
const auto project = tmp.path / "hello_world";

tests::runCabin({ "run", "--target-dir", "tmpdir" }, project).unwrap();
expect(tests::fs::is_directory(project / "tmpdir" / "dev"));
};

"cabin run"_test = [] {
const tests::TempDir tmp;
tests::runCabin({ "new", "hello_world" }, tmp.path).unwrap();
Expand Down
9 changes: 9 additions & 0 deletions tests/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ int main() {
using boost::ut::expect;
using boost::ut::operator""_test;

"cabin test uses cli target-dir"_test = [] {
const tests::TempDir tmp;
tests::runCabin({ "new", "hello_world" }, tmp.path).unwrap();
const auto project = tmp.path / "hello_world";

tests::runCabin({ "test", "--target-dir", "tmpdir" }, project).unwrap();
expect(tests::fs::is_directory(project / "tmpdir" / "test"));
};

"cabin test basic"_test = [] {
const tests::TempDir tmp;
tests::runCabin({ "new", "test_project" }, tmp.path).unwrap();
Expand Down
Loading