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"
11 #include <cm/string_view>
12 #include <cmext/string_view>
14 #include "cmsys/Directory.hxx"
16 #include "cmExportTryCompileFileGenerator.h"
17 #include "cmGlobalGenerator.h"
18 #include "cmMakefile.h"
19 #include "cmMessageType.h"
20 #include "cmOutputConverter.h"
21 #include "cmPolicies.h"
23 #include "cmStringAlgorithms.h"
24 #include "cmSystemTools.h"
27 #include "cmVersion.h"
31 class LanguageStandardState
34 LanguageStandardState(std::string&& lang)
35 : StandardFlag(lang + "_STANDARD")
36 , RequiredFlag(lang + "_STANDARD_REQUIRED")
37 , ExtensionFlag(lang + "_EXTENSIONS")
41 void Enabled(bool isEnabled) { this->IsEnabled = isEnabled; }
43 bool UpdateIfMatches(std::vector<std::string> const& argv, size_t& index)
46 if (argv[index] == this->StandardFlag) {
47 this->DidStandard = true;
48 this->StandardValue = argv[++index];
50 } else if (argv[index] == this->RequiredFlag) {
51 this->DidStandardRequired = true;
52 this->RequiredValue = argv[++index];
54 } else if (argv[index] == this->ExtensionFlag) {
55 this->DidExtensions = true;
56 this->ExtensionValue = argv[++index];
62 bool Validate(cmMakefile* const makefile) const
64 if (this->DidStandard) {
65 makefile->IssueMessage(
66 MessageType::FATAL_ERROR,
67 cmStrCat(this->StandardFlag,
68 " allowed only in source file signature."));
71 if (this->DidStandardRequired) {
72 makefile->IssueMessage(
73 MessageType::FATAL_ERROR,
74 cmStrCat(this->RequiredFlag,
75 " allowed only in source file signature."));
78 if (this->DidExtensions) {
79 makefile->IssueMessage(
80 MessageType::FATAL_ERROR,
81 cmStrCat(this->ExtensionFlag,
82 " allowed only in source file signature."));
91 return !this->DidStandard && !this->DidStandardRequired &&
95 void LoadUnsetPropertyValues(cmMakefile* const makefile, bool honorStandard,
97 std::vector<std::string>& warnCMP0067Variables)
99 if (!this->IsEnabled) {
103 auto lookupStdVar = [&](std::string const& var) -> std::string {
104 std::string value = makefile->GetSafeDefinition(var);
105 if (warnCMP0067 && !value.empty()) {
107 warnCMP0067Variables.emplace_back(var);
112 if (honorStandard || warnCMP0067) {
113 if (!this->DidStandard) {
114 this->StandardValue =
115 lookupStdVar(cmStrCat("CMAKE_", this->StandardFlag));
117 if (!this->DidStandardRequired) {
118 this->RequiredValue =
119 lookupStdVar(cmStrCat("CMAKE_", this->RequiredFlag));
121 if (!this->DidExtensions) {
122 this->ExtensionValue =
123 lookupStdVar(cmStrCat("CMAKE_", this->ExtensionFlag));
128 void WriteProperties(FILE* fout, std::string const& targetName) const
130 if (!this->IsEnabled) {
134 auto writeProp = [&](std::string const& prop, std::string const& value) {
135 fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
137 cmOutputConverter::EscapeForCMake(prop).c_str(),
138 cmOutputConverter::EscapeForCMake(value).c_str());
141 if (!this->StandardValue.empty()) {
142 writeProp(this->StandardFlag, this->StandardValue);
144 if (!this->RequiredValue.empty()) {
145 writeProp(this->RequiredFlag, this->RequiredValue);
147 if (!this->ExtensionValue.empty()) {
148 writeProp(this->ExtensionFlag, this->ExtensionValue);
153 bool IsEnabled = false;
154 bool DidStandard = false;
155 bool DidStandardRequired = false;
156 bool DidExtensions = false;
158 std::string StandardFlag;
159 std::string RequiredFlag;
160 std::string ExtensionFlag;
162 std::string StandardValue;
163 std::string RequiredValue;
164 std::string ExtensionValue;
167 constexpr size_t lang_property_start = 0;
168 constexpr size_t lang_property_size = 4;
169 constexpr size_t pie_property_start = 4;
170 constexpr size_t pie_property_size = 2;
171 #define SETUP_LANGUAGE(name, lang) \
172 static const std::string name[lang_property_size + pie_property_size + 1] = \
173 { "CMAKE_" #lang "_COMPILER_EXTERNAL_TOOLCHAIN", \
174 "CMAKE_" #lang "_COMPILER_TARGET", \
175 "CMAKE_" #lang "_LINK_NO_PIE_SUPPORTED", \
176 "CMAKE_" #lang "_PIE_SUPPORTED", "" }
178 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
179 SETUP_LANGUAGE(c_properties, C);
180 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
181 SETUP_LANGUAGE(cxx_properties, CXX);
183 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
184 SETUP_LANGUAGE(cuda_properties, CUDA);
185 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
186 SETUP_LANGUAGE(fortran_properties, Fortran);
187 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
188 SETUP_LANGUAGE(hip_properties, HIP);
189 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
190 SETUP_LANGUAGE(objc_properties, OBJC);
191 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
192 SETUP_LANGUAGE(objcxx_properties, OBJCXX);
193 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
194 SETUP_LANGUAGE(ispc_properties, ISPC);
195 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
196 SETUP_LANGUAGE(swift_properties, Swift);
197 #undef SETUP_LANGUAGE
199 std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES";
200 std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
201 std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS";
202 std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES";
203 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
204 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
205 std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX";
206 std::string const kCMAKE_LINK_SEARCH_END_STATIC =
207 "CMAKE_LINK_SEARCH_END_STATIC";
208 std::string const kCMAKE_LINK_SEARCH_START_STATIC =
209 "CMAKE_LINK_SEARCH_START_STATIC";
210 std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT =
211 "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT";
212 std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES";
213 std::string const kCMAKE_OSX_DEPLOYMENT_TARGET = "CMAKE_OSX_DEPLOYMENT_TARGET";
214 std::string const kCMAKE_OSX_SYSROOT = "CMAKE_OSX_SYSROOT";
215 std::string const kCMAKE_APPLE_ARCH_SYSROOTS = "CMAKE_APPLE_ARCH_SYSROOTS";
216 std::string const kCMAKE_POSITION_INDEPENDENT_CODE =
217 "CMAKE_POSITION_INDEPENDENT_CODE";
218 std::string const kCMAKE_SYSROOT = "CMAKE_SYSROOT";
219 std::string const kCMAKE_SYSROOT_COMPILE = "CMAKE_SYSROOT_COMPILE";
220 std::string const kCMAKE_SYSROOT_LINK = "CMAKE_SYSROOT_LINK";
221 std::string const kCMAKE_ARMClang_CMP0123 = "CMAKE_ARMClang_CMP0123";
222 std::string const kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES =
223 "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES";
224 std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES =
225 "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES";
226 std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED";
227 std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT =
228 "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT";
230 /* GHS Multi platform variables */
231 std::set<std::string> const ghs_platform_vars{
232 "GHS_TARGET_PLATFORM", "GHS_PRIMARY_TARGET", "GHS_TOOLSET_ROOT",
233 "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME",
238 int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
241 this->BinaryDirectory = argv[1];
242 this->OutputFile.clear();
243 // which signature were we called with ?
244 this->SrcFileSignature = true;
246 cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE;
247 cmValue tt = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE");
248 if (!isTryRun && cmNonempty(tt)) {
249 if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) {
250 targetType = cmStateEnums::EXECUTABLE;
252 cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) {
253 targetType = cmStateEnums::STATIC_LIBRARY;
255 this->Makefile->IssueMessage(
256 MessageType::FATAL_ERROR,
257 cmStrCat("Invalid value '", *tt,
258 "' for CMAKE_TRY_COMPILE_TARGET_TYPE. Only '",
259 cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE),
261 cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY),
267 std::string sourceDirectory = argv[2];
268 std::string projectName;
269 std::string targetName;
270 std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0]
271 std::vector<std::string> compileDefs;
272 std::string cmakeInternal;
273 std::string outputVariable;
274 std::string copyFile;
275 std::string copyFileError;
276 LanguageStandardState cState("C");
277 LanguageStandardState cudaState("CUDA");
278 LanguageStandardState cxxState("CXX");
279 LanguageStandardState hipState("HIP");
280 LanguageStandardState objcState("OBJC");
281 LanguageStandardState objcxxState("OBJCXX");
282 std::vector<std::string> targets;
283 std::vector<std::string> linkOptions;
284 std::string libsToLink = " ";
285 bool useOldLinkLibs = true;
286 char targetNameBuf[64];
287 bool didOutputVariable = false;
288 bool didCopyFile = false;
289 bool didCopyFileError = false;
290 bool useSources = argv[2] == "SOURCES";
291 std::vector<std::string> sources;
297 DoingCompileDefinitions,
306 Doing doing = useSources ? DoingSources : DoingNone;
307 for (size_t i = 3; i < argv.size(); ++i) {
308 if (argv[i] == "CMAKE_FLAGS") {
309 doing = DoingCMakeFlags;
310 } else if (argv[i] == "COMPILE_DEFINITIONS") {
311 doing = DoingCompileDefinitions;
312 } else if (argv[i] == "LINK_OPTIONS") {
313 doing = DoingLinkOptions;
314 } else if (argv[i] == "LINK_LIBRARIES") {
315 doing = DoingLinkLibraries;
316 useOldLinkLibs = false;
317 } else if (argv[i] == "OUTPUT_VARIABLE") {
318 doing = DoingOutputVariable;
319 didOutputVariable = true;
320 } else if (argv[i] == "COPY_FILE") {
321 doing = DoingCopyFile;
323 } else if (argv[i] == "COPY_FILE_ERROR") {
324 doing = DoingCopyFileError;
325 didCopyFileError = true;
326 } else if (cState.UpdateIfMatches(argv, i) ||
327 cxxState.UpdateIfMatches(argv, i) ||
328 cudaState.UpdateIfMatches(argv, i) ||
329 hipState.UpdateIfMatches(argv, i) ||
330 objcState.UpdateIfMatches(argv, i) ||
331 objcxxState.UpdateIfMatches(argv, i)) {
333 } else if (argv[i] == "__CMAKE_INTERNAL") {
334 doing = DoingCMakeInternal;
335 } else if (doing == DoingCMakeFlags) {
336 cmakeFlags.emplace_back(argv[i]);
337 } else if (doing == DoingCompileDefinitions) {
338 cmExpandList(argv[i], compileDefs);
339 } else if (doing == DoingLinkOptions) {
340 linkOptions.emplace_back(argv[i]);
341 } else if (doing == DoingLinkLibraries) {
342 libsToLink += "\"" + cmTrimWhitespace(argv[i]) + "\" ";
343 if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) {
344 switch (tgt->GetType()) {
345 case cmStateEnums::SHARED_LIBRARY:
346 case cmStateEnums::STATIC_LIBRARY:
347 case cmStateEnums::INTERFACE_LIBRARY:
348 case cmStateEnums::UNKNOWN_LIBRARY:
350 case cmStateEnums::EXECUTABLE:
351 if (tgt->IsExecutableWithExports()) {
356 this->Makefile->IssueMessage(
357 MessageType::FATAL_ERROR,
358 cmStrCat("Only libraries may be used as try_compile or try_run "
359 "IMPORTED LINK_LIBRARIES. Got ",
360 tgt->GetName(), " of type ",
361 cmState::GetTargetTypeName(tgt->GetType()), "."));
364 if (tgt->IsImported()) {
365 targets.emplace_back(argv[i]);
368 } else if (doing == DoingOutputVariable) {
369 outputVariable = argv[i];
371 } else if (doing == DoingCopyFile) {
374 } else if (doing == DoingCopyFileError) {
375 copyFileError = argv[i];
377 } else if (doing == DoingSources) {
378 sources.emplace_back(argv[i]);
379 } else if (doing == DoingCMakeInternal) {
380 cmakeInternal = argv[i];
383 this->SrcFileSignature = false;
384 projectName = argv[i];
385 } else if (i == 4 && !this->SrcFileSignature) {
386 targetName = argv[i];
388 std::ostringstream m;
389 m << "try_compile given unknown argument \"" << argv[i] << "\".";
390 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m.str());
394 if (didCopyFile && copyFile.empty()) {
395 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
396 "COPY_FILE must be followed by a file path");
400 if (didCopyFileError && copyFileError.empty()) {
401 this->Makefile->IssueMessage(
402 MessageType::FATAL_ERROR,
403 "COPY_FILE_ERROR must be followed by a variable name");
407 if (didCopyFileError && !didCopyFile) {
408 this->Makefile->IssueMessage(
409 MessageType::FATAL_ERROR,
410 "COPY_FILE_ERROR may be used only with COPY_FILE");
414 if (didOutputVariable && outputVariable.empty()) {
415 this->Makefile->IssueMessage(
416 MessageType::FATAL_ERROR,
417 "OUTPUT_VARIABLE must be followed by a variable name");
421 if (useSources && sources.empty()) {
422 this->Makefile->IssueMessage(
423 MessageType::FATAL_ERROR,
424 "SOURCES must be followed by at least one source file");
428 if (!this->SrcFileSignature) {
429 if (!cState.Validate(this->Makefile)) {
432 if (!cudaState.Validate(this->Makefile)) {
435 if (!hipState.Validate(this->Makefile)) {
438 if (!cxxState.Validate(this->Makefile)) {
441 if (!objcState.Validate(this->Makefile)) {
444 if (!objcxxState.Validate(this->Makefile)) {
449 // compute the binary dir when TRY_COMPILE is called with a src file
451 if (this->SrcFileSignature) {
452 this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
454 // only valid for srcfile signatures
455 if (!compileDefs.empty()) {
456 this->Makefile->IssueMessage(
457 MessageType::FATAL_ERROR,
458 "COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE");
461 if (!copyFile.empty()) {
462 this->Makefile->IssueMessage(
463 MessageType::FATAL_ERROR,
464 "COPY_FILE specified on a srcdir type TRY_COMPILE");
468 // make sure the binary directory exists
469 cmSystemTools::MakeDirectory(this->BinaryDirectory);
471 // do not allow recursive try Compiles
472 if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) {
473 std::ostringstream e;
474 e << "Attempt at a recursive or nested TRY_COMPILE in directory\n"
475 << " " << this->BinaryDirectory << "\n";
476 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
480 std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
481 // which signature are we using? If we are using var srcfile bindir
482 if (this->SrcFileSignature) {
483 // remove any CMakeCache.txt files so we will have a clean test
484 std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
485 cmSystemTools::RemoveFile(ccFile);
489 sources.emplace_back(argv[2]);
492 // Detect languages to enable.
493 cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
494 std::set<std::string> testLangs;
495 for (std::string const& si : sources) {
496 std::string ext = cmSystemTools::GetFilenameLastExtension(si);
497 std::string lang = gg->GetLanguageFromExtension(ext.c_str());
499 testLangs.insert(lang);
501 std::ostringstream err;
502 err << "Unknown extension \"" << ext << "\" for file\n"
504 << "try_compile() works only for enabled languages. "
505 << "Currently these are:\n ";
506 std::vector<std::string> langs;
507 gg->GetEnabledLanguages(langs);
508 err << cmJoin(langs, " ");
509 err << "\nSee project() command to enable other languages.";
510 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str());
515 // when the only language is ISPC we know that the output
516 // type must by a static library
517 if (testLangs.size() == 1 && testLangs.count("ISPC") == 1) {
518 targetType = cmStateEnums::STATIC_LIBRARY;
521 std::string const tcConfig =
522 this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
524 // we need to create a directory and CMakeLists file etc...
525 // first create the directories
526 sourceDirectory = this->BinaryDirectory;
528 // now create a CMakeLists.txt file in that directory
529 FILE* fout = cmsys::SystemTools::Fopen(outFileName, "w");
531 std::ostringstream e;
532 /* clang-format off */
533 e << "Failed to open\n"
534 << " " << outFileName << "\n"
535 << cmSystemTools::GetLastSystemError();
536 /* clang-format on */
537 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
541 cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
542 fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
543 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
544 cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion());
546 fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def->c_str());
549 /* Set MSVC runtime library policy to match our selection. */
550 if (cmValue msvcRuntimeLibraryDefault =
551 this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
552 fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
553 !msvcRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
556 /* Set Watcom runtime library policy to match our selection. */
557 if (cmValue watcomRuntimeLibraryDefault = this->Makefile->GetDefinition(
558 kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT)) {
559 fprintf(fout, "cmake_policy(SET CMP0136 %s)\n",
560 !watcomRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
563 /* Set CUDA architectures policy to match outer project. */
564 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0104) !=
566 testLangs.find("CUDA") != testLangs.end() &&
567 this->Makefile->GetSafeDefinition(kCMAKE_CUDA_ARCHITECTURES).empty()) {
568 fprintf(fout, "cmake_policy(SET CMP0104 OLD)\n");
571 /* Set ARMClang cpu/arch policy to match outer project. */
572 if (cmValue cmp0123 =
573 this->Makefile->GetDefinition(kCMAKE_ARMClang_CMP0123)) {
574 fprintf(fout, "cmake_policy(SET CMP0123 %s)\n",
575 *cmp0123 == "NEW"_s ? "NEW" : "OLD");
578 /* Set cache/normal variable policy to match outer project.
579 It may affect toolchain files. */
580 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
582 fprintf(fout, "cmake_policy(SET CMP0126 OLD)\n");
585 /* Set language extensions policy to match outer project. */
586 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0128) !=
588 fprintf(fout, "cmake_policy(SET CMP0128 OLD)\n");
591 std::string projectLangs;
592 for (std::string const& li : testLangs) {
593 projectLangs += " " + li;
594 std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
595 std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
596 if (cmValue rulesOverridePath =
597 this->Makefile->GetDefinition(rulesOverrideLang)) {
598 fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
599 rulesOverridePath->c_str());
600 } else if (cmValue rulesOverridePath2 =
601 this->Makefile->GetDefinition(rulesOverrideBase)) {
602 fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(),
603 rulesOverridePath2->c_str());
606 fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str());
607 if (cmakeInternal == "ABI") {
608 // This is the ABI detection step, also used for implicit includes.
609 // Erase any include_directories() calls from the toolchain file so
610 // that we do not see them as implicit. Our ABI detection source
611 // does not include any system headers anyway.
613 "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n");
615 fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
616 for (std::string const& li : testLangs) {
617 std::string langFlags = "CMAKE_" + li + "_FLAGS";
618 cmValue flags = this->Makefile->GetDefinition(langFlags);
619 fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
620 cmOutputConverter::EscapeForCMake(*flags).c_str());
622 "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
623 " ${COMPILE_DEFINITIONS}\")\n",
624 li.c_str(), li.c_str());
626 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0066)) {
627 case cmPolicies::WARN:
628 if (this->Makefile->PolicyOptionalWarningEnabled(
629 "CMAKE_POLICY_WARNING_CMP0066")) {
630 std::ostringstream w;
631 /* clang-format off */
632 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n"
633 "For compatibility with older versions of CMake, try_compile "
634 "is not honoring caller config-specific compiler flags "
635 "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project."
637 /* clang-format on */
638 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
641 case cmPolicies::OLD:
642 // OLD behavior is to do nothing.
644 case cmPolicies::REQUIRED_IF_USED:
645 case cmPolicies::REQUIRED_ALWAYS:
646 this->Makefile->IssueMessage(
647 MessageType::FATAL_ERROR,
648 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066));
650 case cmPolicies::NEW: {
651 // NEW behavior is to pass config-specific compiler flags.
652 static std::string const cfgDefault = "DEBUG";
653 std::string const cfg =
654 !tcConfig.empty() ? cmSystemTools::UpperCase(tcConfig) : cfgDefault;
655 for (std::string const& li : testLangs) {
656 std::string const langFlagsCfg =
657 cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
658 cmValue flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
659 fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
660 cmOutputConverter::EscapeForCMake(*flagsCfg).c_str());
664 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0056)) {
665 case cmPolicies::WARN:
666 if (this->Makefile->PolicyOptionalWarningEnabled(
667 "CMAKE_POLICY_WARNING_CMP0056")) {
668 std::ostringstream w;
669 /* clang-format off */
670 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0056) << "\n"
671 "For compatibility with older versions of CMake, try_compile "
672 "is not honoring caller link flags (e.g. CMAKE_EXE_LINKER_FLAGS) "
673 "in the test project."
675 /* clang-format on */
676 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
679 case cmPolicies::OLD:
680 // OLD behavior is to do nothing.
682 case cmPolicies::REQUIRED_IF_USED:
683 case cmPolicies::REQUIRED_ALWAYS:
684 this->Makefile->IssueMessage(
685 MessageType::FATAL_ERROR,
686 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0056));
688 case cmPolicies::NEW:
689 // NEW behavior is to pass linker flags.
691 cmValue exeLinkFlags =
692 this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS");
693 fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n",
694 cmOutputConverter::EscapeForCMake(*exeLinkFlags).c_str());
699 "set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}"
700 " ${EXE_LINKER_FLAGS}\")\n");
701 fprintf(fout, "include_directories(${INCLUDE_DIRECTORIES})\n");
702 fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n");
703 fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n");
704 // handle any compile flags we need to pass on
705 if (!compileDefs.empty()) {
706 // Pass using bracket arguments to preserve content.
707 fprintf(fout, "add_definitions([==[%s]==])\n",
708 cmJoin(compileDefs, "]==] [==[").c_str());
711 /* Use a random file name to avoid rapid creation and deletion
712 of the same executable name (some filesystems fail on that). */
713 snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
714 cmSystemTools::RandomSeed() & 0xFFFFF);
715 targetName = targetNameBuf;
717 if (!targets.empty()) {
718 std::string fname = "/" + std::string(targetName) + "Targets.cmake";
719 cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
721 tcfg.SetExportFile((this->BinaryDirectory + fname).c_str());
722 tcfg.SetConfig(tcConfig);
724 if (!tcfg.GenerateImportFile()) {
725 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
726 "could not write export file.");
730 fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
734 /* Set the appropriate policy information for ENABLE_EXPORTS */
735 fprintf(fout, "cmake_policy(SET CMP0065 %s)\n",
736 this->Makefile->GetPolicyStatus(cmPolicies::CMP0065) ==
741 /* Set the appropriate policy information for PIE link flags */
742 fprintf(fout, "cmake_policy(SET CMP0083 %s)\n",
743 this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
748 // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
749 // adding that flag in the platform and compiler language files
751 "include(\"${CMAKE_ROOT}/Modules/Internal/"
752 "HeaderpadWorkaround.cmake\")\n");
754 if (targetType == cmStateEnums::EXECUTABLE) {
755 /* Put the executable at a known location (for COPY_FILE). */
756 fprintf(fout, "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
757 this->BinaryDirectory.c_str());
758 /* Create the actual executable. */
759 fprintf(fout, "add_executable(%s", targetName.c_str());
760 } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
762 /* Put the static library at a known location (for COPY_FILE). */
763 fprintf(fout, "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"%s\")\n",
764 this->BinaryDirectory.c_str());
765 /* Create the actual static library. */
766 fprintf(fout, "add_library(%s STATIC", targetName.c_str());
768 for (std::string const& si : sources) {
769 fprintf(fout, " \"%s\"", si.c_str());
771 // Add dependencies on any non-temporary sources.
772 if (si.find("CMakeTmp") == std::string::npos) {
773 this->Makefile->AddCMakeDependFile(si);
776 fprintf(fout, ")\n");
778 cState.Enabled(testLangs.find("C") != testLangs.end());
779 cxxState.Enabled(testLangs.find("CXX") != testLangs.end());
780 cudaState.Enabled(testLangs.find("CUDA") != testLangs.end());
781 hipState.Enabled(testLangs.find("HIP") != testLangs.end());
782 objcState.Enabled(testLangs.find("OBJC") != testLangs.end());
783 objcxxState.Enabled(testLangs.find("OBJCXX") != testLangs.end());
785 bool warnCMP0067 = false;
786 bool honorStandard = true;
788 if (cState.DidNone() && cxxState.DidNone() && objcState.DidNone() &&
789 objcxxState.DidNone() && cudaState.DidNone() && hipState.DidNone()) {
790 switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) {
791 case cmPolicies::WARN:
792 warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled(
793 "CMAKE_POLICY_WARNING_CMP0067");
795 case cmPolicies::OLD:
796 // OLD behavior is to not honor the language standard variables.
797 honorStandard = false;
799 case cmPolicies::REQUIRED_IF_USED:
800 case cmPolicies::REQUIRED_ALWAYS:
801 this->Makefile->IssueMessage(
802 MessageType::FATAL_ERROR,
803 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
805 case cmPolicies::NEW:
806 // NEW behavior is to honor the language standard variables.
807 // We already initialized honorStandard to true.
812 std::vector<std::string> warnCMP0067Variables;
814 cState.LoadUnsetPropertyValues(this->Makefile, honorStandard, warnCMP0067,
815 warnCMP0067Variables);
816 cxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
817 warnCMP0067, warnCMP0067Variables);
818 cudaState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
819 warnCMP0067, warnCMP0067Variables);
820 hipState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
821 warnCMP0067, warnCMP0067Variables);
822 objcState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
823 warnCMP0067, warnCMP0067Variables);
824 objcxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
825 warnCMP0067, warnCMP0067Variables);
827 if (!warnCMP0067Variables.empty()) {
828 std::ostringstream w;
829 /* clang-format off */
830 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n"
831 "For compatibility with older versions of CMake, try_compile "
832 "is not honoring language standard variables in the test project:\n"
834 /* clang-format on */
835 for (std::string const& vi : warnCMP0067Variables) {
836 w << " " << vi << "\n";
838 this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
841 cState.WriteProperties(fout, targetName);
842 cxxState.WriteProperties(fout, targetName);
843 cudaState.WriteProperties(fout, targetName);
844 hipState.WriteProperties(fout, targetName);
845 objcState.WriteProperties(fout, targetName);
846 objcxxState.WriteProperties(fout, targetName);
848 if (!linkOptions.empty()) {
849 std::vector<std::string> options;
850 options.reserve(linkOptions.size());
851 for (const auto& option : linkOptions) {
852 options.emplace_back(cmOutputConverter::EscapeForCMake(option));
855 if (targetType == cmStateEnums::STATIC_LIBRARY) {
857 "set_property(TARGET %s PROPERTY STATIC_LIBRARY_OPTIONS %s)\n",
858 targetName.c_str(), cmJoin(options, " ").c_str());
860 fprintf(fout, "target_link_options(%s PRIVATE %s)\n",
861 targetName.c_str(), cmJoin(options, " ").c_str());
865 if (useOldLinkLibs) {
866 fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
869 fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
873 projectName = "CMAKE_TRY_COMPILE";
876 // Forward a set of variables to the inner project cache.
877 if ((this->SrcFileSignature ||
878 this->Makefile->GetPolicyStatus(cmPolicies::CMP0137) ==
880 !this->Makefile->IsOn("CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES")) {
881 std::set<std::string> vars;
882 vars.insert(&c_properties[lang_property_start],
883 &c_properties[lang_property_start + lang_property_size]);
884 vars.insert(&cxx_properties[lang_property_start],
885 &cxx_properties[lang_property_start + lang_property_size]);
886 vars.insert(&cuda_properties[lang_property_start],
887 &cuda_properties[lang_property_start + lang_property_size]);
888 vars.insert(&fortran_properties[lang_property_start],
889 &fortran_properties[lang_property_start + lang_property_size]);
890 vars.insert(&hip_properties[lang_property_start],
891 &hip_properties[lang_property_start + lang_property_size]);
892 vars.insert(&objc_properties[lang_property_start],
893 &objc_properties[lang_property_start + lang_property_size]);
894 vars.insert(&objcxx_properties[lang_property_start],
895 &objcxx_properties[lang_property_start + lang_property_size]);
896 vars.insert(&ispc_properties[lang_property_start],
897 &ispc_properties[lang_property_start + lang_property_size]);
898 vars.insert(&swift_properties[lang_property_start],
899 &swift_properties[lang_property_start + lang_property_size]);
900 vars.insert(kCMAKE_CUDA_ARCHITECTURES);
901 vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
902 vars.insert(kCMAKE_ENABLE_EXPORTS);
903 vars.insert(kCMAKE_HIP_ARCHITECTURES);
904 vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
905 vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
906 vars.insert(kCMAKE_ISPC_HEADER_SUFFIX);
907 vars.insert(kCMAKE_LINK_SEARCH_END_STATIC);
908 vars.insert(kCMAKE_LINK_SEARCH_START_STATIC);
909 vars.insert(kCMAKE_OSX_ARCHITECTURES);
910 vars.insert(kCMAKE_OSX_DEPLOYMENT_TARGET);
911 vars.insert(kCMAKE_OSX_SYSROOT);
912 vars.insert(kCMAKE_APPLE_ARCH_SYSROOTS);
913 vars.insert(kCMAKE_POSITION_INDEPENDENT_CODE);
914 vars.insert(kCMAKE_SYSROOT);
915 vars.insert(kCMAKE_SYSROOT_COMPILE);
916 vars.insert(kCMAKE_SYSROOT_LINK);
917 vars.insert(kCMAKE_WARN_DEPRECATED);
918 vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
919 vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
921 if (cmValue varListStr = this->Makefile->GetDefinition(
922 kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
923 std::vector<std::string> varList = cmExpandedList(*varListStr);
924 vars.insert(varList.begin(), varList.end());
927 if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
929 // To ensure full support of PIE, propagate cache variables
930 // driving the link options
931 vars.insert(&c_properties[pie_property_start],
932 &c_properties[pie_property_start + pie_property_size]);
933 vars.insert(&cxx_properties[pie_property_start],
934 &cxx_properties[pie_property_start + pie_property_size]);
935 vars.insert(&cuda_properties[pie_property_start],
936 &cuda_properties[pie_property_start + pie_property_size]);
937 vars.insert(&fortran_properties[pie_property_start],
938 &fortran_properties[pie_property_start + pie_property_size]);
939 vars.insert(&hip_properties[pie_property_start],
940 &hip_properties[pie_property_start + pie_property_size]);
941 vars.insert(&objc_properties[pie_property_start],
942 &objc_properties[pie_property_start + pie_property_size]);
943 vars.insert(&objcxx_properties[pie_property_start],
944 &objcxx_properties[pie_property_start + pie_property_size]);
945 vars.insert(&ispc_properties[pie_property_start],
946 &ispc_properties[pie_property_start + pie_property_size]);
947 vars.insert(&swift_properties[pie_property_start],
948 &swift_properties[pie_property_start + pie_property_size]);
951 /* for the TRY_COMPILEs we want to be able to specify the architecture.
952 So the user can set CMAKE_OSX_ARCHITECTURES to i386;ppc and then set
953 CMAKE_TRY_COMPILE_OSX_ARCHITECTURES first to i386 and then to ppc to
954 have the tests run for each specific architecture. Since
955 cmLocalGenerator doesn't allow building for "the other"
956 architecture only via CMAKE_OSX_ARCHITECTURES.
958 if (cmValue tcArchs = this->Makefile->GetDefinition(
959 kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
960 vars.erase(kCMAKE_OSX_ARCHITECTURES);
961 std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
962 cmakeFlags.emplace_back(std::move(flag));
965 for (std::string const& var : vars) {
966 if (cmValue val = this->Makefile->GetDefinition(var)) {
967 std::string flag = "-D" + var + "=" + *val;
968 cmakeFlags.emplace_back(std::move(flag));
973 if (this->Makefile->GetState()->UseGhsMultiIDE()) {
974 // Forward the GHS variables to the inner project cache.
975 for (std::string const& var : ghs_platform_vars) {
976 if (cmValue val = this->Makefile->GetDefinition(var)) {
977 std::string flag = "-D" + var + "=" + "'" + *val + "'";
978 cmakeFlags.emplace_back(std::move(flag));
983 bool erroroc = cmSystemTools::GetErrorOccurredFlag();
984 cmSystemTools::ResetErrorOccurredFlag();
986 // actually do the try compile now that everything is setup
987 int res = this->Makefile->TryCompile(
988 sourceDirectory, this->BinaryDirectory, projectName, targetName,
989 this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, &cmakeFlags,
992 cmSystemTools::SetErrorOccurred();
995 // set the result var to the return value to indicate success or failure
996 this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"),
997 "Result of TRY_COMPILE",
998 cmStateEnums::INTERNAL);
1000 if (!outputVariable.empty()) {
1001 this->Makefile->AddDefinition(outputVariable, output);
1004 if (this->SrcFileSignature) {
1005 std::string copyFileErrorMessage;
1006 this->FindOutputFile(targetName, targetType);
1008 if ((res == 0) && !copyFile.empty()) {
1009 if (this->OutputFile.empty() ||
1010 !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) {
1011 std::ostringstream emsg;
1012 /* clang-format off */
1013 emsg << "Cannot copy output executable\n"
1014 << " '" << this->OutputFile << "'\n"
1015 << "to destination specified by COPY_FILE:\n"
1016 << " '" << copyFile << "'\n";
1017 /* clang-format on */
1018 if (!this->FindErrorMessage.empty()) {
1019 emsg << this->FindErrorMessage;
1021 if (copyFileError.empty()) {
1022 this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str());
1025 copyFileErrorMessage = emsg.str();
1029 if (!copyFileError.empty()) {
1030 this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
1036 void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
1038 if (binDir.empty()) {
1042 if (binDir.find("CMakeTmp") == std::string::npos) {
1043 cmSystemTools::Error(
1044 "TRY_COMPILE attempt to remove -rf directory that does not contain "
1050 cmsys::Directory dir;
1052 std::set<std::string> deletedFiles;
1053 for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) {
1054 const char* fileName = dir.GetFile(i);
1055 if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0 &&
1056 // Do not delete NFS temporary files.
1057 !cmHasPrefix(fileName, ".nfs")) {
1058 if (deletedFiles.insert(fileName).second) {
1059 std::string const fullPath =
1060 std::string(binDir).append("/").append(fileName);
1061 if (cmSystemTools::FileIsSymlink(fullPath)) {
1062 cmSystemTools::RemoveFile(fullPath);
1063 } else if (cmSystemTools::FileIsDirectory(fullPath)) {
1064 this->CleanupFiles(fullPath);
1065 cmSystemTools::RemoveADirectory(fullPath);
1068 // Sometimes anti-virus software hangs on to new files so we
1069 // cannot delete them immediately. Try a few times.
1070 cmSystemTools::WindowsFileRetry retry =
1071 cmSystemTools::GetWindowsFileRetry();
1072 cmsys::Status status;
1073 while (!((status = cmSystemTools::RemoveFile(fullPath))) &&
1074 --retry.Count && cmSystemTools::FileExists(fullPath)) {
1075 cmSystemTools::Delay(retry.Delay);
1077 if (retry.Count == 0)
1079 cmsys::Status status = cmSystemTools::RemoveFile(fullPath);
1083 this->Makefile->IssueMessage(
1084 MessageType::FATAL_ERROR,
1085 cmStrCat("The file:\n ", fullPath,
1086 "\ncould not be removed:\n ", status.GetString()));
1094 void cmCoreTryCompile::FindOutputFile(const std::string& targetName,
1095 cmStateEnums::TargetType targetType)
1097 this->FindErrorMessage.clear();
1098 this->OutputFile.clear();
1099 std::string tmpOutputFile = "/";
1100 if (targetType == cmStateEnums::EXECUTABLE) {
1101 tmpOutputFile += targetName;
1103 this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
1104 } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
1107 this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX");
1108 tmpOutputFile += targetName;
1110 this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX");
1113 // a list of directories where to search for the compilation result
1114 // at first directly in the binary dir
1115 std::vector<std::string> searchDirs;
1116 searchDirs.emplace_back();
1119 this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
1120 // if a config was specified try that first
1121 if (cmNonempty(config)) {
1122 std::string tmp = cmStrCat('/', *config);
1123 searchDirs.emplace_back(std::move(tmp));
1125 searchDirs.emplace_back("/Debug");
1126 #if defined(__APPLE__)
1127 std::string app = "/" + targetName + ".app";
1128 if (cmNonempty(config)) {
1129 std::string tmp = cmStrCat('/', *config, app);
1130 searchDirs.emplace_back(std::move(tmp));
1132 std::string tmp = "/Debug" + app;
1133 searchDirs.emplace_back(std::move(tmp));
1134 searchDirs.emplace_back(std::move(app));
1136 searchDirs.emplace_back("/Development");
1138 for (std::string const& sdir : searchDirs) {
1139 std::string command = cmStrCat(this->BinaryDirectory, sdir, tmpOutputFile);
1140 if (cmSystemTools::FileExists(command)) {
1141 this->OutputFile = cmSystemTools::CollapseFullPath(command);
1146 std::ostringstream emsg;
1147 emsg << "Unable to find the executable at any of:\n";
1148 emsg << cmWrap(" " + this->BinaryDirectory, searchDirs, tmpOutputFile, "\n")
1150 this->FindErrorMessage = emsg.str();