1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCoreTryCompile.h"
12 #include <cm/string_view>
13 #include <cmext/string_view>
15 #include "cmsys/Directory.hxx"
16 #include "cmsys/FStream.hxx"
18 #include "cmArgumentParser.h"
19 #include "cmExportTryCompileFileGenerator.h"
20 #include "cmGlobalGenerator.h"
21 #include "cmMakefile.h"
22 #include "cmMessageType.h"
23 #include "cmOutputConverter.h"
24 #include "cmPolicies.h"
27 #include "cmStringAlgorithms.h"
28 #include "cmSystemTools.h"
31 #include "cmVersion.h"
35 constexpr const char* unique_binary_directory = "CMAKE_BINARY_DIR_USE_MKDTEMP";
36 constexpr size_t lang_property_start = 0;
37 constexpr size_t lang_property_size = 4;
38 constexpr size_t pie_property_start = 4;
39 constexpr size_t pie_property_size = 2;
40 #define SETUP_LANGUAGE(name, lang) \
41 static const std::string name[lang_property_size + pie_property_size + 1] = \
42 { "CMAKE_" #lang "_COMPILER_EXTERNAL_TOOLCHAIN", \
43 "CMAKE_" #lang "_COMPILER_TARGET", \
44 "CMAKE_" #lang "_LINK_NO_PIE_SUPPORTED", \
45 "CMAKE_" #lang "_PIE_SUPPORTED", "" }
47 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
48 SETUP_LANGUAGE(c_properties, C);
49 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
50 SETUP_LANGUAGE(cxx_properties, CXX);
52 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
53 SETUP_LANGUAGE(cuda_properties, CUDA);
54 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
55 SETUP_LANGUAGE(fortran_properties, Fortran);
56 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
57 SETUP_LANGUAGE(hip_properties, HIP);
58 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
59 SETUP_LANGUAGE(objc_properties, OBJC);
60 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
61 SETUP_LANGUAGE(objcxx_properties, OBJCXX);
62 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
63 SETUP_LANGUAGE(ispc_properties, ISPC);
64 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
65 SETUP_LANGUAGE(swift_properties, Swift);
68 std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES";
69 std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
70 std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS";
71 std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES";
72 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
73 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
74 std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX";
75 std::string const kCMAKE_LINK_SEARCH_END_STATIC =
76 "CMAKE_LINK_SEARCH_END_STATIC";
77 std::string const kCMAKE_LINK_SEARCH_START_STATIC =
78 "CMAKE_LINK_SEARCH_START_STATIC";
79 std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT =
80 "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT";
81 std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES";
82 std::string const kCMAKE_OSX_DEPLOYMENT_TARGET = "CMAKE_OSX_DEPLOYMENT_TARGET";
83 std::string const kCMAKE_OSX_SYSROOT = "CMAKE_OSX_SYSROOT";
84 std::string const kCMAKE_APPLE_ARCH_SYSROOTS = "CMAKE_APPLE_ARCH_SYSROOTS";
85 std::string const kCMAKE_POSITION_INDEPENDENT_CODE =
86 "CMAKE_POSITION_INDEPENDENT_CODE";
87 std::string const kCMAKE_SYSROOT = "CMAKE_SYSROOT";
88 std::string const kCMAKE_SYSROOT_COMPILE = "CMAKE_SYSROOT_COMPILE";
89 std::string const kCMAKE_SYSROOT_LINK = "CMAKE_SYSROOT_LINK";
90 std::string const kCMAKE_ARMClang_CMP0123 = "CMAKE_ARMClang_CMP0123";
91 std::string const kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES =
92 "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES";
93 std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES =
94 "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES";
95 std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED";
96 std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT =
97 "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT";
98 std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT =
99 "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT";
101 /* GHS Multi platform variables */
102 std::set<std::string> const ghs_platform_vars{
103 "GHS_TARGET_PLATFORM", "GHS_PRIMARY_TARGET", "GHS_TOOLSET_ROOT",
104 "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME",
107 using Arguments = cmCoreTryCompile::Arguments;
109 ArgumentParser::Continue TryCompileLangProp(Arguments& args,
113 args.LangProps[std::string(key)] = std::string(val);
114 return ArgumentParser::Continue::No;
117 ArgumentParser::Continue TryCompileCompileDefs(Arguments& args,
120 cmExpandList(val, args.CompileDefs);
121 return ArgumentParser::Continue::Yes;
124 cmArgumentParser<Arguments> makeTryCompileParser(
125 const cmArgumentParser<Arguments>& base)
127 return cmArgumentParser<Arguments>{ base }.Bind("OUTPUT_VARIABLE"_s,
128 &Arguments::OutputVariable);
131 cmArgumentParser<Arguments> makeTryRunParser(
132 const cmArgumentParser<Arguments>& base)
134 return cmArgumentParser<Arguments>{ base }
135 .Bind("COMPILE_OUTPUT_VARIABLE"_s, &Arguments::CompileOutputVariable)
136 .Bind("RUN_OUTPUT_VARIABLE"_s, &Arguments::RunOutputVariable)
137 .Bind("RUN_OUTPUT_STDOUT_VARIABLE"_s, &Arguments::RunOutputStdOutVariable)
138 .Bind("RUN_OUTPUT_STDERR_VARIABLE"_s, &Arguments::RunOutputStdErrVariable)
139 .Bind("WORKING_DIRECTORY"_s, &Arguments::RunWorkingDirectory)
140 .Bind("ARGS"_s, &Arguments::RunArgs)
141 /* keep semicolon on own line */;
144 #define BIND_LANG_PROPS(lang) \
145 Bind(#lang "_STANDARD"_s, TryCompileLangProp) \
146 .Bind(#lang "_STANDARD_REQUIRED"_s, TryCompileLangProp) \
147 .Bind(#lang "_EXTENSIONS"_s, TryCompileLangProp)
149 auto const TryCompileBaseArgParser =
150 cmArgumentParser<Arguments>{}
151 .Bind(0, &Arguments::CompileResultVariable)
152 .Bind("NO_CACHE"_s, &Arguments::NoCache)
153 .Bind("CMAKE_FLAGS"_s, &Arguments::CMakeFlags)
154 .Bind("__CMAKE_INTERNAL"_s, &Arguments::CMakeInternal)
155 /* keep semicolon on own line */;
157 auto const TryCompileBaseSourcesArgParser =
158 cmArgumentParser<Arguments>{ TryCompileBaseArgParser }
159 .Bind("SOURCES"_s, &Arguments::Sources)
160 .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs,
161 ArgumentParser::ExpectAtLeast{ 0 })
162 .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries)
163 .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions)
164 .Bind("COPY_FILE"_s, &Arguments::CopyFileTo)
165 .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError)
167 .BIND_LANG_PROPS(CUDA)
168 .BIND_LANG_PROPS(CXX)
169 .BIND_LANG_PROPS(HIP)
170 .BIND_LANG_PROPS(OBJC)
171 .BIND_LANG_PROPS(OBJCXX)
172 /* keep semicolon on own line */;
174 auto const TryCompileBaseNewSourcesArgParser =
175 cmArgumentParser<Arguments>{ TryCompileBaseSourcesArgParser }
176 .Bind("SOURCE_FROM_CONTENT"_s, &Arguments::SourceFromContent)
177 .Bind("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar)
178 .Bind("SOURCE_FROM_FILE"_s, &Arguments::SourceFromFile)
179 /* keep semicolon on own line */;
181 auto const TryCompileBaseProjectArgParser =
182 cmArgumentParser<Arguments>{ TryCompileBaseArgParser }
183 .Bind("PROJECT"_s, &Arguments::ProjectName)
184 .Bind("SOURCE_DIR"_s, &Arguments::SourceDirectoryOrFile)
185 .Bind("BINARY_DIR"_s, &Arguments::BinaryDirectory)
186 .Bind("TARGET"_s, &Arguments::TargetName)
187 /* keep semicolon on own line */;
189 auto const TryCompileProjectArgParser =
190 makeTryCompileParser(TryCompileBaseProjectArgParser);
192 auto const TryCompileSourcesArgParser =
193 makeTryCompileParser(TryCompileBaseNewSourcesArgParser);
195 auto const TryCompileOldArgParser =
196 makeTryCompileParser(TryCompileBaseSourcesArgParser)
197 .Bind(1, &Arguments::BinaryDirectory)
198 .Bind(2, &Arguments::SourceDirectoryOrFile)
199 .Bind(3, &Arguments::ProjectName)
200 .Bind(4, &Arguments::TargetName)
201 /* keep semicolon on own line */;
203 auto const TryRunSourcesArgParser =
204 makeTryRunParser(TryCompileBaseNewSourcesArgParser);
206 auto const TryRunOldArgParser = makeTryRunParser(TryCompileOldArgParser);
208 #undef BIND_LANG_PROPS
210 std::string const TryCompileDefaultConfig = "DEBUG";
213 Arguments cmCoreTryCompile::ParseArgs(
214 const cmRange<std::vector<std::string>::const_iterator>& args,
215 const cmArgumentParser<Arguments>& parser,
216 std::vector<std::string>& unparsedArguments)
218 auto arguments = parser.Parse(args, &unparsedArguments, 0);
219 if (!arguments.MaybeReportError(*(this->Makefile)) &&
220 !unparsedArguments.empty()) {
221 std::string m = "Unknown arguments:";
222 for (const auto& i : unparsedArguments) {
223 m = cmStrCat(m, "\n \"", i, "\"");
225 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
230 Arguments cmCoreTryCompile::ParseArgs(
231 cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun)
233 std::vector<std::string> unparsedArguments;
234 const auto& second = *(++args.begin());
236 if (!isTryRun && second == "PROJECT") {
237 // New PROJECT signature (try_compile only).
239 this->ParseArgs(args, TryCompileProjectArgParser, unparsedArguments);
240 if (!arguments.BinaryDirectory) {
241 arguments.BinaryDirectory = unique_binary_directory;
246 if (cmHasLiteralPrefix(second, "SOURCE")) {
247 // New SOURCES signature.
248 auto arguments = this->ParseArgs(
249 args, isTryRun ? TryRunSourcesArgParser : TryCompileSourcesArgParser,
251 arguments.BinaryDirectory = unique_binary_directory;
256 auto arguments = this->ParseArgs(
257 args, isTryRun ? TryRunOldArgParser : TryCompileOldArgParser,
259 // For historical reasons, treat some empty-valued keyword
260 // arguments as if they were not specified at all.
261 if (arguments.OutputVariable && arguments.OutputVariable->empty()) {
262 arguments.OutputVariable = cm::nullopt;
265 if (arguments.CompileOutputVariable &&
266 arguments.CompileOutputVariable->empty()) {
267 arguments.CompileOutputVariable = cm::nullopt;
269 if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) {
270 arguments.RunOutputVariable = cm::nullopt;
272 if (arguments.RunOutputStdOutVariable &&
273 arguments.RunOutputStdOutVariable->empty()) {
274 arguments.RunOutputStdOutVariable = cm::nullopt;
276 if (arguments.RunOutputStdErrVariable &&
277 arguments.RunOutputStdErrVariable->empty()) {
278 arguments.RunOutputStdErrVariable = cm::nullopt;
280 if (arguments.RunWorkingDirectory &&
281 arguments.RunWorkingDirectory->empty()) {
282 arguments.RunWorkingDirectory = cm::nullopt;
288 bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
289 cmStateEnums::TargetType targetType)
291 this->OutputFile.clear();
292 // which signature were we called with ?
293 this->SrcFileSignature = true;
295 bool useUniqueBinaryDirectory = false;
296 std::string sourceDirectory;
297 std::string projectName;
298 std::string targetName;
299 if (arguments.ProjectName) {
300 this->SrcFileSignature = false;
301 if (!arguments.SourceDirectoryOrFile ||
302 arguments.SourceDirectoryOrFile->empty()) {
303 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
304 "No <srcdir> specified.");
307 sourceDirectory = *arguments.SourceDirectoryOrFile;
308 projectName = *arguments.ProjectName;
309 if (arguments.TargetName) {
310 targetName = *arguments.TargetName;
313 projectName = "CMAKE_TRY_COMPILE";
314 /* Use a random file name to avoid rapid creation and deletion
315 of the same executable name (some filesystems fail on that). */
316 char targetNameBuf[64];
317 snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
318 cmSystemTools::RandomSeed() & 0xFFFFF);
319 targetName = targetNameBuf;
322 if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) {
323 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
324 "No <bindir> specified.");
327 if (*arguments.BinaryDirectory == unique_binary_directory) {
328 // leave empty until we're ready to create it, so we don't try to remove
329 // a non-existing directory if we abort due to e.g. bad arguments
330 this->BinaryDirectory.clear();
331 useUniqueBinaryDirectory = true;
333 if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) {
334 this->Makefile->IssueMessage(
335 MessageType::FATAL_ERROR,
336 cmStrCat("<bindir> is not an absolute path:\n '",
337 *arguments.BinaryDirectory, "'"));
340 this->BinaryDirectory = *arguments.BinaryDirectory;
341 // compute the binary dir when TRY_COMPILE is called with a src file
343 if (this->SrcFileSignature) {
344 this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
348 std::vector<std::string> targets;
349 if (arguments.LinkLibraries) {
350 for (std::string const& i : *arguments.LinkLibraries) {
351 if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) {
352 switch (tgt->GetType()) {
353 case cmStateEnums::SHARED_LIBRARY:
354 case cmStateEnums::STATIC_LIBRARY:
355 case cmStateEnums::INTERFACE_LIBRARY:
356 case cmStateEnums::UNKNOWN_LIBRARY:
358 case cmStateEnums::EXECUTABLE:
359 if (tgt->IsExecutableWithExports()) {
364 this->Makefile->IssueMessage(
365 MessageType::FATAL_ERROR,
366 cmStrCat("Only libraries may be used as try_compile or try_run "
367 "IMPORTED LINK_LIBRARIES. Got ",
368 tgt->GetName(), " of type ",
369 cmState::GetTargetTypeName(tgt->GetType()), "."));
372 if (tgt->IsImported()) {
373 targets.emplace_back(i);
379 if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) {
380 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
381 "COPY_FILE must be followed by a file path");
385 if (arguments.CopyFileError && arguments.CopyFileError->empty()) {
386 this->Makefile->IssueMessage(
387 MessageType::FATAL_ERROR,
388 "COPY_FILE_ERROR must be followed by a variable name");
392 if (arguments.CopyFileError && !arguments.CopyFileTo) {
393 this->Makefile->IssueMessage(
394 MessageType::FATAL_ERROR,
395 "COPY_FILE_ERROR may be used only with COPY_FILE");
399 if (arguments.Sources && arguments.Sources->empty()) {
400 this->Makefile->IssueMessage(
401 MessageType::FATAL_ERROR,
402 "SOURCES must be followed by at least one source file");
406 if (this->SrcFileSignature) {
407 if (arguments.SourceFromContent &&
408 arguments.SourceFromContent->size() % 2) {
409 this->Makefile->IssueMessage(
410 MessageType::FATAL_ERROR,
411 "SOURCE_FROM_CONTENT requires exactly two arguments");
414 if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) {
415 this->Makefile->IssueMessage(
416 MessageType::FATAL_ERROR,
417 "SOURCE_FROM_VAR requires exactly two arguments");
420 if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) {
421 this->Makefile->IssueMessage(
422 MessageType::FATAL_ERROR,
423 "SOURCE_FROM_FILE requires exactly two arguments");
427 // only valid for srcfile signatures
428 if (!arguments.LangProps.empty()) {
429 this->Makefile->IssueMessage(
430 MessageType::FATAL_ERROR,
431 cmStrCat(arguments.LangProps.begin()->first,
432 " allowed only in source file signature"));
435 if (!arguments.CompileDefs.empty()) {
436 this->Makefile->IssueMessage(
437 MessageType::FATAL_ERROR,
438 "COMPILE_DEFINITIONS allowed only in source file signature");
441 if (arguments.CopyFileTo) {
442 this->Makefile->IssueMessage(
443 MessageType::FATAL_ERROR,
444 "COPY_FILE allowed only in source file signature");
449 // make sure the binary directory exists
450 if (useUniqueBinaryDirectory) {
451 this->BinaryDirectory =
452 cmStrCat(this->Makefile->GetHomeOutputDirectory(),
453 "/CMakeFiles/CMakeScratch/TryCompile-XXXXXX");
454 cmSystemTools::MakeTempDirectory(this->BinaryDirectory);
456 cmSystemTools::MakeDirectory(this->BinaryDirectory);
459 // do not allow recursive try Compiles
460 if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) {
461 std::ostringstream e;
462 e << "Attempt at a recursive or nested TRY_COMPILE in directory\n"
463 << " " << this->BinaryDirectory << "\n";
464 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
468 std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
469 // which signature are we using? If we are using var srcfile bindir
470 if (this->SrcFileSignature) {
471 // remove any CMakeCache.txt files so we will have a clean test
472 std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
473 cmSystemTools::RemoveFile(ccFile);
476 std::vector<std::string> sources;
477 if (arguments.Sources) {
478 sources = std::move(*arguments.Sources);
479 } else if (arguments.SourceDirectoryOrFile) {
480 sources.emplace_back(*arguments.SourceDirectoryOrFile);
482 if (arguments.SourceFromContent) {
483 auto const k = arguments.SourceFromContent->size();
484 for (auto i = decltype(k){ 0 }; i < k; i += 2) {
485 const auto& name = (*arguments.SourceFromContent)[i + 0];
486 const auto& content = (*arguments.SourceFromContent)[i + 1];
487 auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT");
491 sources.emplace_back(std::move(out));
494 if (arguments.SourceFromVar) {
495 auto const k = arguments.SourceFromVar->size();
496 for (auto i = decltype(k){ 0 }; i < k; i += 2) {
497 const auto& name = (*arguments.SourceFromVar)[i + 0];
498 const auto& var = (*arguments.SourceFromVar)[i + 1];
499 const auto& content = this->Makefile->GetDefinition(var);
500 auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR");
504 sources.emplace_back(std::move(out));
507 if (arguments.SourceFromFile) {
508 auto const k = arguments.SourceFromFile->size();
509 for (auto i = decltype(k){ 0 }; i < k; i += 2) {
510 const auto& dst = (*arguments.SourceFromFile)[i + 0];
511 const auto& src = (*arguments.SourceFromFile)[i + 1];
513 if (!cmSystemTools::GetFilenamePath(dst).empty()) {
515 cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\"");
516 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
520 auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst);
521 auto const result = cmSystemTools::CopyFileAlways(src, dstPath);
522 if (!result.IsSuccess()) {
523 const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src,
524 "\": ", result.GetString());
525 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
529 sources.emplace_back(std::move(dstPath));
532 // TODO: ensure sources is not empty
534 // Detect languages to enable.
535 cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
536 std::set<std::string> testLangs;
537 for (std::string const& si : sources) {
538 std::string ext = cmSystemTools::GetFilenameLastExtension(si);
539 std::string lang = gg->GetLanguageFromExtension(ext.c_str());
541 testLangs.insert(lang);
543 std::ostringstream err;
544 err << "Unknown extension \"" << ext << "\" for file\n"
546 << "try_compile() works only for enabled languages. "
547 << "Currently these are:\n ";
548 std::vector<std::string> langs;
549 gg->GetEnabledLanguages(langs);
550 err << cmJoin(langs, " ");
551 err << "\nSee project() command to enable other languages.";
552 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str());
557 // when the only language is ISPC we know that the output
558 // type must by a static library
559 if (testLangs.size() == 1 && testLangs.count("ISPC") == 1) {
560 targetType = cmStateEnums::STATIC_LIBRARY;
563 std::string const tcConfig =
564 this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
566 // we need to create a directory and CMakeLists file etc...
567 // first create the directories
568 sourceDirectory = this->BinaryDirectory;
570 // now create a CMakeLists.txt file in that directory
571 FILE* fout = cmsys::SystemTools::Fopen(outFileName, "w");
573 std::ostringstream e;
574 /* clang-format off */
575 e << "Failed to open\n"
576 << " " << outFileName << "\n"
577 << cmSystemTools::GetLastSystemError();
578 /* clang-format on */
579 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
583 cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
584 fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
585 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
586 cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion());
588 fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def->c_str());
591 /* Set MSVC runtime library policy to match our selection. */
592 if (cmValue msvcRuntimeLibraryDefault =
593 this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
594 fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
595 !msvcRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
598 /* Set Watcom runtime library policy to match our selection. */
599 if (cmValue watcomRuntimeLibraryDefault = this->Makefile->GetDefinition(
600 kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT)) {
601 fprintf(fout, "cmake_policy(SET CMP0136 %s)\n",
602 !watcomRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
605 /* Set CUDA architectures policy to match outer project. */
606 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0104) !=
608 testLangs.find("CUDA") != testLangs.end() &&
609 this->Makefile->GetSafeDefinition(kCMAKE_CUDA_ARCHITECTURES).empty()) {
610 fprintf(fout, "cmake_policy(SET CMP0104 OLD)\n");
613 /* Set ARMClang cpu/arch policy to match outer project. */
614 if (cmValue cmp0123 =
615 this->Makefile->GetDefinition(kCMAKE_ARMClang_CMP0123)) {
616 fprintf(fout, "cmake_policy(SET CMP0123 %s)\n",
617 *cmp0123 == "NEW"_s ? "NEW" : "OLD");
620 /* Set MSVC debug information format policy to match our selection. */
621 if (cmValue msvcDebugInformationFormatDefault =
622 this->Makefile->GetDefinition(
623 kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)) {
624 fprintf(fout, "cmake_policy(SET CMP0141 %s)\n",
625 !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD");
628 /* Set cache/normal variable policy to match outer project.
629 It may affect toolchain files. */
630 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
632 fprintf(fout, "cmake_policy(SET CMP0126 OLD)\n");
635 /* Set language extensions policy to match outer project. */
636 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0128) !=
638 fprintf(fout, "cmake_policy(SET CMP0128 OLD)\n");
641 std::string projectLangs;
642 for (std::string const& li : testLangs) {
643 projectLangs += " " + li;
644 std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
645 std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
646 if (cmValue rulesOverridePath =
647 this->Makefile->GetDefinition(rulesOverrideLang)) {
648 fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
649 rulesOverridePath->c_str());
650 } else if (cmValue rulesOverridePath2 =
651 this->Makefile->GetDefinition(rulesOverrideBase)) {
652 fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(),
653 rulesOverridePath2->c_str());
656 fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str());
657 if (arguments.CMakeInternal == "ABI") {
658 // This is the ABI detection step, also used for implicit includes.
659 // Erase any include_directories() calls from the toolchain file so
660 // that we do not see them as implicit. Our ABI detection source
661 // does not include any system headers anyway.
663 "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n");
665 // The link and compile lines for ABI detection step need to not use
666 // response files so we can extract implicit includes given to
667 // the underlying host compiler
668 if (testLangs.find("CUDA") != testLangs.end()) {
669 fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n");
670 fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n");
671 fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n");
674 fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
675 for (std::string const& li : testLangs) {
676 std::string langFlags = "CMAKE_" + li + "_FLAGS";
677 cmValue flags = this->Makefile->GetDefinition(langFlags);
678 fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
679 cmOutputConverter::EscapeForCMake(*flags).c_str());
681 "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
682 " ${COMPILE_DEFINITIONS}\")\n",
683 li.c_str(), li.c_str());
685 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0066)) {
686 case cmPolicies::WARN:
687 if (this->Makefile->PolicyOptionalWarningEnabled(
688 "CMAKE_POLICY_WARNING_CMP0066")) {
689 std::ostringstream w;
690 /* clang-format off */
691 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n"
692 "For compatibility with older versions of CMake, try_compile "
693 "is not honoring caller config-specific compiler flags "
694 "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project."
696 /* clang-format on */
697 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
700 case cmPolicies::OLD:
701 // OLD behavior is to do nothing.
703 case cmPolicies::REQUIRED_IF_USED:
704 case cmPolicies::REQUIRED_ALWAYS:
705 this->Makefile->IssueMessage(
706 MessageType::FATAL_ERROR,
707 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066));
709 case cmPolicies::NEW: {
710 // NEW behavior is to pass config-specific compiler flags.
711 std::string const cfg = !tcConfig.empty()
712 ? cmSystemTools::UpperCase(tcConfig)
713 : TryCompileDefaultConfig;
714 for (std::string const& li : testLangs) {
715 std::string const langFlagsCfg =
716 cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
717 cmValue flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
718 fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
719 cmOutputConverter::EscapeForCMake(*flagsCfg).c_str());
723 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0056)) {
724 case cmPolicies::WARN:
725 if (this->Makefile->PolicyOptionalWarningEnabled(
726 "CMAKE_POLICY_WARNING_CMP0056")) {
727 std::ostringstream w;
728 /* clang-format off */
729 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0056) << "\n"
730 "For compatibility with older versions of CMake, try_compile "
731 "is not honoring caller link flags (e.g. CMAKE_EXE_LINKER_FLAGS) "
732 "in the test project."
734 /* clang-format on */
735 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
738 case cmPolicies::OLD:
739 // OLD behavior is to do nothing.
741 case cmPolicies::REQUIRED_IF_USED:
742 case cmPolicies::REQUIRED_ALWAYS:
743 this->Makefile->IssueMessage(
744 MessageType::FATAL_ERROR,
745 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0056));
747 case cmPolicies::NEW:
748 // NEW behavior is to pass linker flags.
750 cmValue exeLinkFlags =
751 this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS");
752 fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n",
753 cmOutputConverter::EscapeForCMake(*exeLinkFlags).c_str());
758 "set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}"
759 " ${EXE_LINKER_FLAGS}\")\n");
760 fprintf(fout, "include_directories(${INCLUDE_DIRECTORIES})\n");
761 fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n");
762 fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n");
763 // handle any compile flags we need to pass on
764 if (!arguments.CompileDefs.empty()) {
765 // Pass using bracket arguments to preserve content.
766 fprintf(fout, "add_definitions([==[%s]==])\n",
767 cmJoin(arguments.CompileDefs, "]==] [==[").c_str());
770 if (!targets.empty()) {
771 std::string fname = "/" + std::string(targetName) + "Targets.cmake";
772 cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
774 tcfg.SetExportFile((this->BinaryDirectory + fname).c_str());
775 tcfg.SetConfig(tcConfig);
777 if (!tcfg.GenerateImportFile()) {
778 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
779 "could not write export file.");
783 fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
787 /* Set the appropriate policy information for ENABLE_EXPORTS */
788 fprintf(fout, "cmake_policy(SET CMP0065 %s)\n",
789 this->Makefile->GetPolicyStatus(cmPolicies::CMP0065) ==
794 /* Set the appropriate policy information for PIE link flags */
795 fprintf(fout, "cmake_policy(SET CMP0083 %s)\n",
796 this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
801 // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
802 // adding that flag in the platform and compiler language files
804 "include(\"${CMAKE_ROOT}/Modules/Internal/"
805 "HeaderpadWorkaround.cmake\")\n");
807 if (targetType == cmStateEnums::EXECUTABLE) {
808 /* Put the executable at a known location (for COPY_FILE). */
809 fprintf(fout, "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
810 this->BinaryDirectory.c_str());
811 /* Create the actual executable. */
812 fprintf(fout, "add_executable(%s", targetName.c_str());
813 } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
815 /* Put the static library at a known location (for COPY_FILE). */
816 fprintf(fout, "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"%s\")\n",
817 this->BinaryDirectory.c_str());
818 /* Create the actual static library. */
819 fprintf(fout, "add_library(%s STATIC", targetName.c_str());
821 for (std::string const& si : sources) {
822 fprintf(fout, " \"%s\"", si.c_str());
824 // Add dependencies on any non-temporary sources.
825 if (!IsTemporary(si)) {
826 this->Makefile->AddCMakeDependFile(si);
829 fprintf(fout, ")\n");
831 /* Write out the output location of the target we are building */
832 std::string perConfigGenex;
833 if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
834 perConfigGenex = "_$<UPPER_CASE:$<CONFIG>>";
837 "file(GENERATE OUTPUT "
838 "\"${CMAKE_BINARY_DIR}/%s%s_loc\"\n",
839 targetName.c_str(), perConfigGenex.c_str());
840 fprintf(fout, " CONTENT $<TARGET_FILE:%s>)\n", targetName.c_str());
842 bool warnCMP0067 = false;
843 bool honorStandard = true;
845 if (arguments.LangProps.empty()) {
846 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) {
847 case cmPolicies::WARN:
848 warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled(
849 "CMAKE_POLICY_WARNING_CMP0067");
851 case cmPolicies::OLD:
852 // OLD behavior is to not honor the language standard variables.
853 honorStandard = false;
855 case cmPolicies::REQUIRED_IF_USED:
856 case cmPolicies::REQUIRED_ALWAYS:
857 this->Makefile->IssueMessage(
858 MessageType::FATAL_ERROR,
859 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
861 case cmPolicies::NEW:
862 // NEW behavior is to honor the language standard variables.
863 // We already initialized honorStandard to true.
868 std::vector<std::string> warnCMP0067Variables;
870 if (honorStandard || warnCMP0067) {
871 static std::array<std::string, 6> const possibleLangs{
872 { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" }
874 static std::array<cm::string_view, 3> const langPropSuffixes{
875 { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s }
877 for (std::string const& lang : possibleLangs) {
878 if (testLangs.find(lang) == testLangs.end()) {
881 for (cm::string_view propSuffix : langPropSuffixes) {
882 std::string langProp = cmStrCat(lang, propSuffix);
883 if (!arguments.LangProps.count(langProp)) {
884 std::string langPropVar = cmStrCat("CMAKE_"_s, langProp);
885 std::string value = this->Makefile->GetSafeDefinition(langPropVar);
886 if (warnCMP0067 && !value.empty()) {
888 warnCMP0067Variables.emplace_back(langPropVar);
890 if (!value.empty()) {
891 arguments.LangProps[langProp] = value;
898 if (!warnCMP0067Variables.empty()) {
899 std::ostringstream w;
900 /* clang-format off */
901 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n"
902 "For compatibility with older versions of CMake, try_compile "
903 "is not honoring language standard variables in the test project:\n"
905 /* clang-format on */
906 for (std::string const& vi : warnCMP0067Variables) {
907 w << " " << vi << "\n";
909 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
912 for (auto const& p : arguments.LangProps) {
913 if (p.second.empty()) {
916 fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
918 cmOutputConverter::EscapeForCMake(p.first).c_str(),
919 cmOutputConverter::EscapeForCMake(p.second).c_str());
922 if (!arguments.LinkOptions.empty()) {
923 std::vector<std::string> options;
924 options.reserve(arguments.LinkOptions.size());
925 for (const auto& option : arguments.LinkOptions) {
926 options.emplace_back(cmOutputConverter::EscapeForCMake(option));
929 if (targetType == cmStateEnums::STATIC_LIBRARY) {
931 "set_property(TARGET %s PROPERTY STATIC_LIBRARY_OPTIONS %s)\n",
932 targetName.c_str(), cmJoin(options, " ").c_str());
934 fprintf(fout, "target_link_options(%s PRIVATE %s)\n",
935 targetName.c_str(), cmJoin(options, " ").c_str());
939 if (arguments.LinkLibraries) {
940 std::string libsToLink = " ";
941 for (std::string const& i : *arguments.LinkLibraries) {
942 libsToLink += "\"" + cmTrimWhitespace(i) + "\" ";
944 fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
947 fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
953 // Forward a set of variables to the inner project cache.
954 if ((this->SrcFileSignature ||
955 this->Makefile->GetPolicyStatus(cmPolicies::CMP0137) ==
957 !this->Makefile->IsOn("CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES")) {
958 std::set<std::string> vars;
959 vars.insert(&c_properties[lang_property_start],
960 &c_properties[lang_property_start + lang_property_size]);
961 vars.insert(&cxx_properties[lang_property_start],
962 &cxx_properties[lang_property_start + lang_property_size]);
963 vars.insert(&cuda_properties[lang_property_start],
964 &cuda_properties[lang_property_start + lang_property_size]);
965 vars.insert(&fortran_properties[lang_property_start],
966 &fortran_properties[lang_property_start + lang_property_size]);
967 vars.insert(&hip_properties[lang_property_start],
968 &hip_properties[lang_property_start + lang_property_size]);
969 vars.insert(&objc_properties[lang_property_start],
970 &objc_properties[lang_property_start + lang_property_size]);
971 vars.insert(&objcxx_properties[lang_property_start],
972 &objcxx_properties[lang_property_start + lang_property_size]);
973 vars.insert(&ispc_properties[lang_property_start],
974 &ispc_properties[lang_property_start + lang_property_size]);
975 vars.insert(&swift_properties[lang_property_start],
976 &swift_properties[lang_property_start + lang_property_size]);
977 vars.insert(kCMAKE_CUDA_ARCHITECTURES);
978 vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
979 vars.insert(kCMAKE_ENABLE_EXPORTS);
980 vars.insert(kCMAKE_HIP_ARCHITECTURES);
981 vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
982 vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
983 vars.insert(kCMAKE_ISPC_HEADER_SUFFIX);
984 vars.insert(kCMAKE_LINK_SEARCH_END_STATIC);
985 vars.insert(kCMAKE_LINK_SEARCH_START_STATIC);
986 vars.insert(kCMAKE_OSX_ARCHITECTURES);
987 vars.insert(kCMAKE_OSX_DEPLOYMENT_TARGET);
988 vars.insert(kCMAKE_OSX_SYSROOT);
989 vars.insert(kCMAKE_APPLE_ARCH_SYSROOTS);
990 vars.insert(kCMAKE_POSITION_INDEPENDENT_CODE);
991 vars.insert(kCMAKE_SYSROOT);
992 vars.insert(kCMAKE_SYSROOT_COMPILE);
993 vars.insert(kCMAKE_SYSROOT_LINK);
994 vars.insert(kCMAKE_WARN_DEPRECATED);
995 vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
996 vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
997 vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
999 if (cmValue varListStr = this->Makefile->GetDefinition(
1000 kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
1001 std::vector<std::string> varList = cmExpandedList(*varListStr);
1002 vars.insert(varList.begin(), varList.end());
1005 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
1007 // To ensure full support of PIE, propagate cache variables
1008 // driving the link options
1009 vars.insert(&c_properties[pie_property_start],
1010 &c_properties[pie_property_start + pie_property_size]);
1011 vars.insert(&cxx_properties[pie_property_start],
1012 &cxx_properties[pie_property_start + pie_property_size]);
1013 vars.insert(&cuda_properties[pie_property_start],
1014 &cuda_properties[pie_property_start + pie_property_size]);
1015 vars.insert(&fortran_properties[pie_property_start],
1016 &fortran_properties[pie_property_start + pie_property_size]);
1017 vars.insert(&hip_properties[pie_property_start],
1018 &hip_properties[pie_property_start + pie_property_size]);
1019 vars.insert(&objc_properties[pie_property_start],
1020 &objc_properties[pie_property_start + pie_property_size]);
1021 vars.insert(&objcxx_properties[pie_property_start],
1022 &objcxx_properties[pie_property_start + pie_property_size]);
1023 vars.insert(&ispc_properties[pie_property_start],
1024 &ispc_properties[pie_property_start + pie_property_size]);
1025 vars.insert(&swift_properties[pie_property_start],
1026 &swift_properties[pie_property_start + pie_property_size]);
1029 /* for the TRY_COMPILEs we want to be able to specify the architecture.
1030 So the user can set CMAKE_OSX_ARCHITECTURES to i386;ppc and then set
1031 CMAKE_TRY_COMPILE_OSX_ARCHITECTURES first to i386 and then to ppc to
1032 have the tests run for each specific architecture. Since
1033 cmLocalGenerator doesn't allow building for "the other"
1034 architecture only via CMAKE_OSX_ARCHITECTURES.
1036 if (cmValue tcArchs = this->Makefile->GetDefinition(
1037 kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
1038 vars.erase(kCMAKE_OSX_ARCHITECTURES);
1039 std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
1040 arguments.CMakeFlags.emplace_back(std::move(flag));
1043 for (std::string const& var : vars) {
1044 if (cmValue val = this->Makefile->GetDefinition(var)) {
1045 std::string flag = "-D" + var + "=" + *val;
1046 arguments.CMakeFlags.emplace_back(std::move(flag));
1051 if (this->Makefile->GetState()->UseGhsMultiIDE()) {
1052 // Forward the GHS variables to the inner project cache.
1053 for (std::string const& var : ghs_platform_vars) {
1054 if (cmValue val = this->Makefile->GetDefinition(var)) {
1055 std::string flag = "-D" + var + "=" + "'" + *val + "'";
1056 arguments.CMakeFlags.emplace_back(std::move(flag));
1061 if (this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
1063 cmStrCat("Executing try_compile (", *arguments.CompileResultVariable,
1064 ") in:\n ", this->BinaryDirectory);
1065 this->Makefile->IssueMessage(MessageType::LOG, msg);
1068 bool erroroc = cmSystemTools::GetErrorOccurredFlag();
1069 cmSystemTools::ResetErrorOccurredFlag();
1071 // actually do the try compile now that everything is setup
1072 int res = this->Makefile->TryCompile(
1073 sourceDirectory, this->BinaryDirectory, projectName, targetName,
1074 this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL,
1075 &arguments.CMakeFlags, output);
1077 cmSystemTools::SetErrorOccurred();
1080 // set the result var to the return value to indicate success or failure
1081 if (arguments.NoCache) {
1082 this->Makefile->AddDefinition(*arguments.CompileResultVariable,
1083 (res == 0 ? "TRUE" : "FALSE"));
1085 this->Makefile->AddCacheDefinition(
1086 *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"),
1087 "Result of TRY_COMPILE", cmStateEnums::INTERNAL);
1090 if (arguments.OutputVariable) {
1091 this->Makefile->AddDefinition(*arguments.OutputVariable, output);
1094 if (this->SrcFileSignature) {
1095 std::string copyFileErrorMessage;
1096 this->FindOutputFile(targetName);
1098 if ((res == 0) && arguments.CopyFileTo) {
1099 std::string const& copyFile = *arguments.CopyFileTo;
1100 if (this->OutputFile.empty() ||
1101 !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) {
1102 std::ostringstream emsg;
1103 /* clang-format off */
1104 emsg << "Cannot copy output executable\n"
1105 << " '" << this->OutputFile << "'\n"
1106 << "to destination specified by COPY_FILE:\n"
1107 << " '" << copyFile << "'\n";
1108 /* clang-format on */
1109 if (!this->FindErrorMessage.empty()) {
1110 emsg << this->FindErrorMessage;
1112 if (!arguments.CopyFileError) {
1113 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str());
1116 copyFileErrorMessage = emsg.str();
1120 if (arguments.CopyFileError) {
1121 std::string const& copyFileError = *arguments.CopyFileError;
1122 this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
1128 bool cmCoreTryCompile::IsTemporary(std::string const& path)
1130 return ((path.find("CMakeTmp") != std::string::npos) ||
1131 (path.find("CMakeScratch") != std::string::npos));
1134 void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
1136 if (binDir.empty()) {
1140 if (!IsTemporary(binDir)) {
1141 cmSystemTools::Error(
1142 "TRY_COMPILE attempt to remove -rf directory that does not contain "
1143 "CMakeTmp or CMakeScratch: \"" +
1148 cmsys::Directory dir;
1150 std::set<std::string> deletedFiles;
1151 for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) {
1152 const char* fileName = dir.GetFile(i);
1153 if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0 &&
1154 // Do not delete NFS temporary files.
1155 !cmHasPrefix(fileName, ".nfs")) {
1156 if (deletedFiles.insert(fileName).second) {
1157 std::string const fullPath =
1158 std::string(binDir).append("/").append(fileName);
1159 if (cmSystemTools::FileIsSymlink(fullPath)) {
1160 cmSystemTools::RemoveFile(fullPath);
1161 } else if (cmSystemTools::FileIsDirectory(fullPath)) {
1162 this->CleanupFiles(fullPath);
1163 cmSystemTools::RemoveADirectory(fullPath);
1166 // Sometimes anti-virus software hangs on to new files so we
1167 // cannot delete them immediately. Try a few times.
1168 cmSystemTools::WindowsFileRetry retry =
1169 cmSystemTools::GetWindowsFileRetry();
1170 cmsys::Status status;
1171 while (!((status = cmSystemTools::RemoveFile(fullPath))) &&
1172 --retry.Count && cmSystemTools::FileExists(fullPath)) {
1173 cmSystemTools::Delay(retry.Delay);
1175 if (retry.Count == 0)
1177 cmsys::Status status = cmSystemTools::RemoveFile(fullPath);
1181 this->Makefile->IssueMessage(
1182 MessageType::FATAL_ERROR,
1183 cmStrCat("The file:\n ", fullPath,
1184 "\ncould not be removed:\n ", status.GetString()));
1191 if (binDir.find("CMakeScratch") != std::string::npos) {
1192 cmSystemTools::RemoveADirectory(binDir);
1196 void cmCoreTryCompile::FindOutputFile(const std::string& targetName)
1198 this->FindErrorMessage.clear();
1199 this->OutputFile.clear();
1200 std::string tmpOutputFile = "/";
1201 tmpOutputFile += targetName;
1203 if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
1204 std::string const tcConfig =
1205 this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
1206 std::string const cfg = !tcConfig.empty()
1207 ? cmSystemTools::UpperCase(tcConfig)
1208 : TryCompileDefaultConfig;
1209 tmpOutputFile = cmStrCat(tmpOutputFile, '_', cfg);
1211 tmpOutputFile += "_loc";
1213 std::string command = cmStrCat(this->BinaryDirectory, tmpOutputFile);
1214 if (!cmSystemTools::FileExists(command)) {
1215 std::ostringstream emsg;
1216 emsg << "Unable to find the recorded try_compile output location:\n";
1217 emsg << cmStrCat(" ", command, "\n");
1218 this->FindErrorMessage = emsg.str();
1222 std::string outputFileLocation;
1223 cmsys::ifstream ifs(command.c_str());
1224 cmSystemTools::GetLineFromStream(ifs, outputFileLocation);
1225 if (!cmSystemTools::FileExists(outputFileLocation)) {
1226 std::ostringstream emsg;
1227 emsg << "Recorded try_compile output location doesn't exist:\n";
1228 emsg << cmStrCat(" ", outputFileLocation, "\n");
1229 this->FindErrorMessage = emsg.str();
1233 this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation);
1236 std::string cmCoreTryCompile::WriteSource(std::string const& filename,
1237 std::string const& content,
1238 char const* command) const
1240 if (!cmSystemTools::GetFilenamePath(filename).empty()) {
1242 cmStrCat(command, " given invalid filename \"", filename, "\"");
1243 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1247 auto filepath = cmStrCat(this->BinaryDirectory, "/", filename);
1248 cmsys::ofstream file{ filepath.c_str(), std::ios::out };
1251 cmStrCat(command, " failed to open \"", filename, "\" for writing");
1252 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1258 const auto& msg = cmStrCat(command, " failed to write \"", filename, "\"");
1259 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);