-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.mcpp
More file actions
145 lines (127 loc) · 5.85 KB
/
Copy pathbuild.mcpp
File metadata and controls
145 lines (127 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// build.mcpp — generate the embedded Lua stdlib.
//
// The Lua stdlib lives under src/lua-stdlib/ as readable modules, and has to
// reach the binary as raw-string literals. This program is the only thing that
// turns one into the other: the generated .cppm is written into MCPP_OUT_DIR
// and handed to the build with `generated=`, so there is exactly one copy of
// each module under version control and drift between the two is not a state
// the repository can be in.
//
// It has been in that state. `xvm.files` shipped in 0.0.47 as an edit to the
// generated file alone; the .lua that is nominally its source never had it. An
// earlier xmake before_build hook would have regenerated from that stale source
// and silently deleted the feature from the shipped stdlib. Nothing checked,
// because a build that reads neither copy cannot notice they disagree.
//
// Writes go to MCPP_OUT_DIR, never the package root: as a registry dependency
// this package's root is shared between projects and may be read-only.
import std;
import mcpp;
namespace fs = std::filesystem;
namespace {
// MSVC C2026 caps a string literal at 16380 bytes. Chunking is not optional
// even though CI is Linux-only -- xlings consumes this library on Windows.
constexpr std::size_t MSVC_LIMIT = 15000;
struct Module {
const char* var;
const char* path; // const char*, not string_view: mcpp's directive API
}; // takes const char*, and .data() on a view is a trap
// Order is the load order in xpkg-executor.cppm's load_stdlib.
constexpr Module MODULES[] = {
{"prelude_lua", "src/lua-stdlib/prelude.lua"},
{"log_lua", "src/lua-stdlib/xim/libxpkg/log.lua"},
{"pkginfo_lua", "src/lua-stdlib/xim/libxpkg/pkginfo.lua"},
{"system_lua", "src/lua-stdlib/xim/libxpkg/system.lua"},
{"subos_lua", "src/lua-stdlib/xim/libxpkg/subos.lua"},
{"xvm_lua", "src/lua-stdlib/xim/libxpkg/xvm.lua"},
{"utils_lua", "src/lua-stdlib/xim/libxpkg/utils.lua"},
{"pkgmanager_lua", "src/lua-stdlib/xim/libxpkg/pkgmanager.lua"},
{"elfpatch_lua", "src/lua-stdlib/xim/libxpkg/elfpatch.lua"},
{"json_lua", "src/lua-stdlib/xim/libxpkg/json.lua"},
{"base64_lua", "src/lua-stdlib/xim/libxpkg/base64.lua"},
};
[[noreturn]] void fail(std::string_view what, const fs::path& p) {
// stdout, not stderr: stderr can interleave into the buffered directive
// stream. A non-zero exit is what aborts the build.
std::println("libxpkg build.mcpp: {} {}", what, p.string());
std::exit(1);
}
std::string slurp(const fs::path& p) {
std::ifstream f(p, std::ios::binary);
if (!f) fail("cannot read", p);
std::ostringstream ss;
ss << f.rdbuf();
return std::move(ss).str();
}
// One literal, wrapped in its own newlines. Reassembly therefore gains a blank
// line at every seam -- inert in Lua, and the reason chunk boundaries are
// allowed to move without anything downstream caring.
void emit_literal(std::string& out, std::string_view name, std::string_view body) {
out += "inline constexpr std::string_view ";
out += name;
out += " = R\"__LUA__(\n";
out += body;
out += "\n)__LUA__\";\n\n";
}
void embed(std::string& out, std::string_view var, const std::string& content) {
if (content.size() <= MSVC_LIMIT) {
emit_literal(out, var, content);
return;
}
std::vector<std::string> chunks;
std::size_t pos = 0;
for (int idx = 0; pos < content.size(); ++idx) {
auto chunk = std::string_view{content}.substr(
pos, std::min(MSVC_LIMIT, content.size() - pos));
// Pull the boundary back to the last line break so a split never lands
// mid-line; a raw string would keep it valid either way, but a diff of
// the generated file stays readable this way.
if (pos + MSVC_LIMIT < content.size()) {
if (auto nl = chunk.find_last_of('\n'); nl != std::string_view::npos)
chunk = chunk.substr(0, nl);
}
auto name = std::format("{}_{}", var, idx);
emit_literal(out, name, chunk);
chunks.push_back(std::move(name));
pos += chunk.size();
}
out += std::format("inline const std::string {}_storage = []{{ std::string s;\n", var);
for (const auto& c : chunks) out += std::format(" s += {};\n", c);
out += " return s; }();\n";
out += std::format("inline const std::string_view {} = {}_storage;\n\n", var, var);
}
} // namespace
int main() {
const fs::path root = mcpp::manifest_dir();
const fs::path out = fs::path(mcpp::out_dir()) / "xpkg-lua-stdlib.cppm";
std::string text =
"// Generated by build.mcpp from src/lua-stdlib/ — do not edit.\n"
"// This file is not under version control; edit the .lua sources.\n"
"module;\n"
"export module mcpplibs.xpkg.lua_stdlib;\n"
"import std;\n\n"
"export namespace mcpplibs::xpkg::detail {\n\n";
for (const auto& m : MODULES) {
const auto src = root / m.path;
embed(text, m.var, slurp(src));
// Declared input: without this the program is re-run only when it or
// the toolchain changes, and a .lua edit would not reach the binary.
mcpp::rerun_if_changed(m.path);
}
text += "} // namespace mcpplibs::xpkg::detail\n";
{
std::error_code ec;
fs::create_directories(out.parent_path(), ec);
std::ofstream f(out, std::ios::binary);
if (!f) fail("cannot write", out);
f << text;
if (!f) fail("write failed for", out);
}
// Absolute, deliberately. A relative `generated=` resolves against the
// project root for the root package but against MCPP_OUT_DIR for a
// dependency's build.mcpp, and this program has to be right in both roles
// -- xlings consumes libxpkg from the registry.
mcpp::generated(out.string().c_str());
mcpp::rerun_if_changed("build.mcpp");
return 0;
}