-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathclang.cppm
More file actions
309 lines (270 loc) · 12.4 KB
/
clang.cppm
File metadata and controls
309 lines (270 loc) · 12.4 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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
// mcpp.toolchain.clang - Clang/libc++ compiler behavior.
export module mcpp.toolchain.clang;
import std;
import mcpp.toolchain.model;
import mcpp.toolchain.msvc;
import mcpp.toolchain.probe;
import mcpp.xlings;
import mcpp.platform;
export namespace mcpp::toolchain::clang {
bool matches_version_output(std::string_view firstLineLower,
std::string_view fullOutputLower);
std::optional<std::filesystem::path> find_libcxx_std_module_source(
const std::filesystem::path& cxx_binary,
const std::string& envPrefix);
void enrich_toolchain(Toolchain& tc, const std::string& envPrefix);
std::filesystem::path std_bmi_path(const std::filesystem::path& cacheDir);
std::filesystem::path staged_std_bmi_path(const std::filesystem::path& outputDir);
std::vector<std::string> std_module_build_commands(const Toolchain& tc,
const std::filesystem::path& cacheDir,
const std::filesystem::path& bmiPath,
std::string_view sysrootFlag,
std::string_view cppStandardFlag);
std::optional<std::filesystem::path> find_libcxx_std_compat_source(
const std::filesystem::path& cxx_binary,
const std::string& envPrefix);
std::filesystem::path std_compat_bmi_path(const std::filesystem::path& cacheDir);
std::filesystem::path staged_std_compat_bmi_path(const std::filesystem::path& outputDir);
std::vector<std::string> std_compat_build_commands(const Toolchain& tc,
const std::filesystem::path& cacheDir,
const std::filesystem::path& bmiPath,
const std::filesystem::path& stdBmiPath,
std::string_view sysrootFlag,
std::string_view cppStandardFlag);
std::filesystem::path archive_tool(const Toolchain& tc);
// Locate clang-scan-deps in the same bin/ directory as clang++.
std::optional<std::filesystem::path> find_scan_deps(const Toolchain& tc);
} // namespace mcpp::toolchain::clang
namespace mcpp::toolchain::clang {
namespace {
std::optional<std::string>
json_string_value_after(std::string_view body, std::size_t start, std::string_view key) {
auto keyToken = std::string{"\""} + std::string(key) + "\"";
auto keyPos = body.find(keyToken, start);
if (keyPos == std::string_view::npos) return std::nullopt;
auto colon = body.find(':', keyPos + keyToken.size());
if (colon == std::string_view::npos) return std::nullopt;
auto quote = body.find('"', colon + 1);
if (quote == std::string_view::npos) return std::nullopt;
std::string out;
for (std::size_t i = quote + 1; i < body.size(); ++i) {
char c = body[i];
if (c == '"') return out;
if (c == '\\' && i + 1 < body.size()) {
out.push_back(body[++i]);
} else {
out.push_back(c);
}
}
return std::nullopt;
}
} // namespace
bool matches_version_output(std::string_view firstLineLower,
std::string_view fullOutputLower) {
return firstLineLower.find("clang version") != std::string::npos
|| firstLineLower.find("apple clang version") != std::string::npos
|| fullOutputLower.find("clang") != std::string::npos;
}
std::optional<std::filesystem::path> find_libcxx_std_module_source(
const std::filesystem::path& cxx_binary,
const std::string& envPrefix)
{
auto manifest_r = mcpp::toolchain::run_capture(std::format(
"{}{} -print-library-module-manifest-path {}",
envPrefix,
mcpp::xlings::shq(cxx_binary.string()),
mcpp::platform::null_redirect));
if (manifest_r) {
auto manifestPath = std::filesystem::path(
mcpp::toolchain::trim_line(*manifest_r));
if (!manifestPath.empty() && std::filesystem::exists(manifestPath)) {
std::ifstream is(manifestPath);
std::stringstream ss;
ss << is.rdbuf();
auto body = ss.str();
std::size_t cursor = 0;
while (true) {
auto logical = body.find("\"logical-name\"", cursor);
if (logical == std::string::npos) break;
auto name = json_string_value_after(body, logical, "logical-name");
if (name && *name == "std") {
auto src = json_string_value_after(body, logical, "source-path");
if (src) {
std::filesystem::path p = *src;
if (p.is_relative())
p = manifestPath.parent_path() / p;
std::error_code ec;
auto canon = std::filesystem::weakly_canonical(p, ec);
if (!ec) p = canon;
if (std::filesystem::exists(p)) return p;
}
}
cursor = logical + 1;
}
}
}
auto root = cxx_binary.parent_path().parent_path();
auto fallback = root / "share" / "libc++" / "v1" / "std.cppm";
if (std::filesystem::exists(fallback)) return fallback;
return std::nullopt;
}
void enrich_toolchain(Toolchain& tc, const std::string& envPrefix) {
// Clang targeting MSVC uses MSVC STL, not libc++.
bool msvTarget = is_msvc_target(tc);
tc.stdlibId = msvTarget ? "msvc-stl" : "libc++";
tc.stdlibVersion = tc.version.empty() ? "unknown" : tc.version;
tc.linkRuntimeDirs = mcpp::toolchain::discover_link_runtime_dirs(
tc.binaryPath, tc.targetTriple);
if (auto p = find_libcxx_std_module_source(tc.binaryPath, envPrefix)) {
tc.stdModuleSource = *p;
tc.hasImportStd = true;
}
#if defined(_WIN32)
// Fallback: if libc++ std.cppm not found, look for MSVC STL's std.ixx.
// Uses msvc.cppm which searches via vswhere, env vars, and known paths.
if (!tc.hasImportStd && msvTarget) {
if (auto p = mcpp::toolchain::msvc::find_std_module_source()) {
tc.stdModuleSource = *p;
tc.hasImportStd = true;
}
}
#endif
if (tc.hasImportStd) {
if (auto p = find_libcxx_std_compat_source(tc.binaryPath, envPrefix)) {
tc.stdCompatSource = *p;
}
}
}
std::filesystem::path std_bmi_path(const std::filesystem::path& cacheDir) {
return cacheDir / "pcm.cache" / "std.pcm";
}
std::filesystem::path staged_std_bmi_path(const std::filesystem::path& outputDir) {
return outputDir / "pcm.cache" / "std.pcm";
}
std::vector<std::string> std_module_build_commands(const Toolchain& tc,
const std::filesystem::path& cacheDir,
const std::filesystem::path& bmiPath,
std::string_view sysrootFlag,
std::string_view cppStandardFlag) {
auto relBmi = std::filesystem::relative(bmiPath, cacheDir).string();
#if defined(_WIN32)
// Windows: use absolute paths, raw binary path as first token
// (cmd.exe strips leading quotes), shq for args with spaces.
// -x c++-module is needed for MSVC STL's .ixx files (Clang doesn't
// recognize the .ixx extension as a module source by default).
auto absBmi = (cacheDir / relBmi).string();
auto ext = tc.stdModuleSource.extension().string();
// MSVC STL's std.ixx needs -x c++-module (Clang doesn't recognize .ixx)
// and generates harmless warnings about #include in module purview and
// the reserved 'std' module name — suppress both.
std::string ixxFlags = (ext == ".ixx")
? " -x c++-module -Wno-include-angled-in-module-purview -Wno-reserved-module-identifier"
: "";
return {
std::format(
"{} {}{}{} "
"--precompile {} -o {}",
tc.binaryPath.string(),
cppStandardFlag,
ixxFlags,
sysrootFlag,
mcpp::xlings::shq(tc.stdModuleSource.string()),
mcpp::xlings::shq(absBmi)),
std::format(
"{} {}{} "
"{} -c -o {}",
tc.binaryPath.string(),
cppStandardFlag,
sysrootFlag,
mcpp::xlings::shq(absBmi),
mcpp::xlings::shq((cacheDir / "std.o").string()))
};
#else
return {
std::format(
"cd {} && {}{} {} -Wno-reserved-module-identifier{} "
"--precompile {} -o {} 2>&1",
mcpp::xlings::shq(cacheDir.string()),
mcpp::toolchain::compiler_env_prefix(tc),
mcpp::xlings::shq(tc.binaryPath.string()),
cppStandardFlag,
sysrootFlag,
mcpp::xlings::shq(tc.stdModuleSource.string()),
mcpp::xlings::shq(relBmi)),
std::format(
"cd {} && {}{} {} -Wno-reserved-module-identifier{} "
"{} -c -o std.o 2>&1",
mcpp::xlings::shq(cacheDir.string()),
mcpp::toolchain::compiler_env_prefix(tc),
mcpp::xlings::shq(tc.binaryPath.string()),
cppStandardFlag,
sysrootFlag,
mcpp::xlings::shq(relBmi))
};
#endif
}
std::filesystem::path archive_tool(const Toolchain& tc) {
auto llvmAr = tc.binaryPath.parent_path() /
(std::string("llvm-ar") + std::string(mcpp::platform::exe_suffix));
if (std::filesystem::exists(llvmAr)) return llvmAr;
return {};
}
std::optional<std::filesystem::path> find_scan_deps(const Toolchain& tc) {
auto p = tc.binaryPath.parent_path() /
(std::string("clang-scan-deps") + std::string(mcpp::platform::exe_suffix));
if (std::filesystem::exists(p)) return p;
return std::nullopt;
}
std::optional<std::filesystem::path> find_libcxx_std_compat_source(
const std::filesystem::path& cxx_binary,
const std::string& envPrefix)
{
// Same search strategy as find_libcxx_std_module_source but for std.compat
auto root = cxx_binary.parent_path().parent_path();
auto p = root / "share" / "libc++" / "v1" / "std.compat.cppm";
if (std::filesystem::exists(p)) return p;
return std::nullopt;
}
std::filesystem::path std_compat_bmi_path(const std::filesystem::path& cacheDir) {
return cacheDir / "pcm.cache" / "std.compat.pcm";
}
std::filesystem::path staged_std_compat_bmi_path(const std::filesystem::path& outputDir) {
return outputDir / "pcm.cache" / "std.compat.pcm";
}
std::vector<std::string> std_compat_build_commands(const Toolchain& tc,
const std::filesystem::path& cacheDir,
const std::filesystem::path& bmiPath,
const std::filesystem::path& stdBmiPath,
std::string_view sysrootFlag,
std::string_view cppStandardFlag)
{
auto relBmi = std::filesystem::relative(bmiPath, cacheDir).string();
auto relStdBmi = std::filesystem::relative(stdBmiPath, cacheDir).string();
// std.compat depends on std, so we need -fmodule-file=std=<std.pcm>
// Note: the path after = must NOT be shell-quoted separately; the
// entire -fmodule-file flag is a single token to the compiler.
return {
std::format("cd {} && {}{} {} -Wno-reserved-module-identifier{} "
"-fmodule-file=std={} "
"--precompile {} -o {} 2>&1",
mcpp::xlings::shq(cacheDir.string()),
mcpp::toolchain::compiler_env_prefix(tc),
mcpp::xlings::shq(tc.binaryPath.string()),
cppStandardFlag,
sysrootFlag,
relStdBmi,
mcpp::xlings::shq(tc.stdCompatSource.string()),
mcpp::xlings::shq(relBmi)),
std::format("cd {} && {}{} {} -Wno-reserved-module-identifier{} "
"-fmodule-file=std={} "
"{} -c -o std.compat.o 2>&1",
mcpp::xlings::shq(cacheDir.string()),
mcpp::toolchain::compiler_env_prefix(tc),
mcpp::xlings::shq(tc.binaryPath.string()),
cppStandardFlag,
sysrootFlag,
relStdBmi,
mcpp::xlings::shq(relBmi))
};
}
} // namespace mcpp::toolchain::clang