/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Driver/ToolChains/PS4CPU.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- PS4CPU.cpp - PS4CPU ToolChain Implementations ----------*- C++ -*-===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "PS4CPU.h" |
10 | | #include "CommonArgs.h" |
11 | | #include "clang/Config/config.h" |
12 | | #include "clang/Driver/Compilation.h" |
13 | | #include "clang/Driver/Driver.h" |
14 | | #include "clang/Driver/DriverDiagnostic.h" |
15 | | #include "clang/Driver/Options.h" |
16 | | #include "clang/Driver/SanitizerArgs.h" |
17 | | #include "llvm/Option/ArgList.h" |
18 | | #include "llvm/Support/FileSystem.h" |
19 | | #include "llvm/Support/Path.h" |
20 | | #include <cstdlib> // ::getenv |
21 | | |
22 | | using namespace clang::driver; |
23 | | using namespace clang; |
24 | | using namespace llvm::opt; |
25 | | |
26 | | // Helper to paste bits of an option together and return a saved string. |
27 | | static const char *makeArgString(const ArgList &Args, const char *Prefix, |
28 | 44 | const char *Base, const char *Suffix) { |
29 | | // Basically "Prefix + Base + Suffix" all converted to Twine then saved. |
30 | 44 | return Args.MakeArgString(Twine(StringRef(Prefix), Base) + Suffix); |
31 | 44 | } |
32 | | |
33 | | void tools::PScpu::addProfileRTArgs(const ToolChain &TC, const ArgList &Args, |
34 | 269 | ArgStringList &CmdArgs) { |
35 | 269 | assert(TC.getTriple().isPS()); |
36 | 269 | auto &PSTC = static_cast<const toolchains::PS4PS5Base &>(TC); |
37 | | |
38 | 269 | if ((Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs, |
39 | 269 | false) || |
40 | 269 | Args.hasFlag(options::OPT_fprofile_generate, |
41 | 265 | options::OPT_fno_profile_generate, false) || |
42 | 269 | Args.hasFlag(options::OPT_fprofile_generate_EQ, |
43 | 261 | options::OPT_fno_profile_generate, false) || |
44 | 269 | Args.hasFlag(options::OPT_fprofile_instr_generate, |
45 | 259 | options::OPT_fno_profile_instr_generate, false) || |
46 | 269 | Args.hasFlag(options::OPT_fprofile_instr_generate_EQ, |
47 | 257 | options::OPT_fno_profile_instr_generate, false) || |
48 | 269 | Args.hasFlag(options::OPT_fcs_profile_generate, |
49 | 255 | options::OPT_fno_profile_generate, false) || |
50 | 269 | Args.hasFlag(options::OPT_fcs_profile_generate_EQ, |
51 | 253 | options::OPT_fno_profile_generate, false) || |
52 | 269 | Args.hasArg(options::OPT_fcreate_profile)253 || |
53 | 269 | Args.hasArg(options::OPT_coverage)253 )) |
54 | 18 | CmdArgs.push_back(makeArgString( |
55 | 18 | Args, "--dependent-lib=", PSTC.getProfileRTLibName(), "")); |
56 | 269 | } |
57 | | |
58 | | void tools::PScpu::Assembler::ConstructJob(Compilation &C, const JobAction &JA, |
59 | | const InputInfo &Output, |
60 | | const InputInfoList &Inputs, |
61 | | const ArgList &Args, |
62 | 0 | const char *LinkingOutput) const { |
63 | 0 | auto &TC = static_cast<const toolchains::PS4PS5Base &>(getToolChain()); |
64 | 0 | claimNoWarnArgs(Args); |
65 | 0 | ArgStringList CmdArgs; |
66 | |
|
67 | 0 | Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); |
68 | |
|
69 | 0 | CmdArgs.push_back("-o"); |
70 | 0 | CmdArgs.push_back(Output.getFilename()); |
71 | |
|
72 | 0 | assert(Inputs.size() == 1 && "Unexpected number of inputs."); |
73 | 0 | const InputInfo &Input = Inputs[0]; |
74 | 0 | assert(Input.isFilename() && "Invalid input."); |
75 | 0 | CmdArgs.push_back(Input.getFilename()); |
76 | |
|
77 | 0 | std::string AsName = TC.qualifyPSCmdName("as"); |
78 | 0 | const char *Exec = Args.MakeArgString(TC.GetProgramPath(AsName.c_str())); |
79 | 0 | C.addCommand(std::make_unique<Command>(JA, *this, |
80 | 0 | ResponseFileSupport::AtFileUTF8(), |
81 | 0 | Exec, CmdArgs, Inputs, Output)); |
82 | 0 | } |
83 | | |
84 | | void tools::PScpu::addSanitizerArgs(const ToolChain &TC, const ArgList &Args, |
85 | 269 | ArgStringList &CmdArgs) { |
86 | 269 | assert(TC.getTriple().isPS()); |
87 | 269 | auto &PSTC = static_cast<const toolchains::PS4PS5Base &>(TC); |
88 | 269 | PSTC.addSanitizerArgs(Args, CmdArgs, "--dependent-lib=lib", ".a"); |
89 | 269 | } |
90 | | |
91 | | void toolchains::PS4CPU::addSanitizerArgs(const ArgList &Args, |
92 | | ArgStringList &CmdArgs, |
93 | | const char *Prefix, |
94 | 225 | const char *Suffix) const { |
95 | 225 | auto arg = [&](const char *Name) -> const char * { |
96 | 10 | return makeArgString(Args, Prefix, Name, Suffix); |
97 | 10 | }; |
98 | 225 | const SanitizerArgs &SanArgs = getSanitizerArgs(Args); |
99 | 225 | if (SanArgs.needsUbsanRt()) |
100 | 2 | CmdArgs.push_back(arg("SceDbgUBSanitizer_stub_weak")); |
101 | 225 | if (SanArgs.needsAsanRt()) |
102 | 8 | CmdArgs.push_back(arg("SceDbgAddressSanitizer_stub_weak")); |
103 | 225 | } |
104 | | |
105 | | void toolchains::PS5CPU::addSanitizerArgs(const ArgList &Args, |
106 | | ArgStringList &CmdArgs, |
107 | | const char *Prefix, |
108 | 172 | const char *Suffix) const { |
109 | 172 | auto arg = [&](const char *Name) -> const char * { |
110 | 16 | return makeArgString(Args, Prefix, Name, Suffix); |
111 | 16 | }; |
112 | 172 | const SanitizerArgs &SanArgs = getSanitizerArgs(Args); |
113 | 172 | if (SanArgs.needsUbsanRt()) |
114 | 6 | CmdArgs.push_back(arg("SceUBSanitizer_nosubmission_stub_weak")); |
115 | 172 | if (SanArgs.needsAsanRt()) |
116 | 8 | CmdArgs.push_back(arg("SceAddressSanitizer_nosubmission_stub_weak")); |
117 | 172 | if (SanArgs.needsTsanRt()) |
118 | 2 | CmdArgs.push_back(arg("SceThreadSanitizer_nosubmission_stub_weak")); |
119 | 172 | } |
120 | | |
121 | | void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, |
122 | | const InputInfo &Output, |
123 | | const InputInfoList &Inputs, |
124 | | const ArgList &Args, |
125 | 143 | const char *LinkingOutput) const { |
126 | 143 | auto &TC = static_cast<const toolchains::PS4PS5Base &>(getToolChain()); |
127 | 143 | const Driver &D = TC.getDriver(); |
128 | 143 | ArgStringList CmdArgs; |
129 | | |
130 | | // Silence warning for "clang -g foo.o -o foo" |
131 | 143 | Args.ClaimAllArgs(options::OPT_g_Group); |
132 | | // and "clang -emit-llvm foo.o -o foo" |
133 | 143 | Args.ClaimAllArgs(options::OPT_emit_llvm); |
134 | | // and for "clang -w foo.o -o foo". Other warning options are already |
135 | | // handled somewhere else. |
136 | 143 | Args.ClaimAllArgs(options::OPT_w); |
137 | | |
138 | 143 | if (!D.SysRoot.empty()) |
139 | 2 | CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); |
140 | | |
141 | 143 | if (Args.hasArg(options::OPT_pie)) |
142 | 0 | CmdArgs.push_back("-pie"); |
143 | | |
144 | 143 | if (Args.hasArg(options::OPT_rdynamic)) |
145 | 0 | CmdArgs.push_back("-export-dynamic"); |
146 | 143 | if (Args.hasArg(options::OPT_shared)) |
147 | 10 | CmdArgs.push_back("--shared"); |
148 | | |
149 | 143 | if (Output.isFilename()) { |
150 | 143 | CmdArgs.push_back("-o"); |
151 | 143 | CmdArgs.push_back(Output.getFilename()); |
152 | 143 | } else { |
153 | 0 | assert(Output.isNothing() && "Invalid output."); |
154 | 0 | } |
155 | | |
156 | 143 | const bool UseLTO = D.isUsingLTO(); |
157 | 143 | const bool UseJMC = |
158 | 143 | Args.hasFlag(options::OPT_fjmc, options::OPT_fno_jmc, false); |
159 | 143 | const bool IsPS4 = TC.getTriple().isPS4(); |
160 | | |
161 | 143 | const char *PS4LTOArgs = ""; |
162 | 143 | auto AddCodeGenFlag = [&](Twine Flag) { |
163 | 30 | if (IsPS4) |
164 | 21 | PS4LTOArgs = Args.MakeArgString(Twine(PS4LTOArgs) + " " + Flag); |
165 | 9 | else |
166 | 9 | CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=") + Flag)); |
167 | 30 | }; |
168 | | |
169 | 143 | if (UseLTO) { |
170 | | // We default to creating the arange section, but LTO does not. Enable it |
171 | | // here. |
172 | 23 | AddCodeGenFlag("-generate-arange-section"); |
173 | | |
174 | | // This tells LTO to perform JustMyCode instrumentation. |
175 | 23 | if (UseJMC) |
176 | 3 | AddCodeGenFlag("-enable-jmc-instrument"); |
177 | | |
178 | 23 | if (Arg *A = Args.getLastArg(options::OPT_fcrash_diagnostics_dir)) |
179 | 3 | AddCodeGenFlag(Twine("-crash-diagnostics-dir=") + A->getValue()); |
180 | | |
181 | 23 | StringRef Parallelism = getLTOParallelism(Args, D); |
182 | 23 | if (!Parallelism.empty()) { |
183 | 2 | if (IsPS4) |
184 | 1 | AddCodeGenFlag(Twine("-threads=") + Parallelism); |
185 | 1 | else |
186 | 1 | CmdArgs.push_back(Args.MakeArgString(Twine("-plugin-opt=jobs=") + Parallelism)); |
187 | 2 | } |
188 | | |
189 | 23 | if (IsPS4) { |
190 | 16 | const char *Prefix = nullptr; |
191 | 16 | if (D.getLTOMode() == LTOK_Thin) |
192 | 8 | Prefix = "-lto-thin-debug-options="; |
193 | 8 | else if (D.getLTOMode() == LTOK_Full) |
194 | 8 | Prefix = "-lto-debug-options="; |
195 | 0 | else |
196 | 0 | llvm_unreachable("new LTO mode?"); |
197 | | |
198 | 16 | CmdArgs.push_back(Args.MakeArgString(Twine(Prefix) + PS4LTOArgs)); |
199 | 16 | } |
200 | 23 | } |
201 | | |
202 | 143 | if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) |
203 | 128 | TC.addSanitizerArgs(Args, CmdArgs, "-l", ""); |
204 | | |
205 | 143 | if (D.isUsingLTO() && Args.hasArg(options::OPT_funified_lto)23 ) { |
206 | 4 | if (D.getLTOMode() == LTOK_Thin) |
207 | 2 | CmdArgs.push_back("--lto=thin"); |
208 | 2 | else if (D.getLTOMode() == LTOK_Full) |
209 | 2 | CmdArgs.push_back("--lto=full"); |
210 | 4 | } |
211 | | |
212 | 143 | Args.AddAllArgs(CmdArgs, options::OPT_L); |
213 | 143 | Args.AddAllArgs(CmdArgs, options::OPT_T_Group); |
214 | 143 | Args.AddAllArgs(CmdArgs, options::OPT_s); |
215 | 143 | Args.AddAllArgs(CmdArgs, options::OPT_t); |
216 | 143 | Args.AddAllArgs(CmdArgs, options::OPT_r); |
217 | | |
218 | 143 | if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) |
219 | 0 | CmdArgs.push_back("--no-demangle"); |
220 | | |
221 | 143 | AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA); |
222 | | |
223 | 143 | if (Args.hasArg(options::OPT_pthread)) { |
224 | 0 | CmdArgs.push_back("-lpthread"); |
225 | 0 | } |
226 | | |
227 | 143 | if (UseJMC) { |
228 | 5 | CmdArgs.push_back("--whole-archive"); |
229 | 5 | if (IsPS4) |
230 | 3 | CmdArgs.push_back("-lSceDbgJmc"); |
231 | 2 | else |
232 | 2 | CmdArgs.push_back("-lSceJmc_nosubmission"); |
233 | 5 | CmdArgs.push_back("--no-whole-archive"); |
234 | 5 | } |
235 | | |
236 | 143 | if (Args.hasArg(options::OPT_fuse_ld_EQ)) { |
237 | 10 | D.Diag(diag::err_drv_unsupported_opt_for_target) |
238 | 10 | << "-fuse-ld" << TC.getTriple().str(); |
239 | 10 | } |
240 | | |
241 | 143 | std::string LdName = TC.qualifyPSCmdName(TC.getLinkerBaseName()); |
242 | 143 | const char *Exec = Args.MakeArgString(TC.GetProgramPath(LdName.c_str())); |
243 | | |
244 | 143 | C.addCommand(std::make_unique<Command>(JA, *this, |
245 | 143 | ResponseFileSupport::AtFileUTF8(), |
246 | 143 | Exec, CmdArgs, Inputs, Output)); |
247 | 143 | } |
248 | | |
249 | | toolchains::PS4PS5Base::PS4PS5Base(const Driver &D, const llvm::Triple &Triple, |
250 | | const ArgList &Args, StringRef Platform, |
251 | | const char *EnvVar) |
252 | 292 | : Generic_ELF(D, Triple, Args) { |
253 | 292 | if (Args.hasArg(clang::driver::options::OPT_static)) |
254 | 2 | D.Diag(clang::diag::err_drv_unsupported_opt_for_target) |
255 | 2 | << "-static" << Platform; |
256 | | |
257 | | // Determine where to find the PS4/PS5 libraries. |
258 | | // If -isysroot was passed, use that as the SDK base path. |
259 | | // If not, we use the EnvVar if it exists; otherwise use the driver's |
260 | | // installation path, which should be <SDK_DIR>/host_tools/bin. |
261 | 292 | SmallString<80> Whence; |
262 | 292 | if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { |
263 | 14 | SDKRootDir = A->getValue(); |
264 | 14 | if (!llvm::sys::fs::exists(SDKRootDir)) |
265 | 12 | D.Diag(clang::diag::warn_missing_sysroot) << SDKRootDir; |
266 | 14 | Whence = A->getSpelling(); |
267 | 278 | } else if (const char *EnvValue = getenv(EnvVar)) { |
268 | 32 | SDKRootDir = EnvValue; |
269 | 32 | Whence = { "environment variable '", EnvVar, "'" }; |
270 | 246 | } else { |
271 | 246 | SDKRootDir = D.Dir + "/../../"; |
272 | 246 | Whence = "compiler's location"; |
273 | 246 | } |
274 | | |
275 | 292 | SmallString<512> SDKIncludeDir(SDKRootDir); |
276 | 292 | llvm::sys::path::append(SDKIncludeDir, "target/include"); |
277 | 292 | if (!Args.hasArg(options::OPT_nostdinc) && |
278 | 292 | !Args.hasArg(options::OPT_nostdlibinc)284 && |
279 | 292 | !Args.hasArg(options::OPT_isysroot)284 && |
280 | 292 | !Args.hasArg(options::OPT__sysroot_EQ)270 && |
281 | 292 | !llvm::sys::fs::exists(SDKIncludeDir)260 ) { |
282 | 260 | D.Diag(clang::diag::warn_drv_unable_to_find_directory_expected) |
283 | 260 | << Twine(Platform, " system headers").str() << SDKIncludeDir << Whence; |
284 | 260 | } |
285 | | |
286 | 292 | SmallString<512> SDKLibDir(SDKRootDir); |
287 | 292 | llvm::sys::path::append(SDKLibDir, "target/lib"); |
288 | 292 | if (!Args.hasArg(options::OPT_nostdlib) && |
289 | 292 | !Args.hasArg(options::OPT_nodefaultlibs)282 && |
290 | 292 | !Args.hasArg(options::OPT__sysroot_EQ)277 && !Args.hasArg(options::OPT_E)265 && |
291 | 292 | !Args.hasArg(options::OPT_c)257 && !Args.hasArg(options::OPT_S)161 && |
292 | 292 | !Args.hasArg(options::OPT_emit_ast)149 && |
293 | 292 | !llvm::sys::fs::exists(SDKLibDir)143 ) { |
294 | 143 | D.Diag(clang::diag::warn_drv_unable_to_find_directory_expected) |
295 | 143 | << Twine(Platform, " system libraries").str() << SDKLibDir << Whence; |
296 | 143 | return; |
297 | 143 | } |
298 | 149 | getFilePaths().push_back(std::string(SDKLibDir.str())); |
299 | 149 | } |
300 | | |
301 | | void toolchains::PS4PS5Base::AddClangSystemIncludeArgs( |
302 | | const ArgList &DriverArgs, |
303 | 282 | ArgStringList &CC1Args) const { |
304 | 282 | const Driver &D = getDriver(); |
305 | | |
306 | 282 | if (DriverArgs.hasArg(options::OPT_nostdinc)) |
307 | 8 | return; |
308 | | |
309 | 274 | if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { |
310 | 274 | SmallString<128> Dir(D.ResourceDir); |
311 | 274 | llvm::sys::path::append(Dir, "include"); |
312 | 274 | addSystemInclude(DriverArgs, CC1Args, Dir.str()); |
313 | 274 | } |
314 | | |
315 | 274 | if (DriverArgs.hasArg(options::OPT_nostdlibinc)) |
316 | 0 | return; |
317 | | |
318 | 274 | addExternCSystemInclude(DriverArgs, CC1Args, |
319 | 274 | SDKRootDir + "/target/include"); |
320 | 274 | addExternCSystemInclude(DriverArgs, CC1Args, |
321 | 274 | SDKRootDir + "/target/include_common"); |
322 | 274 | } |
323 | | |
324 | 0 | Tool *toolchains::PS4CPU::buildAssembler() const { |
325 | 0 | return new tools::PScpu::Assembler(*this); |
326 | 0 | } |
327 | | |
328 | 2 | Tool *toolchains::PS5CPU::buildAssembler() const { |
329 | | // PS5 does not support an external assembler. |
330 | 2 | getDriver().Diag(clang::diag::err_no_external_assembler); |
331 | 2 | return nullptr; |
332 | 2 | } |
333 | | |
334 | 143 | Tool *toolchains::PS4PS5Base::buildLinker() const { |
335 | 143 | return new tools::PScpu::Linker(*this); |
336 | 143 | } |
337 | | |
338 | 681 | SanitizerMask toolchains::PS4PS5Base::getSupportedSanitizers() const { |
339 | 681 | SanitizerMask Res = ToolChain::getSupportedSanitizers(); |
340 | 681 | Res |= SanitizerKind::Address; |
341 | 681 | Res |= SanitizerKind::PointerCompare; |
342 | 681 | Res |= SanitizerKind::PointerSubtract; |
343 | 681 | Res |= SanitizerKind::Vptr; |
344 | 681 | return Res; |
345 | 681 | } |
346 | | |
347 | 297 | SanitizerMask toolchains::PS5CPU::getSupportedSanitizers() const { |
348 | 297 | SanitizerMask Res = PS4PS5Base::getSupportedSanitizers(); |
349 | 297 | Res |= SanitizerKind::Thread; |
350 | 297 | return Res; |
351 | 297 | } |
352 | | |
353 | | void toolchains::PS4PS5Base::addClangTargetOptions( |
354 | | const ArgList &DriverArgs, ArgStringList &CC1Args, |
355 | 284 | Action::OffloadKind DeviceOffloadingKind) const { |
356 | | // PS4/PS5 do not use init arrays. |
357 | 284 | if (DriverArgs.hasArg(options::OPT_fuse_init_array)) { |
358 | 2 | Arg *A = DriverArgs.getLastArg(options::OPT_fuse_init_array); |
359 | 2 | getDriver().Diag(clang::diag::err_drv_unsupported_opt_for_target) |
360 | 2 | << A->getAsString(DriverArgs) << getTriple().str(); |
361 | 2 | } |
362 | | |
363 | 284 | CC1Args.push_back("-fno-use-init-array"); |
364 | | |
365 | 284 | const Arg *A = |
366 | 284 | DriverArgs.getLastArg(options::OPT_fvisibility_from_dllstorageclass, |
367 | 284 | options::OPT_fno_visibility_from_dllstorageclass); |
368 | 284 | if (!A || |
369 | 284 | A->getOption().matches(options::OPT_fvisibility_from_dllstorageclass)8 ) { |
370 | 282 | CC1Args.push_back("-fvisibility-from-dllstorageclass"); |
371 | | |
372 | 282 | if (DriverArgs.hasArg(options::OPT_fvisibility_dllexport_EQ)) |
373 | 4 | DriverArgs.AddLastArg(CC1Args, options::OPT_fvisibility_dllexport_EQ); |
374 | 278 | else |
375 | 278 | CC1Args.push_back("-fvisibility-dllexport=protected"); |
376 | | |
377 | 282 | if (DriverArgs.hasArg(options::OPT_fvisibility_nodllstorageclass_EQ)) |
378 | 8 | DriverArgs.AddLastArg(CC1Args, |
379 | 8 | options::OPT_fvisibility_nodllstorageclass_EQ); |
380 | 274 | else |
381 | 274 | CC1Args.push_back("-fvisibility-nodllstorageclass=hidden"); |
382 | | |
383 | 282 | if (DriverArgs.hasArg(options::OPT_fvisibility_externs_dllimport_EQ)) |
384 | 8 | DriverArgs.AddLastArg(CC1Args, |
385 | 8 | options::OPT_fvisibility_externs_dllimport_EQ); |
386 | 274 | else |
387 | 274 | CC1Args.push_back("-fvisibility-externs-dllimport=default"); |
388 | | |
389 | 282 | if (DriverArgs.hasArg( |
390 | 282 | options::OPT_fvisibility_externs_nodllstorageclass_EQ)) |
391 | 4 | DriverArgs.AddLastArg( |
392 | 4 | CC1Args, options::OPT_fvisibility_externs_nodllstorageclass_EQ); |
393 | 278 | else |
394 | 278 | CC1Args.push_back("-fvisibility-externs-nodllstorageclass=default"); |
395 | 282 | } |
396 | 284 | } |
397 | | |
398 | | // PS4 toolchain. |
399 | | toolchains::PS4CPU::PS4CPU(const Driver &D, const llvm::Triple &Triple, |
400 | | const llvm::opt::ArgList &Args) |
401 | 162 | : PS4PS5Base(D, Triple, Args, "PS4", "SCE_ORBIS_SDK_DIR") {} |
402 | | |
403 | | // PS5 toolchain. |
404 | | toolchains::PS5CPU::PS5CPU(const Driver &D, const llvm::Triple &Triple, |
405 | | const llvm::opt::ArgList &Args) |
406 | 130 | : PS4PS5Base(D, Triple, Args, "PS5", "SCE_PROSPERO_SDK_DIR") {} |