resolve cyclic dependency with zstd
[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 std::string const TryCompileDefaultConfig = "DEBUG";
211 }
212
213 Arguments cmCoreTryCompile::ParseArgs(
214   const cmRange<std::vector<std::string>::const_iterator>& args,
215   const cmArgumentParser<Arguments>& parser,
216   std::vector<std::string>& unparsedArguments)
217 {
218   auto arguments = parser.Parse(args, &unparsedArguments, 0);
219   if (!arguments.MaybeReportError(*(this->Makefile)) &&
220       !unparsedArguments.empty()) {
221     std::string m = "Unknown arguments:";
222     for (const auto& i : unparsedArguments) {
223       m = cmStrCat(m, "\n  \"", i, "\"");
224     }
225     this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
226   }
227   return arguments;
228 }
229
230 Arguments cmCoreTryCompile::ParseArgs(
231   cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun)
232 {
233   std::vector<std::string> unparsedArguments;
234   const auto& second = *(++args.begin());
235
236   if (!isTryRun && second == "PROJECT") {
237     // New PROJECT signature (try_compile only).
238     auto arguments =
239       this->ParseArgs(args, TryCompileProjectArgParser, unparsedArguments);
240     if (!arguments.BinaryDirectory) {
241       arguments.BinaryDirectory = unique_binary_directory;
242     }
243     return arguments;
244   }
245
246   if (cmHasLiteralPrefix(second, "SOURCE")) {
247     // New SOURCES signature.
248     auto arguments = this->ParseArgs(
249       args, isTryRun ? TryRunSourcesArgParser : TryCompileSourcesArgParser,
250       unparsedArguments);
251     arguments.BinaryDirectory = unique_binary_directory;
252     return arguments;
253   }
254
255   // Old signature.
256   auto arguments = this->ParseArgs(
257     args, isTryRun ? TryRunOldArgParser : TryCompileOldArgParser,
258     unparsedArguments);
259   // For historical reasons, treat some empty-valued keyword
260   // arguments as if they were not specified at all.
261   if (arguments.OutputVariable && arguments.OutputVariable->empty()) {
262     arguments.OutputVariable = cm::nullopt;
263   }
264   if (isTryRun) {
265     if (arguments.CompileOutputVariable &&
266         arguments.CompileOutputVariable->empty()) {
267       arguments.CompileOutputVariable = cm::nullopt;
268     }
269     if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) {
270       arguments.RunOutputVariable = cm::nullopt;
271     }
272     if (arguments.RunOutputStdOutVariable &&
273         arguments.RunOutputStdOutVariable->empty()) {
274       arguments.RunOutputStdOutVariable = cm::nullopt;
275     }
276     if (arguments.RunOutputStdErrVariable &&
277         arguments.RunOutputStdErrVariable->empty()) {
278       arguments.RunOutputStdErrVariable = cm::nullopt;
279     }
280     if (arguments.RunWorkingDirectory &&
281         arguments.RunWorkingDirectory->empty()) {
282       arguments.RunWorkingDirectory = cm::nullopt;
283     }
284   }
285   return arguments;
286 }
287
288 bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
289                                       cmStateEnums::TargetType targetType)
290 {
291   this->OutputFile.clear();
292   // which signature were we called with ?
293   this->SrcFileSignature = true;
294
295   bool useUniqueBinaryDirectory = false;
296   std::string sourceDirectory;
297   std::string projectName;
298   std::string targetName;
299   if (arguments.ProjectName) {
300     this->SrcFileSignature = false;
301     if (!arguments.SourceDirectoryOrFile ||
302         arguments.SourceDirectoryOrFile->empty()) {
303       this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
304                                    "No <srcdir> specified.");
305       return false;
306     }
307     sourceDirectory = *arguments.SourceDirectoryOrFile;
308     projectName = *arguments.ProjectName;
309     if (arguments.TargetName) {
310       targetName = *arguments.TargetName;
311     }
312   } else {
313     projectName = "CMAKE_TRY_COMPILE";
314     /* Use a random file name to avoid rapid creation and deletion
315        of the same executable name (some filesystems fail on that).  */
316     char targetNameBuf[64];
317     snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
318              cmSystemTools::RandomSeed() & 0xFFFFF);
319     targetName = targetNameBuf;
320   }
321
322   if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) {
323     this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
324                                  "No <bindir> specified.");
325     return false;
326   }
327   if (*arguments.BinaryDirectory == unique_binary_directory) {
328     // leave empty until we're ready to create it, so we don't try to remove
329     // a non-existing directory if we abort due to e.g. bad arguments
330     this->BinaryDirectory.clear();
331     useUniqueBinaryDirectory = true;
332   } else {
333     if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) {
334       this->Makefile->IssueMessage(
335         MessageType::FATAL_ERROR,
336         cmStrCat("<bindir> is not an absolute path:\n '",
337                  *arguments.BinaryDirectory, "'"));
338       return false;
339     }
340     this->BinaryDirectory = *arguments.BinaryDirectory;
341     // compute the binary dir when TRY_COMPILE is called with a src file
342     // signature
343     if (this->SrcFileSignature) {
344       this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
345     }
346   }
347
348   std::vector<std::string> targets;
349   if (arguments.LinkLibraries) {
350     for (std::string const& i : *arguments.LinkLibraries) {
351       if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) {
352         switch (tgt->GetType()) {
353           case cmStateEnums::SHARED_LIBRARY:
354           case cmStateEnums::STATIC_LIBRARY:
355           case cmStateEnums::INTERFACE_LIBRARY:
356           case cmStateEnums::UNKNOWN_LIBRARY:
357             break;
358           case cmStateEnums::EXECUTABLE:
359             if (tgt->IsExecutableWithExports()) {
360               break;
361             }
362             CM_FALLTHROUGH;
363           default:
364             this->Makefile->IssueMessage(
365               MessageType::FATAL_ERROR,
366               cmStrCat("Only libraries may be used as try_compile or try_run "
367                        "IMPORTED LINK_LIBRARIES.  Got ",
368                        tgt->GetName(), " of type ",
369                        cmState::GetTargetTypeName(tgt->GetType()), "."));
370             return false;
371         }
372         if (tgt->IsImported()) {
373           targets.emplace_back(i);
374         }
375       }
376     }
377   }
378
379   if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) {
380     this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
381                                  "COPY_FILE must be followed by a file path");
382     return false;
383   }
384
385   if (arguments.CopyFileError && arguments.CopyFileError->empty()) {
386     this->Makefile->IssueMessage(
387       MessageType::FATAL_ERROR,
388       "COPY_FILE_ERROR must be followed by a variable name");
389     return false;
390   }
391
392   if (arguments.CopyFileError && !arguments.CopyFileTo) {
393     this->Makefile->IssueMessage(
394       MessageType::FATAL_ERROR,
395       "COPY_FILE_ERROR may be used only with COPY_FILE");
396     return false;
397   }
398
399   if (arguments.Sources && arguments.Sources->empty()) {
400     this->Makefile->IssueMessage(
401       MessageType::FATAL_ERROR,
402       "SOURCES must be followed by at least one source file");
403     return false;
404   }
405
406   if (this->SrcFileSignature) {
407     if (arguments.SourceFromContent &&
408         arguments.SourceFromContent->size() % 2) {
409       this->Makefile->IssueMessage(
410         MessageType::FATAL_ERROR,
411         "SOURCE_FROM_CONTENT requires exactly two arguments");
412       return false;
413     }
414     if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) {
415       this->Makefile->IssueMessage(
416         MessageType::FATAL_ERROR,
417         "SOURCE_FROM_VAR requires exactly two arguments");
418       return false;
419     }
420     if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) {
421       this->Makefile->IssueMessage(
422         MessageType::FATAL_ERROR,
423         "SOURCE_FROM_FILE requires exactly two arguments");
424       return false;
425     }
426   } else {
427     // only valid for srcfile signatures
428     if (!arguments.LangProps.empty()) {
429       this->Makefile->IssueMessage(
430         MessageType::FATAL_ERROR,
431         cmStrCat(arguments.LangProps.begin()->first,
432                  " allowed only in source file signature"));
433       return false;
434     }
435     if (!arguments.CompileDefs.empty()) {
436       this->Makefile->IssueMessage(
437         MessageType::FATAL_ERROR,
438         "COMPILE_DEFINITIONS allowed only in source file signature");
439       return false;
440     }
441     if (arguments.CopyFileTo) {
442       this->Makefile->IssueMessage(
443         MessageType::FATAL_ERROR,
444         "COPY_FILE allowed only in source file signature");
445       return false;
446     }
447   }
448
449   // make sure the binary directory exists
450   if (useUniqueBinaryDirectory) {
451     this->BinaryDirectory =
452       cmStrCat(this->Makefile->GetHomeOutputDirectory(),
453                "/CMakeFiles/CMakeScratch/TryCompile-XXXXXX");
454     cmSystemTools::MakeTempDirectory(this->BinaryDirectory);
455   } else {
456     cmSystemTools::MakeDirectory(this->BinaryDirectory);
457   }
458
459   // do not allow recursive try Compiles
460   if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) {
461     std::ostringstream e;
462     e << "Attempt at a recursive or nested TRY_COMPILE in directory\n"
463       << "  " << this->BinaryDirectory << "\n";
464     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
465     return false;
466   }
467
468   std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
469   // which signature are we using? If we are using var srcfile bindir
470   if (this->SrcFileSignature) {
471     // remove any CMakeCache.txt files so we will have a clean test
472     std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
473     cmSystemTools::RemoveFile(ccFile);
474
475     // Choose sources.
476     std::vector<std::string> sources;
477     if (arguments.Sources) {
478       sources = std::move(*arguments.Sources);
479     } else if (arguments.SourceDirectoryOrFile) {
480       sources.emplace_back(*arguments.SourceDirectoryOrFile);
481     }
482     if (arguments.SourceFromContent) {
483       auto const k = arguments.SourceFromContent->size();
484       for (auto i = decltype(k){ 0 }; i < k; i += 2) {
485         const auto& name = (*arguments.SourceFromContent)[i + 0];
486         const auto& content = (*arguments.SourceFromContent)[i + 1];
487         auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT");
488         if (out.empty()) {
489           return false;
490         }
491         sources.emplace_back(std::move(out));
492       }
493     }
494     if (arguments.SourceFromVar) {
495       auto const k = arguments.SourceFromVar->size();
496       for (auto i = decltype(k){ 0 }; i < k; i += 2) {
497         const auto& name = (*arguments.SourceFromVar)[i + 0];
498         const auto& var = (*arguments.SourceFromVar)[i + 1];
499         const auto& content = this->Makefile->GetDefinition(var);
500         auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR");
501         if (out.empty()) {
502           return false;
503         }
504         sources.emplace_back(std::move(out));
505       }
506     }
507     if (arguments.SourceFromFile) {
508       auto const k = arguments.SourceFromFile->size();
509       for (auto i = decltype(k){ 0 }; i < k; i += 2) {
510         const auto& dst = (*arguments.SourceFromFile)[i + 0];
511         const auto& src = (*arguments.SourceFromFile)[i + 1];
512
513         if (!cmSystemTools::GetFilenamePath(dst).empty()) {
514           const auto& msg =
515             cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\"");
516           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
517           return false;
518         }
519
520         auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst);
521         auto const result = cmSystemTools::CopyFileAlways(src, dstPath);
522         if (!result.IsSuccess()) {
523           const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src,
524                                      "\": ", result.GetString());
525           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
526           return false;
527         }
528
529         sources.emplace_back(std::move(dstPath));
530       }
531     }
532     // TODO: ensure sources is not empty
533
534     // Detect languages to enable.
535     cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
536     std::set<std::string> testLangs;
537     for (std::string const& si : sources) {
538       std::string ext = cmSystemTools::GetFilenameLastExtension(si);
539       std::string lang = gg->GetLanguageFromExtension(ext.c_str());
540       if (!lang.empty()) {
541         testLangs.insert(lang);
542       } else {
543         std::ostringstream err;
544         err << "Unknown extension \"" << ext << "\" for file\n"
545             << "  " << si << "\n"
546             << "try_compile() works only for enabled languages.  "
547             << "Currently these are:\n  ";
548         std::vector<std::string> langs;
549         gg->GetEnabledLanguages(langs);
550         err << cmJoin(langs, " ");
551         err << "\nSee project() command to enable other languages.";
552         this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str());
553         return false;
554       }
555     }
556
557     // when the only language is ISPC we know that the output
558     // type must by a static library
559     if (testLangs.size() == 1 && testLangs.count("ISPC") == 1) {
560       targetType = cmStateEnums::STATIC_LIBRARY;
561     }
562
563     std::string const tcConfig =
564       this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
565
566     // we need to create a directory and CMakeLists file etc...
567     // first create the directories
568     sourceDirectory = this->BinaryDirectory;
569
570     // now create a CMakeLists.txt file in that directory
571     FILE* fout = cmsys::SystemTools::Fopen(outFileName, "w");
572     if (!fout) {
573       std::ostringstream e;
574       /* clang-format off */
575       e << "Failed to open\n"
576         << "  " << outFileName << "\n"
577         << cmSystemTools::GetLastSystemError();
578       /* clang-format on */
579       this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
580       return false;
581     }
582
583     cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
584     fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
585             cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
586             cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion());
587     if (def) {
588       fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def->c_str());
589     }
590
591     /* Set MSVC runtime library policy to match our selection.  */
592     if (cmValue msvcRuntimeLibraryDefault =
593           this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
594       fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
595               !msvcRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
596     }
597
598     /* Set Watcom runtime library policy to match our selection.  */
599     if (cmValue watcomRuntimeLibraryDefault = this->Makefile->GetDefinition(
600           kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT)) {
601       fprintf(fout, "cmake_policy(SET CMP0136 %s)\n",
602               !watcomRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
603     }
604
605     /* Set CUDA architectures policy to match outer project.  */
606     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0104) !=
607           cmPolicies::NEW &&
608         testLangs.find("CUDA") != testLangs.end() &&
609         this->Makefile->GetSafeDefinition(kCMAKE_CUDA_ARCHITECTURES).empty()) {
610       fprintf(fout, "cmake_policy(SET CMP0104 OLD)\n");
611     }
612
613     /* Set ARMClang cpu/arch policy to match outer project.  */
614     if (cmValue cmp0123 =
615           this->Makefile->GetDefinition(kCMAKE_ARMClang_CMP0123)) {
616       fprintf(fout, "cmake_policy(SET CMP0123 %s)\n",
617               *cmp0123 == "NEW"_s ? "NEW" : "OLD");
618     }
619
620     /* Set MSVC debug information format policy to match our selection.  */
621     if (cmValue msvcDebugInformationFormatDefault =
622           this->Makefile->GetDefinition(
623             kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)) {
624       fprintf(fout, "cmake_policy(SET CMP0141 %s)\n",
625               !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD");
626     }
627
628     /* Set cache/normal variable policy to match outer project.
629        It may affect toolchain files.  */
630     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
631         cmPolicies::NEW) {
632       fprintf(fout, "cmake_policy(SET CMP0126 OLD)\n");
633     }
634
635     /* Set language extensions policy to match outer project.  */
636     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0128) !=
637         cmPolicies::NEW) {
638       fprintf(fout, "cmake_policy(SET CMP0128 OLD)\n");
639     }
640
641     std::string projectLangs;
642     for (std::string const& li : testLangs) {
643       projectLangs += " " + li;
644       std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
645       std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
646       if (cmValue rulesOverridePath =
647             this->Makefile->GetDefinition(rulesOverrideLang)) {
648         fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
649                 rulesOverridePath->c_str());
650       } else if (cmValue rulesOverridePath2 =
651                    this->Makefile->GetDefinition(rulesOverrideBase)) {
652         fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(),
653                 rulesOverridePath2->c_str());
654       }
655     }
656     fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str());
657     if (arguments.CMakeInternal == "ABI") {
658       // This is the ABI detection step, also used for implicit includes.
659       // Erase any include_directories() calls from the toolchain file so
660       // that we do not see them as implicit.  Our ABI detection source
661       // does not include any system headers anyway.
662       fprintf(fout,
663               "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n");
664
665       // The link and compile lines for ABI detection step need to not use
666       // response files so we can extract implicit includes given to
667       // the underlying host compiler
668       if (testLangs.find("CUDA") != testLangs.end()) {
669         fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n");
670         fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n");
671         fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n");
672       }
673     }
674     fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
675     for (std::string const& li : testLangs) {
676       std::string langFlags = "CMAKE_" + li + "_FLAGS";
677       cmValue flags = this->Makefile->GetDefinition(langFlags);
678       fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
679               cmOutputConverter::EscapeForCMake(*flags).c_str());
680       fprintf(fout,
681               "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
682               " ${COMPILE_DEFINITIONS}\")\n",
683               li.c_str(), li.c_str());
684     }
685     switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0066)) {
686       case cmPolicies::WARN:
687         if (this->Makefile->PolicyOptionalWarningEnabled(
688               "CMAKE_POLICY_WARNING_CMP0066")) {
689           std::ostringstream w;
690           /* clang-format off */
691           w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n"
692             "For compatibility with older versions of CMake, try_compile "
693             "is not honoring caller config-specific compiler flags "
694             "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project."
695             ;
696           /* clang-format on */
697           this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
698         }
699         CM_FALLTHROUGH;
700       case cmPolicies::OLD:
701         // OLD behavior is to do nothing.
702         break;
703       case cmPolicies::REQUIRED_IF_USED:
704       case cmPolicies::REQUIRED_ALWAYS:
705         this->Makefile->IssueMessage(
706           MessageType::FATAL_ERROR,
707           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066));
708         CM_FALLTHROUGH;
709       case cmPolicies::NEW: {
710         // NEW behavior is to pass config-specific compiler flags.
711         std::string const cfg = !tcConfig.empty()
712           ? cmSystemTools::UpperCase(tcConfig)
713           : TryCompileDefaultConfig;
714         for (std::string const& li : testLangs) {
715           std::string const langFlagsCfg =
716             cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
717           cmValue flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
718           fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
719                   cmOutputConverter::EscapeForCMake(*flagsCfg).c_str());
720         }
721       } break;
722     }
723     switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0056)) {
724       case cmPolicies::WARN:
725         if (this->Makefile->PolicyOptionalWarningEnabled(
726               "CMAKE_POLICY_WARNING_CMP0056")) {
727           std::ostringstream w;
728           /* clang-format off */
729           w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0056) << "\n"
730             "For compatibility with older versions of CMake, try_compile "
731             "is not honoring caller link flags (e.g. CMAKE_EXE_LINKER_FLAGS) "
732             "in the test project."
733             ;
734           /* clang-format on */
735           this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
736         }
737         CM_FALLTHROUGH;
738       case cmPolicies::OLD:
739         // OLD behavior is to do nothing.
740         break;
741       case cmPolicies::REQUIRED_IF_USED:
742       case cmPolicies::REQUIRED_ALWAYS:
743         this->Makefile->IssueMessage(
744           MessageType::FATAL_ERROR,
745           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0056));
746         CM_FALLTHROUGH;
747       case cmPolicies::NEW:
748         // NEW behavior is to pass linker flags.
749         {
750           cmValue exeLinkFlags =
751             this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS");
752           fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n",
753                   cmOutputConverter::EscapeForCMake(*exeLinkFlags).c_str());
754         }
755         break;
756     }
757     fprintf(fout,
758             "set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}"
759             " ${EXE_LINKER_FLAGS}\")\n");
760     fprintf(fout, "include_directories(${INCLUDE_DIRECTORIES})\n");
761     fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n");
762     fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n");
763     // handle any compile flags we need to pass on
764     if (!arguments.CompileDefs.empty()) {
765       // Pass using bracket arguments to preserve content.
766       fprintf(fout, "add_definitions([==[%s]==])\n",
767               cmJoin(arguments.CompileDefs, "]==] [==[").c_str());
768     }
769
770     if (!targets.empty()) {
771       std::string fname = "/" + std::string(targetName) + "Targets.cmake";
772       cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
773                                            testLangs);
774       tcfg.SetExportFile((this->BinaryDirectory + fname).c_str());
775       tcfg.SetConfig(tcConfig);
776
777       if (!tcfg.GenerateImportFile()) {
778         this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
779                                      "could not write export file.");
780         fclose(fout);
781         return false;
782       }
783       fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
784               fname.c_str());
785     }
786
787     /* Set the appropriate policy information for ENABLE_EXPORTS */
788     fprintf(fout, "cmake_policy(SET CMP0065 %s)\n",
789             this->Makefile->GetPolicyStatus(cmPolicies::CMP0065) ==
790                 cmPolicies::NEW
791               ? "NEW"
792               : "OLD");
793
794     /* Set the appropriate policy information for PIE link flags */
795     fprintf(fout, "cmake_policy(SET CMP0083 %s)\n",
796             this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
797                 cmPolicies::NEW
798               ? "NEW"
799               : "OLD");
800
801     // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
802     // adding that flag in the platform and compiler language files
803     fprintf(fout,
804             "include(\"${CMAKE_ROOT}/Modules/Internal/"
805             "HeaderpadWorkaround.cmake\")\n");
806
807     if (targetType == cmStateEnums::EXECUTABLE) {
808       /* Put the executable at a known location (for COPY_FILE).  */
809       fprintf(fout, "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
810               this->BinaryDirectory.c_str());
811       /* Create the actual executable.  */
812       fprintf(fout, "add_executable(%s", targetName.c_str());
813     } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
814     {
815       /* Put the static library at a known location (for COPY_FILE).  */
816       fprintf(fout, "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"%s\")\n",
817               this->BinaryDirectory.c_str());
818       /* Create the actual static library.  */
819       fprintf(fout, "add_library(%s STATIC", targetName.c_str());
820     }
821     for (std::string const& si : sources) {
822       fprintf(fout, " \"%s\"", si.c_str());
823
824       // Add dependencies on any non-temporary sources.
825       if (!IsTemporary(si)) {
826         this->Makefile->AddCMakeDependFile(si);
827       }
828     }
829     fprintf(fout, ")\n");
830
831     /* Write out the output location of the target we are building */
832     std::string perConfigGenex;
833     if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
834       perConfigGenex = "_$<UPPER_CASE:$<CONFIG>>";
835     }
836     fprintf(fout,
837             "file(GENERATE OUTPUT "
838             "\"${CMAKE_BINARY_DIR}/%s%s_loc\"\n",
839             targetName.c_str(), perConfigGenex.c_str());
840     fprintf(fout, "     CONTENT $<TARGET_FILE:%s>)\n", targetName.c_str());
841
842     bool warnCMP0067 = false;
843     bool honorStandard = true;
844
845     if (arguments.LangProps.empty()) {
846       switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) {
847         case cmPolicies::WARN:
848           warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled(
849             "CMAKE_POLICY_WARNING_CMP0067");
850           CM_FALLTHROUGH;
851         case cmPolicies::OLD:
852           // OLD behavior is to not honor the language standard variables.
853           honorStandard = false;
854           break;
855         case cmPolicies::REQUIRED_IF_USED:
856         case cmPolicies::REQUIRED_ALWAYS:
857           this->Makefile->IssueMessage(
858             MessageType::FATAL_ERROR,
859             cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
860           break;
861         case cmPolicies::NEW:
862           // NEW behavior is to honor the language standard variables.
863           // We already initialized honorStandard to true.
864           break;
865       }
866     }
867
868     std::vector<std::string> warnCMP0067Variables;
869
870     if (honorStandard || warnCMP0067) {
871       static std::array<std::string, 6> const possibleLangs{
872         { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" }
873       };
874       static std::array<cm::string_view, 3> const langPropSuffixes{
875         { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s }
876       };
877       for (std::string const& lang : possibleLangs) {
878         if (testLangs.find(lang) == testLangs.end()) {
879           continue;
880         }
881         for (cm::string_view propSuffix : langPropSuffixes) {
882           std::string langProp = cmStrCat(lang, propSuffix);
883           if (!arguments.LangProps.count(langProp)) {
884             std::string langPropVar = cmStrCat("CMAKE_"_s, langProp);
885             std::string value = this->Makefile->GetSafeDefinition(langPropVar);
886             if (warnCMP0067 && !value.empty()) {
887               value.clear();
888               warnCMP0067Variables.emplace_back(langPropVar);
889             }
890             if (!value.empty()) {
891               arguments.LangProps[langProp] = value;
892             }
893           }
894         }
895       }
896     }
897
898     if (!warnCMP0067Variables.empty()) {
899       std::ostringstream w;
900       /* clang-format off */
901       w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n"
902         "For compatibility with older versions of CMake, try_compile "
903         "is not honoring language standard variables in the test project:\n"
904         ;
905       /* clang-format on */
906       for (std::string const& vi : warnCMP0067Variables) {
907         w << "  " << vi << "\n";
908       }
909       this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
910     }
911
912     for (auto const& p : arguments.LangProps) {
913       if (p.second.empty()) {
914         continue;
915       }
916       fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
917               targetName.c_str(),
918               cmOutputConverter::EscapeForCMake(p.first).c_str(),
919               cmOutputConverter::EscapeForCMake(p.second).c_str());
920     }
921
922     if (!arguments.LinkOptions.empty()) {
923       std::vector<std::string> options;
924       options.reserve(arguments.LinkOptions.size());
925       for (const auto& option : arguments.LinkOptions) {
926         options.emplace_back(cmOutputConverter::EscapeForCMake(option));
927       }
928
929       if (targetType == cmStateEnums::STATIC_LIBRARY) {
930         fprintf(fout,
931                 "set_property(TARGET %s PROPERTY STATIC_LIBRARY_OPTIONS %s)\n",
932                 targetName.c_str(), cmJoin(options, " ").c_str());
933       } else {
934         fprintf(fout, "target_link_options(%s PRIVATE %s)\n",
935                 targetName.c_str(), cmJoin(options, " ").c_str());
936       }
937     }
938
939     if (arguments.LinkLibraries) {
940       std::string libsToLink = " ";
941       for (std::string const& i : *arguments.LinkLibraries) {
942         libsToLink += "\"" + cmTrimWhitespace(i) + "\" ";
943       }
944       fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
945               libsToLink.c_str());
946     } else {
947       fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
948               targetName.c_str());
949     }
950     fclose(fout);
951   }
952
953   // Forward a set of variables to the inner project cache.
954   if ((this->SrcFileSignature ||
955        this->Makefile->GetPolicyStatus(cmPolicies::CMP0137) ==
956          cmPolicies::NEW) &&
957       !this->Makefile->IsOn("CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES")) {
958     std::set<std::string> vars;
959     vars.insert(&c_properties[lang_property_start],
960                 &c_properties[lang_property_start + lang_property_size]);
961     vars.insert(&cxx_properties[lang_property_start],
962                 &cxx_properties[lang_property_start + lang_property_size]);
963     vars.insert(&cuda_properties[lang_property_start],
964                 &cuda_properties[lang_property_start + lang_property_size]);
965     vars.insert(&fortran_properties[lang_property_start],
966                 &fortran_properties[lang_property_start + lang_property_size]);
967     vars.insert(&hip_properties[lang_property_start],
968                 &hip_properties[lang_property_start + lang_property_size]);
969     vars.insert(&objc_properties[lang_property_start],
970                 &objc_properties[lang_property_start + lang_property_size]);
971     vars.insert(&objcxx_properties[lang_property_start],
972                 &objcxx_properties[lang_property_start + lang_property_size]);
973     vars.insert(&ispc_properties[lang_property_start],
974                 &ispc_properties[lang_property_start + lang_property_size]);
975     vars.insert(&swift_properties[lang_property_start],
976                 &swift_properties[lang_property_start + lang_property_size]);
977     vars.insert(kCMAKE_CUDA_ARCHITECTURES);
978     vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
979     vars.insert(kCMAKE_ENABLE_EXPORTS);
980     vars.insert(kCMAKE_HIP_ARCHITECTURES);
981     vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
982     vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
983     vars.insert(kCMAKE_ISPC_HEADER_SUFFIX);
984     vars.insert(kCMAKE_LINK_SEARCH_END_STATIC);
985     vars.insert(kCMAKE_LINK_SEARCH_START_STATIC);
986     vars.insert(kCMAKE_OSX_ARCHITECTURES);
987     vars.insert(kCMAKE_OSX_DEPLOYMENT_TARGET);
988     vars.insert(kCMAKE_OSX_SYSROOT);
989     vars.insert(kCMAKE_APPLE_ARCH_SYSROOTS);
990     vars.insert(kCMAKE_POSITION_INDEPENDENT_CODE);
991     vars.insert(kCMAKE_SYSROOT);
992     vars.insert(kCMAKE_SYSROOT_COMPILE);
993     vars.insert(kCMAKE_SYSROOT_LINK);
994     vars.insert(kCMAKE_WARN_DEPRECATED);
995     vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
996     vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
997     vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
998
999     if (cmValue varListStr = this->Makefile->GetDefinition(
1000           kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
1001       std::vector<std::string> varList = cmExpandedList(*varListStr);
1002       vars.insert(varList.begin(), varList.end());
1003     }
1004
1005     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
1006         cmPolicies::NEW) {
1007       // To ensure full support of PIE, propagate cache variables
1008       // driving the link options
1009       vars.insert(&c_properties[pie_property_start],
1010                   &c_properties[pie_property_start + pie_property_size]);
1011       vars.insert(&cxx_properties[pie_property_start],
1012                   &cxx_properties[pie_property_start + pie_property_size]);
1013       vars.insert(&cuda_properties[pie_property_start],
1014                   &cuda_properties[pie_property_start + pie_property_size]);
1015       vars.insert(&fortran_properties[pie_property_start],
1016                   &fortran_properties[pie_property_start + pie_property_size]);
1017       vars.insert(&hip_properties[pie_property_start],
1018                   &hip_properties[pie_property_start + pie_property_size]);
1019       vars.insert(&objc_properties[pie_property_start],
1020                   &objc_properties[pie_property_start + pie_property_size]);
1021       vars.insert(&objcxx_properties[pie_property_start],
1022                   &objcxx_properties[pie_property_start + pie_property_size]);
1023       vars.insert(&ispc_properties[pie_property_start],
1024                   &ispc_properties[pie_property_start + pie_property_size]);
1025       vars.insert(&swift_properties[pie_property_start],
1026                   &swift_properties[pie_property_start + pie_property_size]);
1027     }
1028
1029     /* for the TRY_COMPILEs we want to be able to specify the architecture.
1030        So the user can set CMAKE_OSX_ARCHITECTURES to i386;ppc and then set
1031        CMAKE_TRY_COMPILE_OSX_ARCHITECTURES first to i386 and then to ppc to
1032        have the tests run for each specific architecture. Since
1033        cmLocalGenerator doesn't allow building for "the other"
1034        architecture only via CMAKE_OSX_ARCHITECTURES.
1035        */
1036     if (cmValue tcArchs = this->Makefile->GetDefinition(
1037           kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
1038       vars.erase(kCMAKE_OSX_ARCHITECTURES);
1039       std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
1040       arguments.CMakeFlags.emplace_back(std::move(flag));
1041     }
1042
1043     for (std::string const& var : vars) {
1044       if (cmValue val = this->Makefile->GetDefinition(var)) {
1045         std::string flag = "-D" + var + "=" + *val;
1046         arguments.CMakeFlags.emplace_back(std::move(flag));
1047       }
1048     }
1049   }
1050
1051   if (this->Makefile->GetState()->UseGhsMultiIDE()) {
1052     // Forward the GHS variables to the inner project cache.
1053     for (std::string const& var : ghs_platform_vars) {
1054       if (cmValue val = this->Makefile->GetDefinition(var)) {
1055         std::string flag = "-D" + var + "=" + "'" + *val + "'";
1056         arguments.CMakeFlags.emplace_back(std::move(flag));
1057       }
1058     }
1059   }
1060
1061   if (this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
1062     auto msg =
1063       cmStrCat("Executing try_compile (", *arguments.CompileResultVariable,
1064                ") in:\n  ", this->BinaryDirectory);
1065     this->Makefile->IssueMessage(MessageType::LOG, msg);
1066   }
1067
1068   bool erroroc = cmSystemTools::GetErrorOccurredFlag();
1069   cmSystemTools::ResetErrorOccurredFlag();
1070   std::string output;
1071   // actually do the try compile now that everything is setup
1072   int res = this->Makefile->TryCompile(
1073     sourceDirectory, this->BinaryDirectory, projectName, targetName,
1074     this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL,
1075     &arguments.CMakeFlags, output);
1076   if (erroroc) {
1077     cmSystemTools::SetErrorOccurred();
1078   }
1079
1080   // set the result var to the return value to indicate success or failure
1081   if (arguments.NoCache) {
1082     this->Makefile->AddDefinition(*arguments.CompileResultVariable,
1083                                   (res == 0 ? "TRUE" : "FALSE"));
1084   } else {
1085     this->Makefile->AddCacheDefinition(
1086       *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"),
1087       "Result of TRY_COMPILE", cmStateEnums::INTERNAL);
1088   }
1089
1090   if (arguments.OutputVariable) {
1091     this->Makefile->AddDefinition(*arguments.OutputVariable, output);
1092   }
1093
1094   if (this->SrcFileSignature) {
1095     std::string copyFileErrorMessage;
1096     this->FindOutputFile(targetName);
1097
1098     if ((res == 0) && arguments.CopyFileTo) {
1099       std::string const& copyFile = *arguments.CopyFileTo;
1100       if (this->OutputFile.empty() ||
1101           !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) {
1102         std::ostringstream emsg;
1103         /* clang-format off */
1104         emsg << "Cannot copy output executable\n"
1105              << "  '" << this->OutputFile << "'\n"
1106              << "to destination specified by COPY_FILE:\n"
1107              << "  '" << copyFile << "'\n";
1108         /* clang-format on */
1109         if (!this->FindErrorMessage.empty()) {
1110           emsg << this->FindErrorMessage;
1111         }
1112         if (!arguments.CopyFileError) {
1113           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str());
1114           return false;
1115         }
1116         copyFileErrorMessage = emsg.str();
1117       }
1118     }
1119
1120     if (arguments.CopyFileError) {
1121       std::string const& copyFileError = *arguments.CopyFileError;
1122       this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
1123     }
1124   }
1125   return res == 0;
1126 }
1127
1128 bool cmCoreTryCompile::IsTemporary(std::string const& path)
1129 {
1130   return ((path.find("CMakeTmp") != std::string::npos) ||
1131           (path.find("CMakeScratch") != std::string::npos));
1132 }
1133
1134 void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
1135 {
1136   if (binDir.empty()) {
1137     return;
1138   }
1139
1140   if (!IsTemporary(binDir)) {
1141     cmSystemTools::Error(
1142       "TRY_COMPILE attempt to remove -rf directory that does not contain "
1143       "CMakeTmp or CMakeScratch: \"" +
1144       binDir + "\"");
1145     return;
1146   }
1147
1148   cmsys::Directory dir;
1149   dir.Load(binDir);
1150   std::set<std::string> deletedFiles;
1151   for (unsigned long i = 0; i < dir.GetNumberOfFiles(); ++i) {
1152     const char* fileName = dir.GetFile(i);
1153     if (strcmp(fileName, ".") != 0 && strcmp(fileName, "..") != 0 &&
1154         // Do not delete NFS temporary files.
1155         !cmHasPrefix(fileName, ".nfs")) {
1156       if (deletedFiles.insert(fileName).second) {
1157         std::string const fullPath =
1158           std::string(binDir).append("/").append(fileName);
1159         if (cmSystemTools::FileIsSymlink(fullPath)) {
1160           cmSystemTools::RemoveFile(fullPath);
1161         } else if (cmSystemTools::FileIsDirectory(fullPath)) {
1162           this->CleanupFiles(fullPath);
1163           cmSystemTools::RemoveADirectory(fullPath);
1164         } else {
1165 #ifdef _WIN32
1166           // Sometimes anti-virus software hangs on to new files so we
1167           // cannot delete them immediately.  Try a few times.
1168           cmSystemTools::WindowsFileRetry retry =
1169             cmSystemTools::GetWindowsFileRetry();
1170           cmsys::Status status;
1171           while (!((status = cmSystemTools::RemoveFile(fullPath))) &&
1172                  --retry.Count && cmSystemTools::FileExists(fullPath)) {
1173             cmSystemTools::Delay(retry.Delay);
1174           }
1175           if (retry.Count == 0)
1176 #else
1177           cmsys::Status status = cmSystemTools::RemoveFile(fullPath);
1178           if (!status)
1179 #endif
1180           {
1181             this->Makefile->IssueMessage(
1182               MessageType::FATAL_ERROR,
1183               cmStrCat("The file:\n  ", fullPath,
1184                        "\ncould not be removed:\n  ", status.GetString()));
1185           }
1186         }
1187       }
1188     }
1189   }
1190
1191   if (binDir.find("CMakeScratch") != std::string::npos) {
1192     cmSystemTools::RemoveADirectory(binDir);
1193   }
1194 }
1195
1196 void cmCoreTryCompile::FindOutputFile(const std::string& targetName)
1197 {
1198   this->FindErrorMessage.clear();
1199   this->OutputFile.clear();
1200   std::string tmpOutputFile = "/";
1201   tmpOutputFile += targetName;
1202
1203   if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
1204     std::string const tcConfig =
1205       this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
1206     std::string const cfg = !tcConfig.empty()
1207       ? cmSystemTools::UpperCase(tcConfig)
1208       : TryCompileDefaultConfig;
1209     tmpOutputFile = cmStrCat(tmpOutputFile, '_', cfg);
1210   }
1211   tmpOutputFile += "_loc";
1212
1213   std::string command = cmStrCat(this->BinaryDirectory, tmpOutputFile);
1214   if (!cmSystemTools::FileExists(command)) {
1215     std::ostringstream emsg;
1216     emsg << "Unable to find the recorded try_compile output location:\n";
1217     emsg << cmStrCat("  ", command, "\n");
1218     this->FindErrorMessage = emsg.str();
1219     return;
1220   }
1221
1222   std::string outputFileLocation;
1223   cmsys::ifstream ifs(command.c_str());
1224   cmSystemTools::GetLineFromStream(ifs, outputFileLocation);
1225   if (!cmSystemTools::FileExists(outputFileLocation)) {
1226     std::ostringstream emsg;
1227     emsg << "Recorded try_compile output location doesn't exist:\n";
1228     emsg << cmStrCat("  ", outputFileLocation, "\n");
1229     this->FindErrorMessage = emsg.str();
1230     return;
1231   }
1232
1233   this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation);
1234 }
1235
1236 std::string cmCoreTryCompile::WriteSource(std::string const& filename,
1237                                           std::string const& content,
1238                                           char const* command) const
1239 {
1240   if (!cmSystemTools::GetFilenamePath(filename).empty()) {
1241     const auto& msg =
1242       cmStrCat(command, " given invalid filename \"", filename, "\"");
1243     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1244     return {};
1245   }
1246
1247   auto filepath = cmStrCat(this->BinaryDirectory, "/", filename);
1248   cmsys::ofstream file{ filepath.c_str(), std::ios::out };
1249   if (!file) {
1250     const auto& msg =
1251       cmStrCat(command, " failed to open \"", filename, "\" for writing");
1252     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1253     return {};
1254   }
1255
1256   file << content;
1257   if (!file) {
1258     const auto& msg = cmStrCat(command, " failed to write \"", filename, "\"");
1259     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
1260     return {};
1261   }
1262
1263   file.close();
1264   return filepath;
1265 }