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
2 changes: 1 addition & 1 deletion include/Builder/BuildGraph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class BuildGraph {

Result<void> installDeps(bool includeDevDeps);
void enableCoverage();
Result<void> plan();
Result<void> plan(bool logAnalysis = true);
Result<void> writeBuildFilesIfNeeded() const;
Result<void> generateCompdb() const;

Expand Down
6 changes: 2 additions & 4 deletions lib/Builder/BuildGraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -839,11 +839,9 @@ Result<void> BuildGraph::generateCompdb() const {
return Ok();
}

Result<void> BuildGraph::plan() {
static bool loggedAnalysis = false;
if (!loggedAnalysis) {
Result<void> BuildGraph::plan(const bool logAnalysis) {
if (logAnalysis) {
Diag::info("Analyzing", "project dependencies...");
loggedAnalysis = true;
}

const bool buildProj = !isUpToDate("build.ninja");
Expand Down
3 changes: 2 additions & 1 deletion src/Builder/Builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Result<void> Builder::schedule(const ScheduleOptions& options) {
graphState->enableCoverage();
}
Try(graphState->installDeps(options.includeDevDeps));
Try(graphState->plan());
const bool logAnalysis = !options.suppressAnalysisLog;
Try(graphState->plan(logAnalysis));
outDir = graphState->outBasePath();
return Ok();
}
Expand Down
1 change: 1 addition & 0 deletions src/Builder/Builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace fs = std::filesystem;
struct ScheduleOptions {
bool includeDevDeps = false;
bool enableCoverage = false;
bool suppressAnalysisLog = false;
};

class Builder {
Expand Down
159 changes: 87 additions & 72 deletions src/Cmd/New.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
#include "Rustify/Result.hpp"

#include <cstdlib>
#include <format>
#include <fstream>
#include <spdlog/spdlog.h>
#include <string>
#include <string_view>
#include <vector>

namespace cabin {

Expand All @@ -26,57 +28,62 @@ const Subcmd NEW_CMD = //
.setArg(Arg{ "name" })
.setMainFn(newMain);

static constexpr std::string_view MAIN_CC =
"#include <print>\n\n"
"int main(int argc, char* argv[]) {\n"
" std::println(\"Hello, world!\");\n"
" return 0;\n"
"}\n";
static constexpr std::string_view MAIN_CC = R"(#include <print>

static constexpr std::string_view LIB_CC = "int libFunction() { return 0; }\n";
int main(int argc, char* argv[]) {
std::println("Hello, world!");
return 0;
}
)";

static std::string toNamespaceName(std::string_view projectName) {
return replaceAll(std::string(projectName), "-", "_");
}

static std::string getAuthor() noexcept {
try {
git2::Config config = git2::Config();
config.openDefault();
return config.getString("user.name") + " <" + config.getString("user.email")
+ ">";
return std::format("{} <{}>", config.getString("user.name"),
config.getString("user.email"));
} catch (const git2::Exception& e) {
spdlog::debug("{}", e.what());
return "";
}
}

std::string createCabinToml(const std::string_view projectName) noexcept {
std::string cabinToml = "[package]\n"
"name = \"";
cabinToml += projectName;
cabinToml += "\"\n"
"version = \"0.1.0\"\n"
"authors = [\"";
cabinToml += getAuthor();
cabinToml += "\"]\n"
"edition = \"23\"\n";
return cabinToml;
const std::string author = getAuthor();
return std::format(R"([package]
name = "{}"
version = "0.1.0"
authors = ["{}"]
edition = "23"
)",
projectName, author);
}

static std::string getHeader(const std::string_view projectName) noexcept {
const std::string projectNameUpper = toMacroName(projectName);
std::string header = "#ifndef " + projectNameUpper
+ "_HPP\n"
"#define "
+ projectNameUpper
+ "_HPP\n\n"
"namespace ";
header += projectName;
header += " {\n}\n\n"
"#endif // !"
+ projectNameUpper + "_HPP\n";
return header;
const std::string ns = toNamespaceName(projectName);
return std::format(R"(#ifndef {0}_HPP
#define {0}_HPP

namespace {1} {{
void hello_world();
}} // namespace {1}

#endif // !{0}_HPP
)",
projectNameUpper, ns);
}

static Result<void> writeToFile(std::ofstream& ofs, const fs::path& fpath,
const std::string_view text,
struct FileTemplate {
fs::path path;
std::string contents;
};

static Result<void> writeToFile(const fs::path& fpath, const std::string& text,
const bool skipIfExists = false) {
if (fs::exists(fpath)) {
if (skipIfExists) {
Expand All @@ -85,55 +92,63 @@ static Result<void> writeToFile(std::ofstream& ofs, const fs::path& fpath,
Bail("refusing to overwrite `{}`; file already exists", fpath.string());
}

ofs.open(fpath);
if (ofs.is_open()) {
ofs << text;
}
ofs.close();

if (!ofs) {
Bail("writing `{}` failed", fpath.string());
}
ofs.clear();
std::ofstream ofs(fpath, std::ios::trunc);
Ensure(ofs.is_open(), "opening `{}` failed", fpath.string());
ofs << text;
Ensure(static_cast<bool>(ofs), "writing `{}` failed", fpath.string());
return Ok();
}

Result<void> createProjectFiles(const bool isBin, const fs::path& root,
const std::string_view projectName,
const bool skipExisting) {
std::ofstream ofs;

std::vector<FileTemplate> templates;
if (isBin) {
fs::create_directories(root / fs::path("src"));
fs::create_directories(root / fs::path("lib"));
Try(writeToFile(ofs, root / fs::path("cabin.toml"),
createCabinToml(projectName), skipExisting));
Try(writeToFile(ofs, root / fs::path(".gitignore"), "/cabin-out",
skipExisting));
Try(writeToFile(ofs, root / fs::path("src") / "main.cc", MAIN_CC,
skipExisting));
Try(writeToFile(ofs, root / fs::path("lib") / "lib.cc", LIB_CC,
skipExisting));

Diag::info("Created", "binary (application) `{}` package", projectName);
fs::create_directories(root / "src");
templates.push_back(FileTemplate{
.path = root / "cabin.toml",
.contents = createCabinToml(projectName),
});
templates.push_back(
FileTemplate{ .path = root / ".gitignore", .contents = "/cabin-out" });
templates.push_back(FileTemplate{ .path = root / "src" / "main.cc",
.contents = std::string(MAIN_CC) });
} else {
fs::create_directories(root / fs::path("include") / projectName);
fs::create_directories(root / fs::path("lib"));
fs::create_directories(root / fs::path("src"));
Try(writeToFile(ofs, root / fs::path("cabin.toml"),
createCabinToml(projectName), skipExisting));
Try(writeToFile(ofs, root / fs::path(".gitignore"),
"/cabin-out\ncabin.lock", skipExisting));
Try(writeToFile(
ofs,
(root / fs::path("include") / projectName / projectName).string()
+ ".hpp",
getHeader(projectName), skipExisting));
Try(writeToFile(ofs, root / fs::path("lib") / "lib.cc", LIB_CC,
skipExisting));

Diag::info("Created", "library `{}` package", projectName);
const fs::path includeDir = root / "include" / projectName;
const fs::path libPath = (root / "lib" / projectName).string() + ".cc";
fs::create_directories(includeDir);
fs::create_directories(root / "lib");
const std::string ns = toNamespaceName(projectName);
templates.push_back(FileTemplate{
.path = root / "cabin.toml",
.contents = createCabinToml(projectName),
});
templates.push_back(FileTemplate{ .path = root / ".gitignore",
.contents = "/cabin-out\ncabin.lock" });
templates.push_back(FileTemplate{
.path = (includeDir / projectName).string() + ".hpp",
.contents = getHeader(projectName),
});
const std::string libImpl = std::format(
R"(#include "{0}/{0}.hpp"
#include <print>

namespace {1} {{
void hello_world() {{
std::println("Hello, world from {0}!");
}}
}} // namespace {1}
)",
projectName, ns);
templates.push_back(FileTemplate{ .path = libPath, .contents = libImpl });
}

for (const FileTemplate& file : templates) {
Try(writeToFile(file.path, file.contents, skipExisting));
}

Diag::info("Created", "{} `{}` package",
isBin ? "binary (application)" : "library", projectName);
return Ok();
}

Expand Down
9 changes: 7 additions & 2 deletions src/Cmd/Tidy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,17 @@ static Result<void> tidyMain(const CliArgsView args) {
std::string compdbDir;
const std::array<BuildProfile, 2> profiles{ BuildProfile::Dev,
BuildProfile::Test };
bool isFirstProfile = true;
for (const BuildProfile& profile : profiles) {
Builder builder(projectRoot, profile);
const bool includeDevDeps = (profile == BuildProfile::Test);
Try(builder.schedule(ScheduleOptions{ .includeDevDeps = includeDevDeps,
.enableCoverage = false }));
Try(builder.schedule(ScheduleOptions{
.includeDevDeps = includeDevDeps,
.enableCoverage = false,
.suppressAnalysisLog = !isFirstProfile,
}));
compdbDir = builder.compdbRoot();
isFirstProfile = false;
}

std::string runClangTidy = "run-clang-tidy";
Expand Down
2 changes: 1 addition & 1 deletion tests/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ int main() {
expect(tests::fs::is_regular_file(outDir / "targets.ninja"));
expect(tests::fs::is_regular_file(outDir / "ninja_project"));
expect(tests::fs::is_directory(outDir / "ninja_project.d"));
expect(tests::fs::is_regular_file(outDir / "libninja_project.a"));
expect(!tests::fs::exists(outDir / "libninja_project.a"));
expect(!tests::fs::exists(outDir / "Makefile"));
};

Expand Down
4 changes: 2 additions & 2 deletions tests/fmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ int main() {
auto sanitizedFirstOut = tests::sanitizeOutput(firstFmt.out);
expect(sanitizedFirstOut.empty());
auto sanitizedFirstErr = tests::sanitizeOutput(firstFmt.err);
const std::string expectedFirstErr = " Formatted 1 out of 2 files\n";
const std::string expectedFirstErr = " Formatted 1 out of 1 file\n";
expect(sanitizedFirstErr == expectedFirstErr);

const auto afterFirst = tests::readFile(mainFile);
Expand All @@ -63,7 +63,7 @@ int main() {
auto sanitizedSecondOut = tests::sanitizeOutput(secondFmt.out);
expect(sanitizedSecondOut.empty());
auto sanitizedSecondErr = tests::sanitizeOutput(secondFmt.err);
const std::string expectedSecondErr = " Formatted 0 out of 2 files\n";
const std::string expectedSecondErr = " Formatted 0 out of 1 file\n";
expect(sanitizedSecondErr == expectedSecondErr);

const auto afterSecond = tests::readFile(mainFile);
Expand Down
2 changes: 1 addition & 1 deletion tests/init.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ int main() {
expect(result.status.success()) << result.status.toString();

expect(tests::readFile(mainPath) == "int main() { return 42; }\n");
expect(tests::fs::is_regular_file(project / "lib" / "lib.cc"));
expect(!tests::fs::exists(project / "lib" / "lib.cc"));
expect(tests::fs::is_regular_file(project / "cabin.toml"));
};
}
5 changes: 5 additions & 0 deletions tests/new.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ int main() {
expect(tests::fs::is_regular_file(project / "cabin.toml"));
expect(tests::fs::is_directory(project / "src"));
expect(tests::fs::is_regular_file(project / "src/main.cc"));
expect(!tests::fs::exists(project / "lib"));
expect(!tests::fs::exists(project / "include"));

auto sanitizedOut = tests::sanitizeOutput(result.out);
expect(sanitizedOut.empty());
Expand All @@ -43,6 +45,9 @@ int main() {
expect(tests::fs::is_regular_file(project / ".gitignore"));
expect(tests::fs::is_regular_file(project / "cabin.toml"));
expect(tests::fs::is_directory(project / "include"));
expect(tests::fs::is_regular_file(project
/ "include/hello_world/hello_world.hpp"));
expect(tests::fs::is_regular_file(project / "lib/hello_world.cc"));

auto sanitizedOut = tests::sanitizeOutput(result.out);
expect(sanitizedOut.empty());
Expand Down
4 changes: 3 additions & 1 deletion tests/run.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ int main() {
sanitizedErr == analyzing + binErr + libErr + tailErr;
const bool errMatchesWithLibFirst =
sanitizedErr == analyzing + libErr + binErr + tailErr;
const bool errMatchesBinOnly = sanitizedErr == analyzing + binErr + tailErr;
const bool errMatchesWithoutLib = sanitizedErr == analyzing + tailErr;
expect(errMatchesWithLib || errMatchesWithLibFirst || errMatchesWithoutLib)
expect(errMatchesWithLib || errMatchesWithLibFirst || errMatchesBinOnly
|| errMatchesWithoutLib)
<< sanitizedErr;

expect(tests::fs::is_directory(project / "cabin-out"));
Expand Down
Loading
Loading