Repository navigation
Expand file tree
/
Copy pathbuild.mcpp
More file actions
179 lines (165 loc) · 6.95 KB
/
Copy pathbuild.mcpp
File metadata and controls
179 lines (165 loc) · 6.95 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// boost-m (mwrap) — generate the root module's closure partition.
//
// `import mcpplibs.mwrap.boost;` must re-export exactly the modules of the
// currently enabled feature set: a root that names a module whose feature is off
// would not compile, and a root that names nothing would make the umbrella
// import useless. src/boost.cppm is therefore a fixed, feature-independent
// interface root that re-exports one partition, `:closure`, and this program
// writes that partition for the active feature set.
//
// The partition is written into MCPP_OUT_DIR — one directory per configuration
// (target, profile, toolchain, feature set) — and selected through mcpp::source
// with its absolute path. The package root is shared by every consumer of a
// checkout (and may be read-only), so a fixed path under it would let two
// builds with different features overwrite each other's closure.
//
// Inputs (generated by tools/gen_features.py, committed):
// generated/meta/modules.lst feature<TAB>module-suffix, module-bearing libs
// generated/meta/profiles.lst axis<TAB>feature<TAB>arch, backend profiles
//
// The module-less compiled features (log, unit-test-framework) have no `.cppm`
// and appear in neither list: their library TUs still ship under their feature,
// but there is no CMI to re-export, so consumers include the upstream headers.
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <map>
#include <string>
#include <vector>
import mcpp;
namespace {
struct ModuleEntry {
std::string feature;
std::string suffix;
};
std::vector<std::string> read_lines(const char* rel)
{
std::vector<std::string> out;
std::ifstream in(rel);
std::string line;
while (std::getline(in, line)) {
if (line.empty() || line[0] == '#') continue;
out.push_back(line);
}
return out;
}
} // namespace
int main()
{
namespace fs = std::filesystem;
const std::string manifestDir = mcpp::manifest_dir();
if (manifestDir.empty()) {
std::fputs("build.mcpp: MCPP_MANIFEST_DIR not set\n", stderr);
return 1;
}
// Module list: the feature -> module-suffix map of every module-bearing
// library, in the order tools/gen_features.py emits.
constexpr const char* kModulesList = "generated/meta/modules.lst";
mcpp::rerun_if_changed(kModulesList);
std::ifstream mlst(kModulesList);
if (!mlst) {
std::fprintf(stderr, "build.mcpp: cannot read %s\n", kModulesList);
return 1;
}
std::vector<ModuleEntry> modules;
std::string mline;
while (std::getline(mlst, mline)) {
if (mline.empty() || mline[0] == '#') continue;
const auto tab = mline.find('\t');
if (tab == std::string::npos) {
std::fprintf(stderr, "build.mcpp: malformed line in %s: %s\n",
kModulesList, mline.c_str());
return 1;
}
modules.push_back({mline.substr(0, tab), mline.substr(tab + 1)});
}
// Profile features (mcpp `backend = "<axis>-<impl>"` sugar): two profiles on
// one axis set contradictory macros, and an arch-gated profile is invalid off
// that arch. Refuse before the module graph is scanned so the diagnostic
// names the features.
constexpr const char* kProfilesList = "generated/meta/profiles.lst";
mcpp::rerun_if_changed(kProfilesList);
std::ifstream plst(kProfilesList);
if (!plst) {
std::fprintf(stderr, "build.mcpp: cannot read %s\n", kProfilesList);
return 1;
}
std::map<std::string, std::vector<std::string>> active_profiles;
std::string pline;
while (std::getline(plst, pline)) {
if (pline.empty() || pline[0] == '#') continue;
const auto t1 = pline.find('\t');
if (t1 == std::string::npos) continue;
const auto t2 = pline.find('\t', t1 + 1);
const std::string axis = pline.substr(0, t1);
const std::string feat = pline.substr(
t1 + 1, t2 == std::string::npos ? std::string::npos : t2 - t1 - 1);
const std::string arch = t2 == std::string::npos ? std::string() : pline.substr(t2 + 1);
if (!mcpp::has_feature(feat.c_str())) continue;
if (!arch.empty() && arch != mcpp::target_arch()) {
std::fprintf(stderr,
"build.mcpp: profile feature '%s' is only valid on arch '%s' "
"(target is '%s')\n",
feat.c_str(), arch.c_str(), mcpp::target_arch());
return 1;
}
active_profiles[axis].push_back(feat);
}
for (auto const& [axis, feats] : active_profiles) {
if (feats.size() <= 1) continue;
std::fprintf(stderr, "build.mcpp: conflicting profile features on axis '%s':",
axis.c_str());
for (auto const& f : feats) std::fprintf(stderr, " %s", f.c_str());
std::fputs("\n activate at most one profile per axis.\n", stderr);
return 1;
}
std::string body =
"// generated by build.mcpp — partition of mcpplibs.mwrap.boost re-exporting "
"the active feature closure\n";
body += "export module mcpplibs.mwrap.boost:closure;\n";
// Known toolchain limits of the aggregate closure, reported rather than
// silently worked around. gcc 16.1 ICEs while writing the BMI of any
// aggregate that re-exports boost.sort (direct `import
// mcpplibs.mwrap.boost.sort;` consumers are unaffected), so the root
// closure leaves it out on gcc with an explicit warning. docs/usage.md
// records the limit; llvm and MSVC keep it in the closure.
bool skip_sort = false;
#if defined(__GNUC__) && !defined(__clang__)
skip_sort = true;
#endif
bool warned_sort = false;
for (auto const& m : modules) {
if (!mcpp::has_feature(m.feature.c_str())) continue;
if (skip_sort && m.suffix == "sort") {
warned_sort = true;
continue;
}
body += "export import mcpplibs.mwrap.boost." + m.suffix + ";\n";
}
if (warned_sort) {
mcpp::warning("build.mcpp: the root module leaves out mcpplibs.mwrap.boost.sort "
"under gcc (known BMI ICE when an aggregate re-exports it); "
"`import mcpplibs.mwrap.boost.sort;` directly instead.");
}
const fs::path out = fs::path(mcpp::out_dir()) / "boost_closure.cppm";
std::error_code ec;
fs::create_directories(out.parent_path(), ec);
std::string existing;
{
std::ifstream in(out);
if (in) existing.assign((std::istreambuf_iterator<char>(in)),
std::istreambuf_iterator<char>());
}
// mtime-stable write: touch only when the content changed, so an unchanged
// feature set does not rebuild the module.
if (existing != body) {
std::ofstream os(out, std::ios::trunc);
os << body;
if (!os) {
std::fprintf(stderr, "build.mcpp: cannot write %s\n", out.string().c_str());
return 1;
}
}
mcpp::source(out.string().c_str());
return 0;
}