BuildBase.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. //
  2. // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include <Poco/File.h>
  23. #include <Atomic/IO/Log.h>
  24. #include <Atomic/IO/FileSystem.h>
  25. #include <Atomic/Resource/JSONFile.h>
  26. #include "../Subprocess/SubprocessSystem.h"
  27. #include "../Project/Project.h"
  28. #include "../ToolEnvironment.h"
  29. #include "../Assets/Asset.h"
  30. #include "../Assets/AssetDatabase.h"
  31. #include "BuildSystem.h"
  32. #include "BuildEvents.h"
  33. #include "BuildBase.h"
  34. #include "ResourcePackager.h"
  35. #include "AssetBuildConfig.h"
  36. namespace ToolCore
  37. {
  38. BuildBase::BuildBase(Context * context, Project* project, PlatformID platform) : Object(context),
  39. platformID_(platform),
  40. project_(project),
  41. containsMDL_(false),
  42. buildFailed_(false),
  43. assetBuildTag_(String::EMPTY),
  44. fileIncludedResourcesLog_(nullptr)
  45. {
  46. if (UseResourcePackager())
  47. resourcePackager_ = new ResourcePackager(context, this);
  48. fileIncludedResourcesLog_ = new File(context_, "BuildIncludedResources.log", Atomic::FILE_WRITE);
  49. ReadAssetBuildConfig();
  50. }
  51. BuildBase::~BuildBase()
  52. {
  53. for (unsigned i = 0; i < resourceEntries_.Size(); i++)
  54. {
  55. delete resourceEntries_[i];
  56. }
  57. fileIncludedResourcesLog_->Close();
  58. delete fileIncludedResourcesLog_;
  59. }
  60. #ifdef ATOMIC_PLATFORM_WINDOWS
  61. bool BuildBase::BuildClean(const String& path)
  62. {
  63. if (buildFailed_)
  64. {
  65. LOGERRORF("BuildBase::BuildClean - Attempt to clean directory of failed build, %s", path.CString());
  66. return false;
  67. }
  68. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  69. if (!fileSystem->DirExists(path))
  70. return true;
  71. // On Windows, do a little dance with the folder to avoid issues
  72. // with deleting folder and immediately recreating it
  73. String pathName, fileName, ext;
  74. SplitPath(path, pathName, fileName, ext);
  75. pathName = AddTrailingSlash(pathName);
  76. unsigned i = 0;
  77. while (true)
  78. {
  79. String newPath = ToString("%s%s_Temp_%u", pathName.CString(), fileName.CString(), i++);
  80. if (!fileSystem->DirExists(newPath))
  81. {
  82. if (!MoveFileExW(GetWideNativePath(path).CString(), GetWideNativePath(newPath).CString(), MOVEFILE_WRITE_THROUGH))
  83. {
  84. FailBuild(ToString("BuildBase::BuildClean: Unable to move directory %s -> ", path.CString(), newPath.CString()));
  85. return false;
  86. }
  87. // Remove the moved directory
  88. return BuildRemoveDirectory(newPath);
  89. }
  90. else
  91. {
  92. LOGWARNINGF("BuildBase::BuildClean - temp build folder exists, removing: %s", newPath.CString());
  93. fileSystem->RemoveDir(newPath, true);
  94. }
  95. if (i == 255)
  96. {
  97. FailBuild(ToString("BuildBase::BuildClean: Unable to move directory ( i == 255) %s -> ", path.CString(), newPath.CString()));
  98. return false;
  99. }
  100. }
  101. return false;
  102. }
  103. #else
  104. bool BuildBase::BuildClean(const String& path)
  105. {
  106. return BuildRemoveDirectory(path);
  107. }
  108. #endif
  109. bool BuildBase::BuildCreateDirectory(const String& path)
  110. {
  111. if (buildFailed_)
  112. {
  113. LOGERRORF("BuildBase::BuildCreateDirectory - Attempt to create directory of failed build, %s", path.CString());
  114. return false;
  115. }
  116. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  117. if (fileSystem->DirExists(path))
  118. return true;
  119. bool result = fileSystem->CreateDir(path);
  120. if (!result)
  121. {
  122. FailBuild(ToString("BuildBase::BuildCreateDirectory: Unable to create directory %s", path.CString()));
  123. return false;
  124. }
  125. return true;
  126. }
  127. bool BuildBase::BuildCopyFile(const String& srcFileName, const String& destFileName)
  128. {
  129. if (buildFailed_)
  130. {
  131. LOGERRORF("BuildBase::BuildCopyFile - Attempt to copy file of failed build, %s", srcFileName.CString());
  132. return false;
  133. }
  134. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  135. bool result = fileSystem->Copy(srcFileName, destFileName);
  136. if (!result)
  137. {
  138. FailBuild(ToString("BuildBase::BuildCopyFile: Unable to copy file %s -> %s", srcFileName.CString(), destFileName.CString()));
  139. return false;
  140. }
  141. return true;
  142. }
  143. bool BuildBase::BuildCopyDir(const String& srcDir, const String& destDir)
  144. {
  145. if (buildFailed_)
  146. {
  147. LOGERRORF("BuildBase::BuildCopyDir - Attempt to copy directory of failed build, %s", srcDir.CString());
  148. return false;
  149. }
  150. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  151. bool result = fileSystem->CopyDir(srcDir, destDir);
  152. if (!result)
  153. {
  154. FailBuild(ToString("BuildBase::BuildCopyDir: Unable to copy dir %s -> %s", srcDir.CString(), destDir.CString()));
  155. return false;
  156. }
  157. return true;
  158. }
  159. bool BuildBase::CheckIncludeResourceFile(const String & resourceDir, const String & fileName)
  160. {
  161. return (GetExtension(fileName) != ".psd");
  162. }
  163. bool BuildBase::BuildRemoveDirectory(const String& path)
  164. {
  165. if (buildFailed_)
  166. {
  167. LOGERRORF("BuildBase::BuildRemoveDirectory - Attempt to remove directory of failed build, %s", path.CString());
  168. return false;
  169. }
  170. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  171. if (!fileSystem->DirExists(path))
  172. return true;
  173. #ifdef ATOMIC_PLATFORM_LINUX
  174. bool result = true; // fileSystem->RemoveDir(path, true); crashes on linux
  175. Poco::File dirs(buildPath_.CString());
  176. dirs.remove(true);
  177. if (fileSystem->DirExists(buildPath_))
  178. result = false;
  179. #else
  180. bool result = fileSystem->RemoveDir(path, true);
  181. #endif
  182. if (!result)
  183. {
  184. FailBuild(ToString("BuildBase::BuildRemoveDirectory: Unable to remove directory %s", path.CString()));
  185. return false;
  186. }
  187. return true;
  188. }
  189. void BuildBase::BuildLog(const String& message, bool sendEvent)
  190. {
  191. buildLog_.Push(message);
  192. if (sendEvent)
  193. {
  194. String colorMsg = ToString("<color #D4FB79>%s</color>\n", message.CString());
  195. VariantMap buildOutput;
  196. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  197. SendEvent(E_BUILDOUTPUT, buildOutput);
  198. }
  199. }
  200. void BuildBase::BuildWarn(const String& warning, bool sendEvent)
  201. {
  202. buildWarnings_.Push(warning);
  203. if (sendEvent)
  204. {
  205. String colorMsg = ToString("<color #FFFF00>%s</color>\n", warning.CString());
  206. VariantMap buildOutput;
  207. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  208. SendEvent(E_BUILDOUTPUT, buildOutput);
  209. }
  210. }
  211. void BuildBase::BuildError(const String& error, bool sendEvent)
  212. {
  213. buildErrors_.Push(error);
  214. if (sendEvent)
  215. {
  216. String colorMsg = ToString("<color #FF0000>%s</color>\n", error.CString());
  217. VariantMap buildOutput;
  218. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  219. SendEvent(E_BUILDOUTPUT, buildOutput);
  220. }
  221. }
  222. void BuildBase::FailBuild(const String& message)
  223. {
  224. if (buildFailed_)
  225. {
  226. LOGERRORF("BuildBase::FailBuild - Attempt to fail already failed build: %s", message.CString());
  227. return;
  228. }
  229. buildFailed_ = true;
  230. BuildError(message);
  231. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  232. buildSystem->BuildComplete(platformID_, buildPath_, false, message);
  233. }
  234. void BuildBase::HandleSubprocessOutputEvent(StringHash eventType, VariantMap& eventData)
  235. {
  236. // E_SUBPROCESSOUTPUT
  237. const String& text = eventData[SubprocessOutput::P_TEXT].GetString();
  238. // convert to a build output event and forward to subscribers
  239. VariantMap buildOutputData;
  240. buildOutputData[BuildOutput::P_TEXT] = text;
  241. SendEvent(E_BUILDOUTPUT, buildOutputData);
  242. }
  243. void BuildBase::GetDefaultResourcePaths(Vector<String>& paths)
  244. {
  245. paths.Clear();
  246. ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
  247. paths.Push(AddTrailingSlash(tenv->GetCoreDataDir()));
  248. paths.Push(AddTrailingSlash(tenv->GetPlayerDataDir()));
  249. }
  250. String BuildBase::GetSettingsDirectory()
  251. {
  252. return project_->GetProjectPath() + "/Settings";
  253. }
  254. void BuildBase::BuildDefaultResourceEntries()
  255. {
  256. String buildLogOutput(String::EMPTY);
  257. for (unsigned i = 0; i < resourceDirs_.Size(); i++)
  258. {
  259. String resourceDir = resourceDirs_[i];
  260. fileIncludedResourcesLog_->WriteLine("\nBuildBase::BuildDefaultResourceEntries - Default resources being included from: " + resourceDir);
  261. Vector<String> fileNames;
  262. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  263. fileSystem->ScanDir(fileNames, resourceDir, "*.*", SCAN_FILES, true);
  264. for (unsigned i = 0; i < fileNames.Size(); i++)
  265. {
  266. const String& filename = fileNames[i];
  267. AddToResourcePackager(filename, resourceDir);
  268. }
  269. }
  270. }
  271. void BuildBase::BuildProjectResourceEntries()
  272. {
  273. String buildLogOutput(String::EMPTY);
  274. if (AssetBuildConfig::IsLoaded())
  275. {
  276. buildLogOutput += "\nBaseBuild::BuildProjectResourceEntries - ./Settings/AssetBuildConfig.json found. ";
  277. if (!assetBuildTag_.Empty())
  278. {
  279. buildLogOutput += "Using build-tag parameter : " + assetBuildTag_;
  280. fileIncludedResourcesLog_->WriteLine(buildLogOutput);
  281. BuildFilteredProjectResourceEntries();
  282. return;
  283. }
  284. buildLogOutput += "No build-tag parameter used.";
  285. }
  286. buildLogOutput += "\nBaseBuild::BuildProjectResourceEntries - No custom asset include configuration being used - ";
  287. buildLogOutput += "AssetBuildConfig.json not loaded, OR no build-tag parameter used.";
  288. fileIncludedResourcesLog_->WriteLine(buildLogOutput);
  289. BuildAllProjectResourceEntries();
  290. }
  291. void BuildBase::BuildAllProjectResourceEntries()
  292. {
  293. for (unsigned i = 0; i < projectResourceDir_.Size(); i++)
  294. {
  295. String projectResourceDir = projectResourceDir_[i];
  296. fileIncludedResourcesLog_->WriteLine("\nBuildBase::BuildAllProjectResourceEntries - Project resources being included from: " + projectResourceDir);
  297. Vector<String> fileNamesInProject;
  298. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  299. fileSystem->ScanDir(fileNamesInProject, projectResourceDir, "*.*", SCAN_FILES, true);
  300. for (unsigned i = 0; i < fileNamesInProject.Size(); i++)
  301. {
  302. AddToResourcePackager(fileNamesInProject[i], projectResourceDir);
  303. }
  304. }
  305. }
  306. void BuildBase::BuildFilteredProjectResourceEntries()
  307. {
  308. // Loading up the AssetBuildConfig.json,
  309. // obtaining a list of files to include in the build.
  310. VariantMap resourceTags;
  311. AssetBuildConfig::ApplyConfig(resourceTags);
  312. Vector<String> assetBuildConfigFiles;
  313. VariantMap::ConstIterator itr = resourceTags.Begin();
  314. while (itr != resourceTags.End())
  315. {
  316. if (itr->first_ == assetBuildTag_)
  317. {
  318. assetBuildConfigFiles = itr->second_.GetStringVector();
  319. for (unsigned i = 0; i < assetBuildConfigFiles.Size(); ++i)
  320. {
  321. // remove case sensitivity
  322. assetBuildConfigFiles[i] = assetBuildConfigFiles[i].ToLower();
  323. }
  324. break;
  325. }
  326. itr++;
  327. if (itr == resourceTags.End())
  328. {
  329. LOGERRORF("BuildBase::BuildFilteredProjectResourceEntries - Asset build-tag \"%s\" not defined in ./Settings/AssetBuildConfig.json", assetBuildTag_.CString());
  330. }
  331. }
  332. // find any folders defined in assetbuildconfig.json, and add the non-".asset" files in them.
  333. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  334. Vector<String> filesInFolderToAdd;
  335. for (unsigned i = 0; i < assetBuildConfigFiles.Size(); ++i)
  336. {
  337. String &filename = assetBuildConfigFiles[i];
  338. if (GetExtension(filename) == String::EMPTY &&
  339. fileSystem->DirExists(project_->GetResourcePath() + filename))
  340. {
  341. // rename 'filename' to 'folder' for context
  342. String folder(filename);
  343. // add a trailing slash if not defined
  344. if (folder.Back() != '/')
  345. folder = AddTrailingSlash(folder);
  346. Vector<String> filesInFolder;
  347. fileSystem->ScanDir(filesInFolder, project_->GetResourcePath() + folder, "*.*", SCAN_FILES, true);
  348. for (unsigned j = 0; j < filesInFolder.Size(); ++j)
  349. {
  350. String path = filesInFolder[j];
  351. // not interested in .asset files for now, will be included later.
  352. if (GetExtension(path) != ".asset")
  353. {
  354. filesInFolderToAdd.Push(folder + path.ToLower());
  355. }
  356. }
  357. }
  358. }
  359. // add the files defined using a folder in AssetBuildConfig.json
  360. for (unsigned i = 0; i < filesInFolderToAdd.Size(); ++i)
  361. {
  362. assetBuildConfigFiles.Push(filesInFolderToAdd[i]);
  363. }
  364. // check if the files in AssetBuildConfig.json exist,
  365. // as well as their corresponding .asset file
  366. Vector<String> filesInResourceFolder;
  367. Vector<String> resourceFilesToInclude;
  368. fileSystem->ScanDir(filesInResourceFolder, project_->GetResourcePath(), "*.*", SCAN_FILES, true);
  369. for (unsigned j = 0; j < filesInResourceFolder.Size(); ++j)
  370. {
  371. // don't want to checks to be case sensitive
  372. filesInResourceFolder[j] = filesInResourceFolder[j].ToLower();
  373. }
  374. for (unsigned i = 0; i < assetBuildConfigFiles.Size(); ++i)
  375. {
  376. // .asset file is of primary importance since we used it to identify the associated cached file.
  377. // without the .asset file the resource is removed from being included in the build.
  378. // don't want checks to be case sensitive
  379. String &filename = assetBuildConfigFiles[i];
  380. if (filesInResourceFolder.Contains(filename) &&
  381. filesInResourceFolder.Contains(filename + ".asset"))
  382. {
  383. resourceFilesToInclude.Push(filename);
  384. resourceFilesToInclude.Push(filename + ".asset");
  385. continue;
  386. }
  387. fileIncludedResourcesLog_->WriteLine("File " + filename + " ignored since it does not have an associated .asset file");
  388. }
  389. // add valid files included from the AssetBuildConfig.json
  390. for (StringVector::ConstIterator it = resourceFilesToInclude.Begin(); it != resourceFilesToInclude.End(); ++it)
  391. {
  392. AddToResourcePackager(*it, project_->GetResourcePath());
  393. }
  394. // Get associated cache GUID from the asset file
  395. Vector<String> filesWithGUIDtoInclude;
  396. for (StringVector::Iterator it = resourceFilesToInclude.Begin(); it != resourceFilesToInclude.End(); ++it)
  397. {
  398. String &filename = *it;
  399. if (GetExtension(*it) == ".asset")
  400. {
  401. SharedPtr<File> file(new File(context_, project_->GetResourcePath() + *it));
  402. SharedPtr<JSONFile> json(new JSONFile(context_));
  403. json->Load(*file);
  404. file->Close();
  405. JSONValue root = json->GetRoot();
  406. int test = root.Get("version").GetInt();
  407. assert(root.Get("version").GetInt() == ASSET_VERSION);
  408. String guid = root.Get("guid").GetString();
  409. filesWithGUIDtoInclude.Push(guid);
  410. }
  411. }
  412. // Obtain files in cache folder,
  413. // Check if the file contains the guid, and add it to the cacheFilesToInclude
  414. Vector<String> filesInCacheFolder;
  415. Vector<String> cacheFilesToInclude;
  416. AssetDatabase* db = GetSubsystem<AssetDatabase>();
  417. String cachePath = db->GetCachePath();
  418. fileSystem->ScanDir(filesInCacheFolder, cachePath, "*.*", SCAN_FILES, false);
  419. for (unsigned i = 0; i < filesWithGUIDtoInclude.Size(); ++i)
  420. {
  421. String &guid = filesWithGUIDtoInclude[i];
  422. for (unsigned j = 0; j < filesInCacheFolder.Size(); ++j)
  423. {
  424. const String &filename = GetFileName(filesInCacheFolder[j]);
  425. String &filenamePath = filesInCacheFolder[j];
  426. if (filename.Contains(guid))
  427. {
  428. cacheFilesToInclude.Push(filesInCacheFolder[j]);
  429. // do not continue...
  430. // there might be multiple files with the same guid having an guid_animaiton extention.
  431. }
  432. }
  433. }
  434. // include a texture file's cached .dds file when building in windows
  435. #ifdef ATOMIC_PLATFORM_DESKTOP
  436. for (StringVector::ConstIterator it = resourceFilesToInclude.Begin(); it != resourceFilesToInclude.End(); ++it)
  437. {
  438. if (!CheckIncludeResourceFile(project_->GetResourcePath(), *it))
  439. {
  440. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  441. String associatedDDSpath = "DDS/" + *it + ".dds";
  442. String compressedPath = cachePath + associatedDDSpath;
  443. if (fileSystem->FileExists(compressedPath))
  444. {
  445. cacheFilesToInclude.Push(associatedDDSpath);
  446. }
  447. }
  448. }
  449. #endif
  450. // Add the cache files to the resource packager
  451. for (StringVector::ConstIterator it = cacheFilesToInclude.Begin(); it != cacheFilesToInclude.End(); ++it)
  452. {
  453. AddToResourcePackager(*it, cachePath);
  454. }
  455. }
  456. void BuildBase::AddToResourcePackager(const String& filename, const String& resourceDir)
  457. {
  458. // Check if the file is already included in the resourceEntries_ list
  459. for (unsigned j = 0; j < resourceEntries_.Size(); j++)
  460. {
  461. const BuildResourceEntry* entry = resourceEntries_[j];
  462. if (entry->packagePath_ == filename)
  463. {
  464. BuildWarn(ToString("Resource Path: %s already exists", filename.CString()));
  465. continue;
  466. }
  467. }
  468. if (!CheckIncludeResourceFile(resourceDir, filename))
  469. {
  470. fileIncludedResourcesLog_->WriteLine(resourceDir + filename + " skipped because of file extention: " + GetExtension(filename));
  471. return;
  472. }
  473. BuildResourceEntry* newEntry = new BuildResourceEntry;
  474. // BEGIN LICENSE MANAGEMENT
  475. if (GetExtension(filename) == ".mdl")
  476. {
  477. containsMDL_ = true;
  478. }
  479. // END LICENSE MANAGEMENT
  480. newEntry->absolutePath_ = resourceDir + filename;
  481. newEntry->resourceDir_ = resourceDir;
  482. newEntry->packagePath_ = filename;
  483. resourceEntries_.Push(newEntry);
  484. assert(resourcePackager_.NotNull());
  485. resourcePackager_->AddResourceEntry(newEntry);
  486. fileIncludedResourcesLog_->WriteLine(newEntry->absolutePath_);
  487. }
  488. void BuildBase::GenerateResourcePackage(const String& resourcePackagePath)
  489. {
  490. resourcePackager_->GeneratePackage(resourcePackagePath);
  491. }
  492. void BuildBase::AddResourceDir(const String& dir)
  493. {
  494. assert(!resourceDirs_.Contains(dir));
  495. resourceDirs_.Push(dir);
  496. }
  497. void BuildBase::AddProjectResourceDir(const String& dir)
  498. {
  499. assert(!projectResourceDir_.Contains(dir));
  500. projectResourceDir_.Push(dir);
  501. }
  502. void BuildBase::ReadAssetBuildConfig()
  503. {
  504. String projectPath = project_->GetProjectPath();
  505. projectPath = RemoveTrailingSlash(projectPath);
  506. String filename = projectPath + "Settings/AssetBuildConfig.json";
  507. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  508. if (!fileSystem->FileExists(filename))
  509. return;
  510. if (AssetBuildConfig::LoadFromFile(context_, filename))
  511. {
  512. VariantMap assetBuildConfig;
  513. AssetBuildConfig::ApplyConfig(assetBuildConfig);
  514. }
  515. }
  516. }