CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmGeneratorTarget_Sources.cxx
blob94e88971a9db32d5ff565d20022a0472fa857712
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 /* clang-format off */
4 #include "cmGeneratorTarget.h"
5 /* clang-format on */
7 #include <cstddef>
8 #include <map>
9 #include <memory>
10 #include <set>
11 #include <sstream>
12 #include <string>
13 #include <type_traits>
14 #include <unordered_set>
15 #include <utility>
16 #include <vector>
18 #include <cm/string_view>
19 #include <cmext/algorithm>
20 #include <cmext/string_view>
22 #include "cmsys/RegularExpression.hxx"
24 #include "cmAlgorithms.h"
25 #include "cmEvaluatedTargetProperty.h"
26 #include "cmFileSet.h"
27 #include "cmGeneratorExpression.h"
28 #include "cmGeneratorExpressionDAGChecker.h"
29 #include "cmGlobalGenerator.h"
30 #include "cmLinkItem.h"
31 #include "cmList.h"
32 #include "cmListFileCache.h"
33 #include "cmLocalGenerator.h"
34 #include "cmMakefile.h"
35 #include "cmMessageType.h"
36 #include "cmSourceFile.h"
37 #include "cmSourceFileLocation.h"
38 #include "cmSourceGroup.h"
39 #include "cmStateTypes.h"
40 #include "cmStringAlgorithms.h"
41 #include "cmSystemTools.h"
42 #include "cmTarget.h"
43 #include "cmValue.h"
44 #include "cmake.h"
46 namespace {
47 using UseTo = cmGeneratorTarget::UseTo;
49 void AddObjectEntries(cmGeneratorTarget const* headTarget,
50 std::string const& config,
51 cmGeneratorExpressionDAGChecker* dagChecker,
52 EvaluatedTargetPropertyEntries& entries)
54 if (cmLinkImplementationLibraries const* impl =
55 headTarget->GetLinkImplementationLibraries(config, UseTo::Compile)) {
56 entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
57 for (cmLinkImplItem const& lib : impl->Libraries) {
58 if (lib.Target &&
59 lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
60 std::string uniqueName =
61 headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
62 lib.Target);
63 std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">";
64 cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(),
65 lib.Backtrace);
66 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
67 cge->SetEvaluateForBuildsystem(true);
69 EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
70 cmExpandList(cge->Evaluate(headTarget->GetLocalGenerator(), config,
71 headTarget, dagChecker),
72 ee.Values);
73 if (cge->GetHadContextSensitiveCondition()) {
74 ee.ContextDependent = true;
76 entries.Entries.emplace_back(std::move(ee));
82 void addFileSetEntry(cmGeneratorTarget const* headTarget,
83 std::string const& config,
84 cmGeneratorExpressionDAGChecker* dagChecker,
85 cmFileSet const* fileSet,
86 EvaluatedTargetPropertyEntries& entries)
88 auto dirCges = fileSet->CompileDirectoryEntries();
89 auto dirs = fileSet->EvaluateDirectoryEntries(
90 dirCges, headTarget->GetLocalGenerator(), config, headTarget, dagChecker);
91 bool contextSensitiveDirs = false;
92 for (auto const& dirCge : dirCges) {
93 if (dirCge->GetHadContextSensitiveCondition()) {
94 contextSensitiveDirs = true;
95 break;
98 cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance();
99 for (auto& entryCge : fileSet->CompileFileEntries()) {
100 auto tpe = cmGeneratorTarget::TargetPropertyEntry::CreateFileSet(
101 dirs, contextSensitiveDirs, std::move(entryCge), fileSet);
102 entries.Entries.emplace_back(
103 EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, *tpe));
104 EvaluatedTargetPropertyEntry const& entry = entries.Entries.back();
105 for (auto const& file : entry.Values) {
106 auto* sf = headTarget->Makefile->GetOrCreateSource(file);
107 if (fileSet->GetType() == "HEADERS"_s) {
108 sf->SetProperty("HEADER_FILE_ONLY", "TRUE");
111 #ifndef CMAKE_BOOTSTRAP
112 std::string e;
113 std::string w;
114 auto path = sf->ResolveFullPath(&e, &w);
115 if (!w.empty()) {
116 cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
118 if (path.empty()) {
119 if (!e.empty()) {
120 cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
122 return;
124 bool found = false;
125 for (auto const& sg : headTarget->Makefile->GetSourceGroups()) {
126 if (sg.MatchChildrenFiles(path)) {
127 found = true;
128 break;
131 if (!found) {
132 if (fileSet->GetType() == "HEADERS"_s) {
133 headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
134 ->AddGroupFile(path);
137 #endif
142 void AddFileSetEntries(cmGeneratorTarget const* headTarget,
143 std::string const& config,
144 cmGeneratorExpressionDAGChecker* dagChecker,
145 EvaluatedTargetPropertyEntries& entries)
147 for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) {
148 for (auto const& name : cmList{ entry.Value }) {
149 auto const* headerSet = headTarget->Target->GetFileSet(name);
150 addFileSetEntry(headTarget, config, dagChecker, headerSet, entries);
153 for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
154 for (auto const& name : cmList{ entry.Value }) {
155 auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
156 addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries);
161 bool processSources(cmGeneratorTarget const* tgt,
162 EvaluatedTargetPropertyEntries& entries,
163 std::vector<BT<std::string>>& srcs,
164 std::unordered_set<std::string>& uniqueSrcs,
165 bool debugSources)
167 cmMakefile* mf = tgt->Target->GetMakefile();
169 bool contextDependent = entries.HadContextSensitiveCondition;
171 for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
172 if (entry.ContextDependent) {
173 contextDependent = true;
176 cmLinkImplItem const& item = entry.LinkImplItem;
177 std::string const& targetName = item.AsStr();
179 for (std::string& src : entry.Values) {
180 cmSourceFile* sf = mf->GetOrCreateSource(src);
181 std::string e;
182 std::string w;
183 std::string fullPath = sf->ResolveFullPath(&e, &w);
184 cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
185 if (!w.empty()) {
186 cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
188 if (fullPath.empty()) {
189 if (!e.empty()) {
190 cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
192 return contextDependent;
195 if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) {
196 std::ostringstream err;
197 if (!targetName.empty()) {
198 err << "Target \"" << targetName
199 << "\" contains relative path in its INTERFACE_SOURCES:\n \""
200 << src << "\"";
201 } else {
202 err << "Found relative path while evaluating sources of \""
203 << tgt->GetName() << "\":\n \"" << src << "\"\n";
205 tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
206 err.str());
207 return contextDependent;
209 src = fullPath;
211 std::string usedSources;
212 for (std::string const& src : entry.Values) {
213 if (uniqueSrcs.insert(src).second) {
214 srcs.emplace_back(src, entry.Backtrace);
215 if (debugSources) {
216 usedSources += " * " + src + "\n";
220 if (!usedSources.empty()) {
221 tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
222 MessageType::LOG,
223 std::string("Used sources for target ") + tgt->GetName() + ":\n" +
224 usedSources,
225 entry.Backtrace);
228 return contextDependent;
232 std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
233 std::string const& config) const
235 std::vector<BT<std::string>> files;
237 if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
238 // At configure-time, this method can be called as part of getting the
239 // LOCATION property or to export() a file to be include()d. However
240 // there is no cmGeneratorTarget at configure-time, so search the SOURCES
241 // for TARGET_OBJECTS instead for backwards compatibility with OLD
242 // behavior of CMP0024 and CMP0026 only.
244 cmBTStringRange sourceEntries = this->Target->GetSourceEntries();
245 for (auto const& entry : sourceEntries) {
246 cmList items{ entry.Value };
247 for (auto const& item : items) {
248 if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") &&
249 item.back() == '>') {
250 continue;
252 files.emplace_back(item);
255 return files;
258 cmList debugProperties{ this->Makefile->GetDefinition(
259 "CMAKE_DEBUG_TARGET_PROPERTIES") };
260 bool debugSources =
261 !this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES");
263 if (this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) {
264 this->DebugSourcesDone = true;
267 cmGeneratorExpressionDAGChecker dagChecker(this, "SOURCES", nullptr, nullptr,
268 this->LocalGenerator, config);
270 EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
271 this, config, std::string(), &dagChecker, this->SourceEntries);
273 std::unordered_set<std::string> uniqueSrcs;
274 bool contextDependentDirectSources =
275 processSources(this, entries, files, uniqueSrcs, debugSources);
277 // Collect INTERFACE_SOURCES of all direct link-dependencies.
278 EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries;
279 AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(),
280 &dagChecker, linkInterfaceSourcesEntries,
281 IncludeRuntimeInterface::No, UseTo::Compile);
282 bool contextDependentInterfaceSources = processSources(
283 this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
285 // Collect TARGET_OBJECTS of direct object link-dependencies.
286 bool contextDependentObjects = false;
287 if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
288 EvaluatedTargetPropertyEntries linkObjectsEntries;
289 AddObjectEntries(this, config, &dagChecker, linkObjectsEntries);
290 contextDependentObjects = processSources(this, linkObjectsEntries, files,
291 uniqueSrcs, debugSources);
292 // Note that for imported targets or multi-config generators supporting
293 // cross-config builds the paths to the object files must be per-config,
294 // so contextDependentObjects will be true here even if object libraries
295 // are specified without per-config generator expressions.
298 // Collect this target's file sets.
299 EvaluatedTargetPropertyEntries fileSetEntries;
300 AddFileSetEntries(this, config, &dagChecker, fileSetEntries);
301 bool contextDependentFileSets =
302 processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
304 // Determine if sources are context-dependent or not.
305 if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
306 !contextDependentObjects && !contextDependentFileSets) {
307 this->SourcesAreContextDependent = Tribool::False;
308 } else {
309 this->SourcesAreContextDependent = Tribool::True;
312 return files;
315 void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files,
316 const std::string& config) const
318 std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config);
319 files.reserve(tmp.size());
320 for (BT<cmSourceFile*>& v : tmp) {
321 files.push_back(v.Value);
325 std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles(
326 std::string const& config) const
328 std::vector<BT<cmSourceFile*>> files;
329 if (!this->GlobalGenerator->GetConfigureDoneCMP0026()) {
330 // Since we are still configuring not all sources may exist yet,
331 // so we need to avoid full source classification because that
332 // requires the absolute paths to all sources to be determined.
333 // Since this is only for compatibility with old policies that
334 // projects should not depend on anymore, just compute the files
335 // without memoizing them.
336 std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
337 std::set<cmSourceFile*> emitted;
338 for (BT<std::string> const& s : srcs) {
339 cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
340 if (emitted.insert(sf).second) {
341 files.emplace_back(sf, s.Backtrace);
344 return files;
347 KindedSources const& kinded = this->GetKindedSources(config);
348 files.reserve(kinded.Sources.size());
349 for (SourceAndKind const& si : kinded.Sources) {
350 files.push_back(si.Source);
352 return files;
355 void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
356 std::vector<cmSourceFile*>& files, const std::string& config) const
358 std::vector<BT<cmSourceFile*>> tmp =
359 this->GetSourceFilesWithoutObjectLibraries(config);
360 files.reserve(tmp.size());
361 for (BT<cmSourceFile*>& v : tmp) {
362 files.push_back(v.Value);
366 std::vector<BT<cmSourceFile*>>
367 cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
368 std::string const& config) const
370 std::vector<BT<cmSourceFile*>> files;
371 KindedSources const& kinded = this->GetKindedSources(config);
372 files.reserve(kinded.Sources.size());
373 for (SourceAndKind const& si : kinded.Sources) {
374 if (si.Source.Value->GetObjectLibrary().empty()) {
375 files.push_back(si.Source);
378 return files;
381 cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources(
382 std::string const& config) const
384 // If we already processed one configuration and found no dependency
385 // on configuration then always use the one result.
386 if (this->SourcesAreContextDependent == Tribool::False) {
387 return this->KindedSourcesMap.begin()->second;
390 // Lookup any existing link implementation for this configuration.
391 std::string const key = cmSystemTools::UpperCase(config);
392 auto it = this->KindedSourcesMap.find(key);
393 if (it != this->KindedSourcesMap.end()) {
394 if (!it->second.Initialized) {
395 std::ostringstream e;
396 e << "The SOURCES of \"" << this->GetName()
397 << "\" use a generator expression that depends on the "
398 "SOURCES themselves.";
399 this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
400 MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
401 static KindedSources empty;
402 return empty;
404 return it->second;
407 // Add an entry to the map for this configuration.
408 KindedSources& files = this->KindedSourcesMap[key];
409 this->ComputeKindedSources(files, config);
410 files.Initialized = true;
411 return files;
414 void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
415 std::string const& config) const
417 // Get the source file paths by string.
418 std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
420 cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
421 std::vector<cmSourceFile*> badObjLib;
423 std::set<cmSourceFile*> emitted;
424 for (BT<std::string> const& s : srcs) {
425 // Create each source at most once.
426 cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
427 if (!emitted.insert(sf).second) {
428 continue;
431 // Compute the kind (classification) of this source file.
432 SourceKind kind;
433 std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
434 cmFileSet const* fs = this->GetFileSetForSource(config, sf);
435 if (sf->GetCustomCommand()) {
436 kind = SourceKindCustomCommand;
437 } else if (!this->Target->IsNormal() && !this->Target->IsImported() &&
438 fs && (fs->GetType() == "CXX_MODULES"_s)) {
439 kind = SourceKindCxxModuleSource;
440 } else if (this->Target->GetType() == cmStateEnums::UTILITY ||
441 this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY
442 // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
443 // NOLINTNEXTLINE(bugprone-branch-clone)
445 kind = SourceKindExtra;
446 } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) {
447 kind = SourceKindUnityBatched;
448 // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
449 // NOLINTNEXTLINE(bugprone-branch-clone)
450 } else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) {
451 kind = SourceKindHeader;
452 } else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
453 kind = SourceKindExternalObject;
454 } else if (!sf->GetOrDetermineLanguage().empty()) {
455 kind = SourceKindObjectSource;
456 } else if (ext == "def") {
457 kind = SourceKindModuleDefinition;
458 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
459 badObjLib.push_back(sf);
461 } else if (ext == "idl") {
462 kind = SourceKindIDL;
463 if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
464 badObjLib.push_back(sf);
466 } else if (ext == "resx") {
467 kind = SourceKindResx;
468 } else if (ext == "appxmanifest") {
469 kind = SourceKindAppManifest;
470 } else if (ext == "manifest") {
471 if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
472 kind = SourceKindExtra;
473 } else {
474 kind = SourceKindManifest;
476 } else if (ext == "pfx") {
477 kind = SourceKindCertificate;
478 } else if (ext == "xaml") {
479 kind = SourceKindXaml;
480 } else if (header_regex.find(sf->ResolveFullPath())) {
481 kind = SourceKindHeader;
482 } else {
483 kind = SourceKindExtra;
486 // Save this classified source file in the result vector.
487 files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind });
490 if (!badObjLib.empty()) {
491 std::ostringstream e;
492 e << "OBJECT library \"" << this->GetName() << "\" contains:\n";
493 for (cmSourceFile* i : badObjLib) {
494 e << " " << i->GetLocation().GetName() << "\n";
496 e << "but may contain only sources that compile, header files, and "
497 "other files that would not affect linking of a normal library.";
498 this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
499 MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
503 std::vector<cmGeneratorTarget::AllConfigSource> const&
504 cmGeneratorTarget::GetAllConfigSources() const
506 if (this->AllConfigSources.empty()) {
507 this->ComputeAllConfigSources();
509 return this->AllConfigSources;
512 void cmGeneratorTarget::ComputeAllConfigSources() const
514 std::vector<std::string> configs =
515 this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
517 std::map<cmSourceFile const*, size_t> index;
519 for (size_t ci = 0; ci < configs.size(); ++ci) {
520 KindedSources const& sources = this->GetKindedSources(configs[ci]);
521 for (SourceAndKind const& src : sources.Sources) {
522 auto mi = index.find(src.Source.Value);
523 if (mi == index.end()) {
524 AllConfigSource acs;
525 acs.Source = src.Source.Value;
526 acs.Kind = src.Kind;
527 this->AllConfigSources.push_back(std::move(acs));
528 std::map<cmSourceFile const*, size_t>::value_type entry(
529 src.Source.Value, this->AllConfigSources.size() - 1);
530 mi = index.insert(entry).first;
532 this->AllConfigSources[mi->second].Configs.push_back(ci);
537 std::vector<cmGeneratorTarget::AllConfigSource>
538 cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const
540 std::vector<AllConfigSource> result;
541 for (AllConfigSource const& source : this->GetAllConfigSources()) {
542 if (source.Kind == kind) {
543 result.push_back(source);
546 return result;
549 std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const
551 std::set<std::string> languages;
552 std::vector<AllConfigSource> const& sources = this->GetAllConfigSources();
553 for (AllConfigSource const& si : sources) {
554 std::string const& lang = si.Source->GetOrDetermineLanguage();
555 if (!lang.empty()) {
556 languages.emplace(lang);
559 return languages;