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
211 Arguments cmCoreTryCompile::ParseArgs(
212 const cmRange<std::vector<std::string>::const_iterator>& args,
213 const cmArgumentParser<Arguments>& parser,
214 std::vector<std::string>& unparsedArguments)
216 auto arguments = parser.Parse(args, &unparsedArguments, 0);
217 if (!arguments.MaybeReportError(*(this->Makefile)) &&
218 !unparsedArguments.empty()) {
219 std::string m = "Unknown arguments:";
220 for (const auto& i : unparsedArguments) {
221 m = cmStrCat(m, "\n \"", i, "\"");
223 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
228 Arguments cmCoreTryCompile::ParseArgs(
229 cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun)
231 std::vector<std::string> unparsedArguments;
232 const auto& second = *(++args.begin());
234 if (!isTryRun && second == "PROJECT") {
235 // New PROJECT signature (try_compile only).
237 this->ParseArgs(args, TryCompileProjectArgParser, unparsedArguments);
238 if (!arguments.BinaryDirectory) {
239 arguments.BinaryDirectory = unique_binary_directory;
244 if (cmHasLiteralPrefix(second, "SOURCE")) {
245 // New SOURCES signature.
246 auto arguments = this->ParseArgs(
247 args, isTryRun ? TryRunSourcesArgParser : TryCompileSourcesArgParser,
249 arguments.BinaryDirectory = unique_binary_directory;
254 auto arguments = this->ParseArgs(
255 args, isTryRun ? TryRunOldArgParser : TryCompileOldArgParser,
257 // For historical reasons, treat some empty-valued keyword
258 // arguments as if they were not specified at all.
259 if (arguments.OutputVariable && arguments.OutputVariable->empty()) {
260 arguments.OutputVariable = cm::nullopt;
263 if (arguments.CompileOutputVariable &&
264 arguments.CompileOutputVariable->empty()) {
265 arguments.CompileOutputVariable = cm::nullopt;
267 if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) {
268 arguments.RunOutputVariable = cm::nullopt;
270 if (arguments.RunOutputStdOutVariable &&
271 arguments.RunOutputStdOutVariable->empty()) {
272 arguments.RunOutputStdOutVariable = cm::nullopt;
274 if (arguments.RunOutputStdErrVariable &&
275 arguments.RunOutputStdErrVariable->empty()) {
276 arguments.RunOutputStdErrVariable = cm::nullopt;
278 if (arguments.RunWorkingDirectory &&
279 arguments.RunWorkingDirectory->empty()) {
280 arguments.RunWorkingDirectory = cm::nullopt;
286 bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
287 cmStateEnums::TargetType targetType)
289 this->OutputFile.clear();
290 // which signature were we called with ?
291 this->SrcFileSignature = true;
293 bool useUniqueBinaryDirectory = false;
294 std::string sourceDirectory;
295 std::string projectName;
296 std::string targetName;
297 if (arguments.ProjectName) {
298 this->SrcFileSignature = false;
299 if (!arguments.SourceDirectoryOrFile ||
300 arguments.SourceDirectoryOrFile->empty()) {
301 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
302 "No <srcdir> specified.");
305 sourceDirectory = *arguments.SourceDirectoryOrFile;
306 projectName = *arguments.ProjectName;
307 if (arguments.TargetName) {
308 targetName = *arguments.TargetName;
311 projectName = "CMAKE_TRY_COMPILE";
312 /* Use a random file name to avoid rapid creation and deletion
313 of the same executable name (some filesystems fail on that). */
314 char targetNameBuf[64];
315 snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
316 cmSystemTools::RandomSeed() & 0xFFFFF);
317 targetName = targetNameBuf;
320 if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) {
321 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
322 "No <bindir> specified.");
325 if (*arguments.BinaryDirectory == unique_binary_directory) {
326 // leave empty until we're ready to create it, so we don't try to remove
327 // a non-existing directory if we abort due to e.g. bad arguments
328 this->BinaryDirectory.clear();
329 useUniqueBinaryDirectory = true;
331 if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) {
332 this->Makefile->IssueMessage(
333 MessageType::FATAL_ERROR,
334 cmStrCat("<bindir> is not an absolute path:\n '",
335 *arguments.BinaryDirectory, "'"));
338 this->BinaryDirectory = *arguments.BinaryDirectory;
339 // compute the binary dir when TRY_COMPILE is called with a src file
341 if (this->SrcFileSignature) {
342 this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
346 std::vector<std::string> targets;
347 if (arguments.LinkLibraries) {
348 for (std::string const& i : *arguments.LinkLibraries) {
349 if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) {
350 switch (tgt->GetType()) {
351 case cmStateEnums::SHARED_LIBRARY:
352 case cmStateEnums::STATIC_LIBRARY:
353 case cmStateEnums::INTERFACE_LIBRARY:
354 case cmStateEnums::UNKNOWN_LIBRARY:
356 case cmStateEnums::EXECUTABLE:
357 if (tgt->IsExecutableWithExports()) {
362 this->Makefile->IssueMessage(
363 MessageType::FATAL_ERROR,
364 cmStrCat("Only libraries may be used as try_compile or try_run "
365 "IMPORTED LINK_LIBRARIES. Got ",
366 tgt->GetName(), " of type ",
367 cmState::GetTargetTypeName(tgt->GetType()), "."));
370 if (tgt->IsImported()) {
371 targets.emplace_back(i);
377 if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) {
378 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
379 "COPY_FILE must be followed by a file path");
383 if (arguments.CopyFileError && arguments.CopyFileError->empty()) {
384 this->Makefile->IssueMessage(
385 MessageType::FATAL_ERROR,
386 "COPY_FILE_ERROR must be followed by a variable name");
390 if (arguments.CopyFileError && !arguments.CopyFileTo) {
391 this->Makefile->IssueMessage(
392 MessageType::FATAL_ERROR,
393 "COPY_FILE_ERROR may be used only with COPY_FILE");
397 if (arguments.Sources && arguments.Sources->empty()) {
398 this->Makefile->IssueMessage(
399 MessageType::FATAL_ERROR,
400 "SOURCES must be followed by at least one source file");
404 if (this->SrcFileSignature) {
405 if (arguments.SourceFromContent &&
406 arguments.SourceFromContent->size() % 2) {
407 this->Makefile->IssueMessage(
408 MessageType::FATAL_ERROR,
409 "SOURCE_FROM_CONTENT requires exactly two arguments");
412 if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) {
413 this->Makefile->IssueMessage(
414 MessageType::FATAL_ERROR,
415 "SOURCE_FROM_VAR requires exactly two arguments");
418 if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) {
419 this->Makefile->IssueMessage(
420 MessageType::FATAL_ERROR,
421 "SOURCE_FROM_FILE requires exactly two arguments");
425 // only valid for srcfile signatures
426 if (!arguments.LangProps.empty()) {
427 this->Makefile->IssueMessage(
428 MessageType::FATAL_ERROR,
429 cmStrCat(arguments.LangProps.begin()->first,
430 " allowed only in source file signature"));
433 if (!arguments.CompileDefs.empty()) {
434 this->Makefile->IssueMessage(
435 MessageType::FATAL_ERROR,
436 "COMPILE_DEFINITIONS allowed only in source file signature");
439 if (arguments.CopyFileTo) {
440 this->Makefile->IssueMessage(
441 MessageType::FATAL_ERROR,
442 "COPY_FILE allowed only in source file signature");
447 // make sure the binary directory exists
448 if (useUniqueBinaryDirectory) {
449 this->BinaryDirectory =
450 cmStrCat(this->Makefile->GetHomeOutputDirectory(),
451 "/CMakeFiles/CMakeScratch/TryCompile-XXXXXX");
452 cmSystemTools::MakeTempDirectory(this->BinaryDirectory);
454 cmSystemTools::MakeDirectory(this->BinaryDirectory);
457 // do not allow recursive try Compiles
458 if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) {
459 std::ostringstream e;
460 e << "Attempt at a recursive or nested TRY_COMPILE in directory\n"
461 << " " << this->BinaryDirectory << "\n";
462 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
466 std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
467 // which signature are we using? If we are using var srcfile bindir
468 if (this->SrcFileSignature) {
469 // remove any CMakeCache.txt files so we will have a clean test
470 std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
471 cmSystemTools::RemoveFile(ccFile);
474 std::vector<std::string> sources;
475 if (arguments.Sources) {
476 sources = std::move(*arguments.Sources);
477 } else if (arguments.SourceDirectoryOrFile) {
478 sources.emplace_back(*arguments.SourceDirectoryOrFile);
480 if (arguments.SourceFromContent) {
481 auto const k = arguments.SourceFromContent->size();
482 for (auto i = decltype(k){ 0 }; i < k; i += 2) {
483 const auto& name = (*arguments.SourceFromContent)[i + 0];
484 const auto& content = (*arguments.SourceFromContent)[i + 1];
485 auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT");
489 sources.emplace_back(std::move(out));
492 if (arguments.SourceFromVar) {
493 auto const k = arguments.SourceFromVar->size();
494 for (auto i = decltype(k){ 0 }; i < k; i += 2) {
495 const auto& name = (*arguments.SourceFromVar)[i + 0];
496 const auto& var = (*arguments.SourceFromVar)[i + 1];
497 const auto& content = this->Makefile->GetDefinition(var);
498 auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR");
502 sources.emplace_back(std::move(out));
505 if (arguments.SourceFromFile) {
506 auto const k = arguments.SourceFromFile->size();
507 for (auto i = decltype(k){ 0 }; i < k; i += 2) {
508 const auto& dst = (*arguments.SourceFromFile)[i + 0];
509 const auto& src = (*arguments.SourceFromFile)[i + 1];
511 if (!cmSystemTools::GetFilenamePath(dst).empty()) {
513 cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\"");
514 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
518 auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst);
519 auto const result = cmSystemTools::CopyFileAlways(src, dstPath);
520 if (!result.IsSuccess()) {
521 const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src,
522 "\": ", result.GetString());
523 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
527 sources.emplace_back(std::move(dstPath));
530 // TODO: ensure sources is not empty
532 // Detect languages to enable.
533 cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
534 std::set<std::string> testLangs;
535 for (std::string const& si : sources) {
536 std::string ext = cmSystemTools::GetFilenameLastExtension(si);
537 std::string lang = gg->GetLanguageFromExtension(ext.c_str());
539 testLangs.insert(lang);
541 std::ostringstream err;
542 err << "Unknown extension \"" << ext << "\" for file\n"
544 << "try_compile() works only for enabled languages. "
545 << "Currently these are:\n ";
546 std::vector<std::string> langs;
547 gg->GetEnabledLanguages(langs);
548 err << cmJoin(langs, " ");
549 err << "\nSee project() command to enable other languages.";
550 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str());
555 // when the only language is ISPC we know that the output
556 // type must by a static library
557 if (testLangs.size() == 1 && testLangs.count("ISPC") == 1) {
558 targetType = cmStateEnums::STATIC_LIBRARY;
561 std::string const tcConfig =
562 this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
564 // we need to create a directory and CMakeLists file etc...
565 // first create the directories
566 sourceDirectory = this->BinaryDirectory;
568 // now create a CMakeLists.txt file in that directory
569 FILE* fout = cmsys::SystemTools::Fopen(outFileName, "w");
571 std::ostringstream e;
572 /* clang-format off */
573 e << "Failed to open\n"
574 << " " << outFileName << "\n"
575 << cmSystemTools::GetLastSystemError();
576 /* clang-format on */
577 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
581 cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
582 fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
583 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
584 cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion());
586 fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def->c_str());
589 /* Set MSVC runtime library policy to match our selection. */
590 if (cmValue msvcRuntimeLibraryDefault =
591 this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
592 fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
593 !msvcRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
596 /* Set Watcom runtime library policy to match our selection. */
597 if (cmValue watcomRuntimeLibraryDefault = this->Makefile->GetDefinition(
598 kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT)) {
599 fprintf(fout, "cmake_policy(SET CMP0136 %s)\n",
600 !watcomRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
603 /* Set CUDA architectures policy to match outer project. */
604 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0104) !=
606 testLangs.find("CUDA") != testLangs.end() &&
607 this->Makefile->GetSafeDefinition(kCMAKE_CUDA_ARCHITECTURES).empty()) {
608 fprintf(fout, "cmake_policy(SET CMP0104 OLD)\n");
611 /* Set ARMClang cpu/arch policy to match outer project. */
612 if (cmValue cmp0123 =
613 this->Makefile->GetDefinition(kCMAKE_ARMClang_CMP0123)) {
614 fprintf(fout, "cmake_policy(SET CMP0123 %s)\n",
615 *cmp0123 == "NEW"_s ? "NEW" : "OLD");
618 /* Set MSVC debug information format policy to match our selection. */
619 if (cmValue msvcDebugInformationFormatDefault =
620 this->Makefile->GetDefinition(
621 kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)) {
622 fprintf(fout, "cmake_policy(SET CMP0141 %s)\n",
623 !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD");
626 /* Set cache/normal variable policy to match outer project.
627 It may affect toolchain files. */
628 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
630 fprintf(fout, "cmake_policy(SET CMP0126 OLD)\n");
633 /* Set language extensions policy to match outer project. */
634 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0128) !=
636 fprintf(fout, "cmake_policy(SET CMP0128 OLD)\n");
639 std::string projectLangs;
640 for (std::string const& li : testLangs) {
641 projectLangs += " " + li;
642 std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
643 std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
644 if (cmValue rulesOverridePath =
645 this->Makefile->GetDefinition(rulesOverrideLang)) {
646 fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
647 rulesOverridePath->c_str());
648 } else if (cmValue rulesOverridePath2 =
649 this->Makefile->GetDefinition(rulesOverrideBase)) {
650 fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(),
651 rulesOverridePath2->c_str());
654 fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str());
655 if (arguments.CMakeInternal == "ABI") {
656 // This is the ABI detection step, also used for implicit includes.
657 // Erase any include_directories() calls from the toolchain file so
658 // that we do not see them as implicit. Our ABI detection source
659 // does not include any system headers anyway.
661 "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n");
663 // The link and compile lines for ABI detection step need to not use
664 // response files so we can extract implicit includes given to
665 // the underlying host compiler
666 if (testLangs.find("CUDA") != testLangs.end()) {
667 fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n");
668 fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n");
669 fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n");
672 fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
673 for (std::string const& li : testLangs) {
674 std::string langFlags = "CMAKE_" + li + "_FLAGS";
675 cmValue flags = this->Makefile->GetDefinition(langFlags);
676 fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
677 cmOutputConverter::EscapeForCMake(*flags).c_str());
679 "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
680 " ${COMPILE_DEFINITIONS}\")\n",
681 li.c_str(), li.c_str());
683 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0066)) {
684 case cmPolicies::WARN:
685 if (this->Makefile->PolicyOptionalWarningEnabled(
686 "CMAKE_POLICY_WARNING_CMP0066")) {
687 std::ostringstream w;
688 /* clang-format off */
689 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n"
690 "For compatibility with older versions of CMake, try_compile "
691 "is not honoring caller config-specific compiler flags "
692 "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project."
694 /* clang-format on */
695 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
698 case cmPolicies::OLD:
699 // OLD behavior is to do nothing.
701 case cmPolicies::REQUIRED_IF_USED:
702 case cmPolicies::REQUIRED_ALWAYS:
703 this->Makefile->IssueMessage(
704 MessageType::FATAL_ERROR,
705 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066));
707 case cmPolicies::NEW: {
708 // NEW behavior is to pass config-specific compiler flags.
709 static std::string const cfgDefault = "DEBUG";
710 std::string const cfg =
711 !tcConfig.empty() ? cmSystemTools::UpperCase(tcConfig) : cfgDefault;
712 for (std::string const& li : testLangs) {
713 std::string const langFlagsCfg =
714 cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
715 cmValue flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
716 fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
717 cmOutputConverter::EscapeForCMake(*flagsCfg).c_str());
721 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0056)) {
722 case cmPolicies::WARN:
723 if (this->Makefile->PolicyOptionalWarningEnabled(
724 "CMAKE_POLICY_WARNING_CMP0056")) {
725 std::ostringstream w;
726 /* clang-format off */
727 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0056) << "\n"
728 "For compatibility with older versions of CMake, try_compile "
729 "is not honoring caller link flags (e.g. CMAKE_EXE_LINKER_FLAGS) "
730 "in the test project."
732 /* clang-format on */
733 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
736 case cmPolicies::OLD:
737 // OLD behavior is to do nothing.
739 case cmPolicies::REQUIRED_IF_USED:
740 case cmPolicies::REQUIRED_ALWAYS:
741 this->Makefile->IssueMessage(
742 MessageType::FATAL_ERROR,
743 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0056));
745 case cmPolicies::NEW:
746 // NEW behavior is to pass linker flags.
748 cmValue exeLinkFlags =
749 this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS");
750 fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n",
751 cmOutputConverter::EscapeForCMake(*exeLinkFlags).c_str());
756 "set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}"
757 " ${EXE_LINKER_FLAGS}\")\n");
758 fprintf(fout, "include_directories(${INCLUDE_DIRECTORIES})\n");
759 fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n");
760 fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n");
761 // handle any compile flags we need to pass on
762 if (!arguments.CompileDefs.empty()) {
763 // Pass using bracket arguments to preserve content.
764 fprintf(fout, "add_definitions([==[%s]==])\n",
765 cmJoin(arguments.CompileDefs, "]==] [==[").c_str());
768 if (!targets.empty()) {
769 std::string fname = "/" + std::string(targetName) + "Targets.cmake";
770 cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
772 tcfg.SetExportFile((this->BinaryDirectory + fname).c_str());
773 tcfg.SetConfig(tcConfig);
775 if (!tcfg.GenerateImportFile()) {
776 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
777 "could not write export file.");
781 fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
785 /* Set the appropriate policy information for ENABLE_EXPORTS */
786 fprintf(fout, "cmake_policy(SET CMP0065 %s)\n",
787 this->Makefile->GetPolicyStatus(cmPolicies::CMP0065) ==
792 /* Set the appropriate policy information for PIE link flags */
793 fprintf(fout, "cmake_policy(SET CMP0083 %s)\n",
794 this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
799 // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
800 // adding that flag in the platform and compiler language files
802 "include(\"${CMAKE_ROOT}/Modules/Internal/"
803 "HeaderpadWorkaround.cmake\")\n");
805 if (targetType == cmStateEnums::EXECUTABLE) {
806 /* Put the executable at a known location (for COPY_FILE). */
807 fprintf(fout, "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
808 this->BinaryDirectory.c_str());
809 /* Create the actual executable. */
810 fprintf(fout, "add_executable(%s", targetName.c_str());
811 } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
813 /* Put the static library at a known location (for COPY_FILE). */
814 fprintf(fout, "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"%s\")\n",
815 this->BinaryDirectory.c_str());
816 /* Create the actual static library. */
817 fprintf(fout, "add_library(%s STATIC", targetName.c_str());
819 for (std::string const& si : sources) {
820 fprintf(fout, " \"%s\"", si.c_str());
822 // Add dependencies on any non-temporary sources.
823 if (!IsTemporary(si)) {
824 this->Makefile->AddCMakeDependFile(si);
827 fprintf(fout, ")\n");
829 /* Write out the output location of the target we are building */
830 std::string perConfigGenex;
831 if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
832 perConfigGenex = "_$<UPPER_CASE:$<CONFIG>>";
835 "file(GENERATE OUTPUT "
836 "\"${CMAKE_BINARY_DIR}/%s%s_loc\"\n",
837 targetName.c_str(), perConfigGenex.c_str());
838 fprintf(fout, " CONTENT $<TARGET_FILE:%s>)\n", targetName.c_str());
840 bool warnCMP0067 = false;
841 bool honorStandard = true;
843 if (arguments.LangProps.empty()) {
844 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) {
845 case cmPolicies::WARN:
846 warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled(
847 "CMAKE_POLICY_WARNING_CMP0067");
849 case cmPolicies::OLD:
850 // OLD behavior is to not honor the language standard variables.
851 honorStandard = false;
853 case cmPolicies::REQUIRED_IF_USED:
854 case cmPolicies::REQUIRED_ALWAYS:
855 this->Makefile->IssueMessage(
856 MessageType::FATAL_ERROR,
857 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
859 case cmPolicies::NEW:
860 // NEW behavior is to honor the language standard variables.
861 // We already initialized honorStandard to true.
866 std::vector<std::string> warnCMP0067Variables;
868 if (honorStandard || warnCMP0067) {
869 static std::array<std::string, 6> const possibleLangs{
870 { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" }
872 static std::array<cm::string_view, 3> const langPropSuffixes{
873 { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s }
875 for (std::string const& lang : possibleLangs) {
876 if (testLangs.find(lang) == testLangs.end()) {
879 for (cm::string_view propSuffix : langPropSuffixes) {
880 std::string langProp = cmStrCat(lang, propSuffix);
881 if (!arguments.LangProps.count(langProp)) {
882 std::string langPropVar = cmStrCat("CMAKE_"_s, langProp);
883 std::string value = this->Makefile->GetSafeDefinition(langPropVar);
884 if (warnCMP0067 && !value.empty()) {
886 warnCMP0067Variables.emplace_back(langPropVar);
888 if (!value.empty()) {
889 arguments.LangProps[langProp] = value;
896 if (!warnCMP0067Variables.empty()) {
897 std::ostringstream w;
898 /* clang-format off */
899 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n"
900 "For compatibility with older versions of CMake, try_compile "
901 "is not honoring language standard variables in the test project:\n"
903 /* clang-format on */
904 for (std::string const& vi : warnCMP0067Variables) {
905 w << " " << vi << "\n";
907 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
910 for (auto const& p : arguments.LangProps) {
911 if (p.second.empty()) {
914 fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
916 cmOutputConverter::EscapeForCMake(p.first).c_str(),
917 cmOutputConverter::EscapeForCMake(p.second).c_str());
920 if (!arguments.LinkOptions.empty()) {
921 std::vector<std::string> options;
922 options.reserve(arguments.LinkOptions.size());
923 for (const auto& option : arguments.LinkOptions) {
924 options.emplace_back(cmOutputConverter::EscapeForCMake(option));
927 if (targetType == cmStateEnums::STATIC_LIBRARY) {
929 "set_property(TARGET %s PROPERTY STATIC_LIBRARY_OPTIONS %s)\n",
930 targetName.c_str(), cmJoin(options, " ").c_str());
932 fprintf(fout, "target_link_options(%s PRIVATE %s)\n",
933 targetName.c_str(), cmJoin(options, " ").c_str());
937 if (arguments.LinkLibraries) {
938 std::string libsToLink = " ";
939 for (std::string const& i : *arguments.LinkLibraries) {
940 libsToLink += "\"" + cmTrimWhitespace(i) + "\" ";
942 fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
945 fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
951 // Forward a set of variables to the inner project cache.
952 if ((this->SrcFileSignature ||
953 this->Makefile->GetPolicyStatus(cmPolicies::CMP0137) ==
955 !this->Makefile->IsOn("CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES")) {
956 std::set<std::string> vars;
957 vars.insert(&c_properties[lang_property_start],
958 &c_properties[lang_property_start + lang_property_size]);
959 vars.insert(&cxx_properties[lang_property_start],
960 &cxx_properties[lang_property_start + lang_property_size]);
961 vars.insert(&cuda_properties[lang_property_start],
962 &cuda_properties[lang_property_start + lang_property_size]);
963 vars.insert(&fortran_properties[lang_property_start],
964 &fortran_properties[lang_property_start + lang_property_size]);
965 vars.insert(&hip_properties[lang_property_start],
966 &hip_properties[lang_property_start + lang_property_size]);
967 vars.insert(&objc_properties[lang_property_start],
968 &objc_properties[lang_property_start + lang_property_size]);
969 vars.insert(&objcxx_properties[lang_property_start],
970 &objcxx_properties[lang_property_start + lang_property_size]);
971 vars.insert(&ispc_properties[lang_property_start],
972 &ispc_properties[lang_property_start + lang_property_size]);
973 vars.insert(&swift_properties[lang_property_start],
974 &swift_properties[lang_property_start + lang_property_size]);
975 vars.insert(kCMAKE_CUDA_ARCHITECTURES);
976 vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
977 vars.insert(kCMAKE_ENABLE_EXPORTS);
978 vars.insert(kCMAKE_HIP_ARCHITECTURES);
979 vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
980 vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
981 vars.insert(kCMAKE_ISPC_HEADER_SUFFIX);
982 vars.insert(kCMAKE_LINK_SEARCH_END_STATIC);
983 vars.insert(kCMAKE_LINK_SEARCH_START_STATIC);
984 vars.insert(kCMAKE_OSX_ARCHITECTURES);
985 vars.insert(kCMAKE_OSX_DEPLOYMENT_TARGET);
986 vars.insert(kCMAKE_OSX_SYSROOT);
987 vars.insert(kCMAKE_APPLE_ARCH_SYSROOTS);
988 vars.insert(kCMAKE_POSITION_INDEPENDENT_CODE);
989 vars.insert(kCMAKE_SYSROOT);
990 vars.insert(kCMAKE_SYSROOT_COMPILE);
991 vars.insert(kCMAKE_SYSROOT_LINK);
992 vars.insert(kCMAKE_WARN_DEPRECATED);
993 vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
994 vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
995 vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
997 if (cmValue varListStr = this->Makefile->GetDefinition(
998 kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
999 std::vector<std::string> varList = cmExpandedList(*varListStr);
1000 vars.insert(varList.begin(), varList.end());
1003 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
1005 // To ensure full support of PIE, propagate cache variables
1006 // driving the link options
1007 vars.insert(&c_properties[pie_property_start],
1008 &c_properties[pie_property_start + pie_property_size]);
1009 vars.insert(&cxx_properties[pie_property_start],
1010 &cxx_properties[pie_property_start + pie_property_size]);
1011 vars.insert(&cuda_properties[pie_property_start],
1012 &cuda_properties[pie_property_start + pie_property_size]);
1013 vars.insert(&fortran_properties[pie_property_start],
1014 &fortran_properties[pie_property_start + pie_property_size]);
1015 vars.insert(&hip_properties[pie_property_start],
1016 &hip_properties[pie_property_start + pie_property_size]);
1017 vars.insert(&objc_properties[pie_property_start],
1018 &objc_properties[pie_property_start + pie_property_size]);
1019 vars.insert(&objcxx_properties[pie_property_start],
1020 &objcxx_properties[pie_property_start + pie_property_size]);
1021 vars.insert(&ispc_properties[pie_property_start],
1022 &ispc_properties[pie_property_start + pie_property_size]);
1023 vars.insert(&swift_properties[pie_property_start],
1024 &swift_properties[pie_property_start + pie_property_size]);
1027 /* for the TRY_COMPILEs we want to be able to specify the architecture.
1028 So the user can set CMAKE_OSX_ARCHITECTURES to i386;ppc and then set
1029 CMAKE_TRY_COMPILE_OSX_ARCHITECTURES first to i386 and then to ppc to
1030 have the tests run for each specific architecture. Since
1031 cmLocalGenerator doesn't allow building for "the other"
1032 architecture only via CMAKE_OSX_ARCHITECTURES.
1034 if (cmValue tcArchs = this->Makefile->GetDefinition(
1035 kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
1036 vars.erase(kCMAKE_OSX_ARCHITECTURES);
1037 std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
1038 arguments.CMakeFlags.emplace_back(std::move(flag));
1041 for (std::string const& var : vars) {
1042 if (cmValue val = this->Makefile->GetDefinition(var)) {
1043 std::string flag = "-D" + var + "=" + *val;
1044 arguments.CMakeFlags.emplace_back(std::move(flag));
1049 if (this->Makefile->GetState()->UseGhsMultiIDE()) {
1050 // Forward the GHS variables to the inner project cache.
1051 for (std::string const& var : ghs_platform_vars) {
1052 if (cmValue val = this->Makefile->GetDefinition(var)) {
1053 std::string flag = "-D" + var + "=" + "'" + *val + "'";
1054 arguments.CMakeFlags.emplace_back(std::move(flag));
1059 if (this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
1061 cmStrCat("Executing try_compile (", *arguments.CompileResultVariable,
1062 ") in:\n ", this->BinaryDirectory);
1063 this->Makefile->IssueMessage(MessageType::LOG, msg);
1066 bool erroroc = cmSystemTools::GetErrorOccurredFlag();
1067 cmSystemTools::ResetErrorOccurredFlag();
1069 // actually do the try compile now that everything is setup
1070 int res = this->Makefile->TryCompile(
1071 sourceDirectory, this->BinaryDirectory, projectName, targetName,
1072 this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL,
1073 &arguments.CMakeFlags, output);
1075 cmSystemTools::SetErrorOccurred();
1078 // set the result var to the return value to indicate success or failure
1079 if (arguments.NoCache) {
1080 this->Makefile->AddDefinition(*arguments.CompileResultVariable,
1081 (res == 0 ? "TRUE" : "FALSE"));
1083 this->Makefile->AddCacheDefinition(
1084 *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"),
1085 "Result of TRY_COMPILE", cmStateEnums::INTERNAL);
1088 if (arguments.OutputVariable) {
1089 this->Makefile->AddDefinition(*arguments.OutputVariable, output);
1092 if (this->SrcFileSignature) {
1093 std::string copyFileErrorMessage;
1094 this->FindOutputFile(targetName);
1096 if ((res == 0) && arguments.CopyFileTo) {
1097 std::string const& copyFile = *arguments.CopyFileTo;
1098 if (this->OutputFile.empty() ||
1099 !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) {
1100 std::ostringstream emsg;
1101 /* clang-format off */
1102 emsg << "Cannot copy output executable\n"
1103 << " '" << this->OutputFile << "'\n"
1104 << "to destination specified by COPY_FILE:\n"
1105 << " '" << copyFile << "'\n";
1106 /* clang-format on */
1107 if (!this->FindErrorMessage.empty()) {
1108 emsg << this->FindErrorMessage;
1110 if (!arguments.CopyFileError) {
1111 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str());
1114 copyFileErrorMessage = emsg.str();
1118 if (arguments.CopyFileError) {
1119 std::string const& copyFileError = *arguments.CopyFileError;
1120 this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
1126 bool cmCoreTryCompile::IsTemporary(std::string const& path)
1128 return ((path.find("CMakeTmp") != std::string::npos) ||
1129 (path.find("CMakeScratch") != std::string::npos));
1132 void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
1134 if (binDir.empty()) {
1138 if (!IsTemporary(binDir)) {
1139 cmSystemTools::Error(
1140 "TRY_COMPILE attempt to remove -rf directory that does not contain "
1141 "CMakeTmp or CMakeScratch: \"" +
1146 cmsys::Directory dir;
1148 std::set<std::string> deletedFiles;
1149 for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) {
1150 const char* fileName = dir.GetFile(i);
1151 if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0 &&
1152 // Do not delete NFS temporary files.
1153 !cmHasPrefix(fileName, ".nfs")) {
1154 if (deletedFiles.insert(fileName).second) {
1155 std::string const fullPath =
1156 std::string(binDir).append("/").append(fileName);
1157 if (cmSystemTools::FileIsSymlink(fullPath)) {
1158 cmSystemTools::RemoveFile(fullPath);
1159 } else if (cmSystemTools::FileIsDirectory(fullPath)) {
1160 this->CleanupFiles(fullPath);
1161 cmSystemTools::RemoveADirectory(fullPath);
1164 // Sometimes anti-virus software hangs on to new files so we
1165 // cannot delete them immediately. Try a few times.
1166 cmSystemTools::WindowsFileRetry retry =
1167 cmSystemTools::GetWindowsFileRetry();
1168 cmsys::Status status;
1169 while (!((status = cmSystemTools::RemoveFile(fullPath))) &&
1170 --retry.Count && cmSystemTools::FileExists(fullPath)) {
1171 cmSystemTools::Delay(retry.Delay);
1173 if (retry.Count == 0)
1175 cmsys::Status status = cmSystemTools::RemoveFile(fullPath);
1179 this->Makefile->IssueMessage(
1180 MessageType::FATAL_ERROR,
1181 cmStrCat("The file:\n ", fullPath,
1182 "\ncould not be removed:\n ", status.GetString()));
1189 if (binDir.find("CMakeScratch") != std::string::npos) {
1190 cmSystemTools::RemoveADirectory(binDir);
1194 void cmCoreTryCompile::FindOutputFile(const std::string& targetName)
1196 this->FindErrorMessage.clear();
1197 this->OutputFile.clear();
1198 std::string tmpOutputFile = "/";
1199 tmpOutputFile += targetName;
1201 if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
1202 tmpOutputFile += "_DEBUG";
1204 tmpOutputFile += "_loc";
1206 std::string command = cmStrCat(this->BinaryDirectory, tmpOutputFile);
1207 if (!cmSystemTools::FileExists(command)) {
1208 std::ostringstream emsg;
1209 emsg << "Unable to find the recorded try_compile output location:\n";
1210 emsg << cmStrCat(" ", command, "\n");
1211 this->FindErrorMessage = emsg.str();
1215 std::string outputFileLocation;
1216 cmsys::ifstream ifs(command.c_str());
1217 cmSystemTools::GetLineFromStream(ifs, outputFileLocation);
1218 if (!cmSystemTools::FileExists(outputFileLocation)) {
1219 std::ostringstream emsg;
1220 emsg << "Recorded try_compile output location doesn't exist:\n";
1221 emsg << cmStrCat(" ", outputFileLocation, "\n");
1222 this->FindErrorMessage = emsg.str();
1226 this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation);
1229 std::string cmCoreTryCompile::WriteSource(std::string const& filename,
1230 std::string const& content,
1231 char const* command) const
1233 if (!cmSystemTools::GetFilenamePath(filename).empty()) {
1235 cmStrCat(command, " given invalid filename \"", filename, "\"");
1236 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1240 auto filepath = cmStrCat(this->BinaryDirectory, "/", filename);
1241 cmsys::ofstream file{ filepath.c_str(), std::ios::out };
1244 cmStrCat(command, " failed to open \"", filename, "\" for writing");
1245 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1251 const auto& msg = cmStrCat(command, " failed to write \"", filename, "\"");
1252 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);