Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Source / cmCoreTryCompile.cxx
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"
4
5 #include <array>
6 #include <cstdio>
7 #include <cstring>
8 #include <set>
9 #include <sstream>
10 #include <utility>
11
12 #include <cm/string_view>
13 #include <cmext/string_view>
14
15 #include "cmsys/Directory.hxx"
16 #include "cmsys/FStream.hxx"
17
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"
25 #include "cmRange.h"
26 #include "cmState.h"
27 #include "cmStringAlgorithms.h"
28 #include "cmSystemTools.h"
29 #include "cmTarget.h"
30 #include "cmValue.h"
31 #include "cmVersion.h"
32 #include "cmake.h"
33
34 namespace {
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", "" }
46
47 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
48 SETUP_LANGUAGE(c_properties, C);
49 // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
50 SETUP_LANGUAGE(cxx_properties, CXX);
51
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);
66 #undef SETUP_LANGUAGE
67
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";
100
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",
105   "GHS_OS_DIR_OPTION"
106 };
107 using Arguments = cmCoreTryCompile::Arguments;
108
109 ArgumentParser::Continue TryCompileLangProp(Arguments& args,
110                                             cm::string_view key,
111                                             cm::string_view val)
112 {
113   args.LangProps[std::string(key)] = std::string(val);
114   return ArgumentParser::Continue::No;
115 }
116
117 ArgumentParser::Continue TryCompileCompileDefs(Arguments& args,
118                                                cm::string_view val)
119 {
120   cmExpandList(val, args.CompileDefs);
121   return ArgumentParser::Continue::Yes;
122 }
123
124 cmArgumentParser<Arguments> makeTryCompileParser(
125   const cmArgumentParser<Arguments>& base)
126 {
127   return cmArgumentParser<Arguments>{ base }.Bind("OUTPUT_VARIABLE"_s,
128                                                   &Arguments::OutputVariable);
129 }
130
131 cmArgumentParser<Arguments> makeTryRunParser(
132   const cmArgumentParser<Arguments>& base)
133 {
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 */;
142 }
143
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)
148
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 */;
156
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)
166     .BIND_LANG_PROPS(C)
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 */;
173
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 */;
180
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 */;
188
189 auto const TryCompileProjectArgParser =
190   makeTryCompileParser(TryCompileBaseProjectArgParser);
191
192 auto const TryCompileSourcesArgParser =
193   makeTryCompileParser(TryCompileBaseNewSourcesArgParser);
194
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 */;
202
203 auto const TryRunSourcesArgParser =
204   makeTryRunParser(TryCompileBaseNewSourcesArgParser);
205
206 auto const TryRunOldArgParser = makeTryRunParser(TryCompileOldArgParser);
207
208 #undef BIND_LANG_PROPS
209 }
210
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)
215 {
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, "\"");
222     }
223     this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
224   }
225   return arguments;
226 }
227
228 Arguments cmCoreTryCompile::ParseArgs(
229   cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun)
230 {
231   std::vector<std::string> unparsedArguments;
232   const auto& second = *(++args.begin());
233
234   if (!isTryRun && second == "PROJECT") {
235     // New PROJECT signature (try_compile only).
236     auto arguments =
237       this->ParseArgs(args, TryCompileProjectArgParser, unparsedArguments);
238     if (!arguments.BinaryDirectory) {
239       arguments.BinaryDirectory = unique_binary_directory;
240     }
241     return arguments;
242   }
243
244   if (cmHasLiteralPrefix(second, "SOURCE")) {
245     // New SOURCES signature.
246     auto arguments = this->ParseArgs(
247       args, isTryRun ? TryRunSourcesArgParser : TryCompileSourcesArgParser,
248       unparsedArguments);
249     arguments.BinaryDirectory = unique_binary_directory;
250     return arguments;
251   }
252
253   // Old signature.
254   auto arguments = this->ParseArgs(
255     args, isTryRun ? TryRunOldArgParser : TryCompileOldArgParser,
256     unparsedArguments);
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;
261   }
262   if (isTryRun) {
263     if (arguments.CompileOutputVariable &&
264         arguments.CompileOutputVariable->empty()) {
265       arguments.CompileOutputVariable = cm::nullopt;
266     }
267     if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) {
268       arguments.RunOutputVariable = cm::nullopt;
269     }
270     if (arguments.RunOutputStdOutVariable &&
271         arguments.RunOutputStdOutVariable->empty()) {
272       arguments.RunOutputStdOutVariable = cm::nullopt;
273     }
274     if (arguments.RunOutputStdErrVariable &&
275         arguments.RunOutputStdErrVariable->empty()) {
276       arguments.RunOutputStdErrVariable = cm::nullopt;
277     }
278     if (arguments.RunWorkingDirectory &&
279         arguments.RunWorkingDirectory->empty()) {
280       arguments.RunWorkingDirectory = cm::nullopt;
281     }
282   }
283   return arguments;
284 }
285
286 bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
287                                       cmStateEnums::TargetType targetType)
288 {
289   this->OutputFile.clear();
290   // which signature were we called with ?
291   this->SrcFileSignature = true;
292
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.");
303       return false;
304     }
305     sourceDirectory = *arguments.SourceDirectoryOrFile;
306     projectName = *arguments.ProjectName;
307     if (arguments.TargetName) {
308       targetName = *arguments.TargetName;
309     }
310   } else {
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;
318   }
319
320   if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) {
321     this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
322                                  "No <bindir> specified.");
323     return false;
324   }
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;
330   } else {
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, "'"));
336       return false;
337     }
338     this->BinaryDirectory = *arguments.BinaryDirectory;
339     // compute the binary dir when TRY_COMPILE is called with a src file
340     // signature
341     if (this->SrcFileSignature) {
342       this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
343     }
344   }
345
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:
355             break;
356           case cmStateEnums::EXECUTABLE:
357             if (tgt->IsExecutableWithExports()) {
358               break;
359             }
360             CM_FALLTHROUGH;
361           default:
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()), "."));
368             return false;
369         }
370         if (tgt->IsImported()) {
371           targets.emplace_back(i);
372         }
373       }
374     }
375   }
376
377   if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) {
378     this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
379                                  "COPY_FILE must be followed by a file path");
380     return false;
381   }
382
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");
387     return false;
388   }
389
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");
394     return false;
395   }
396
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");
401     return false;
402   }
403
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");
410       return false;
411     }
412     if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) {
413       this->Makefile->IssueMessage(
414         MessageType::FATAL_ERROR,
415         "SOURCE_FROM_VAR requires exactly two arguments");
416       return false;
417     }
418     if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) {
419       this->Makefile->IssueMessage(
420         MessageType::FATAL_ERROR,
421         "SOURCE_FROM_FILE requires exactly two arguments");
422       return false;
423     }
424   } else {
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"));
431       return false;
432     }
433     if (!arguments.CompileDefs.empty()) {
434       this->Makefile->IssueMessage(
435         MessageType::FATAL_ERROR,
436         "COMPILE_DEFINITIONS allowed only in source file signature");
437       return false;
438     }
439     if (arguments.CopyFileTo) {
440       this->Makefile->IssueMessage(
441         MessageType::FATAL_ERROR,
442         "COPY_FILE allowed only in source file signature");
443       return false;
444     }
445   }
446
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);
453   } else {
454     cmSystemTools::MakeDirectory(this->BinaryDirectory);
455   }
456
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());
463     return false;
464   }
465
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);
472
473     // Choose sources.
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);
479     }
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");
486         if (out.empty()) {
487           return false;
488         }
489         sources.emplace_back(std::move(out));
490       }
491     }
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");
499         if (out.empty()) {
500           return false;
501         }
502         sources.emplace_back(std::move(out));
503       }
504     }
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];
510
511         if (!cmSystemTools::GetFilenamePath(dst).empty()) {
512           const auto& msg =
513             cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\"");
514           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
515           return false;
516         }
517
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);
524           return false;
525         }
526
527         sources.emplace_back(std::move(dstPath));
528       }
529     }
530     // TODO: ensure sources is not empty
531
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());
538       if (!lang.empty()) {
539         testLangs.insert(lang);
540       } else {
541         std::ostringstream err;
542         err << "Unknown extension \"" << ext << "\" for file\n"
543             << "  " << si << "\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());
551         return false;
552       }
553     }
554
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;
559     }
560
561     std::string const tcConfig =
562       this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
563
564     // we need to create a directory and CMakeLists file etc...
565     // first create the directories
566     sourceDirectory = this->BinaryDirectory;
567
568     // now create a CMakeLists.txt file in that directory
569     FILE* fout = cmsys::SystemTools::Fopen(outFileName, "w");
570     if (!fout) {
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());
578       return false;
579     }
580
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());
585     if (def) {
586       fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def->c_str());
587     }
588
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");
594     }
595
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");
601     }
602
603     /* Set CUDA architectures policy to match outer project.  */
604     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0104) !=
605           cmPolicies::NEW &&
606         testLangs.find("CUDA") != testLangs.end() &&
607         this->Makefile->GetSafeDefinition(kCMAKE_CUDA_ARCHITECTURES).empty()) {
608       fprintf(fout, "cmake_policy(SET CMP0104 OLD)\n");
609     }
610
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");
616     }
617
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");
624     }
625
626     /* Set cache/normal variable policy to match outer project.
627        It may affect toolchain files.  */
628     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
629         cmPolicies::NEW) {
630       fprintf(fout, "cmake_policy(SET CMP0126 OLD)\n");
631     }
632
633     /* Set language extensions policy to match outer project.  */
634     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0128) !=
635         cmPolicies::NEW) {
636       fprintf(fout, "cmake_policy(SET CMP0128 OLD)\n");
637     }
638
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());
652       }
653     }
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.
660       fprintf(fout,
661               "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n");
662
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");
670       }
671     }
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());
678       fprintf(fout,
679               "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
680               " ${COMPILE_DEFINITIONS}\")\n",
681               li.c_str(), li.c_str());
682     }
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."
693             ;
694           /* clang-format on */
695           this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
696         }
697         CM_FALLTHROUGH;
698       case cmPolicies::OLD:
699         // OLD behavior is to do nothing.
700         break;
701       case cmPolicies::REQUIRED_IF_USED:
702       case cmPolicies::REQUIRED_ALWAYS:
703         this->Makefile->IssueMessage(
704           MessageType::FATAL_ERROR,
705           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066));
706         CM_FALLTHROUGH;
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());
718         }
719       } break;
720     }
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."
731             ;
732           /* clang-format on */
733           this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
734         }
735         CM_FALLTHROUGH;
736       case cmPolicies::OLD:
737         // OLD behavior is to do nothing.
738         break;
739       case cmPolicies::REQUIRED_IF_USED:
740       case cmPolicies::REQUIRED_ALWAYS:
741         this->Makefile->IssueMessage(
742           MessageType::FATAL_ERROR,
743           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0056));
744         CM_FALLTHROUGH;
745       case cmPolicies::NEW:
746         // NEW behavior is to pass linker flags.
747         {
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());
752         }
753         break;
754     }
755     fprintf(fout,
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());
766     }
767
768     if (!targets.empty()) {
769       std::string fname = "/" + std::string(targetName) + "Targets.cmake";
770       cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
771                                            testLangs);
772       tcfg.SetExportFile((this->BinaryDirectory + fname).c_str());
773       tcfg.SetConfig(tcConfig);
774
775       if (!tcfg.GenerateImportFile()) {
776         this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
777                                      "could not write export file.");
778         fclose(fout);
779         return false;
780       }
781       fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
782               fname.c_str());
783     }
784
785     /* Set the appropriate policy information for ENABLE_EXPORTS */
786     fprintf(fout, "cmake_policy(SET CMP0065 %s)\n",
787             this->Makefile->GetPolicyStatus(cmPolicies::CMP0065) ==
788                 cmPolicies::NEW
789               ? "NEW"
790               : "OLD");
791
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) ==
795                 cmPolicies::NEW
796               ? "NEW"
797               : "OLD");
798
799     // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
800     // adding that flag in the platform and compiler language files
801     fprintf(fout,
802             "include(\"${CMAKE_ROOT}/Modules/Internal/"
803             "HeaderpadWorkaround.cmake\")\n");
804
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)
812     {
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());
818     }
819     for (std::string const& si : sources) {
820       fprintf(fout, " \"%s\"", si.c_str());
821
822       // Add dependencies on any non-temporary sources.
823       if (!IsTemporary(si)) {
824         this->Makefile->AddCMakeDependFile(si);
825       }
826     }
827     fprintf(fout, ")\n");
828
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>>";
833     }
834     fprintf(fout,
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());
839
840     bool warnCMP0067 = false;
841     bool honorStandard = true;
842
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");
848           CM_FALLTHROUGH;
849         case cmPolicies::OLD:
850           // OLD behavior is to not honor the language standard variables.
851           honorStandard = false;
852           break;
853         case cmPolicies::REQUIRED_IF_USED:
854         case cmPolicies::REQUIRED_ALWAYS:
855           this->Makefile->IssueMessage(
856             MessageType::FATAL_ERROR,
857             cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
858           break;
859         case cmPolicies::NEW:
860           // NEW behavior is to honor the language standard variables.
861           // We already initialized honorStandard to true.
862           break;
863       }
864     }
865
866     std::vector<std::string> warnCMP0067Variables;
867
868     if (honorStandard || warnCMP0067) {
869       static std::array<std::string, 6> const possibleLangs{
870         { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" }
871       };
872       static std::array<cm::string_view, 3> const langPropSuffixes{
873         { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s }
874       };
875       for (std::string const& lang : possibleLangs) {
876         if (testLangs.find(lang) == testLangs.end()) {
877           continue;
878         }
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()) {
885               value.clear();
886               warnCMP0067Variables.emplace_back(langPropVar);
887             }
888             if (!value.empty()) {
889               arguments.LangProps[langProp] = value;
890             }
891           }
892         }
893       }
894     }
895
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"
902         ;
903       /* clang-format on */
904       for (std::string const& vi : warnCMP0067Variables) {
905         w << "  " << vi << "\n";
906       }
907       this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
908     }
909
910     for (auto const& p : arguments.LangProps) {
911       if (p.second.empty()) {
912         continue;
913       }
914       fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
915               targetName.c_str(),
916               cmOutputConverter::EscapeForCMake(p.first).c_str(),
917               cmOutputConverter::EscapeForCMake(p.second).c_str());
918     }
919
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));
925       }
926
927       if (targetType == cmStateEnums::STATIC_LIBRARY) {
928         fprintf(fout,
929                 "set_property(TARGET %s PROPERTY STATIC_LIBRARY_OPTIONS %s)\n",
930                 targetName.c_str(), cmJoin(options, " ").c_str());
931       } else {
932         fprintf(fout, "target_link_options(%s PRIVATE %s)\n",
933                 targetName.c_str(), cmJoin(options, " ").c_str());
934       }
935     }
936
937     if (arguments.LinkLibraries) {
938       std::string libsToLink = " ";
939       for (std::string const& i : *arguments.LinkLibraries) {
940         libsToLink += "\"" + cmTrimWhitespace(i) + "\" ";
941       }
942       fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
943               libsToLink.c_str());
944     } else {
945       fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
946               targetName.c_str());
947     }
948     fclose(fout);
949   }
950
951   // Forward a set of variables to the inner project cache.
952   if ((this->SrcFileSignature ||
953        this->Makefile->GetPolicyStatus(cmPolicies::CMP0137) ==
954          cmPolicies::NEW) &&
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);
996
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());
1001     }
1002
1003     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
1004         cmPolicies::NEW) {
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]);
1025     }
1026
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.
1033        */
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));
1039     }
1040
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));
1045       }
1046     }
1047   }
1048
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));
1055       }
1056     }
1057   }
1058
1059   if (this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
1060     auto msg =
1061       cmStrCat("Executing try_compile (", *arguments.CompileResultVariable,
1062                ") in:\n  ", this->BinaryDirectory);
1063     this->Makefile->IssueMessage(MessageType::LOG, msg);
1064   }
1065
1066   bool erroroc = cmSystemTools::GetErrorOccurredFlag();
1067   cmSystemTools::ResetErrorOccurredFlag();
1068   std::string output;
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);
1074   if (erroroc) {
1075     cmSystemTools::SetErrorOccurred();
1076   }
1077
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"));
1082   } else {
1083     this->Makefile->AddCacheDefinition(
1084       *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"),
1085       "Result of TRY_COMPILE", cmStateEnums::INTERNAL);
1086   }
1087
1088   if (arguments.OutputVariable) {
1089     this->Makefile->AddDefinition(*arguments.OutputVariable, output);
1090   }
1091
1092   if (this->SrcFileSignature) {
1093     std::string copyFileErrorMessage;
1094     this->FindOutputFile(targetName);
1095
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;
1109         }
1110         if (!arguments.CopyFileError) {
1111           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str());
1112           return false;
1113         }
1114         copyFileErrorMessage = emsg.str();
1115       }
1116     }
1117
1118     if (arguments.CopyFileError) {
1119       std::string const& copyFileError = *arguments.CopyFileError;
1120       this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
1121     }
1122   }
1123   return res == 0;
1124 }
1125
1126 bool cmCoreTryCompile::IsTemporary(std::string const& path)
1127 {
1128   return ((path.find("CMakeTmp") != std::string::npos) ||
1129           (path.find("CMakeScratch") != std::string::npos));
1130 }
1131
1132 void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
1133 {
1134   if (binDir.empty()) {
1135     return;
1136   }
1137
1138   if (!IsTemporary(binDir)) {
1139     cmSystemTools::Error(
1140       "TRY_COMPILE attempt to remove -rf directory that does not contain "
1141       "CMakeTmp or CMakeScratch: \"" +
1142       binDir + "\"");
1143     return;
1144   }
1145
1146   cmsys::Directory dir;
1147   dir.Load(binDir);
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);
1162         } else {
1163 #ifdef _WIN32
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);
1172           }
1173           if (retry.Count == 0)
1174 #else
1175           cmsys::Status status = cmSystemTools::RemoveFile(fullPath);
1176           if (!status)
1177 #endif
1178           {
1179             this->Makefile->IssueMessage(
1180               MessageType::FATAL_ERROR,
1181               cmStrCat("The file:\n  ", fullPath,
1182                        "\ncould not be removed:\n  ", status.GetString()));
1183           }
1184         }
1185       }
1186     }
1187   }
1188
1189   if (binDir.find("CMakeScratch") != std::string::npos) {
1190     cmSystemTools::RemoveADirectory(binDir);
1191   }
1192 }
1193
1194 void cmCoreTryCompile::FindOutputFile(const std::string& targetName)
1195 {
1196   this->FindErrorMessage.clear();
1197   this->OutputFile.clear();
1198   std::string tmpOutputFile = "/";
1199   tmpOutputFile += targetName;
1200
1201   if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
1202     tmpOutputFile += "_DEBUG";
1203   }
1204   tmpOutputFile += "_loc";
1205
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();
1212     return;
1213   }
1214
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();
1223     return;
1224   }
1225
1226   this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation);
1227 }
1228
1229 std::string cmCoreTryCompile::WriteSource(std::string const& filename,
1230                                           std::string const& content,
1231                                           char const* command) const
1232 {
1233   if (!cmSystemTools::GetFilenamePath(filename).empty()) {
1234     const auto& msg =
1235       cmStrCat(command, " given invalid filename \"", filename, "\"");
1236     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1237     return {};
1238   }
1239
1240   auto filepath = cmStrCat(this->BinaryDirectory, "/", filename);
1241   cmsys::ofstream file{ filepath.c_str(), std::ios::out };
1242   if (!file) {
1243     const auto& msg =
1244       cmStrCat(command, " failed to open \"", filename, "\" for writing");
1245     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1246     return {};
1247   }
1248
1249   file << content;
1250   if (!file) {
1251     const auto& msg = cmStrCat(command, " failed to write \"", filename, "\"");
1252     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1253     return {};
1254   }
1255
1256   file.close();
1257   return filepath;
1258 }