asdf
This commit is contained in:
commit
490063ccc5
6
src/main.cc
Normal file
6
src/main.cc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#include <cstdio>
|
||||
|
||||
auto main() -> int {
|
||||
std::printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
581
weave.cc
Executable file
581
weave.cc
Executable file
|
|
@ -0,0 +1,581 @@
|
|||
///$(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>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
// Type aliases for convenience
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using usize = std::size_t;
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using isize = std::ptrdiff_t;
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
|
||||
template <typename T>
|
||||
using RefT = std::reference_wrapper<T>;
|
||||
|
||||
template <class... Ts> struct overloads : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
class CowStr {
|
||||
std::variant<std::string_view, std::string> data_;
|
||||
|
||||
public:
|
||||
CowStr() : data_(std::string_view{}) {}
|
||||
|
||||
CowStr(std::string_view s) : data_(s) {}
|
||||
CowStr(const char* s) : data_(std::string_view{s}) {}
|
||||
CowStr(std::string s) : data_(std::move(s)) {}
|
||||
|
||||
[[nodiscard]] bool is_borrowed() const {
|
||||
return std::holds_alternative<std::string_view>(data_);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_owned() const {
|
||||
return std::holds_alternative<std::string>(data_);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view view() const {
|
||||
if (const auto* borrowed = std::get_if<std::string_view>(&data_)) {
|
||||
return *borrowed;
|
||||
}
|
||||
return std::get<std::string>(data_);
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* c_str() {
|
||||
if (auto* borrowed = std::get_if<std::string_view>(&data_)) {
|
||||
data_ = std::string(*borrowed);
|
||||
}
|
||||
return std::get<std::string>(data_).c_str();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string into_owned() && {
|
||||
if (auto* owned = std::get_if<std::string>(&data_)) {
|
||||
return std::move(*owned);
|
||||
}
|
||||
return std::string(std::get<std::string_view>(data_));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string to_owned() const {
|
||||
if (const auto* owned = std::get_if<std::string>(&data_)) {
|
||||
return *owned;
|
||||
}
|
||||
return std::string(std::get<std::string_view>(data_));
|
||||
}
|
||||
|
||||
std::string& to_mut() {
|
||||
if (auto* borrowed = std::get_if<std::string_view>(&data_)) {
|
||||
data_ = std::string(*borrowed);
|
||||
}
|
||||
return std::get<std::string>(data_);
|
||||
}
|
||||
};
|
||||
|
||||
namespace json {
|
||||
|
||||
static void append_utf8(std::string &out, u32 cp) {
|
||||
if (cp <= 0x7F) {
|
||||
out.push_back(static_cast<char>(cp));
|
||||
} else if (cp <= 0x7FF) {
|
||||
out.push_back(static_cast<char>(0xC0 | ((cp >> 6) & 0x1F)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
} else if (cp <= 0xFFFF) {
|
||||
out.push_back(static_cast<char>(0xE0 | ((cp >> 12) & 0x0F)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
} else {
|
||||
out.push_back(static_cast<char>(0xF0 | ((cp >> 18) & 0x07)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
|
||||
out.push_back(static_cast<char>(0x80 | (cp & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
static int hex_value(char c) {
|
||||
if ('0' <= c && c <= '9') return c - '0';
|
||||
if ('a' <= c && c <= 'f') return 10 + (c - 'a');
|
||||
if ('A' <= c && c <= 'F') return 10 + (c - 'A');
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Unescape a JSON string literal body (without surrounding quotes).
|
||||
// Throws std::runtime_error on malformed escape sequences.
|
||||
std::string json_unescape(const std::string &in) {
|
||||
std::string out;
|
||||
out.reserve(in.size());
|
||||
for (size_t i = 0; i < in.size(); ++i) {
|
||||
char c = in[i];
|
||||
if (c != '\\') {
|
||||
out.push_back(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Escape sequence
|
||||
if (++i >= in.size()) throw std::runtime_error("Invalid escape: trailing backslash");
|
||||
char e = in[i];
|
||||
switch (e) {
|
||||
case '"': out.push_back('"'); break;
|
||||
case '\\': out.push_back('\\'); break;
|
||||
case '/': out.push_back('/'); break;
|
||||
case 'b': out.push_back('\b'); break;
|
||||
case 'f': out.push_back('\f'); break;
|
||||
case 'n': out.push_back('\n'); break;
|
||||
case 'r': out.push_back('\r'); break;
|
||||
case 't': out.push_back('\t'); break;
|
||||
case 'u': {
|
||||
// parse 4 hex digits
|
||||
if (i + 4 >= in.size()) throw std::runtime_error("Invalid \\u escape: too short");
|
||||
int v = 0;
|
||||
for (int k = 1; k <= 4; ++k) {
|
||||
int hv = hex_value(in[i + k]);
|
||||
if (hv < 0) throw std::runtime_error("Invalid hex digit in \\u escape");
|
||||
v = (v << 4) | hv;
|
||||
}
|
||||
i += 4; // consumed 4 hex digits
|
||||
|
||||
uint32_t codepoint = static_cast<uint32_t>(v);
|
||||
|
||||
// handle UTF-16 surrogate pair
|
||||
if (0xD800 <= codepoint && codepoint <= 0xDBFF) {
|
||||
// high surrogate; expect another \uXXXX for low surrogate
|
||||
if (i + 2 >= in.size() || in[i + 1] != '\\' || in[i + 2] != 'u')
|
||||
throw std::runtime_error("Missing low surrogate after high surrogate");
|
||||
// parse low surrogate
|
||||
if (i + 6 >= in.size()) throw std::runtime_error("Invalid low surrogate \\u escape");
|
||||
int lv = 0;
|
||||
for (int k = 3; k <= 6; ++k) {
|
||||
int hv = hex_value(in[i + k]);
|
||||
if (hv < 0) throw std::runtime_error("Invalid hex digit in low surrogate");
|
||||
lv = (lv << 4) | hv;
|
||||
}
|
||||
i += 6; // consumed '\' 'u' and 4 hex digits (positions i+1..i+6)
|
||||
uint32_t low = static_cast<uint32_t>(lv);
|
||||
if (!(0xDC00 <= low && low <= 0xDFFF)) throw std::runtime_error("Invalid low surrogate value");
|
||||
// combine surrogates to code point
|
||||
codepoint = 0x10000 + (((codepoint - 0xD800) << 10) | (low - 0xDC00));
|
||||
}
|
||||
|
||||
append_utf8(out, codepoint);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error(std::string("Invalid escape character: \\") + e);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string json_escape(const std::string_view &str) {
|
||||
std::ostringstream ss;
|
||||
for (const auto &c : str) {
|
||||
switch (c) {
|
||||
case '\"':
|
||||
ss << "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
ss << "\\\\";
|
||||
break;
|
||||
case '\b':
|
||||
ss << "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
ss << "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
ss << "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
ss << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
ss << "\\t";
|
||||
break;
|
||||
default:
|
||||
if (c >= 0 && c <= 0x1F) {
|
||||
ss << "\\u" << std::hex << std::uppercase << (int)c << std::dec << std::nouppercase;
|
||||
} else {
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
enum class JsonType {
|
||||
Null,
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Array,
|
||||
Object,
|
||||
};
|
||||
|
||||
struct JsonValue;
|
||||
|
||||
struct JsonObject {
|
||||
std::unordered_map<std::string_view, JsonValue> members;
|
||||
|
||||
JsonObject(std::unordered_map<std::string_view, JsonValue> members)
|
||||
: members(std::move(members)) {}
|
||||
|
||||
auto get_field(const std::string_view &key) const -> std::optional<RefT<const JsonValue>>;
|
||||
|
||||
auto get_field_mut(const std::string_view &key) -> std::optional<RefT<JsonValue>>;
|
||||
|
||||
auto insert_field(const std::string_view &key, const JsonValue &value) -> void;
|
||||
};
|
||||
|
||||
struct JsonNull {};
|
||||
struct JsonNumber {
|
||||
std::string_view value;
|
||||
|
||||
JsonNumber(std::string_view value) : value(value) {}
|
||||
};
|
||||
struct JsonValue {
|
||||
std::variant<JsonNull, bool, JsonNumber, std::string_view,
|
||||
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(JsonNumber number) : value(number) {}
|
||||
JsonValue(std::vector<JsonValue> array) : value(std::move(array)) {}
|
||||
JsonValue(JsonObject object) : value(std::move(object)) {}
|
||||
|
||||
auto is_null() const -> bool { return std::holds_alternative<JsonNull>(value); }
|
||||
auto is_boolean() const -> bool {
|
||||
return std::holds_alternative<bool>(value);
|
||||
}
|
||||
auto is_string() const -> bool {
|
||||
|
||||
return std::holds_alternative<std::string_view>(value);
|
||||
}
|
||||
auto is_number() const -> bool {
|
||||
return std::holds_alternative<JsonNumber>(value);
|
||||
}
|
||||
auto is_array() const -> bool {
|
||||
return std::holds_alternative<std::vector<JsonValue>>(value);
|
||||
}
|
||||
auto is_object() const -> bool {
|
||||
return std::holds_alternative<JsonObject>(value);
|
||||
}
|
||||
auto as_null() const -> std::optional<JsonNull> {
|
||||
if (auto pval = std::get_if<JsonNull>(&value)) {
|
||||
return *pval;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
auto as_boolean() const -> std::optional<bool> {
|
||||
if (auto pval = std::get_if<bool>(&value)) {
|
||||
return *pval;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
auto as_string() const -> std::optional<std::string_view> {
|
||||
if (auto pval = std::get_if<std::string_view>(&value)) {
|
||||
return *pval;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
auto as_number() const -> std::optional<RefT<const JsonNumber>> {
|
||||
if (auto pval = std::get_if<JsonNumber>(&value)) {
|
||||
return std::ref(*pval);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
auto as_array() const -> std::optional<RefT<const std::vector<JsonValue>>> {
|
||||
if (auto pval = std::get_if<std::vector<JsonValue>>(&value)) {
|
||||
return std::ref(*pval);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
auto as_object() const -> std::optional<RefT<const JsonObject>> {
|
||||
if (auto pval = std::get_if<JsonObject>(&value)) {
|
||||
return std::ref(*pval);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
friend
|
||||
auto operator<<(std::ostream &os, const JsonValue& val) -> std::ostream &;
|
||||
};
|
||||
|
||||
auto operator<<(std::ostream &os, const JsonValue& val) -> std::ostream & {
|
||||
|
||||
const auto visitor =
|
||||
overloads{
|
||||
[&](JsonNull _) { os << "null"; },
|
||||
[&](bool b) { os << (b ? "true" : "false"); },
|
||||
[&](const JsonNumber &num) { os << num.value; },
|
||||
[&](const std::string_view &str) { os << "\"" << str << "\""; },
|
||||
[&](const JsonObject &obj) {
|
||||
os << "{";
|
||||
auto first = true;
|
||||
for (const auto &[key, value] : obj.members) {
|
||||
if (!first) {
|
||||
os << ",";
|
||||
}
|
||||
os << "\"" << key << "\": " << value;
|
||||
first = false;
|
||||
}
|
||||
os << "}";
|
||||
},
|
||||
[&](const std::vector<JsonValue> &array) {
|
||||
os << "[";
|
||||
auto first = true;
|
||||
for (const auto &value : array) {
|
||||
if (!first) {
|
||||
os << ",";
|
||||
}
|
||||
os << value;
|
||||
first = false;
|
||||
}
|
||||
os << "]";
|
||||
}
|
||||
};
|
||||
|
||||
std::visit(visitor, val.value);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
auto JsonObject::get_field(const std::string_view &key) const -> std::optional<RefT<const JsonValue>> {
|
||||
auto it = members.find(key);
|
||||
if (it != members.end()) {
|
||||
return std::ref(it->second);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
auto JsonObject::get_field_mut(const std::string_view &key) -> std::optional<RefT<JsonValue>> {
|
||||
auto it = members.find(key);
|
||||
if (it != members.end()) {
|
||||
return std::ref(it->second);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
auto JsonObject::insert_field(const std::string_view &key, const JsonValue &value) -> void {
|
||||
members[key] = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Source {
|
||||
std::string_view path;
|
||||
std::string_view flags;
|
||||
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;
|
||||
};
|
||||
|
||||
struct Project {
|
||||
std::string_view name;
|
||||
std::vector<std::string_view> sources;
|
||||
std::unordered_map<std::string, std::string> project_defines;
|
||||
std::vector<std::string> project_flags;
|
||||
|
||||
// rule of five
|
||||
Project(const Project &) = delete;
|
||||
Project(Project &&) = delete;
|
||||
Project &operator=(const Project &) = delete;
|
||||
Project &operator=(Project &&) = delete;
|
||||
|
||||
Project(std::string_view name)
|
||||
: name(std::move(name)), sources({}), project_defines({}), project_flags({}) {}
|
||||
auto add_source(std::string_view source) -> void {
|
||||
sources.push_back(std::move(source));
|
||||
}
|
||||
|
||||
auto into_compile_commands(const std::string_view& working_dir) -> json::JsonValue {
|
||||
std::vector<json::JsonValue> compile_commands{};
|
||||
const auto project_args = [&]() -> std::vector<json::JsonValue> {
|
||||
std::vector<json::JsonValue> args{};
|
||||
for (const auto &flag : project_flags) {
|
||||
args.push_back(json::JsonValue(flag));
|
||||
}
|
||||
for (const auto &[key, value] : project_defines) {
|
||||
args.push_back(json::JsonValue("-D" + key + "=" + value));
|
||||
}
|
||||
return args;
|
||||
}();
|
||||
|
||||
for (const auto &source : sources) {
|
||||
std::printf("Processing source: %s\n", source.data());
|
||||
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))});
|
||||
|
||||
compile_commands.push_back(json::JsonValue(json::JsonObject(fields)));
|
||||
}
|
||||
|
||||
|
||||
return compile_commands;
|
||||
}
|
||||
};
|
||||
static auto ninja_escape(std::string_view s) -> std::string {
|
||||
std::string out;
|
||||
out.reserve(s.size());
|
||||
for (char c : s) {
|
||||
switch (c) {
|
||||
case '$':
|
||||
out += "$$";
|
||||
break;
|
||||
case ':':
|
||||
out += "$:";
|
||||
break;
|
||||
case ' ':
|
||||
out += "$ ";
|
||||
break;
|
||||
case '\n':
|
||||
out += "$\n";
|
||||
break;
|
||||
default:
|
||||
out.push_back(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
static auto shell_join_arguments(const std::vector<json::JsonValue> &args)
|
||||
-> std::string {
|
||||
std::string out;
|
||||
bool first = true;
|
||||
for (const auto &arg0 : args) {
|
||||
if (auto&& arg = arg0.as_string()) {
|
||||
if (!first) {
|
||||
out.push_back(' ');
|
||||
}
|
||||
|
||||
const bool needs_quotes =
|
||||
arg->find_first_of(" \t\n\"'\\$") != std::string_view::npos;
|
||||
|
||||
if (!needs_quotes) {
|
||||
out.append(arg->begin(), arg->end());
|
||||
} else {
|
||||
out.push_back('"');
|
||||
for (char c : *arg) {
|
||||
if (c == '"' || c == '\\') {
|
||||
out.push_back('\\');
|
||||
}
|
||||
out.push_back(c);
|
||||
}
|
||||
out.push_back('"');
|
||||
}
|
||||
|
||||
first = false;
|
||||
} else {
|
||||
throw std::runtime_error("compile_commands entry has non-string argument");
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static auto default_object_path(std::string_view file) -> std::string {
|
||||
return std::string(file) + ".o";
|
||||
}
|
||||
|
||||
auto compile_commands_into_ninja(const json::JsonValue &compile_commands)
|
||||
-> void {
|
||||
if (auto arr = compile_commands.as_array()) {
|
||||
|
||||
std::ostringstream ninja;
|
||||
ninja << "ninja_required_version = 1.3\n\n";
|
||||
ninja << "rule cc\n";
|
||||
ninja << " command = $cmd\n";
|
||||
ninja << " description = CC $in\n\n";
|
||||
|
||||
for (const auto &entry : arr->get()) {
|
||||
const json::JsonObject& obj = entry.as_object().value();
|
||||
const json::JsonValue& file_field = obj.get_field("file").value();
|
||||
auto file = file_field.as_string().value();
|
||||
|
||||
auto output = default_object_path(file);
|
||||
if (const auto &output_field = obj.get_field("output")) {
|
||||
if (const auto out = output_field->get().as_string()) {
|
||||
output = std::string(*out);
|
||||
} else {
|
||||
throw std::runtime_error("'output' must be a string");
|
||||
}
|
||||
}
|
||||
|
||||
std::string cmd{};
|
||||
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();
|
||||
} 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";
|
||||
}
|
||||
|
||||
std::cout << ninja.str();
|
||||
} else {
|
||||
throw std::runtime_error("compile_commands root must be an array");
|
||||
}
|
||||
}
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
|
||||
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"});
|
||||
|
||||
auto compile_commands_json = project.into_compile_commands("/var/code/cxx/loom/");
|
||||
|
||||
|
||||
std::cout << compile_commands_json << std::endl;
|
||||
// std::ofstream os("compile_commands.json");
|
||||
// os << compile_commands_json;
|
||||
// os.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in a new issue