BuildBase.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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 <Atomic/IO/Log.h>
  23. #include <Atomic/IO/FileSystem.h>
  24. #include <Atomic/Resource/JSONFile.h> // EGS:
  25. #include "../Subprocess/SubprocessSystem.h"
  26. #include "../Project/Project.h"
  27. #include "../ToolEnvironment.h"
  28. #include "../Assets/Asset.h" // EGS:
  29. #include "BuildSystem.h"
  30. #include "BuildEvents.h"
  31. #include "BuildBase.h"
  32. #include "ResourcePackager.h"
  33. #include "AssetBuildConfig.h"
  34. namespace ToolCore
  35. {
  36. BuildBase::BuildBase(Context * context, Project* project, PlatformID platform) : Object(context),
  37. platformID_(platform),
  38. containsMDL_(false),
  39. buildFailed_(false),
  40. assetBuildTag_(String::EMPTY)
  41. {
  42. if (UseResourcePackager())
  43. resourcePackager_ = new ResourcePackager(context, this);
  44. project_ = project;
  45. ReadAssetBuildConfig();
  46. }
  47. BuildBase::~BuildBase()
  48. {
  49. for (unsigned i = 0; i < resourceEntries_.Size(); i++)
  50. {
  51. delete resourceEntries_[i];
  52. }
  53. }
  54. #ifdef ATOMIC_PLATFORM_WINDOWS
  55. bool BuildBase::BuildClean(const String& path)
  56. {
  57. if (buildFailed_)
  58. {
  59. LOGERRORF("BuildBase::BuildClean - Attempt to clean directory of failed build, %s", path.CString());
  60. return false;
  61. }
  62. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  63. if (!fileSystem->DirExists(path))
  64. return true;
  65. // On Windows, do a little dance with the folder to avoid issues
  66. // with deleting folder and immediately recreating it
  67. String pathName, fileName, ext;
  68. SplitPath(path, pathName, fileName, ext);
  69. pathName = AddTrailingSlash(pathName);
  70. unsigned i = 0;
  71. while (true)
  72. {
  73. String newPath = ToString("%s%s_Temp_%u", pathName.CString(), fileName.CString(), i++);
  74. if (!fileSystem->DirExists(newPath))
  75. {
  76. if (!MoveFileExW(GetWideNativePath(path).CString(), GetWideNativePath(newPath).CString(), MOVEFILE_WRITE_THROUGH))
  77. {
  78. FailBuild(ToString("BuildBase::BuildClean: Unable to move directory %s -> ", path.CString(), newPath.CString()));
  79. return false;
  80. }
  81. // Remove the moved directory
  82. return BuildRemoveDirectory(newPath);
  83. }
  84. else
  85. {
  86. LOGWARNINGF("BuildBase::BuildClean - temp build folder exists, removing: %s", newPath.CString());
  87. fileSystem->RemoveDir(newPath, true);
  88. }
  89. if (i == 255)
  90. {
  91. FailBuild(ToString("BuildBase::BuildClean: Unable to move directory ( i == 255) %s -> ", path.CString(), newPath.CString()));
  92. return false;
  93. }
  94. }
  95. return false;
  96. }
  97. #else
  98. bool BuildBase::BuildClean(const String& path)
  99. {
  100. return BuildRemoveDirectory(path);
  101. }
  102. #endif
  103. bool BuildBase::BuildCreateDirectory(const String& path)
  104. {
  105. if (buildFailed_)
  106. {
  107. LOGERRORF("BuildBase::BuildCreateDirectory - Attempt to create directory of failed build, %s", path.CString());
  108. return false;
  109. }
  110. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  111. if (fileSystem->DirExists(path))
  112. return true;
  113. bool result = fileSystem->CreateDir(path);
  114. if (!result)
  115. {
  116. FailBuild(ToString("BuildBase::BuildCreateDirectory: Unable to create directory %s", path.CString()));
  117. return false;
  118. }
  119. return true;
  120. }
  121. bool BuildBase::BuildCopyFile(const String& srcFileName, const String& destFileName)
  122. {
  123. if (buildFailed_)
  124. {
  125. LOGERRORF("BuildBase::BuildCopyFile - Attempt to copy file of failed build, %s", srcFileName.CString());
  126. return false;
  127. }
  128. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  129. bool result = fileSystem->Copy(srcFileName, destFileName);
  130. if (!result)
  131. {
  132. FailBuild(ToString("BuildBase::BuildCopyFile: Unable to copy file %s -> %s", srcFileName.CString(), destFileName.CString()));
  133. return false;
  134. }
  135. return true;
  136. }
  137. bool BuildBase::CheckIncludeResourceFile(const String & resourceDir, const String & fileName)
  138. {
  139. return (GetExtension(fileName) != ".psd");
  140. }
  141. bool BuildBase::BuildRemoveDirectory(const String& path)
  142. {
  143. if (buildFailed_)
  144. {
  145. LOGERRORF("BuildBase::BuildRemoveDirectory - Attempt to remove directory of failed build, %s", path.CString());
  146. return false;
  147. }
  148. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  149. if (!fileSystem->DirExists(path))
  150. return true;
  151. bool result = fileSystem->RemoveDir(path, true);
  152. if (!result)
  153. {
  154. FailBuild(ToString("BuildBase::BuildRemoveDirectory: Unable to remove directory %s", path.CString()));
  155. return false;
  156. }
  157. return true;
  158. }
  159. void BuildBase::BuildLog(const String& message, bool sendEvent)
  160. {
  161. buildLog_.Push(message);
  162. if (sendEvent)
  163. {
  164. String colorMsg = ToString("<color #D4FB79>%s</color>\n", message.CString());
  165. VariantMap buildOutput;
  166. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  167. SendEvent(E_BUILDOUTPUT, buildOutput);
  168. }
  169. }
  170. void BuildBase::BuildWarn(const String& warning, bool sendEvent)
  171. {
  172. buildWarnings_.Push(warning);
  173. if (sendEvent)
  174. {
  175. String colorMsg = ToString("<color #FFFF00>%s</color>\n", warning.CString());
  176. VariantMap buildOutput;
  177. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  178. SendEvent(E_BUILDOUTPUT, buildOutput);
  179. }
  180. }
  181. void BuildBase::BuildError(const String& error, bool sendEvent)
  182. {
  183. buildErrors_.Push(error);
  184. if (sendEvent)
  185. {
  186. String colorMsg = ToString("<color #FF0000>%s</color>\n", error.CString());
  187. VariantMap buildOutput;
  188. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  189. SendEvent(E_BUILDOUTPUT, buildOutput);
  190. }
  191. }
  192. void BuildBase::FailBuild(const String& message)
  193. {
  194. if (buildFailed_)
  195. {
  196. LOGERRORF("BuildBase::FailBuild - Attempt to fail already failed build: %s", message.CString());
  197. return;
  198. }
  199. buildFailed_ = true;
  200. BuildError(message);
  201. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  202. buildSystem->BuildComplete(platformID_, buildPath_, false, message);
  203. }
  204. void BuildBase::HandleSubprocessOutputEvent(StringHash eventType, VariantMap& eventData)
  205. {
  206. // E_SUBPROCESSOUTPUT
  207. const String& text = eventData[SubprocessOutput::P_TEXT].GetString();
  208. // convert to a build output event and forward to subscribers
  209. VariantMap buildOutputData;
  210. buildOutputData[BuildOutput::P_TEXT] = text;
  211. SendEvent(E_BUILDOUTPUT, buildOutputData);
  212. }
  213. void BuildBase::GetDefaultResourcePaths(Vector<String>& paths)
  214. {
  215. paths.Clear();
  216. ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
  217. paths.Push(AddTrailingSlash(tenv->GetCoreDataDir()));
  218. paths.Push(AddTrailingSlash(tenv->GetPlayerDataDir()));
  219. }
  220. String BuildBase::GetSettingsDirectory()
  221. {
  222. return project_->GetProjectPath() + "/Settings";
  223. }
  224. void BuildBase::ScanResourceDirectory(const String& resourceDir)
  225. {
  226. {
  227. //LOGINFOF("Adding resource: %s : %s", newEntry->absolutePath_.CString(), newEntry->packagePath_.CString());
  228. }
  229. }
  230. void BuildBase::BuildDefaultResourceEntries()
  231. {
  232. for (unsigned i = 0; i < resourceDirs_.Size(); i++)
  233. {
  234. String resourceDir = resourceDirs_[i];
  235. Vector<String> fileNames;
  236. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  237. fileSystem->ScanDir(fileNames, resourceDir, "*.*", SCAN_FILES, true);
  238. for (unsigned i = 0; i < fileNames.Size(); i++)
  239. {
  240. const String& filename = fileNames[i];
  241. if (!CheckIncludeResourceFile(resourceDir, filename))
  242. continue;
  243. AddToResourcePackager(filename, resourceDir);
  244. }
  245. }
  246. }
  247. void BuildBase::BuildProjectResourceEntries()
  248. {
  249. if (AssetBuildConfig::IsLoaded() && !assetBuildTag_.Empty())
  250. {
  251. // add log comment
  252. BuildFilteredProjectResourceEntries();
  253. }
  254. else
  255. {
  256. // add log comment
  257. BuildAllProjectResourceEntries();
  258. }
  259. }
  260. void BuildBase::BuildAllProjectResourceEntries()
  261. {
  262. for (unsigned i = 0; i < projectResourceDir_.Size(); i++)
  263. {
  264. String projectResourceDir = projectResourceDir_[i];
  265. Vector<String> fileNamesInProject;
  266. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  267. fileSystem->ScanDir(fileNamesInProject, projectResourceDir, "*.*", SCAN_FILES, true);
  268. for (unsigned i = 0; i < fileNamesInProject.Size(); i++)
  269. {
  270. AddToResourcePackager(fileNamesInProject[i], projectResourceDir);
  271. }
  272. }
  273. }
  274. void BuildBase::BuildFilteredProjectResourceEntries()
  275. {
  276. // Loading up the assetbuildconfig.json,
  277. // obtaining a list of files to include in the build.
  278. VariantMap resourceTags;
  279. AssetBuildConfig::ApplyConfig(resourceTags);
  280. Vector<String> resourceFilesToInclude;
  281. VariantMap::ConstIterator itr = resourceTags.Begin();
  282. while (itr != resourceTags.End())
  283. {
  284. if (itr->first_ == assetBuildTag_)
  285. {
  286. resourceFilesToInclude = itr->second_.GetStringVector();
  287. break;
  288. }
  289. itr++;
  290. if (itr == resourceTags.End())
  291. {
  292. LOGERRORF("BuildBase::BuildFilteredProjectResourceEntries - Asset Build Tag \"%s\" not defined in .\\Settings\\assetbuildconfig.json", assetBuildTag_.CString());
  293. }
  294. }
  295. // check if the files in assetbuildconfig.json exist,
  296. // as well as their corresponding .asset file
  297. Vector<String> filesInResourceFolder;
  298. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  299. fileSystem->ScanDir(filesInResourceFolder, project_->GetResourcePath(), "*.*", SCAN_FILES, true);
  300. Vector<String> filesToAdd;
  301. Vector<String> filesToRemove;
  302. for (unsigned i = 0; i < resourceFilesToInclude.Size(); ++i)
  303. {
  304. // .asset file is of primary importance since we used it to identify the associated cached file.
  305. // without the .asset file the resource becomes redundant.
  306. String &filename = resourceFilesToInclude[i];
  307. if (!filesInResourceFolder.Contains(filename + ".asset"))
  308. {
  309. BuildWarn(ToString("BuildBase::BuildFilteredProjectResourceEntries - File \"%s\" associated .asset file not found in the Resources folder.\nRemoving \"%s\" from build pakcage", filename.CString()));
  310. if (filesInResourceFolder.Contains(filename))
  311. {
  312. filesToRemove.Push(filename);
  313. continue;
  314. }
  315. }
  316. filesToAdd.Push(filename + ".asset");
  317. }
  318. // remove files not to include
  319. for (unsigned i = 0; i < filesToRemove.Size(); ++i)
  320. {
  321. String &filename = filesToRemove[i];
  322. if (resourceFilesToInclude.Contains(filename))
  323. {
  324. resourceFilesToInclude.Remove(filename);
  325. }
  326. }
  327. // add files to include
  328. for (unsigned i = 0; i < filesToAdd.Size(); ++i)
  329. {
  330. String &filename = filesToAdd[i];
  331. if (!resourceFilesToInclude.Contains(filename))
  332. {
  333. resourceFilesToInclude.Push(filename);
  334. }
  335. }
  336. // Get associated cache GUID from the asset file
  337. Vector<String> filesWithGUIDtoInclude;
  338. for (auto it = resourceFilesToInclude.Begin(); it != resourceFilesToInclude.End(); ++it)
  339. {
  340. String &filename = *it;
  341. if (GetExtension(*it) == ".asset")
  342. {
  343. SharedPtr<File> file(new File(context_, project_->GetResourcePath() + *it));
  344. SharedPtr<JSONFile> json(new JSONFile(context_));
  345. json->Load(*file);
  346. file->Close();
  347. JSONValue root = json->GetRoot();
  348. int test = root.Get("version").GetInt();
  349. assert(root.Get("version").GetInt() == ASSET_VERSION);
  350. String guid = root.Get("guid").GetString();
  351. filesWithGUIDtoInclude.Push(guid);
  352. }
  353. }
  354. // obtain files in cache folder,
  355. // check if the file contains the guid, and add it to the resourceFilesToInclude
  356. Vector<String> filesInCacheFolder;
  357. Vector<String> cacheFilesToInclude;
  358. fileSystem->ScanDir(filesInCacheFolder, project_->GetProjectPath() + "Cache/", "*.*", SCAN_FILES, true);
  359. for (unsigned i = 0; i < filesWithGUIDtoInclude.Size(); i++)
  360. {
  361. String &guid = filesWithGUIDtoInclude[i];
  362. for (unsigned j = 0; j < filesInCacheFolder.Size(); j++)
  363. {
  364. String &filename = GetFileName(filesInCacheFolder[j]);
  365. if (filename.Contains(guid))
  366. {
  367. cacheFilesToInclude.Push(filesInCacheFolder[j]);
  368. // do not continue...
  369. // there might be multiple files with the same guid
  370. }
  371. }
  372. }
  373. for (auto it = resourceFilesToInclude.Begin(); it != resourceFilesToInclude.End(); ++it)
  374. {
  375. AddToResourcePackager(*it, project_->GetResourcePath());
  376. }
  377. for (auto it = cacheFilesToInclude.Begin(); it != cacheFilesToInclude.End(); ++it)
  378. {
  379. AddToResourcePackager(*it, project_->GetProjectPath() + "Cache/");
  380. }
  381. }
  382. void BuildBase::AddToResourcePackager(const String& filename, const String& resourceDir)
  383. {
  384. // Check if the file is already included in the resourceEntries_ list
  385. for (unsigned j = 0; j < resourceEntries_.Size(); j++)
  386. {
  387. const BuildResourceEntry* entry = resourceEntries_[j];
  388. if (entry->packagePath_ == filename)
  389. {
  390. BuildWarn(ToString("Resource Path: %s already exists", filename.CString()));
  391. continue;
  392. }
  393. }
  394. // Add the file to the resourceEntries_ list
  395. // TODO: Add additional filters
  396. if (GetExtension(filename) == ".psd")
  397. return;
  398. BuildResourceEntry* newEntry = new BuildResourceEntry;
  399. // BEGIN LICENSE MANAGEMENT
  400. if (GetExtension(filename) == ".mdl")
  401. {
  402. containsMDL_ = true;
  403. }
  404. // END LICENSE MANAGEMENT
  405. newEntry->absolutePath_ = resourceDir + filename;
  406. newEntry->resourceDir_ = resourceDir;
  407. newEntry->packagePath_ = filename;
  408. resourceEntries_.Push(newEntry);
  409. assert(resourcePackager_.NotNull());
  410. resourcePackager_->AddResourceEntry(newEntry);
  411. }
  412. void BuildBase::GenerateResourcePackage(const String& resourcePackagePath)
  413. {
  414. resourcePackager_->GeneratePackage(resourcePackagePath);
  415. }
  416. void BuildBase::AddResourceDir(const String& dir)
  417. {
  418. assert(!resourceDirs_.Contains(dir));
  419. resourceDirs_.Push(dir);
  420. }
  421. void BuildBase::AddProjectResourceDir(const String& dir)
  422. {
  423. assert(!projectResourceDir_.Contains(dir));
  424. projectResourceDir_.Push(dir);
  425. }
  426. void BuildBase::ReadAssetBuildConfig()
  427. {
  428. String projectPath = project_->GetProjectPath();
  429. projectPath = RemoveTrailingSlash(projectPath);
  430. String filename = projectPath + "Settings/AssetBuildConfig.json";
  431. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  432. if (!fileSystem->FileExists(filename))
  433. return;
  434. if (AssetBuildConfig::LoadFromFile(context_, filename))
  435. {
  436. VariantMap assetBuildConfig;
  437. AssetBuildConfig::ApplyConfig(assetBuildConfig);
  438. }
  439. }
  440. }