BuildBase.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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 "../Subprocess/SubprocessSystem.h"
  25. #include "../Project/Project.h"
  26. #include "../ToolEnvironment.h"
  27. #include "BuildSystem.h"
  28. #include "BuildEvents.h"
  29. #include "BuildBase.h"
  30. #include "ResourcePackager.h"
  31. #include "AssetBuildConfig.h"
  32. namespace ToolCore
  33. {
  34. BuildBase::BuildBase(Context * context, Project* project, PlatformID platform) : Object(context),
  35. platformID_(platform),
  36. containsMDL_(false),
  37. buildFailed_(false),
  38. assetBuildTag_(String::EMPTY)
  39. {
  40. if (UseResourcePackager())
  41. resourcePackager_ = new ResourcePackager(context, this);
  42. project_ = project;
  43. ReadAssetBuildConfig();
  44. }
  45. BuildBase::~BuildBase()
  46. {
  47. for (unsigned i = 0; i < resourceEntries_.Size(); i++)
  48. {
  49. delete resourceEntries_[i];
  50. }
  51. }
  52. #ifdef ATOMIC_PLATFORM_WINDOWS
  53. bool BuildBase::BuildClean(const String& path)
  54. {
  55. if (buildFailed_)
  56. {
  57. LOGERRORF("BuildBase::BuildClean - Attempt to clean directory of failed build, %s", path.CString());
  58. return false;
  59. }
  60. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  61. if (!fileSystem->DirExists(path))
  62. return true;
  63. // On Windows, do a little dance with the folder to avoid issues
  64. // with deleting folder and immediately recreating it
  65. String pathName, fileName, ext;
  66. SplitPath(path, pathName, fileName, ext);
  67. pathName = AddTrailingSlash(pathName);
  68. unsigned i = 0;
  69. while (true)
  70. {
  71. String newPath = ToString("%s%s_Temp_%u", pathName.CString(), fileName.CString(), i++);
  72. if (!fileSystem->DirExists(newPath))
  73. {
  74. if (!MoveFileExW(GetWideNativePath(path).CString(), GetWideNativePath(newPath).CString(), MOVEFILE_WRITE_THROUGH))
  75. {
  76. FailBuild(ToString("BuildBase::BuildClean: Unable to move directory %s -> ", path.CString(), newPath.CString()));
  77. return false;
  78. }
  79. // Remove the moved directory
  80. return BuildRemoveDirectory(newPath);
  81. }
  82. else
  83. {
  84. LOGWARNINGF("BuildBase::BuildClean - temp build folder exists, removing: %s", newPath.CString());
  85. fileSystem->RemoveDir(newPath, true);
  86. }
  87. if (i == 255)
  88. {
  89. FailBuild(ToString("BuildBase::BuildClean: Unable to move directory ( i == 255) %s -> ", path.CString(), newPath.CString()));
  90. return false;
  91. }
  92. }
  93. return false;
  94. }
  95. #else
  96. bool BuildBase::BuildClean(const String& path)
  97. {
  98. return BuildRemoveDirectory(path);
  99. }
  100. #endif
  101. bool BuildBase::BuildCreateDirectory(const String& path)
  102. {
  103. if (buildFailed_)
  104. {
  105. LOGERRORF("BuildBase::BuildCreateDirectory - Attempt to create directory of failed build, %s", path.CString());
  106. return false;
  107. }
  108. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  109. if (fileSystem->DirExists(path))
  110. return true;
  111. bool result = fileSystem->CreateDir(path);
  112. if (!result)
  113. {
  114. FailBuild(ToString("BuildBase::BuildCreateDirectory: Unable to create directory %s", path.CString()));
  115. return false;
  116. }
  117. return true;
  118. }
  119. bool BuildBase::BuildCopyFile(const String& srcFileName, const String& destFileName)
  120. {
  121. if (buildFailed_)
  122. {
  123. LOGERRORF("BuildBase::BuildCopyFile - Attempt to copy file of failed build, %s", srcFileName.CString());
  124. return false;
  125. }
  126. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  127. bool result = fileSystem->Copy(srcFileName, destFileName);
  128. if (!result)
  129. {
  130. FailBuild(ToString("BuildBase::BuildCopyFile: Unable to copy file %s -> %s", srcFileName.CString(), destFileName.CString()));
  131. return false;
  132. }
  133. return true;
  134. }
  135. bool BuildBase::BuildRemoveDirectory(const String& path)
  136. {
  137. if (buildFailed_)
  138. {
  139. LOGERRORF("BuildBase::BuildRemoveDirectory - Attempt to remove directory of failed build, %s", path.CString());
  140. return false;
  141. }
  142. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  143. if (!fileSystem->DirExists(path))
  144. return true;
  145. bool result = fileSystem->RemoveDir(path, true);
  146. if (!result)
  147. {
  148. FailBuild(ToString("BuildBase::BuildRemoveDirectory: Unable to remove directory %s", path.CString()));
  149. return false;
  150. }
  151. return true;
  152. }
  153. void BuildBase::BuildLog(const String& message, bool sendEvent)
  154. {
  155. buildLog_.Push(message);
  156. if (sendEvent)
  157. {
  158. String colorMsg = ToString("<color #D4FB79>%s</color>\n", message.CString());
  159. VariantMap buildOutput;
  160. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  161. SendEvent(E_BUILDOUTPUT, buildOutput);
  162. }
  163. }
  164. void BuildBase::BuildWarn(const String& warning, bool sendEvent)
  165. {
  166. buildWarnings_.Push(warning);
  167. if (sendEvent)
  168. {
  169. String colorMsg = ToString("<color #FFFF00>%s</color>\n", warning.CString());
  170. VariantMap buildOutput;
  171. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  172. SendEvent(E_BUILDOUTPUT, buildOutput);
  173. }
  174. }
  175. void BuildBase::BuildError(const String& error, bool sendEvent)
  176. {
  177. buildErrors_.Push(error);
  178. if (sendEvent)
  179. {
  180. String colorMsg = ToString("<color #FF0000>%s</color>\n", error.CString());
  181. VariantMap buildOutput;
  182. buildOutput[BuildOutput::P_TEXT] = colorMsg;
  183. SendEvent(E_BUILDOUTPUT, buildOutput);
  184. }
  185. }
  186. void BuildBase::FailBuild(const String& message)
  187. {
  188. if (buildFailed_)
  189. {
  190. LOGERRORF("BuildBase::FailBuild - Attempt to fail already failed build: %s", message.CString());
  191. return;
  192. }
  193. buildFailed_ = true;
  194. BuildError(message);
  195. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  196. buildSystem->BuildComplete(platformID_, buildPath_, false, message);
  197. }
  198. void BuildBase::HandleSubprocessOutputEvent(StringHash eventType, VariantMap& eventData)
  199. {
  200. // E_SUBPROCESSOUTPUT
  201. const String& text = eventData[SubprocessOutput::P_TEXT].GetString();
  202. // convert to a build output event and forward to subscribers
  203. VariantMap buildOutputData;
  204. buildOutputData[BuildOutput::P_TEXT] = text;
  205. SendEvent(E_BUILDOUTPUT, buildOutputData);
  206. }
  207. void BuildBase::GetDefaultResourcePaths(Vector<String>& paths)
  208. {
  209. paths.Clear();
  210. ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
  211. paths.Push(AddTrailingSlash(tenv->GetCoreDataDir()));
  212. paths.Push(AddTrailingSlash(tenv->GetPlayerDataDir()));
  213. }
  214. String BuildBase::GetSettingsDirectory()
  215. {
  216. return project_->GetProjectPath() + "/Settings";
  217. }
  218. void BuildBase::ScanResourceDirectory(const String& resourceDir)
  219. {
  220. Vector<String> fileNames;
  221. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  222. fileSystem->ScanDir(fileNames, resourceDir, "*.*", SCAN_FILES, true);
  223. for (unsigned i = 0; i < fileNames.Size(); i++)
  224. {
  225. const String& filename = fileNames[i];
  226. for (unsigned j = 0; j < resourceEntries_.Size(); j++)
  227. {
  228. const BuildResourceEntry* entry = resourceEntries_[j];
  229. if (entry->packagePath_ == filename)
  230. {
  231. BuildWarn(ToString("Resource Path: %s already exists", filename.CString()));
  232. continue;
  233. }
  234. }
  235. // TODO: Add additional filters
  236. if (GetExtension(filename) == ".psd")
  237. continue;
  238. BuildResourceEntry* newEntry = new BuildResourceEntry;
  239. // BEGIN LICENSE MANAGEMENT
  240. if (GetExtension(filename) == ".mdl")
  241. {
  242. containsMDL_ = true;
  243. }
  244. // END LICENSE MANAGEMENT
  245. newEntry->absolutePath_ = resourceDir + filename;
  246. newEntry->resourceDir_ = resourceDir;
  247. newEntry->packagePath_ = filename;
  248. resourceEntries_.Push(newEntry);
  249. //LOGINFOF("Adding resource: %s : %s", newEntry->absolutePath_.CString(), newEntry->packagePath_.CString());
  250. }
  251. }
  252. void BuildBase::BuildProjectResourceEntries()
  253. {
  254. Vector<String> fileNames;
  255. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  256. fileSystem->ScanDir(fileNames, project_->GetResourcePath(), "*.*", SCAN_FILES, true);
  257. VariantMap resourceTags;
  258. AssetBuildConfig::ApplyConfig(resourceTags);
  259. VariantMap::ConstIterator itr = resourceTags.Begin();
  260. Vector<String> resources;
  261. while (itr != resourceTags.End())
  262. {
  263. if (itr->first_ == assetBuildTag_)
  264. {
  265. resources = itr->second_.GetStringVector();
  266. break;
  267. }
  268. itr++;
  269. }
  270. for (unsigned i = 0; i < fileNames.Size(); i++)
  271. {
  272. const String& filename = fileNames[i];
  273. for (unsigned j = 0; j < resourceEntries_.Size(); j++)
  274. {
  275. const BuildResourceEntry* entry = resourceEntries_[j];
  276. if (entry->packagePath_ == filename)
  277. {
  278. BuildWarn(ToString("Resource Path: %s already exists", filename.CString()));
  279. continue;
  280. }
  281. }
  282. for (auto it = resources.Begin(); it != resources.End(); ++it)
  283. {
  284. if (filename == (*it))
  285. {
  286. // TODO: Add additional filters
  287. if (GetExtension(filename) == ".psd")
  288. break;
  289. BuildResourceEntry* newEntry = new BuildResourceEntry;
  290. // BEGIN LICENSE MANAGEMENT
  291. if (GetExtension(filename) == ".mdl")
  292. {
  293. containsMDL_ = true;
  294. }
  295. // END LICENSE MANAGEMENT
  296. newEntry->absolutePath_ = project_->GetResourcePath() + filename;
  297. newEntry->resourceDir_ = project_->GetResourcePath();
  298. newEntry->packagePath_ = filename;
  299. resourceEntries_.Push(newEntry);
  300. resourcePackager_->AddResourceEntry(newEntry);
  301. break;
  302. }
  303. }
  304. }
  305. }
  306. void BuildBase::BuildResourceEntries()
  307. {
  308. for (unsigned i = 0; i < resourceDirs_.Size(); i++)
  309. {
  310. ScanResourceDirectory(resourceDirs_[i]);
  311. }
  312. if (resourcePackager_.NotNull())
  313. {
  314. for (unsigned i = 0; i < resourceEntries_.Size(); i++)
  315. {
  316. BuildResourceEntry* entry = resourceEntries_[i];
  317. resourcePackager_->AddResourceEntry(entry);
  318. }
  319. }
  320. }
  321. void BuildBase::GenerateResourcePackage(const String& resourcePackagePath)
  322. {
  323. resourcePackager_->GeneratePackage(resourcePackagePath);
  324. }
  325. void BuildBase::AddResourceDir(const String& dir)
  326. {
  327. assert(!resourceDirs_.Contains(dir));
  328. resourceDirs_.Push(dir);
  329. }
  330. void BuildBase::ReadAssetBuildConfig()
  331. {
  332. String projectPath = project_->GetProjectPath();
  333. projectPath = RemoveTrailingSlash(projectPath);
  334. String filename = projectPath + "Settings/AssetBuildConfig.json";
  335. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  336. if (!fileSystem->FileExists(filename))
  337. return;
  338. if (AssetBuildConfig::LoadFromFile(context_, filename))
  339. {
  340. VariantMap assetBuildConfig;
  341. AssetBuildConfig::ApplyConfig(assetBuildConfig);
  342. }
  343. }
  344. }