Add project files

This commit is contained in:
alejo committed 2026-10-24 09:53:48 -03:00
commit f2fe6b8855
114 files changed
+20525

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
# The `breeze` command: registers every command with CLIForge and hands the work to
# breeze_core. Nothing here decides anything; main.cpp and Commands.cpp are only the command line.
add_exe_module(breeze_exe OUTPUT_NAME "breeze" DEPENDS cliforge breeze_core)
+872
View File
@@ -0,0 +1,872 @@
#include "Commands.hpp"
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <set>
#include "breeze/Build.hpp"
#include "breeze/Cli.hpp"
#include "breeze/Config.hpp"
#include "breeze/Error.hpp"
#include "breeze/Fs.hpp"
#include "breeze/Modules.hpp"
#include "breeze/Presets.hpp"
#include "breeze/Process.hpp"
#include "breeze/Project.hpp"
#include "breeze/Sdks.hpp"
#include "breeze/Text.hpp"
#include "breeze/Tools.hpp"
namespace breeze::cli
{
int& exitCode()
{
static int s_code = 0;
return s_code;
}
namespace
{
using Str = std::optional<std::string>;
using Strs = std::vector<std::string>;
// ---------------------------------------------------------------- project
void showOrSet(Setting which, const Str& set)
{
Project project = requireProject();
if (set)
{
std::string before = readSetting(project, which);
writeSetting(project, which, *set);
std::cout << before << " -> " << *set << "\n";
}
else
{
std::cout << readSetting(project, which) << "\n";
}
}
// ---------------------------------------------------------------- config
void configValue(const char* key, const Str& set)
{
GlobalConfig cfg = GlobalConfig::load();
if (set)
{
if (*set == "none")
cfg.erase(key);
else
cfg.set(key, *set);
cfg.save();
std::cout << key << " = " << (*set == "none" ? "<not set>" : *set) << "\n";
return;
}
std::optional<std::string> value = cfg.effective(key);
std::cout << (value ? *value : "<not set>") << "\n";
}
// ---------------------------------------------------------------- defaults
void defaultsBuild(const char* key, const Str& set)
{
Project project = requireProject();
Defaults defaults = Defaults::load(project);
if (!set)
{
std::optional<std::string> v = defaults.build(key);
std::cout << (v ? *v : "<not set>") << "\n";
return;
}
if (*set == "none")
{
defaults.clearBuild(key);
}
else
{
std::string value = *set;
std::string k = key;
if (k == "mode")
value = buildTypeFor(value) == "RelWithDebInfo" ? "ReleaseDebugSymbols"
: buildTypeFor(value);
if (k == "preset")
{
std::optional<std::string> canonical;
for (const Preset& p : listPresets(project))
{
if (text::normalized(p.name) == text::normalized(value))
canonical = p.name;
}
if (!canonical)
throw Error("'" + value +
"' is not a preset of this project for this machine "
"(see `cmake --list-presets`)");
value = *canonical;
}
defaults.setBuild(key, value);
}
defaults.save(project);
std::cout << "default build " << key << " = "
<< defaults.build(key).value_or("<not set>") << "\n";
}
void defaultsRun(const Str& set)
{
Project project = requireProject();
Defaults defaults = Defaults::load(project);
if (!set)
{
std::optional<std::string> v = defaults.run();
std::cout << (v ? *v : "<not set>") << "\n";
return;
}
if (*set == "none")
{
defaults.clearRun();
}
else
{
std::vector<Module> modules = scanModules(project);
const Module* m = findModule(modules, *set);
if (m == nullptr || (m->kind != ModuleKind::Exec && m->kind != ModuleKind::Test &&
m->kind != ModuleKind::Example))
throw Error("'" + *set + "' is not an executable module of this project");
defaults.setRun(m->id);
}
defaults.save(project);
std::cout << "default run = "
<< (*set == "none" ? "<not set>" : defaults.run().value_or("")) << "\n";
}
// ---------------------------------------------------------------- generation
PresetQuery queryOf(const Str& preset, const Str& platform, const Str& arch,
const Str& compiler)
{
PresetQuery q;
q.preset = preset;
q.platform = platform;
q.arch = arch;
q.compiler = compiler;
return q;
}
void clean(bool noBuild, bool docs)
{
Project project = requireProject();
std::vector<std::string> removed = breeze::clean(project, noBuild, docs);
if (removed.empty())
std::cout << "nothing to clean\n";
for (const std::string& r : removed)
std::cout << "removed " << r << "\n";
}
BuildRequest requestOf(const Str& platform, const Str& arch, const Str& compiler,
const Str& mode, const std::optional<std::int32_t>& jobs,
const Str& preset, const Strs& cargs, const Strs& bargs,
bool noConfigure, bool noBuild)
{
BuildRequest r;
r.preset = queryOf(preset, platform, arch, compiler);
r.mode = mode;
if (jobs)
{
if (*jobs < 1)
throw Error("--jobs must be at least 1");
r.jobs = *jobs;
}
r.configureArgs = cargs;
r.buildArgs = bargs;
r.noConfigure = noConfigure;
r.noBuild = noBuild;
return r;
}
void build(const Str& platform, const Str& arch, const Str& compiler, const Str& mode,
const std::optional<std::int32_t>& jobs, const Str& preset, const Strs& cargs,
const Strs& bargs, bool noConfigure)
{
exitCode() = breeze::build(requireProject(),
requestOf(platform, arch, compiler, mode, jobs, preset,
cargs, bargs, noConfigure, false));
}
void install(const Str& platform, const Str& arch, const Str& compiler, const Str& mode,
const std::optional<std::int32_t>& jobs, const Str& preset, const Strs& cargs,
const Strs& bargs, bool noConfigure, bool noBuild)
{
exitCode() = breeze::install(requireProject(),
requestOf(platform, arch, compiler, mode, jobs, preset,
cargs, bargs, noConfigure, noBuild));
}
void docs()
{
exitCode() = breeze::docs(requireProject());
}
void runOne(const Str& id, const Str& preset, const Str& platform, const Str& arch,
const Str& compiler, const Str& mode, const Strs& args)
{
(void)mode; // the SDK keeps one build directory per preset, so the mode is part of what
// was built
exitCode() =
breeze::run(requireProject(), id, queryOf(preset, platform, arch, compiler), args);
}
void runDefault(const Str& preset, const Str& platform, const Str& arch,
const Str& compiler, const Str& mode, const Strs& args)
{
runOne(std::nullopt, preset, platform, arch, compiler, mode, args);
}
void runById(const std::string& id, const Str& preset, const Str& platform, const Str& arch,
const Str& compiler, const Str& mode, const Strs& args)
{
runOne(id, preset, platform, arch, compiler, mode, args);
}
void setup(bool required, bool all)
{
exitCode() = breeze::setup(requireProject(), required, all);
}
// ---------------------------------------------------------------- modules
void add(ModuleKind kind, const std::string& name, const Str& id, const Strs& deps,
LibType type)
{
Project project = requireProject();
NewModule request;
request.kind = kind;
request.name = name;
request.id = id;
request.deps = deps;
request.libType = type;
Module m = addModule(project, request);
std::cout << "added " << kindName(m.kind) << " module '" << m.id << "' in "
<< fs::generic(std::filesystem::relative(m.dir, project.root)) << "\n";
}
void addExec(const std::string& name, const Str& id, const Strs& deps)
{
add(ModuleKind::Exec, name, id, deps, LibType::Unspecified);
}
void addLib(const std::string& name, const Str& id, const Strs& deps, bool isStatic,
bool isShared)
{
if (isStatic == isShared)
throw Error("a library is either --static or --shared; say which (not both)");
add(ModuleKind::Lib, name, id, deps, isShared ? LibType::Shared : LibType::Static);
}
void addObj(const std::string& name, const Str& id, const Strs& deps)
{
add(ModuleKind::Obj, name, id, deps, LibType::Unspecified);
}
void addTest(const std::string& name, const Str& id, const Strs& deps)
{
add(ModuleKind::Test, name, id, deps, LibType::Unspecified);
}
void addExample(const std::string& name, const Str& id, const Strs& deps)
{
add(ModuleKind::Example, name, id, deps, LibType::Unspecified);
}
void deleteIt(const std::string& id, bool yes)
{
Project project = requireProject();
std::vector<Module> modules = scanModules(project);
const Module* m = findModule(modules, id);
if (m == nullptr)
throw Error("there is no module '" + id + "' (see `breeze ls`)");
std::string where = fs::generic(std::filesystem::relative(m->dir, project.root));
if (!yes)
{
if (!proc::stdinIsTerminal())
throw Error("deleting '" + m->id + "' removes " + where +
"; pass --yes to do it without asking");
std::cout << "Delete module '" << m->id << "' and everything in " << where
<< "? [y/N] " << std::flush;
std::string answer;
std::getline(std::cin, answer);
if (text::lower(text::trim(answer)) != "y" &&
text::lower(text::trim(answer)) != "yes")
{
std::cout << "not deleted\n";
return;
}
}
deleteModule(project, id);
std::cout << "deleted " << where << "\n";
}
void rename(const std::string& id, const Str& name, const Str& newId)
{
Project project = requireProject();
RenameResult r = renameModule(project, id, name, newId);
if (r.oldDir != r.newDir)
std::cout << "directory " << r.oldDir << " -> " << r.newDir << "\n";
if (r.oldId != r.newId)
std::cout << "id " << r.oldId << " -> " << r.newId << "\n";
for (const std::string& d : r.updatedDependents)
std::cout << "updated DEPENDS in " << d << "\n";
}
void list(const Str& filter, bool exec, bool obj, bool lib, bool test, bool example,
bool isShared, bool isStatic)
{
Project project = requireProject();
std::vector<Module> modules = scanModules(project);
bool anyKind = exec || obj || lib || test || example;
std::size_t idWidth = 2;
std::size_t dirWidth = 3;
std::size_t kindWidth = 4;
std::vector<const Module*> shown;
for (const Module& m : modules)
{
bool kindOk = !anyKind || (m.kind == ModuleKind::Exec && exec) ||
(m.kind == ModuleKind::Obj && obj) ||
(m.kind == ModuleKind::Lib && lib) ||
(m.kind == ModuleKind::Test && test) ||
(m.kind == ModuleKind::Example && example);
if (!kindOk)
continue;
if (isShared && !(m.kind == ModuleKind::Lib && m.libType == LibType::Shared))
continue;
if (isStatic && !(m.kind == ModuleKind::Lib && m.libType == LibType::Static))
continue;
if (filter && text::lower(m.id).find(text::lower(*filter)) == std::string::npos &&
text::lower(m.dirName).find(text::lower(*filter)) == std::string::npos)
continue;
shown.push_back(&m);
kindWidth = std::max(
kindWidth,
kindName(m.kind).size() +
(m.kind == ModuleKind::Lib && m.libType != LibType::Unspecified ? 7 : 0));
idWidth = std::max(idWidth, m.id.size());
dirWidth = std::max(
dirWidth, fs::generic(std::filesystem::relative(m.dir, project.root)).size());
}
if (shown.empty())
{
std::cout << "no modules\n";
return;
}
std::cout << std::left << std::setw(static_cast<int>(idWidth) + 2) << "ID"
<< std::setw(static_cast<int>(kindWidth) + 2) << "KIND"
<< std::setw(static_cast<int>(dirWidth) + 2) << "DIRECTORY" << "DEPENDS\n";
for (const Module* m : shown)
{
std::string kind = kindName(m->kind);
if (m->kind == ModuleKind::Lib && m->libType != LibType::Unspecified)
kind += m->libType == LibType::Shared ? "/shared" : "/static";
std::string dir = fs::generic(std::filesystem::relative(m->dir, project.root));
std::cout << std::left << std::setw(static_cast<int>(idWidth) + 2) << m->id
<< std::setw(static_cast<int>(kindWidth) + 2) << kind;
if (m->deps.empty())
std::cout << dir << "\n";
else
std::cout << std::setw(static_cast<int>(dirWidth) + 2) << dir
<< text::join(m->deps, " ") << "\n";
}
}
void printTree(const std::vector<Module>& modules, const std::string& id, bool dependencies,
int depth, std::set<std::string>& onPath)
{
const Module* m = findModule(modules, id);
std::vector<std::string> next;
if (m != nullptr)
{
next = dependencies ? m->deps : dependentsOf(modules, *m, false);
}
for (const std::string& n : next)
{
bool known = findModule(modules, n) != nullptr;
std::cout << std::string(static_cast<std::size_t>(depth) * 2, ' ') << n
<< (known ? "" : " (external)") << "\n";
if (known && onPath.insert(n).second)
{
printTree(modules, n, dependencies, depth + 1, onPath);
onPath.erase(n);
}
}
}
void deps(bool of, const std::string& module, bool recursive)
{
Project project = requireProject();
std::vector<Module> modules = scanModules(project);
const Module* m = findModule(modules, module);
if (m == nullptr)
throw Error("there is no module '" + module + "' (see `breeze ls`)");
std::vector<std::string> direct =
of ? dependenciesOf(modules, *m, false) : dependentsOf(modules, *m, false);
if (direct.empty())
{
std::cout << (of ? m->id + " depends on nothing\n"
: "nothing depends on " + m->id + "\n");
return;
}
if (!recursive)
{
for (const std::string& d : direct)
std::cout << d << (findModule(modules, d) != nullptr ? "" : " (external)")
<< "\n";
return;
}
std::set<std::string> onPath{m->id};
printTree(modules, m->id, of, 0, onPath);
}
void sdkVersion()
{
Project project = requireProject();
std::cout << project.sdk.describe() << "\n";
}
void sdkUpdate()
{
sdk::update(requireProject());
}
void sdkUpgrade()
{
sdk::upgrade(requireProject());
}
void sdkMigrate(const std::string& version)
{
sdk::migrate(requireProject(), version);
}
// ---------------------------------------------------------------- cli
void cliVersion()
{
std::cout << self::version() << "\n";
}
void cliPick(const std::string& version)
{
self::pick(version);
}
void cliInstall(const std::string& version)
{
self::install(version);
}
void cliUninstall(const std::string& version)
{
self::uninstall(version);
}
void cliList()
{
self::list();
}
// ---------------------------------------------------------------- tools
void toolInstall(const std::string& repo)
{
tools::install(requireProject(), repo);
}
void toolUninstall(const std::string& id)
{
tools::uninstall(requireProject(), id);
}
void toolUpdate(const std::string& id)
{
tools::update(requireProject(), id);
}
void toolUpgrade(const std::string& id)
{
tools::upgrade(requireProject(), id);
}
void toolMigrate(const std::string& id, const std::string& version)
{
tools::migrate(requireProject(), id, version);
}
void toolList()
{
tools::list(requireProject());
}
// Only here so that `breeze --help` shows it: main() hands `tool <id> ...` to the tool
// before CLIForge sees it.
void toolRun(const std::string& /*id*/, const Strs& /*arguments*/)
{
}
void projectNew(const std::string& name, const Str& id, const Str& sdkVersion)
{
sdk::createProject(name, id, sdkVersion);
}
}
void registerCommands(cliforge::Engine& cli)
{
using std::int32_t;
// ---- cli
cli.command()
.keyword("cli")
.keyword("version")
.describe("Shows this CLI's version")
.action(&cliVersion);
cli.command()
.keyword("cli")
.keyword("pick")
.parameter<std::string>("version", "An installed CLI version")
.describe("Picks the CLI version that runs from now on")
.action(&cliPick);
cli.command()
.keyword("cli")
.keyword("install")
.parameter<std::string>("version", "The CLI version to install")
.describe(
"Installs a CLI version (this program's own, or a release from the CLI source)")
.action(&cliInstall);
cli.command()
.keyword("cli")
.keyword("uninstall")
.parameter<std::string>("version", "An installed CLI version")
.describe("Uninstalls a CLI version")
.action(&cliUninstall);
cli.command()
.keyword("cli")
.keyword("ls")
.describe("Lists the installed CLI versions")
.action(&cliList);
// ---- project
cli.command()
.keyword("project")
.keyword("new")
.parameter<std::string>("name", "The new project's name (and directory)")
.option<std::string>("id", '\0', "The project's id (default: the name in lower case)")
.option<std::string>("sdk", '\0', "The SDK version to start from (default: the latest)")
.describe("Creates a new project")
.action(&projectNew);
cli.command()
.keyword("project")
.keyword("version")
.option<std::string>("set", '\0', "Sets the project's version (major.minor[.patch])")
.describe("Shows or sets the project's version")
.action([](const Str& set) { showOrSet(Setting::Version, set); });
cli.command()
.keyword("project")
.keyword("name")
.option<std::string>("set", '\0', "Sets the project's name")
.describe("Shows or sets the project's name (the CMake project() name)")
.action([](const Str& set) { showOrSet(Setting::Name, set); });
cli.command()
.keyword("project")
.keyword("id")
.option<std::string>("set", '\0', "Sets the project's id")
.describe("Shows or sets the project's id (lower-case; the install component)")
.action([](const Str& set) { showOrSet(Setting::Id, set); });
// ---- setup
cli.command()
.keyword("setup")
.flag("required", '\0', "Install the required tools without asking")
.flag("all", '\0', "Also install the optional tools without asking")
.describe(
"Checks the tools the project builds with and offers to install what is missing")
.action(&setup);
// ---- config
cli.command()
.keyword("config")
.keyword("editor")
.option<std::string>("set", '\0', "Sets the editor ('none' clears it)")
.describe("Shows or sets the editor the CLI opens files with")
.action([](const Str& set) { configValue(config::Editor, set); });
cli.command()
.keyword("config")
.keyword("sdk-source")
.option<std::string>("set", '\0',
"Sets the SDK source: a git repository (URL or path), or a "
"directory of versions ('none' clears it)")
.describe("Shows or sets where SDK versions come from")
.action([](const Str& set) { configValue(config::SdkSource, set); });
cli.command()
.keyword("config")
.keyword("cli-source")
.option<std::string>(
"set", '\0',
"Sets the CLI source: a URL or directory of releases ('none' clears it)")
.describe("Shows or sets where CLI versions come from")
.action([](const Str& set) { configValue(config::CliSource, set); });
// ---- generation
cli.command()
.keyword("clean")
.flag("no-build", 'b', "Do not clean build artifacts")
.flag("docs", 'd', "Clean documentation artifacts")
.describe("Cleans build artifacts")
.action(&clean);
cli.command()
.keyword("build")
.option<std::string>("platform", 'P', "The desired platform (Windows, macOS, Linux)")
.option<std::string>("arch", 'A', "The desired architecture (x64, x86, arm32, arm64)")
.option<std::string>(
"compiler", 'C',
"The desired compiler (MSVC, Clang, ClangCl, GCC, MinGW32, MinGW64, ...)")
.option<std::string>("mode", 'M',
"The configuration (Release, Debug, ReleaseDebugSymbols)")
.option<int32_t>("jobs", 'j', "The job count for the compilation")
.option<std::string>("preset", 'p', "The CMake preset to use")
.option<std::string>("cargs", '\0', "CMake configuration arguments", true)
.option<std::string>("bargs", '\0', "CMake build arguments", true)
.flag("no-configure", '\0', "Do not perform CMake configure")
.describe("Configures and builds the project")
.action(&build);
cli.command()
.keyword("install")
.option<std::string>("platform", 'P', "The desired platform (Windows, macOS, Linux)")
.option<std::string>("arch", 'A', "The desired architecture (x64, x86, arm32, arm64)")
.option<std::string>(
"compiler", 'C',
"The desired compiler (MSVC, Clang, ClangCl, GCC, MinGW32, MinGW64, ...)")
.option<std::string>("mode", 'M',
"The configuration (Release, Debug, ReleaseDebugSymbols)")
.option<int32_t>("jobs", 'j', "The job count for the compilation")
.option<std::string>("preset", 'p', "The CMake preset to use")
.option<std::string>("cargs", '\0', "CMake configuration arguments", true)
.option<std::string>("bargs", '\0', "CMake build arguments", true)
.flag("no-configure", '\0', "Do not perform CMake configure")
.flag("no-build", '\0', "Do not perform CMake build")
.describe("Configures, builds and installs the project")
.action(&install);
cli.command().keyword("docs").describe("Builds the project documentation").action(&docs);
// ---- defaults
for (const char* key : {"platform", "arch", "compiler", "preset", "mode"})
{
cli.command()
.keyword("defaults")
.keyword("build")
.keyword(key)
.option<std::string>("set", '\0',
"Sets this default to the given value ('none' clears it)")
.describe(std::string("Gets or sets the project default build ") + key)
.action([key](const Str& set) { defaultsBuild(key, set); });
}
cli.command()
.keyword("defaults")
.keyword("run")
.option<std::string>("set", '\0',
"Sets this default to the given value ('none' clears it)")
.describe(
"Gets or sets the default executable to run if no `id` is given to `breeze run`")
.action(&defaultsRun);
// ---- run (the id is optional: two commands)
cli.command()
.keyword("run")
.option<std::string>("preset", 'p', "The preset to run")
.option<std::string>("platform", 'P', "The desired platform (Windows, macOS, Linux)")
.option<std::string>("arch", 'A', "The desired architecture (x64, x86, arm32, arm64)")
.option<std::string>(
"compiler", 'C',
"The desired compiler (MSVC, Clang, ClangCl, GCC, MinGW32, MinGW64, ...)")
.option<std::string>("mode", 'M',
"The configuration (Release, Debug, ReleaseDebugSymbols)")
.option<std::string>("args", 'x', "The arguments to pass to the executable", true)
.describe("Runs the default executable (see `breeze defaults run`)")
.action(&runDefault);
cli.command()
.keyword("run")
.parameter<std::string>("id", "The executable module to run")
.option<std::string>("preset", 'p', "The preset to run")
.option<std::string>("platform", 'P', "The desired platform (Windows, macOS, Linux)")
.option<std::string>("arch", 'A', "The desired architecture (x64, x86, arm32, arm64)")
.option<std::string>(
"compiler", 'C',
"The desired compiler (MSVC, Clang, ClangCl, GCC, MinGW32, MinGW64, ...)")
.option<std::string>("mode", 'M',
"The configuration (Release, Debug, ReleaseDebugSymbols)")
.option<std::string>("args", 'x', "The arguments to pass to the executable", true)
.describe("Runs an executable (primary executable, test or example)")
.action(&runById);
// ---- modules
cli.command()
.keyword("add")
.keyword("exec")
.parameter<std::string>("name", "The module's name (its directory)")
.option<std::string>("id", '\0', "The CMake target id (default: <name>_exe)")
.option<std::string>("deps", '\0', "Modules or targets it depends on", true)
.describe("Adds an executable module")
.action(&addExec);
cli.command()
.keyword("add")
.keyword("lib")
.parameter<std::string>("name", "The module's name (its directory)")
.option<std::string>("id", '\0', "The CMake target id (default: <name>_lib)")
.option<std::string>("deps", '\0', "Modules or targets it depends on", true)
.flag("static", '\0', "A static library")
.flag("shared", '\0', "A shared library")
.describe("Adds a library module")
.action(&addLib);
cli.command()
.keyword("add")
.keyword("obj")
.parameter<std::string>("name", "The module's name (its directory)")
.option<std::string>("id", '\0', "The CMake target id (default: <name>_obj)")
.option<std::string>("deps", '\0', "Modules or targets it depends on", true)
.describe("Adds an objects module: compiled to object files that a library or "
"executable takes in")
.action(&addObj);
cli.command()
.keyword("add")
.keyword("test")
.parameter<std::string>("name", "The module's name (its directory)")
.option<std::string>("id", '\0', "The CMake target id (default: <name>_test)")
.option<std::string>("deps", '\0', "Modules or targets it depends on", true)
.describe("Adds a test module")
.action(&addTest);
cli.command()
.keyword("add")
.keyword("example")
.parameter<std::string>("name", "The module's name (its directory)")
.option<std::string>("id", '\0', "The CMake target id (default: <name>_example)")
.option<std::string>("deps", '\0', "Modules or targets it depends on", true)
.describe("Adds an example module")
.action(&addExample);
cli.command()
.keyword("delete")
.parameter<std::string>("id", "The module's id (or directory name)")
.flag("yes", 'y', "Delete without asking")
.describe("Deletes a module")
.action(&deleteIt);
cli.command()
.keyword("rename")
.parameter<std::string>("id", "The module's id (or directory name)")
.option<std::string>("name", '\0', "The new name (directory)")
.option<std::string>("id", '\0', "The new id")
.describe(
"Renames a module's directory and/or id, updating the modules that depend on it")
.action(&rename);
cli.command()
.keyword("ls")
.option<std::string>("filter", '\0',
"Only modules whose id or directory contains this text")
.flag("exec", '\0', "Executable modules")
.flag("obj", '\0', "Objects modules")
.flag("lib", '\0', "Library modules")
.flag("test", '\0', "Test modules")
.flag("example", '\0', "Example modules")
.flag("shared", '\0', "Only shared libraries")
.flag("static", '\0', "Only static libraries")
.describe("Lists the project's modules")
.action(&list);
// ---- deps
cli.command()
.keyword("deps")
.keyword("of")
.parameter<std::string>("module", "The module's id (or directory name)")
.flag("recursive", '\0', "Follow dependencies of dependencies")
.describe("Lists what a module depends on")
.action([](const std::string& module, bool recursive)
{ deps(true, module, recursive); });
cli.command()
.keyword("deps")
.keyword("on")
.parameter<std::string>("module", "The module's id (or directory name)")
.flag("recursive", '\0', "Follow dependents of dependents")
.describe("Lists the modules that depend on a module")
.action([](const std::string& module, bool recursive)
{ deps(false, module, recursive); });
// ---- tools
cli.command()
.keyword("tool")
.keyword("install")
.parameter<std::string>("repo", "The tool's git repository (URL or path)")
.describe("Installs a tool into the project")
.action(&toolInstall);
cli.command()
.keyword("tool")
.keyword("uninstall")
.parameter<std::string>("id", "The tool's id")
.describe("Uninstalls a tool from the project")
.action(&toolUninstall);
cli.command()
.keyword("tool")
.keyword("update")
.parameter<std::string>("id", "The tool's id")
.describe("Looks for newer versions of a tool")
.action(&toolUpdate);
cli.command()
.keyword("tool")
.keyword("upgrade")
.parameter<std::string>("id", "The tool's id")
.describe("Moves a tool to the newest version of its major version")
.action(&toolUpgrade);
cli.command()
.keyword("tool")
.keyword("migrate")
.parameter<std::string>("id", "The tool's id")
.parameter<std::string>("version", "The version to move to")
.describe("Moves a tool to a given version")
.action(&toolMigrate);
cli.command()
.keyword("tool")
.keyword("ls")
.describe("Lists the project's tools")
.action(&toolList);
cli.command()
.keyword("tool")
.parameter<std::string>("id", "An installed tool")
.parameter<std::string>("arguments", "What the tool is asked to do", true)
.describe("Runs a command a tool adds: breeze tool <id> <arguments...>")
.action(&toolRun);
// ---- sdk
cli.command()
.keyword("sdk")
.keyword("version")
.describe("Shows the project's SDK version")
.action(&sdkVersion);
cli.command()
.keyword("sdk")
.keyword("update")
.describe("Looks for newer SDK versions")
.action(&sdkUpdate);
cli.command()
.keyword("sdk")
.keyword("upgrade")
.describe("Moves the project to the newest SDK version of its major version")
.action(&sdkUpgrade);
cli.command()
.keyword("sdk")
.keyword("migrate")
.parameter<std::string>("version", "The SDK version to move to")
.describe("Moves the project to a given SDK version (across majors, and from the "
"legacy layout)")
.action(&sdkMigrate);
}
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <CLIForge/CLIForge.hpp>
namespace breeze::cli
{
/// Registers every command of the spec on the engine.
void registerCommands(cliforge::Engine& engine);
/// The exit code the last command that ran a program asked for. Actions can't return one
/// (CLIForge ignores what they return), so they leave it here and main() ends with it.
int& exitCode();
}
+56
View File
@@ -0,0 +1,56 @@
#include <iostream>
#include <string>
#include <vector>
#include <CLIForge/CLIForge.hpp>
#include "Commands.hpp"
#include "breeze/Arguments.hpp"
#include "breeze/Cli.hpp"
#include "breeze/Error.hpp"
#include "breeze/Project.hpp"
#include "breeze/Tools.hpp"
int main(int argc, char** argv)
{
try
{
std::vector<std::string> given(argv + 1, argv + argc);
// Another CLI version may be the picked one: it takes over (never for `cli ...`).
breeze::self::forwardToPicked(given);
// `breeze tool <id> ...` is the tool's own command line, which CLIForge knows nothing
// about.
if (given.size() >= 2 && given[0] == "tool" && given[1].rfind('-', 0) != 0 &&
!breeze::tools::isReservedName(given[1]))
{
breeze::Project project = breeze::requireProject();
std::optional<int> rc = breeze::tools::run(
project, given[1], std::vector<std::string>(given.begin() + 2, given.end()));
if (!rc)
throw breeze::Error("there is no tool '" + given[1] +
"' installed in this project (see `breeze tool ls`)");
return *rc;
}
cliforge::Engine engine("breeze");
breeze::cli::registerCommands(engine);
std::vector<std::string> args = breeze::cli::normalizeArguments(given);
std::vector<char*> forwarded{argv[0]};
for (std::string& a : args)
forwarded.push_back(a.data());
int rc = engine.run(static_cast<int>(forwarded.size()), forwarded.data());
return rc != 0 ? rc : breeze::cli::exitCode();
}
catch (const breeze::Error& e)
{
std::cerr << "breeze: " << e.what() << "\n";
return e.exitCode();
}
catch (const std::exception& e)
{
std::cerr << "breeze: unexpected error: " << e.what() << "\n";
return 70;
}
}