asdf2
This commit is contained in:
parent
490063ccc5
commit
79a1d566b0
555
weave.cc
555
weave.cc
|
|
@ -1,4 +1,3 @@
|
|||
///$(which true);FLAGS="-g -Wall -Wextra --std=c++23 -O3";c++ $FLAGS -o weave "$0" || { echo "Compilation failed"; exit 1; }; ./weave; exit 0
|
||||
#include <cstdio>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
|
|
@ -9,9 +8,9 @@
|
|||
#include <sstream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <ranges>
|
||||
|
||||
// Type aliases for convenience
|
||||
using u8 = uint8_t;
|
||||
|
|
@ -35,6 +34,71 @@ template <class... Ts> struct overloads : Ts... {
|
|||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template <usize N> struct hash_mix;
|
||||
|
||||
template <> struct hash_mix<64> {
|
||||
auto operator()(u64 x) const -> u64 {
|
||||
auto const m = 0xe9846af9b1a615d;
|
||||
x ^= x >> 32;
|
||||
x *= m;
|
||||
x ^= x >> 32;
|
||||
x *= m;
|
||||
x ^= x >> 28;
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
template<> struct hash_mix<32> {
|
||||
auto operator()(u32 x) const -> u32 {
|
||||
const u32 m1 = 0x21f0aaad;
|
||||
const u32 m2 = 0x735a2d97;
|
||||
|
||||
x ^= x >> 16;
|
||||
x *= m1;
|
||||
x ^= x >> 15;
|
||||
x *= m2;
|
||||
x ^= x >> 15;
|
||||
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> auto hash_combine(usize &seed, const T &value) -> usize {
|
||||
seed =
|
||||
hash_mix<sizeof(usize) * 8>{}(seed + 0x9e3779b9 + std::hash<T>{}(value));
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
concept hashable = requires(const T &a) {
|
||||
{ std::hash<std::remove_cvref_t<T>>{}(a) } -> std::convertible_to<std::size_t>;
|
||||
};
|
||||
|
||||
template <typename R, typename T>
|
||||
concept range_of = std::ranges::input_range<R> &&
|
||||
std::convertible_to<std::ranges::range_value_t<R>, T>;
|
||||
|
||||
|
||||
template <typename T, typename R>
|
||||
auto hash_range(std::size_t &seed, const R &range) -> usize requires range_of<R, T> && hashable<T> {
|
||||
for (const auto &elem : range) {
|
||||
hash_combine(seed, elem);
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
struct std::hash<std::pair<T, U>> {
|
||||
std::size_t operator()(const std::pair<T, U> &p) const requires hashable<T> && hashable<U> {
|
||||
std::size_t seed = 0;
|
||||
hash_combine(seed, std::hash<std::remove_cvref_t<T>>{}(p.first));
|
||||
hash_combine(seed, std::hash<std::remove_cvref_t<T>>{}(p.second));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
class CowStr {
|
||||
std::variant<std::string_view, std::string> data_;
|
||||
|
||||
|
|
@ -250,14 +314,17 @@ enum class JsonType {
|
|||
JsonNumber(std::string_view value) : value(value) {}
|
||||
};
|
||||
struct JsonValue {
|
||||
std::variant<JsonNull, bool, JsonNumber, std::string_view,
|
||||
std::variant<JsonNull, bool, JsonNumber, CowStr,
|
||||
std::vector<JsonValue>, JsonObject>
|
||||
value;
|
||||
|
||||
JsonValue() : value(JsonNull{}) {}
|
||||
JsonValue(JsonNull null) : value(null) {}
|
||||
JsonValue(bool boolean) : value(boolean) {}
|
||||
JsonValue(const std::string_view& str) : value(str) {}
|
||||
JsonValue(CowStr str) : value(str) {}
|
||||
JsonValue(std::string_view str) : value(CowStr(str)) {}
|
||||
JsonValue(std::string str) : value(CowStr(str)) {}
|
||||
JsonValue(const char* str) : value(CowStr(str)) {}
|
||||
JsonValue(JsonNumber number) : value(number) {}
|
||||
JsonValue(std::vector<JsonValue> array) : value(std::move(array)) {}
|
||||
JsonValue(JsonObject object) : value(std::move(object)) {}
|
||||
|
|
@ -268,7 +335,7 @@ enum class JsonType {
|
|||
}
|
||||
auto is_string() const -> bool {
|
||||
|
||||
return std::holds_alternative<std::string_view>(value);
|
||||
return std::holds_alternative<CowStr>(value);
|
||||
}
|
||||
auto is_number() const -> bool {
|
||||
return std::holds_alternative<JsonNumber>(value);
|
||||
|
|
@ -294,8 +361,8 @@ enum class JsonType {
|
|||
}
|
||||
}
|
||||
auto as_string() const -> std::optional<std::string_view> {
|
||||
if (auto pval = std::get_if<std::string_view>(&value)) {
|
||||
return *pval;
|
||||
if (auto pval = std::get_if<CowStr>(&value)) {
|
||||
return pval->view();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -333,7 +400,7 @@ enum class JsonType {
|
|||
[&](JsonNull _) { os << "null"; },
|
||||
[&](bool b) { os << (b ? "true" : "false"); },
|
||||
[&](const JsonNumber &num) { os << num.value; },
|
||||
[&](const std::string_view &str) { os << "\"" << str << "\""; },
|
||||
[&](const CowStr &str) { os << "\"" << str.view() << "\""; },
|
||||
[&](const JsonObject &obj) {
|
||||
os << "{";
|
||||
auto first = true;
|
||||
|
|
@ -389,25 +456,103 @@ enum class JsonType {
|
|||
|
||||
}
|
||||
|
||||
struct SourceFlags;
|
||||
struct Source {
|
||||
std::string_view path;
|
||||
std::string_view flags;
|
||||
std::string_view defines;
|
||||
std::vector<std::string_view> flags;
|
||||
std::unordered_map<std::string_view, std::string_view> defines;
|
||||
|
||||
Source(std::string_view path)
|
||||
: path(std::move(path)), flags(""), defines("") {}
|
||||
// rule of five
|
||||
Source(const Source &) = delete;
|
||||
Source(Source &&) = delete;
|
||||
Source &operator=(const Source &) = delete;
|
||||
Source &operator=(Source &&) = delete;
|
||||
: path(std::move(path)), flags({}), defines({}) {}
|
||||
|
||||
auto with_flag(std::string_view flag) -> Source & {
|
||||
flags.push_back(flag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto as_flags() const -> SourceFlags;
|
||||
};
|
||||
|
||||
struct Module : public Source {
|
||||
std::optional<std::string> name_;
|
||||
|
||||
Module(std::string_view path)
|
||||
: Source(std::move(path)), name_(std::nullopt) {}
|
||||
Module(std::string_view path, std::string name) : Source(std::move(path)), name_(std::move(name)) {}
|
||||
Module(Source source, std::string name) : Source(source), name_(name) {}
|
||||
Module(Source source) : Source(source), name_(std::nullopt) {}
|
||||
|
||||
auto name() const -> CowStr {
|
||||
if (name_) {
|
||||
return CowStr(*name_);
|
||||
} else {
|
||||
auto name = std::string(path.substr(
|
||||
0, path.find_last_of('.'))); // remove extension
|
||||
for (auto &slash :
|
||||
name | std::views::filter([](char c) { return c == '/'; })) {
|
||||
slash = '.';
|
||||
}
|
||||
|
||||
return CowStr(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SourceFlags {
|
||||
const Source &source;
|
||||
|
||||
SourceFlags(const Source &source) : source(source) {}
|
||||
|
||||
friend auto operator<<(std::ostream &os, const SourceFlags &sf) -> std::ostream & {
|
||||
for (const auto &flag : sf.source.flags) {
|
||||
os << flag << " ";
|
||||
}
|
||||
for (const auto &[k, v] : sf.source.defines) {
|
||||
os << "-D" << k << "=" << v << " ";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
friend auto operator<<(std::vector<json::JsonValue> &args, const SourceFlags &sf) -> std::vector<json::JsonValue> & {
|
||||
for (const auto &flag : sf.source.flags) {
|
||||
args.push_back(json::JsonValue(flag));
|
||||
}
|
||||
for (const auto &[k, v] : sf.source.defines) {
|
||||
const auto define = "-D" + std::string(k) + "=" + std::string(v);
|
||||
args.push_back(json::JsonValue(define));
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
};
|
||||
|
||||
auto Source::as_flags() const -> SourceFlags {
|
||||
return SourceFlags(*this);
|
||||
}
|
||||
|
||||
struct SourceHash {
|
||||
std::size_t operator()(const Source &source) const {
|
||||
auto path = std::hash<std::string_view>{}(source.path);
|
||||
auto flags = hash_range<std::string_view>(path, source.flags);
|
||||
auto defines = hash_range<std::pair<std::string_view, std::string_view>>(flags, source.defines);
|
||||
|
||||
return defines;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<Source> {
|
||||
std::size_t operator()(const Source &source) const {
|
||||
return SourceHash{}(source);
|
||||
}
|
||||
};
|
||||
|
||||
struct Project {
|
||||
std::string_view name;
|
||||
std::vector<std::string_view> sources;
|
||||
std::vector<Source> sources;
|
||||
std::unordered_map<std::string, std::string> project_defines;
|
||||
std::vector<std::string> project_flags;
|
||||
std::vector<Module> modules;
|
||||
|
||||
// rule of five
|
||||
Project(const Project &) = delete;
|
||||
|
|
@ -416,10 +561,122 @@ struct Project {
|
|||
Project &operator=(Project &&) = delete;
|
||||
|
||||
Project(std::string_view name)
|
||||
: name(std::move(name)), sources({}), project_defines({}), project_flags({}) {}
|
||||
: name(std::move(name)), sources({}), project_defines({}), project_flags({}), modules({}) {}
|
||||
auto add_source(std::string_view source) -> void {
|
||||
sources.push_back(Source(std::move(source)));
|
||||
}
|
||||
auto add_source(Source source) -> void {
|
||||
sources.push_back(std::move(source));
|
||||
}
|
||||
auto add_source(Source &&source) -> void {
|
||||
sources.push_back(std::move(source));
|
||||
}
|
||||
|
||||
auto add_module(Module module) -> void { modules.push_back(module); }
|
||||
|
||||
auto add_module(Source &&module) -> void {
|
||||
modules.push_back({std::move(module)});
|
||||
}
|
||||
|
||||
auto add_module(std::string_view module) -> void {
|
||||
modules.push_back(Module(std::move(module)));
|
||||
}
|
||||
|
||||
auto into_ninja_commands() -> std::string {
|
||||
const auto build_dir = "target/artifacts/" + std::string(name);
|
||||
|
||||
auto ninja = std::ostringstream{};
|
||||
ninja << "ninja_required_version = 1.3\n";
|
||||
ninja << "build_dir = " << build_dir << "\n";
|
||||
ninja << "target = " << name << "\n\n";
|
||||
ninja << "cxxflags = ";
|
||||
|
||||
auto flags = project_flags | std::views::join_with(' ');
|
||||
for (const auto& flag : flags) {
|
||||
ninja << flag;
|
||||
}
|
||||
ninja << "\n";
|
||||
ninja << "cxxdefs = ";
|
||||
auto defines = project_defines | std::views::transform([](const auto &kv) {
|
||||
return "-D" + kv.first + "=" + kv.second;
|
||||
}) |
|
||||
std::views::join_with(' ');
|
||||
for (const auto& define : defines) {
|
||||
ninja << define;
|
||||
}
|
||||
ninja << "\n\n";
|
||||
|
||||
const std::string link_cmd = "clang++ -fuse-ld=mold";
|
||||
const std::string cxx_cmd = "clang++";
|
||||
|
||||
// link rule
|
||||
{
|
||||
ninja << "rule link\n";
|
||||
ninja << " command = " << link_cmd << " -o $out $in\n\n";
|
||||
}
|
||||
|
||||
// compile rule
|
||||
{
|
||||
ninja << "rule cc\n";
|
||||
ninja << " command = " << cxx_cmd << " $cxxflags $cxxdefs $fileflags $modules -c $in -o $out -MMD -MF $depfile\n";
|
||||
ninja << " depfile = $depfile\n";
|
||||
ninja << " deps = gcc\n\n";
|
||||
}
|
||||
|
||||
// module rule
|
||||
{
|
||||
ninja << "rule cc_mod\n";
|
||||
ninja << " command = " << cxx_cmd
|
||||
<< " $cxxflags $cxxdefs $fileflags $in "
|
||||
"--precompile -o $out -MMD -MF $depfile\n";
|
||||
ninja << " depfile = $depfile\n";
|
||||
ninja << " deps = gcc\n\n";
|
||||
}
|
||||
|
||||
// precompile modules
|
||||
auto module_files = std::ostringstream{};
|
||||
for (const auto &module : modules) {
|
||||
auto module_ss = std::ostringstream{};
|
||||
module_ss << std::hex << std::hash<Source>{}(module);
|
||||
const auto module_obj = module_ss.str();
|
||||
|
||||
ninja << "build $build_dir/" << module_obj << ".pcm: cc_mod " << module.path << "\n";
|
||||
ninja << " depfile = $build_dir/" << module_obj << ".d\n";
|
||||
ninja << " fileflags = " << module.as_flags() << "\n\n";
|
||||
|
||||
auto module_name = module.name();
|
||||
module_files << "-fmodule-file=" << module_name.view() << "=" << "$build_dir/" << module_obj << ".pcm ";
|
||||
}
|
||||
|
||||
ninja << "modules = " << module_files.str() << "\n\n";
|
||||
|
||||
// compile sources
|
||||
auto objects = std::vector<std::string>{};
|
||||
for (const auto &source : sources) {
|
||||
auto object_ss = std::ostringstream{};
|
||||
object_ss << std::hex << std::hash<Source>{}(source);
|
||||
const auto object = object_ss.str();
|
||||
|
||||
ninja << "build $build_dir/" << object << ".o: cc " << source.path << "\n";
|
||||
ninja << " depfile = $build_dir/" << object << ".d\n";
|
||||
ninja << " fileflags = " << source.as_flags() << "\n\n";
|
||||
objects.push_back(object);
|
||||
}
|
||||
|
||||
ninja << "\nbuild $target: link ";
|
||||
auto joined =
|
||||
objects |
|
||||
std::views::transform([](const std::string &obj) -> std::string {
|
||||
return "$build_dir/" + obj + ".o";
|
||||
}) |
|
||||
std::views::join_with(' ');
|
||||
for (const auto &part : joined) {
|
||||
ninja << part;
|
||||
}
|
||||
ninja << "\n\n";
|
||||
|
||||
return ninja.str();
|
||||
}
|
||||
|
||||
auto into_compile_commands(const std::string_view& working_dir) -> json::JsonValue {
|
||||
std::vector<json::JsonValue> compile_commands{};
|
||||
|
|
@ -435,15 +692,19 @@ struct Project {
|
|||
}();
|
||||
|
||||
for (const auto &source : sources) {
|
||||
std::printf("Processing source: %s\n", source.data());
|
||||
std::cout << std::format("Processing source: {}\n", source.path);
|
||||
auto fields = std::unordered_map<std::string_view, json::JsonValue>{};
|
||||
fields["directory"] = json::JsonValue(working_dir);
|
||||
fields["file"] = json::JsonValue(source);
|
||||
fields["arguments"] = json::JsonValue(std::vector<json::JsonValue>{
|
||||
json::JsonValue(std::string_view("g++")), json::JsonValue(std::string_view("-c")),
|
||||
json::JsonValue(std::string_view(source)), json::JsonValue(std::string_view("-o")),
|
||||
json::JsonValue(std::string_view(std::string(source) + std::string(".o"))),
|
||||
json::JsonValue(std::vector<json::JsonValue>(project_args))});
|
||||
fields["file"] = json::JsonValue(source.path);
|
||||
auto arguments = std::vector<json::JsonValue>{
|
||||
json::JsonValue("g++"), json::JsonValue("-c"),
|
||||
json::JsonValue(std::string_view(source.path)),
|
||||
json::JsonValue(std::string_view("-o")),
|
||||
json::JsonValue(std::string(source.path) + std::string(".o"))};
|
||||
arguments.append_range(project_args);
|
||||
arguments << source.as_flags();
|
||||
fields["arguments"] = json::JsonValue(arguments);
|
||||
fields["output"] = json::JsonValue(std::string(source.path) + ".o");
|
||||
|
||||
compile_commands.push_back(json::JsonValue(json::JsonObject(fields)));
|
||||
}
|
||||
|
|
@ -452,6 +713,7 @@ struct Project {
|
|||
return compile_commands;
|
||||
}
|
||||
};
|
||||
|
||||
static auto ninja_escape(std::string_view s) -> std::string {
|
||||
std::string out;
|
||||
out.reserve(s.size());
|
||||
|
|
@ -542,40 +804,263 @@ auto compile_commands_into_ninja(const json::JsonValue &compile_commands)
|
|||
if (auto args_field = obj.get_field("arguments")) {
|
||||
cmd = shell_join_arguments(args_field->get().as_array().value());
|
||||
} else if (auto command_field = obj.get_field("command")) {
|
||||
cmd = command_field->get().as_string().value();
|
||||
cmd = ninja_escape(command_field->get().as_string().value());
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"compile_commands entry must contain either 'arguments' or 'command'");
|
||||
}
|
||||
|
||||
ninja << "build " << ninja_escape(output) << ": cc " << ninja_escape(file) << "\n";
|
||||
ninja << " cmd = " << ninja_escape(cmd) << "\n\n";
|
||||
ninja << " cmd = " << cmd << "\n\n";
|
||||
}
|
||||
|
||||
ninja << "rule link\n";
|
||||
ninja << " command = g++ -fuse-ld=mold -o $out $in\n";
|
||||
|
||||
std::cout << ninja.str();
|
||||
} else {
|
||||
throw std::runtime_error("compile_commands root must be an array");
|
||||
}
|
||||
}
|
||||
|
||||
namespace libc {
|
||||
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
}
|
||||
|
||||
namespace command {
|
||||
|
||||
|
||||
/// returns {child_fd, parent_fd}
|
||||
auto piped_stdio(bool readable) -> std::pair<u32, u32> {
|
||||
int fds[2];
|
||||
if (::libc::pipe(fds) == -1) {
|
||||
throw std::runtime_error("Failed to create pipe");
|
||||
}
|
||||
|
||||
if (readable) {
|
||||
// read end for child, write end for parent
|
||||
return {static_cast<u32>(fds[0]), static_cast<u32>(fds[1])};
|
||||
} else {
|
||||
// write end for child, read end for parent
|
||||
return {static_cast<u32>(fds[1]), static_cast<u32>(fds[0])};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class StdioStrategy {
|
||||
Inherit,
|
||||
Piped,
|
||||
Fd,
|
||||
Null,
|
||||
};
|
||||
|
||||
struct StdioInherit : std::monostate {};
|
||||
struct StdioPiped : std::monostate {};
|
||||
// struct StdioNull : std::monostate {};
|
||||
struct StdioFd {
|
||||
u32 fd;
|
||||
|
||||
StdioFd(u32 fd) : fd(fd) {}
|
||||
};
|
||||
|
||||
class Stdio {
|
||||
StdioStrategy strategy;
|
||||
std::optional<u32> fd_;
|
||||
|
||||
public:
|
||||
Stdio(u32 fd) : strategy(StdioStrategy::Fd), fd_(fd) {}
|
||||
Stdio(StdioFd stdio_fd) : strategy(StdioStrategy::Fd), fd_(stdio_fd.fd) {}
|
||||
Stdio(StdioInherit) : strategy(StdioStrategy::Inherit), fd_(std::nullopt) {}
|
||||
Stdio(StdioPiped) : strategy(StdioStrategy::Piped), fd_(std::nullopt) {}
|
||||
|
||||
auto operator=(u32 fd) -> Stdio & {
|
||||
strategy = StdioStrategy::Fd;
|
||||
fd_ = fd;
|
||||
return *this;
|
||||
}
|
||||
auto operator=(StdioFd stdio_fd) -> Stdio & {
|
||||
strategy = StdioStrategy::Fd;
|
||||
fd_ = stdio_fd.fd;
|
||||
return *this;
|
||||
}
|
||||
auto operator=(StdioInherit) -> Stdio & {
|
||||
strategy = StdioStrategy::Inherit;
|
||||
fd_ = std::nullopt;
|
||||
return *this;
|
||||
}
|
||||
auto operator=(StdioPiped) -> Stdio & {
|
||||
strategy = StdioStrategy::Piped;
|
||||
fd_ = std::nullopt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto fd() const -> std::optional<u32> {
|
||||
if (strategy == StdioStrategy::Fd) {
|
||||
return fd_;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
auto into_child_parent(bool readable) -> std::pair<Stdio, std::optional<u32>> {
|
||||
switch (strategy) {
|
||||
case StdioStrategy::Inherit:
|
||||
return {StdioInherit{}, std::nullopt};
|
||||
case StdioStrategy::Fd:
|
||||
return {{StdioFd(fd_.value())}, std::nullopt};
|
||||
case StdioStrategy::Piped: {
|
||||
auto [child_fd, parent_fd] = piped_stdio(readable);
|
||||
return {{StdioFd(child_fd)}, std::make_optional(parent_fd)};
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Invalid Stdio strategy");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Process {
|
||||
pid_t pid_;
|
||||
|
||||
public:
|
||||
Process(pid_t pid) : pid_(pid) {}
|
||||
|
||||
auto pid() const -> pid_t {
|
||||
return pid_;
|
||||
}
|
||||
|
||||
auto wait() -> int {
|
||||
int status;
|
||||
if (::libc::waitpid(pid_, &status, 0) == -1) {
|
||||
throw std::runtime_error("Failed to wait for child process");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
auto send_signal(int signal) -> void {
|
||||
if (::libc::kill(pid_, signal) == -1) {
|
||||
throw std::runtime_error("Failed to send signal to child process");
|
||||
}
|
||||
}
|
||||
|
||||
auto kill() -> void {
|
||||
send_signal(SIGKILL);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Child : public Process {
|
||||
std::optional<u32> stdout_fd{};
|
||||
|
||||
public:
|
||||
Child(pid_t pid) : Process(pid) {}
|
||||
Child(pid_t pid, std::optional<u32> stdout_fd) : Process(pid), stdout_fd(stdout_fd) {}
|
||||
|
||||
};
|
||||
class Command {
|
||||
std::vector<std::string> args;
|
||||
std::unordered_map<std::string, std::string> env;
|
||||
std::string cmd;
|
||||
std::string current_dir;
|
||||
Stdio stdout{StdioInherit{}};
|
||||
|
||||
public:
|
||||
Command(std::string cmd) : cmd(std::move(cmd)) {}
|
||||
|
||||
auto with_arg(std::string arg) -> Command & {
|
||||
args.push_back(std::move(arg));
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto with_env(std::string key, std::string value) -> Command & {
|
||||
env[std::move(key)] = std::move(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto with_current_dir(std::string dir) -> Command & {
|
||||
current_dir = std::move(dir);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto spawn() -> std::variant<Child, std::error_code> {
|
||||
std::vector<char *> argv;
|
||||
argv.push_back(const_cast<char *>(cmd.c_str()));
|
||||
for (const auto &arg : args) {
|
||||
argv.push_back(const_cast<char *>(arg.c_str()));
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
std::vector<std::string> env_strings;
|
||||
std::vector<char *> envp;
|
||||
for (const auto &[key, value] : env) {
|
||||
env_strings.push_back(key + "=" + value);
|
||||
envp.push_back(const_cast<char *>(env_strings.back().c_str()));
|
||||
}
|
||||
envp.push_back(nullptr);
|
||||
|
||||
auto [child_stdout, stdout_fd] = stdout.into_child_parent(false);
|
||||
|
||||
::libc::posix_spawn_file_actions_t file_actions;
|
||||
::libc::posix_spawn_file_actions_init(&file_actions);
|
||||
if (child_stdout.fd()) {
|
||||
::libc::posix_spawn_file_actions_adddup2(&file_actions, *stdout_fd, STDOUT_FILENO);
|
||||
}
|
||||
|
||||
pid_t pid;
|
||||
int result = ::libc::posix_spawnp(&pid, cmd.c_str(), &file_actions, nullptr, argv.data(), envp.data());
|
||||
::libc::posix_spawn_file_actions_destroy(&file_actions);
|
||||
|
||||
if (result != 0) {
|
||||
return std::error_code(result, std::generic_category());
|
||||
}
|
||||
|
||||
return Child{pid, stdout_fd};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <ext/stdio_filebuf.h>
|
||||
|
||||
auto main() -> int {
|
||||
std::printf("Hello, weave!\n");
|
||||
|
||||
// Example usage
|
||||
Project project("MyProject");
|
||||
project.add_source("src/main.cc");
|
||||
project.project_flags.append_range(std::list{"-g", "-Wall", "-Wextra", "--std=c++26", "-O3"});
|
||||
Project project("Weave");
|
||||
project.add_source("weave.cc");
|
||||
auto std_mod = Module("std.cppm", "std");
|
||||
std_mod.with_flag("--std=c++26")
|
||||
.with_flag("-Wno-reserved-identifier")
|
||||
.with_flag("-Wno-reserved-module-identifier")
|
||||
.with_flag("-Wno-unused-command-line-argument")
|
||||
.with_flag("-I/nix/store/wi8d6w3a8l82kppk060s7bjjw83601xi-gcc-16.1.0/"
|
||||
"include/c++/16.1.0/backward");
|
||||
project.add_module(std_mod);
|
||||
project.project_flags.append_range(
|
||||
std::list{"-Wall", "-Wextra", "--std=c++26", "-O1"});
|
||||
|
||||
auto compile_commands_json = project.into_compile_commands("/var/code/cxx/loom/");
|
||||
const auto ninja = project.into_ninja_commands();
|
||||
{
|
||||
|
||||
std::ofstream os("build.ninja");
|
||||
os << ninja;
|
||||
os.close();
|
||||
|
||||
std::cout << compile_commands_json << std::endl;
|
||||
// std::ofstream os("compile_commands.json");
|
||||
// os << compile_commands_json;
|
||||
// os.close();
|
||||
command::Command cmd("ninja");
|
||||
cmd.with_arg("-f").with_arg("build.ninja");
|
||||
cmd.spawn().visit(overloads{
|
||||
[](command::Child child) {
|
||||
std::printf("Spawned ninja with PID %d\n", child.pid());
|
||||
int status = child.wait();
|
||||
std::printf("Ninja exited with status %d\n", status);
|
||||
},
|
||||
[](std::error_code ec) {
|
||||
std::fprintf(stderr, "Failed to spawn ninja: %s\n", ec.message().c_str());
|
||||
}});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue