ExcludedFolderCache.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <QDirIterator>
  9. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  10. #include <native/AssetManager/ExcludedFolderCache.h>
  11. #include <utilities/assetUtils.h>
  12. #include <utilities/PlatformConfiguration.h>
  13. #include <AzCore/IO/Path/Path.h>
  14. namespace AssetProcessor
  15. {
  16. ExcludedFolderCache::ExcludedFolderCache(const PlatformConfiguration* platformConfig) : m_platformConfig(platformConfig)
  17. {
  18. AZ::Interface<ExcludedFolderCacheInterface>::Register(this);
  19. }
  20. ExcludedFolderCache::~ExcludedFolderCache()
  21. {
  22. AZ::Interface<ExcludedFolderCacheInterface>::Unregister(this);
  23. }
  24. void ExcludedFolderCache::InitializeFromKnownSet(AZStd::unordered_set<AZStd::string>&& excludedFolders)
  25. {
  26. m_excludedFolders = AZStd::move(excludedFolders);
  27. // Add the cache to the list as well
  28. AZStd::string projectCacheRootValue;
  29. AZ::SettingsRegistry::Get()->Get(projectCacheRootValue, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder);
  30. projectCacheRootValue = AssetUtilities::NormalizeFilePath(projectCacheRootValue.c_str()).toUtf8().constData();
  31. m_excludedFolders.emplace(projectCacheRootValue);
  32. // Register to be notified about deletes so we can remove old ignored folders
  33. auto fileStateCache = AZ::Interface<IFileStateRequests>::Get();
  34. if (fileStateCache)
  35. {
  36. m_handler = AZ::Event<FileStateInfo>::Handler(
  37. [this](FileStateInfo fileInfo)
  38. {
  39. if (fileInfo.m_isDirectory)
  40. {
  41. AZStd::scoped_lock lock(m_pendingNewFolderMutex);
  42. m_pendingDeletes.emplace(fileInfo.m_absolutePath.toUtf8().constData());
  43. }
  44. });
  45. fileStateCache->RegisterForDeleteEvent(m_handler);
  46. }
  47. else
  48. {
  49. AZ_Error("ExcludedFolderCache", false, "Failed to find IFileStateRequests interface");
  50. }
  51. m_builtCache = true;
  52. }
  53. const AZStd::unordered_set<AZStd::string>& ExcludedFolderCache::GetExcludedFolders()
  54. {
  55. if (!m_builtCache)
  56. {
  57. AZ_Warning("AssetProcessor", false, "ExcludedFolderCache lazy-rebuilding instead of being prepopulated (May impact startup performance). Call InitializeFromKnownSet.\n");
  58. // manually warm up the cache.
  59. AZStd::unordered_set<AZStd::string> excludedFolders;
  60. for (int i = 0; i < m_platformConfig->GetScanFolderCount(); ++i)
  61. {
  62. const auto& scanFolderInfo = m_platformConfig->GetScanFolderAt(i);
  63. QDir rooted(scanFolderInfo.ScanPath());
  64. QString absolutePath = rooted.absolutePath();
  65. AZStd::stack<QString> dirs;
  66. dirs.push(absolutePath);
  67. while (!dirs.empty())
  68. {
  69. absolutePath = dirs.top();
  70. dirs.pop();
  71. // Scan only folders, do not recurse so we have the chance to ignore a subfolder before going deeper
  72. QDirIterator dirIterator(absolutePath, QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
  73. // Loop all the folders in this directory
  74. while (dirIterator.hasNext())
  75. {
  76. dirIterator.next();
  77. QString pathMatch = rooted.absoluteFilePath(dirIterator.filePath());
  78. if (m_platformConfig->IsFileExcluded(pathMatch))
  79. {
  80. // Add the folder to the list and do not proceed any deeper
  81. pathMatch = AssetUtilities::NormalizeFilePath(pathMatch);
  82. excludedFolders.emplace(pathMatch.toUtf8().constData());
  83. }
  84. else if (scanFolderInfo.RecurseSubFolders())
  85. {
  86. // Folder is not excluded and recurse is enabled, add to the list of folders to check
  87. dirs.push(pathMatch);
  88. }
  89. }
  90. }
  91. }
  92. InitializeFromKnownSet(AZStd::move(excludedFolders));
  93. return m_excludedFolders;
  94. }
  95. // Incorporate any pending folders
  96. AZStd::unordered_set<AZStd::string> pendingAdds;
  97. AZStd::unordered_set<AZStd::string> pendingDeletes;
  98. {
  99. AZStd::scoped_lock lock(m_pendingNewFolderMutex);
  100. pendingAdds.swap(m_pendingNewFolders);
  101. pendingDeletes.swap(m_pendingDeletes);
  102. }
  103. if (!pendingAdds.empty())
  104. {
  105. for (const auto& pendingAdd : pendingAdds)
  106. {
  107. QString normalizedAdd = AssetUtilities::NormalizeFilePath(QString::fromUtf8(pendingAdd.c_str()));
  108. m_excludedFolders.insert(normalizedAdd.toUtf8().constData());
  109. }
  110. }
  111. if (!pendingDeletes.empty())
  112. {
  113. for (const auto& pendingDelete : pendingDeletes)
  114. {
  115. QString normalizedDelete = AssetUtilities::NormalizeFilePath(QString::fromUtf8(pendingDelete.c_str()));
  116. m_excludedFolders.erase(normalizedDelete.toUtf8().constData());
  117. }
  118. }
  119. return m_excludedFolders;
  120. }
  121. void ExcludedFolderCache::FileAdded(QString path)
  122. {
  123. QString relativePath, scanFolderPath;
  124. if (!m_platformConfig->ConvertToRelativePath(path, relativePath, scanFolderPath))
  125. {
  126. AZ_Error("ExcludedFolderCache", false, "Failed to get relative path for newly added file %s", path.toUtf8().constData());
  127. return;
  128. }
  129. AZ::IO::Path azPath(relativePath.toUtf8().constData());
  130. AZ::IO::Path absolutePath(scanFolderPath.toUtf8().constData());
  131. for (const auto& pathPart : azPath)
  132. {
  133. absolutePath /= pathPart;
  134. QString normalized = AssetUtilities::NormalizeFilePath(absolutePath.c_str());
  135. if (m_platformConfig->IsFileExcluded(normalized))
  136. {
  137. // Add the folder to a pending list, since this callback runs on another thread
  138. AZStd::scoped_lock lock(m_pendingNewFolderMutex);
  139. m_pendingNewFolders.emplace(normalized.toUtf8().constData());
  140. break;
  141. }
  142. }
  143. }
  144. }