Add project files
This commit is contained in:
commit
f2fe6b8855
114 files changed
+20525
No files matched your search
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace breeze::cli
|
||||
{
|
||||
|
||||
/// CLIForge reads every token that starts with '-' as an option, so `--cargs -DBUILD_TESTS=ON`
|
||||
/// would try to find an option called -D. The values of the pass-through options (--cargs,
|
||||
/// --bargs and run's --args/-x) are rewritten to the `--cargs=-DBUILD_TESTS=ON` form CLIForge
|
||||
/// does accept, each taking tokens up to the next option of the command.
|
||||
std::vector<std::string> normalizeArguments(const std::vector<std::string>& args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Presets.hpp"
|
||||
#include "breeze/Project.hpp"
|
||||
|
||||
/// Building, installing, running and cleaning a project -- by driving the SDK's own scripts, so the
|
||||
/// environment they set up (the pinned Ninja, the MSVC environment on Windows) is always the one
|
||||
/// used. Where a request needs something the scripts don't offer (extra `cmake --build` arguments,
|
||||
/// skipping the configure), the CLI runs `cmake --build` itself against the build the scripts made.
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
struct BuildRequest
|
||||
{
|
||||
PresetQuery preset;
|
||||
/// Release, Debug, ReleaseDebugSymbols or MinSizeRel (any case).
|
||||
std::optional<std::string> mode;
|
||||
std::optional<int> jobs;
|
||||
std::vector<std::string> configureArgs;
|
||||
std::vector<std::string> buildArgs;
|
||||
bool noConfigure = false;
|
||||
bool noBuild = false;
|
||||
};
|
||||
|
||||
/// The CMake build type for a mode name; throws Error for an unknown one.
|
||||
std::string buildTypeFor(const std::string& mode);
|
||||
|
||||
/// Values of the project's settings (BUILD_OUTPUT_DIR, DOCS_OUTPUT_DIR, ...) as the SDK
|
||||
/// computes them. Falls back to the SDK's defaults for a legacy layout that can't be asked.
|
||||
std::map<std::string, std::string> readConfigVariables(const Project& project,
|
||||
const std::vector<std::string>& names);
|
||||
|
||||
/// The build directory for the preset (or the one built last), as the SDK defines it.
|
||||
std::filesystem::path buildDirFor(const Project& project,
|
||||
const std::optional<std::string>& preset);
|
||||
|
||||
/// Each returns the exit code of the command that did the work.
|
||||
int build(const Project& project, const BuildRequest& request);
|
||||
int install(const Project& project, const BuildRequest& request);
|
||||
int docs(const Project& project);
|
||||
int run(const Project& project, const std::optional<std::string>& id, const PresetQuery& where,
|
||||
const std::vector<std::string>& programArgs);
|
||||
int setup(const Project& project, bool required, bool all);
|
||||
|
||||
/// Removes the build directories (unless keepBuild) and the documentation (if alsoDocs).
|
||||
/// Returns the paths removed, relative to the project.
|
||||
std::vector<std::string> clean(const Project& project, bool keepBuild, bool alsoDocs);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/// Just enough of CMake's syntax to find and edit a command call in a CMakeLists.txt or a settings
|
||||
/// file without disturbing anything else in it: comments, layout and line endings stay as they
|
||||
/// were.
|
||||
namespace breeze::cmake
|
||||
{
|
||||
|
||||
/// One argument of a call. begin/end are byte offsets of the argument as written (quotes
|
||||
/// included); text is its value without the quotes.
|
||||
struct Token
|
||||
{
|
||||
std::size_t begin = 0;
|
||||
std::size_t end = 0;
|
||||
std::string text;
|
||||
bool quoted = false;
|
||||
};
|
||||
|
||||
/// A command call: name(args...). begin/end span the whole call, "name(" through ")".
|
||||
struct Call
|
||||
{
|
||||
std::string name;
|
||||
std::size_t begin = 0;
|
||||
std::size_t end = 0;
|
||||
std::vector<Token> args;
|
||||
};
|
||||
|
||||
/// Every call in `text`, in order, whose name `wanted` accepts. Comments, quoted strings and
|
||||
/// the arguments of other calls are never mistaken for calls.
|
||||
std::vector<Call> findCalls(const std::string& text,
|
||||
const std::function<bool(const std::string&)>& wanted);
|
||||
|
||||
/// The first set(<variable> ...) call.
|
||||
std::optional<Call> findSet(const std::string& text, const std::string& variable);
|
||||
|
||||
/// `value` as a quoted CMake argument.
|
||||
std::string quote(const std::string& value);
|
||||
|
||||
/// Replaces the token's text in `text` (offsets of later tokens are stale afterwards).
|
||||
void replaceToken(std::string& text, const Token& token, const std::string& replacement);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Version.hpp"
|
||||
|
||||
/// The CLI managing itself: which versions of it are installed, which one is picked, and running
|
||||
/// the picked one. Versions live in <breeze home>/cli/<version>/; `cli/current` names the picked
|
||||
/// one, and <breeze home>/bin/breeze is the copy to put on PATH. Whatever `breeze` the shell finds
|
||||
/// hands the command to the picked version, so installing or picking never means changing PATH
|
||||
/// again.
|
||||
namespace breeze::self
|
||||
{
|
||||
|
||||
/// The version of this program.
|
||||
std::string version();
|
||||
|
||||
/// Installed versions, oldest first.
|
||||
std::vector<Version> installed();
|
||||
|
||||
/// The version `cli pick` chose, if any.
|
||||
std::optional<Version> picked();
|
||||
|
||||
/// Installs a version: this very program if that is its version, else a release of the
|
||||
/// configured CLI source. The first version installed is picked.
|
||||
void install(const std::string& version);
|
||||
|
||||
void pick(const std::string& version);
|
||||
|
||||
/// Removes a version; un-picks it if it was the picked one.
|
||||
void uninstall(const std::string& version);
|
||||
|
||||
/// Prints the installed versions, marking the picked one and the running one.
|
||||
void list();
|
||||
|
||||
/// If another version is picked, installed and runnable, runs it with `args` and does not
|
||||
/// return. `cli ...` commands always run in the version that was started.
|
||||
void forwardToPicked(const std::vector<std::string>& args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
/// The CLI's own, global state: where it lives and what the user configured.
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
/// $BREEZE_HOME, or ~/.breeze. Holds config.json, the installed CLI versions (cli/), the SDK
|
||||
/// snapshots (sdk/) and cached indexes.
|
||||
std::filesystem::path breezeHome();
|
||||
|
||||
/// The keys of config.json. Each can also come from the environment (BREEZE_EDITOR,
|
||||
/// BREEZE_SDK_SOURCE, BREEZE_CLI_SOURCE), which wins.
|
||||
namespace config
|
||||
{
|
||||
inline constexpr const char* Editor = "editor";
|
||||
inline constexpr const char* SdkSource = "sdkSource";
|
||||
inline constexpr const char* CliSource = "cliSource";
|
||||
}
|
||||
|
||||
class GlobalConfig
|
||||
{
|
||||
public:
|
||||
/// Reads config.json (an absent file is an empty configuration).
|
||||
static GlobalConfig load();
|
||||
|
||||
/// The environment's value for the key if set, else the file's.
|
||||
[[nodiscard]] std::optional<std::string> effective(const std::string& key) const;
|
||||
|
||||
/// The file's value only.
|
||||
[[nodiscard]] std::optional<std::string> stored(const std::string& key) const;
|
||||
|
||||
void set(const std::string& key, const std::string& value);
|
||||
void erase(const std::string& key);
|
||||
void save() const;
|
||||
|
||||
private:
|
||||
nlohmann::json m_data = nlohmann::json::object();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
/// Every failure the CLI reports to the user: a message and the process exit code to end with.
|
||||
class Error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit Error(const std::string& message, int exitCode = 1)
|
||||
: std::runtime_error(message), m_exitCode(exitCode)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] int exitCode() const noexcept { return m_exitCode; }
|
||||
|
||||
private:
|
||||
int m_exitCode;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/// File system and host helpers. Paths are std::filesystem::path throughout; text files are read
|
||||
/// and written as bytes, so the line endings a file has are the ones it keeps.
|
||||
namespace breeze::fs
|
||||
{
|
||||
|
||||
using std::filesystem::path;
|
||||
|
||||
/// Throws Error when the file can't be read.
|
||||
std::string readText(const path& file);
|
||||
std::optional<std::string> tryReadText(const path& file);
|
||||
|
||||
/// Creates the parent directories, then writes the file (replacing it).
|
||||
void writeText(const path& file, const std::string& text);
|
||||
|
||||
/// Copies `from` into `to` (merging into what is there, overwriting files). Entries whose name
|
||||
/// is in `skipNames` are left out at every level.
|
||||
void copyTree(const path& from, const path& to, const std::vector<std::string>& skipNames = {});
|
||||
|
||||
/// Removes the file or directory tree if it exists. Throws Error on failure.
|
||||
void removeTree(const path& p);
|
||||
|
||||
/// A new, empty directory under the system's temp directory.
|
||||
path makeTempDir(const std::string& prefix);
|
||||
|
||||
/// The user's home directory (HOME, or USERPROFILE on Windows).
|
||||
path homeDir();
|
||||
|
||||
/// Where the running program's executable is.
|
||||
path executablePath();
|
||||
|
||||
/// "linux", "macos" or "windows".
|
||||
std::string hostPlatform();
|
||||
|
||||
/// "x64", "arm64", "arm32" or "x86".
|
||||
std::string hostArch();
|
||||
|
||||
/// chmod +x on POSIX; nothing on Windows.
|
||||
void makeExecutable(const path& file);
|
||||
|
||||
/// True for a regular file that can be run.
|
||||
bool isExecutable(const path& file);
|
||||
|
||||
/// The path with forward slashes, for messages that read the same on every OS.
|
||||
std::string generic(const path& p);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Version.hpp"
|
||||
|
||||
/// The little of git the CLI needs: listing the version tags of a repository and exporting one
|
||||
/// tag's files. Repositories are URLs or local paths; git itself must be installed.
|
||||
namespace breeze::git
|
||||
{
|
||||
|
||||
struct Tag
|
||||
{
|
||||
/// The tag as written in the repository, e.g. "v1.2.0".
|
||||
std::string name;
|
||||
Version version;
|
||||
};
|
||||
|
||||
/// Is `source` something git can clone (a URL, or a local repository) rather than a plain
|
||||
/// directory?
|
||||
bool isRepository(const std::string& source);
|
||||
|
||||
/// The tags of the repository that are versions, in no particular order. Throws Error when the
|
||||
/// repository can't be reached.
|
||||
std::vector<Tag> versionTags(const std::string& repository);
|
||||
|
||||
/// Exports the files of `tag` (the default branch's tip when empty) into `destination`, without
|
||||
/// the repository's own history.
|
||||
void exportTree(const std::string& repository, const std::string& tag,
|
||||
const std::filesystem::path& destination);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Project.hpp"
|
||||
|
||||
/// A project's modules: the directories under source/ (and source/tests/, source/examples/) that
|
||||
/// each register one CMake target with one of the SDK's add_*_module macros. They are read from the
|
||||
/// CMakeLists.txt files themselves, so what the CLI shows is what CMake will do, and the files it
|
||||
/// writes are the ones a person would have written.
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
enum class ModuleKind
|
||||
{
|
||||
Exec,
|
||||
Lib,
|
||||
Obj,
|
||||
Test,
|
||||
Example
|
||||
};
|
||||
|
||||
enum class LibType
|
||||
{
|
||||
Unspecified,
|
||||
Static,
|
||||
Shared
|
||||
};
|
||||
|
||||
/// "exec", "lib", "obj", "test" or "example".
|
||||
std::string kindName(ModuleKind kind);
|
||||
|
||||
struct Module
|
||||
{
|
||||
/// The CMake target: what DEPENDS, `breeze run` and `breeze deps` call it.
|
||||
std::string id;
|
||||
/// The directory's name.
|
||||
std::string dirName;
|
||||
std::filesystem::path dir;
|
||||
ModuleKind kind = ModuleKind::Exec;
|
||||
LibType libType = LibType::Unspecified;
|
||||
std::string outputName;
|
||||
std::vector<std::string> deps;
|
||||
/// The macro that registered it, e.g. add_lib_module.
|
||||
std::string macro;
|
||||
};
|
||||
|
||||
/// Every module, main ones first (by directory name), then tests, then examples.
|
||||
std::vector<Module> scanModules(const Project& project);
|
||||
|
||||
/// By id, else by directory name. Null when there is no such module.
|
||||
const Module* findModule(const std::vector<Module>& modules, const std::string& idOrDir);
|
||||
|
||||
struct NewModule
|
||||
{
|
||||
ModuleKind kind = ModuleKind::Exec;
|
||||
std::string name;
|
||||
std::optional<std::string> id;
|
||||
std::vector<std::string> deps;
|
||||
LibType libType = LibType::Unspecified;
|
||||
};
|
||||
|
||||
/// Creates the module's directory, CMakeLists.txt and starter files. Throws Error on anything
|
||||
/// it would have to guess about or overwrite.
|
||||
Module addModule(const Project& project, const NewModule& request);
|
||||
|
||||
/// Removes the module's directory. Refuses while another module still depends on it.
|
||||
void deleteModule(const Project& project, const std::string& idOrDir);
|
||||
|
||||
struct RenameResult
|
||||
{
|
||||
std::string oldId;
|
||||
std::string newId;
|
||||
std::string oldDir;
|
||||
std::string newDir;
|
||||
/// Modules whose CMakeLists.txt had a DEPENDS entry updated.
|
||||
std::vector<std::string> updatedDependents;
|
||||
};
|
||||
|
||||
/// Renames the directory and/or the id, and rewrites DEPENDS entries elsewhere to the new id.
|
||||
RenameResult renameModule(const Project& project, const std::string& idOrDir,
|
||||
const std::optional<std::string>& newName,
|
||||
const std::optional<std::string>& newId);
|
||||
|
||||
/// What `module` depends on, without repeats, in the order met (nearest first). Entries that
|
||||
/// are not modules of the project (fmt::fmt, pthread) are included as they were written.
|
||||
std::vector<std::string> dependenciesOf(const std::vector<Module>& modules,
|
||||
const Module& module, bool recursive);
|
||||
|
||||
/// The modules that depend on `module`, directly or (recursive) through others.
|
||||
std::vector<std::string> dependentsOf(const std::vector<Module>& modules, const Module& module,
|
||||
bool recursive);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Project.hpp"
|
||||
|
||||
/// The project's CMake presets, which are what a platform, an architecture and a compiler mean:
|
||||
/// every SDK names its configure presets "<platform>-<arch>-<compiler>" (linux-x64-gcc,
|
||||
/// windows-x64-clang-cl).
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
struct Preset
|
||||
{
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string platform; // linux, macos, windows
|
||||
std::string arch; // x64, x86, arm32, arm64
|
||||
std::string compiler; // the rest of the name: gcc, clang, msvc, clang-cl, mingw64, ...
|
||||
};
|
||||
|
||||
/// The configure presets usable on this machine (what `cmake --list-presets` shows).
|
||||
std::vector<Preset> listPresets(const Project& project);
|
||||
|
||||
/// What was asked for; every field is optional and compared ignoring case and punctuation
|
||||
/// ("ClangCl" finds clang-cl).
|
||||
struct PresetQuery
|
||||
{
|
||||
std::optional<std::string> preset;
|
||||
std::optional<std::string> platform;
|
||||
std::optional<std::string> arch;
|
||||
std::optional<std::string> compiler;
|
||||
};
|
||||
|
||||
/// The preset a command should use: the named preset; else one made from the platform,
|
||||
/// architecture and compiler given, the project's defaults and then this machine for whatever
|
||||
/// is missing; else the project's default preset; else none (the SDK's own default). Throws an
|
||||
/// Error that lists the choices when what was asked for doesn't exist. `note` gets a line to
|
||||
/// show the user when the choice involved a guess.
|
||||
std::optional<std::string> resolvePreset(const Project& project, const Defaults& defaults,
|
||||
const PresetQuery& query, std::string* note = nullptr);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/// Running other programs. Arguments are passed as a vector, never through a shell, so nothing
|
||||
/// needs quoting by the caller.
|
||||
namespace breeze::proc
|
||||
{
|
||||
|
||||
struct Options
|
||||
{
|
||||
/// Working directory; empty keeps the current one.
|
||||
std::filesystem::path cwd;
|
||||
/// Variables set (or replaced) for the child only.
|
||||
std::vector<std::pair<std::string, std::string>> env;
|
||||
/// Collect the child's standard output into Result::output instead of showing it.
|
||||
bool capture = false;
|
||||
/// With capture, standard error is collected too (merged into the same string).
|
||||
bool mergeStderr = false;
|
||||
/// Throw the child's output away.
|
||||
bool silent = false;
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
int exitCode = 0;
|
||||
std::string output;
|
||||
};
|
||||
|
||||
/// Runs argv[0] with the rest as arguments (looked up on PATH when it has no directory part)
|
||||
/// and waits. A program that can't be started is exit code 127. Throws Error only for failures
|
||||
/// of this function itself (nothing to run, the system refusing to create a process).
|
||||
Result run(const std::vector<std::string>& argv, const Options& options = {});
|
||||
|
||||
/// Runs a .bat/.cmd script on Windows through cmd.exe, or the script itself elsewhere.
|
||||
Result runScript(const std::filesystem::path& script, const std::vector<std::string>& args,
|
||||
const Options& options = {});
|
||||
|
||||
/// Replaces this process with argv (POSIX), or runs it and exits with its status (Windows).
|
||||
/// Does not return. Throws Error when it can't be started.
|
||||
[[noreturn]] void execReplace(const std::vector<std::string>& argv);
|
||||
|
||||
/// The first match for `program` on PATH, if any.
|
||||
std::optional<std::filesystem::path> findInPath(const std::string& program);
|
||||
|
||||
/// Sets an environment variable for this process and the programs it starts from now on.
|
||||
void setEnvironment(const std::string& name, const std::string& value);
|
||||
|
||||
bool stdinIsTerminal();
|
||||
bool stdoutIsTerminal();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "breeze/Version.hpp"
|
||||
|
||||
/// Finding a breeze project, telling which SDK it has, and reading and writing the project's own
|
||||
/// settings.
|
||||
///
|
||||
/// The CLI is global and projects carry their SDK (the template's breeze/ folder), so each project
|
||||
/// may be on a different SDK version -- or on the layout before breeze/ existed, with scripts/ and
|
||||
/// cmake/ at the root. Everything here asks the project's Sdk where things are instead of assuming.
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
enum class SdkLayout
|
||||
{
|
||||
/// breeze/ (the SDK) and project/ (the project's settings).
|
||||
Breeze,
|
||||
/// The flat layout that came before: scripts/, cmake/ and Configuration.cmake at the root.
|
||||
Legacy
|
||||
};
|
||||
|
||||
struct Sdk
|
||||
{
|
||||
SdkLayout layout = SdkLayout::Breeze;
|
||||
|
||||
/// breeze/VERSION. Empty for the legacy layout and for a breeze/ that predates the file.
|
||||
std::optional<Version> version;
|
||||
|
||||
/// <root>/breeze, or <root> in the legacy layout.
|
||||
std::filesystem::path dir;
|
||||
std::filesystem::path scriptsDir;
|
||||
std::filesystem::path cmakeDir;
|
||||
|
||||
/// project/Configuration.cmake, or Configuration.cmake in the legacy layout.
|
||||
std::filesystem::path settingsFile;
|
||||
|
||||
/// Does this SDK have add_objects_module?
|
||||
bool supportsObjects = false;
|
||||
|
||||
/// "1.0.0", "unversioned (breeze/ layout)" or "legacy (flat layout, unversioned)".
|
||||
[[nodiscard]] std::string describe() const;
|
||||
};
|
||||
|
||||
struct Project
|
||||
{
|
||||
std::filesystem::path root;
|
||||
Sdk sdk;
|
||||
|
||||
/// <root>/.breeze: what the CLI keeps in the project (defaults, installed tools).
|
||||
[[nodiscard]] std::filesystem::path metaDir() const;
|
||||
[[nodiscard]] std::filesystem::path defaultsFile() const;
|
||||
[[nodiscard]] std::filesystem::path toolsDir() const;
|
||||
|
||||
/// The SDK's script `name` for this OS: scripts/<name>.sh, or .bat on Windows.
|
||||
[[nodiscard]] std::filesystem::path script(const std::string& name) const;
|
||||
};
|
||||
|
||||
/// Does `root` hold a breeze project, and with which SDK?
|
||||
std::optional<Sdk> detectSdk(const std::filesystem::path& root);
|
||||
|
||||
/// The nearest project at or above `start`.
|
||||
std::optional<Project> findProject(const std::filesystem::path& start);
|
||||
|
||||
/// findProject, or an Error explaining that the command needs a project.
|
||||
Project requireProject(const std::filesystem::path& start = std::filesystem::current_path());
|
||||
|
||||
enum class Setting
|
||||
{
|
||||
Name, // CMake's project() name
|
||||
Version, // the project's own version, major.minor[.patch[.tweak]]
|
||||
Id // the machine-friendly identifier (PROJECT_CODE_NAME)
|
||||
};
|
||||
|
||||
std::string readSetting(const Project& project, Setting which);
|
||||
|
||||
/// Validates the value, then changes just that value in the file, leaving everything else as it
|
||||
/// is.
|
||||
void writeSetting(const Project& project, Setting which, const std::string& value);
|
||||
|
||||
/// The CLI's per-project defaults, in .breeze/defaults.json (meant to be committed).
|
||||
class Defaults
|
||||
{
|
||||
public:
|
||||
static Defaults load(const Project& project);
|
||||
|
||||
/// key: platform, arch, compiler, preset or mode.
|
||||
[[nodiscard]] std::optional<std::string> build(const std::string& key) const;
|
||||
void setBuild(const std::string& key, const std::string& value);
|
||||
void clearBuild(const std::string& key);
|
||||
|
||||
/// The module id `breeze run` runs when it is given none.
|
||||
[[nodiscard]] std::optional<std::string> run() const;
|
||||
void setRun(const std::string& id);
|
||||
void clearRun();
|
||||
|
||||
void save(const Project& project) const;
|
||||
|
||||
private:
|
||||
nlohmann::json m_data = nlohmann::json::object();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Project.hpp"
|
||||
#include "breeze/Version.hpp"
|
||||
|
||||
/// SDK versions, where they come from and what moving a project between them means.
|
||||
///
|
||||
/// An SDK version is a snapshot of the whole template: breeze/ (the SDK proper, replaced as a whole
|
||||
/// on a change), plus the files the project owns and starts from (project/Configuration.cmake, the
|
||||
/// root CMakeLists.txt, CMakePresets.json, the dot files). Versions come from a *source*, either a
|
||||
/// git repository whose tags are versions (v1.2.0, 1.2.0) or a directory with one subdirectory per
|
||||
/// version.
|
||||
namespace breeze::sdk
|
||||
{
|
||||
|
||||
class Source
|
||||
{
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
Version version;
|
||||
/// The tag, or the directory name, it was found under.
|
||||
std::string ref;
|
||||
};
|
||||
|
||||
/// The configured source (`breeze config sdk-source`, or BREEZE_SDK_SOURCE); throws Error
|
||||
/// when there is none.
|
||||
static Source configured();
|
||||
|
||||
explicit Source(std::string location);
|
||||
|
||||
[[nodiscard]] const std::string& location() const { return m_location; }
|
||||
|
||||
/// All versions, oldest first.
|
||||
[[nodiscard]] std::vector<Entry> versions() const;
|
||||
|
||||
/// The files of one version, fetched into the CLI's cache on first use.
|
||||
[[nodiscard]] std::filesystem::path snapshot(const Entry& entry) const;
|
||||
|
||||
/// "latest" (or nothing), or a version number: the matching entry. Throws Error, listing
|
||||
/// what exists, when there is no such version.
|
||||
[[nodiscard]] Entry resolve(const std::optional<std::string>& wanted) const;
|
||||
|
||||
private:
|
||||
std::string m_location;
|
||||
bool m_isRepository = false;
|
||||
};
|
||||
|
||||
/// Creates ./<name> from an SDK version: a project with that SDK, the settings set to the new
|
||||
/// project's name and id, no demo modules, and one executable module named after the project.
|
||||
void createProject(const std::string& name, const std::optional<std::string>& id,
|
||||
const std::optional<std::string>& sdkVersion);
|
||||
|
||||
/// Reports which SDK versions exist and whether the project is behind.
|
||||
void update(const Project& project);
|
||||
|
||||
/// Moves the project to the newest version with the same major as its own.
|
||||
void upgrade(const Project& project);
|
||||
|
||||
/// Moves the project to exactly this version, whichever way and across majors, and a legacy
|
||||
/// (flat) project to the breeze/ layout.
|
||||
void migrate(const Project& project, const std::string& version);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
/// Small string helpers; nothing here knows about breeze.
|
||||
namespace breeze::text
|
||||
{
|
||||
|
||||
std::string trim(std::string_view s);
|
||||
std::vector<std::string> split(std::string_view s, char delimiter);
|
||||
std::vector<std::string> splitLines(std::string_view s);
|
||||
std::string join(const std::vector<std::string>& parts, std::string_view separator);
|
||||
std::string lower(std::string_view s);
|
||||
bool startsWith(std::string_view s, std::string_view prefix);
|
||||
bool endsWith(std::string_view s, std::string_view suffix);
|
||||
std::string replaceAll(std::string s, std::string_view from, std::string_view to);
|
||||
|
||||
/// "CoreFarewell" -> "core_farewell", "HTTPServer" -> "http_server", "my-app" -> "my_app".
|
||||
std::string snakeCase(std::string_view s);
|
||||
|
||||
/// Lower-case letters and digits only: "Clang-CL" -> "clangcl". For comparing names loosely.
|
||||
std::string normalized(std::string_view s);
|
||||
|
||||
/// A module or project name: a letter, then letters, digits, '_' (and '-' when allowDash).
|
||||
bool isName(std::string_view s, bool allowDash = false);
|
||||
|
||||
/// A CMake target id as this project writes them: letters, digits, '_', '.', '+', '-' and "::".
|
||||
bool isTargetId(std::string_view s);
|
||||
|
||||
/// "x" for a version-less message, with a '<none>' stand-in for an empty value.
|
||||
std::string orNone(std::string_view s);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breeze/Project.hpp"
|
||||
|
||||
/// Tools: extensions a project installs from a repository. A tool is a git repository (or, while it
|
||||
/// is being written, a directory) with a `breeze-tool.json` at its root:
|
||||
///
|
||||
/// { "id": "resembed", "version": "1.0.0", "description": "...", "entry": "bin/resembed.sh" }
|
||||
///
|
||||
/// `entry` may instead be { "posix": "...", "windows": "..." }. Installed, a tool is copied into
|
||||
/// <project>/.breeze/tools/<id>/ and recorded in .breeze/tools.json (meant to be committed). From
|
||||
/// then on `breeze tool <id> <arguments...>` runs its entry with those arguments, in the project's
|
||||
/// directory, with the project described in environment variables (BREEZE_PROJECT_ROOT,
|
||||
/// BREEZE_SDK_DIR, BREEZE_SDK_VERSION, BREEZE_TOOL_ID, BREEZE_TOOL_DIR, BREEZE_CLI, BREEZE_EDITOR).
|
||||
/// What commands a tool offers, and what they do, is entirely up to the tool.
|
||||
namespace breeze::tools
|
||||
{
|
||||
|
||||
struct Tool
|
||||
{
|
||||
std::string id;
|
||||
std::string version;
|
||||
std::string repo;
|
||||
std::string description;
|
||||
std::filesystem::path dir;
|
||||
};
|
||||
|
||||
/// The words after `breeze tool` that belong to the CLI itself and can't be tool ids.
|
||||
bool isReservedName(const std::string& word);
|
||||
|
||||
std::vector<Tool> installed(const Project& project);
|
||||
|
||||
/// Installs the newest version of the tool in `repo`.
|
||||
void install(const Project& project, const std::string& repo);
|
||||
void uninstall(const Project& project, const std::string& id);
|
||||
|
||||
/// Reports whether newer versions of the tool exist.
|
||||
void update(const Project& project, const std::string& id);
|
||||
|
||||
/// Moves the tool to its newest version with the same major version.
|
||||
void upgrade(const Project& project, const std::string& id);
|
||||
|
||||
/// Moves the tool to exactly this version.
|
||||
void migrate(const Project& project, const std::string& id, const std::string& version);
|
||||
|
||||
void list(const Project& project);
|
||||
|
||||
/// Runs the tool's entry with `args`; the entry's exit code, or nullopt when no such tool is
|
||||
/// installed.
|
||||
std::optional<int> run(const Project& project, const std::string& id,
|
||||
const std::vector<std::string>& args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace breeze
|
||||
{
|
||||
|
||||
/// A semantic version: major.minor.patch with an optional pre-release tag.
|
||||
/// A pre-release sorts before the release it leads up to (1.0.0-rc1 < 1.0.0).
|
||||
struct Version
|
||||
{
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
int patch = 0;
|
||||
std::string pre;
|
||||
|
||||
[[nodiscard]] std::string toString() const;
|
||||
};
|
||||
|
||||
/// Accepts "1.2.3", "v1.2.3", "1.2.3-rc1", "1.2" (patch 0) and "1" (minor and patch 0);
|
||||
/// anything else, including build metadata after '+', is dropped or rejected.
|
||||
std::optional<Version> parseVersion(std::string_view text);
|
||||
|
||||
int compare(const Version& a, const Version& b);
|
||||
bool operator<(const Version& a, const Version& b);
|
||||
bool operator==(const Version& a, const Version& b);
|
||||
|
||||
/// The greatest of `versions`; with `sameMajorAs`, only of those with that major version.
|
||||
std::optional<Version> greatest(const std::vector<Version>& versions,
|
||||
std::optional<int> sameMajorAs = std::nullopt);
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user