NETProjectSystem.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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/Environment.h>
  23. #include <Atomic/Core/CoreEvents.h>
  24. #include <Atomic/Core/ProcessUtils.h>
  25. #include <Atomic/IO/Log.h>
  26. #include <Atomic/IO/FileSystem.h>
  27. #include <Atomic/Resource/ResourceEvents.h>
  28. #include <Atomic/UI/UIEvents.h>
  29. #include <Atomic/Input/InputEvents.h>
  30. #include <AtomicEditor/EditorMode/AEEditorEvents.h>
  31. #include "../ToolSystem.h"
  32. #include "../ToolEnvironment.h"
  33. #include "../Assets/AssetEvents.h"
  34. #include "../Assets/AssetDatabase.h"
  35. #include "../Project/Project.h"
  36. #include "../Project/ProjectSettings.h"
  37. #include "../Project/ProjectEvents.h"
  38. #include "../Build/BuildSystem.h"
  39. #include "../Subprocess/SubprocessSystem.h"
  40. #include "NETProjectGen.h"
  41. #include "NETBuildSystem.h"
  42. #include "NETProjectSystem.h"
  43. #ifdef ATOMIC_PLATFORM_WINDOWS
  44. #include <Poco/WinRegistryKey.h>
  45. #endif
  46. namespace ToolCore
  47. {
  48. NETProjectSystem::NETProjectSystem(Context* context) :
  49. Object(context)
  50. {
  51. Initialize();
  52. }
  53. NETProjectSystem::~NETProjectSystem()
  54. {
  55. }
  56. void NETProjectSystem::Clear()
  57. {
  58. quietPeriod_ = 0.0f;
  59. solutionPath_.Clear();
  60. projectAssemblyPath_.Clear();
  61. solutionDirty_ = false;
  62. projectAssemblyDirty_ = false;
  63. }
  64. void NETProjectSystem::OpenSolution()
  65. {
  66. if (!idePath_.Length())
  67. return;
  68. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  69. if (!fileSystem->FileExists(solutionPath_))
  70. {
  71. if (!GenerateSolution())
  72. return;
  73. }
  74. OpenSourceFile(String::EMPTY);
  75. }
  76. void NETProjectSystem::OpenSourceFile(const String& sourceFilePath)
  77. {
  78. if (!idePath_.Length())
  79. return;
  80. String command = idePath_;
  81. StringVector args;
  82. if (ideSubprocess_.Expired())
  83. {
  84. SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
  85. ideSubprocess_ = 0;
  86. #ifdef ATOMIC_PLATFORM_OSX
  87. command = "open";
  88. args.Push("-W");
  89. args.Push("-a");
  90. args.Push(idePath_);
  91. #endif
  92. args.Push(solutionPath_);
  93. if (sourceFilePath.Length())
  94. args.Push(sourceFilePath);
  95. #ifndef ATOMIC_PLATFORM_OSX
  96. QuoteArguments(args);
  97. #endif
  98. try
  99. {
  100. ideSubprocess_ = subs->Launch(command, args);
  101. }
  102. catch (Poco::SystemException)
  103. {
  104. ideSubprocess_ = 0;
  105. }
  106. }
  107. else
  108. {
  109. if (!sourceFilePath.Length())
  110. return;
  111. try
  112. {
  113. std::vector<std::string> args;
  114. #ifdef ATOMIC_PLATFORM_WINDOWS
  115. args.push_back("/edit");
  116. #elif defined ATOMIC_PLATFORM_OSX
  117. command = "open";
  118. args.push_back("-a");
  119. args.push_back(idePath_.CString());
  120. #elif defined ATOMIC_PLATFORM_LINUX
  121. args.push_back(idePath_.CString());
  122. #endif
  123. #ifdef ATOMIC_PLATFORM_OSX
  124. args.push_back(sourceFilePath.CString());
  125. #else
  126. if (sourceFilePath.Contains(" ") && !sourceFilePath.Contains("\""))
  127. args.push_back(("\"" + sourceFilePath + "\"").CString());
  128. else
  129. args.push_back(sourceFilePath.CString());
  130. #endif
  131. Poco::Process::launch(command.CString(), args);
  132. }
  133. catch (Poco::SystemException)
  134. {
  135. }
  136. }
  137. }
  138. void NETProjectSystem::HandleNETBuildResult(StringHash eventType, VariantMap& eventData)
  139. {
  140. using namespace NETBuildResult;
  141. if (eventData[P_SUCCESS].GetBool())
  142. {
  143. ATOMIC_LOGINFOF("NETBuild Success for project");
  144. }
  145. else
  146. {
  147. const String& errorText = eventData[P_ERRORTEXT].GetString();
  148. ATOMIC_LOGERRORF("\n%s\n", errorText.CString());
  149. ATOMIC_LOGERRORF("NETBuild Error for project");
  150. String myerror; // too much error for the dialog! scrape off just the error summary
  151. unsigned where = errorText.Find("Errors:", 0, true);
  152. if ( where != String::NPOS)
  153. myerror = errorText.Substring (where, errorText.Length() - where);
  154. else myerror = errorText; // failed to find summary, send the whole text
  155. using namespace AtomicEditor;
  156. VariantMap errorData;
  157. errorData[EditorModal::P_TYPE] = EDITOR_MODALERROR;
  158. errorData[EditorModal::P_TITLE] = String("NETBuild Errors");
  159. errorData[EditorModal::P_MESSAGE] = myerror;
  160. SendEvent(E_EDITORMODAL, errorData);
  161. }
  162. }
  163. void NETProjectSystem::BuildAtomicProject()
  164. {
  165. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  166. if (!fileSystem->FileExists(solutionPath_))
  167. {
  168. if (!GenerateSolution())
  169. {
  170. ATOMIC_LOGERRORF("NETProjectSystem::BuildAtomicProject - solutionPath does not exist: %s", solutionPath_.CString());
  171. return;
  172. }
  173. }
  174. Project* project = GetSubsystem<ToolSystem>()->GetProject();
  175. NETBuildSystem* buildSystem = GetSubsystem<NETBuildSystem>();
  176. if (buildSystem)
  177. {
  178. NETBuild* build = buildSystem->BuildAtomicProject(project);
  179. if (build)
  180. {
  181. build->SubscribeToEvent(E_NETBUILDRESULT, ATOMIC_HANDLER(NETProjectSystem, HandleNETBuildResult));
  182. }
  183. }
  184. }
  185. bool NETProjectSystem::GenerateResourcePak()
  186. {
  187. ToolSystem* tsystem = GetSubsystem<ToolSystem>();
  188. Project* project = tsystem->GetProject();
  189. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  190. // TODO: We just use WINDOWS platform for PAK generation for now
  191. Platform* platform = tsystem->GetPlatformByName("WINDOWS");
  192. buildSystem->SetBuildPath(project->GetProjectPath() + "AtomicNET/Resources/");
  193. SharedPtr<BuildBase> buildBase(platform->NewBuild(project));
  194. buildBase->SetResourcesOnly(true);
  195. buildBase->SetVerbose(true);
  196. buildSystem->QueueBuild(buildBase);
  197. buildSystem->StartNextBuild();
  198. if (buildBase->GetBuildFailed())
  199. {
  200. const StringVector& errors = buildBase->GetBuildErrors();
  201. ATOMIC_LOGERRORF("NETProjectSystem::GenerateSolution - Unable to Build Resources.pak: %s", errors.Size() ? errors[0].CString() : "Unknown Error");
  202. return false;
  203. }
  204. return true;
  205. }
  206. bool NETProjectSystem::GenerateSolution()
  207. {
  208. ToolSystem* tsystem = GetSubsystem<ToolSystem>();
  209. Project* project = tsystem->GetProject();
  210. if (!project)
  211. {
  212. ATOMIC_LOGERRORF("NETProjectSystem::GenerateSolution - No Project Loaded");
  213. return false;
  214. }
  215. // TODO: Generalize and move me
  216. if (project->GetSupportsPlatform("android") || project->GetSupportsPlatform("ios"))
  217. {
  218. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  219. if (!fileSystem->FileExists(project->GetProjectPath() + "AtomicNET/Resources/AtomicResources.pak"))
  220. {
  221. if (!GenerateResourcePak())
  222. return false;
  223. }
  224. }
  225. SharedPtr<NETProjectGen> gen(new NETProjectGen(context_));
  226. if (!gen->LoadAtomicProject(project->GetProjectPath()))
  227. {
  228. ATOMIC_LOGERRORF("NETProjectSystem::GenerateSolution - Unable to Load Project");
  229. return false;
  230. }
  231. if (!gen->Generate())
  232. {
  233. ATOMIC_LOGERRORF("NETProjectSystem::GenerateSolution - Unable to Generate Project");
  234. return false;
  235. }
  236. return true;
  237. }
  238. void NETProjectSystem::HandleUpdate(StringHash eventType, VariantMap& eventData)
  239. {
  240. using namespace Update;
  241. float delta = eventData[P_TIMESTEP].GetFloat();
  242. quietPeriod_ -= delta;
  243. if (quietPeriod_ < 0.0f)
  244. quietPeriod_ = 0.0f;
  245. if (quietPeriod_ > 0.0f)
  246. return;
  247. if (solutionDirty_)
  248. {
  249. // set to false in case of error, we don't want to keep trying to
  250. // rebuild, TODO: better error handling
  251. solutionDirty_ = false;
  252. GenerateSolution();
  253. }
  254. if (projectAssemblyDirty_)
  255. {
  256. BuildAtomicProject();
  257. projectAssemblyDirty_ = false;
  258. }
  259. }
  260. void NETProjectSystem::HandleProjectLoaded(StringHash eventType, VariantMap& eventData)
  261. {
  262. using namespace ProjectLoaded;
  263. projectPath_ = eventData[P_PROJECTPATH].GetString();
  264. Project* project = static_cast<Project*>(eventData[P_PROJECT].GetPtr());
  265. if (GetExtension(projectPath_) == ".atomic")
  266. projectPath_ = GetParentPath(projectPath_);
  267. String projectName = project->GetProjectSettings()->GetName();
  268. solutionPath_ = AddTrailingSlash(projectPath_) + "AtomicNET/Solution/" + projectName + ".sln";
  269. projectAssemblyPath_ = AddTrailingSlash(projectPath_) + "Resources/" + projectName + ".dll";
  270. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  271. // TODO: We need a better way of marking C# projects
  272. StringVector results;
  273. fileSystem->ScanDir(results, AddTrailingSlash(projectPath_) + "Resources", "*.cs", SCAN_FILES, true);
  274. if (!results.Size())
  275. {
  276. fileSystem->ScanDir(results, AddTrailingSlash(projectPath_) + "Resources", "*.dll", SCAN_FILES, true);
  277. if (!results.Size())
  278. {
  279. solutionPath_.Clear();
  280. return;
  281. }
  282. }
  283. // if the solution or project assemblies don't exist mark as dirty
  284. if (!fileSystem->FileExists(solutionPath_))
  285. solutionDirty_ = true;
  286. if (!fileSystem->FileExists(projectAssemblyPath_))
  287. projectAssemblyDirty_ = true;
  288. }
  289. void NETProjectSystem::HandleAssetScanBegin(StringHash eventType, VariantMap& eventData)
  290. {
  291. }
  292. void NETProjectSystem::HandleAssetScanEnd(StringHash eventType, VariantMap& eventData)
  293. {
  294. if (solutionDirty_)
  295. {
  296. // set to false in case of error, we don't want to keep trying to
  297. // rebuild, TODO: better error handling
  298. solutionDirty_ = false;
  299. GenerateSolution();
  300. }
  301. }
  302. void NETProjectSystem::HandleProjectUnloaded(StringHash eventType, VariantMap& eventData)
  303. {
  304. Clear();
  305. }
  306. void NETProjectSystem::HandleFileChanged(StringHash eventType, VariantMap& eventData)
  307. {
  308. }
  309. void NETProjectSystem::HandleAssetNew(StringHash eventType, VariantMap& eventData)
  310. {
  311. using namespace ResourceAdded;
  312. const String& guid = eventData[P_GUID].ToString();
  313. Asset* asset = GetSubsystem<AssetDatabase>()->GetAssetByGUID(guid);
  314. if (asset->GetExtension() == ".cs")
  315. solutionDirty_ = true;
  316. }
  317. void NETProjectSystem::HandleResourceAdded(StringHash eventType, VariantMap& eventData)
  318. {
  319. }
  320. void NETProjectSystem::HandleResourceRemoved(StringHash eventType, VariantMap& eventData)
  321. {
  322. }
  323. void NETProjectSystem::HandleAssetRenamed(StringHash eventType, VariantMap& eventData)
  324. {
  325. }
  326. void NETProjectSystem::HandleAssetMoved(StringHash eventType, VariantMap& eventData)
  327. {
  328. }
  329. /// handle the results of a recompile, and auto play the project if successful
  330. void NETProjectSystem::HandleNETBuildPlay(StringHash eventType, VariantMap& eventData)
  331. {
  332. using namespace NETBuildResult;
  333. if (eventData[P_SUCCESS].GetBool())
  334. {
  335. ATOMIC_LOGINFOF("NETBuild Success for project, now playing.");
  336. VariantMap shortcutData; // encode a shortcut event to send a play
  337. shortcutData[UIShortcut::P_KEY] = KEY_P;
  338. shortcutData[UIShortcut::P_QUALIFIERS] = QUAL_CTRL;
  339. SendEvent(E_UISHORTCUT, shortcutData);
  340. }
  341. else
  342. {
  343. const String& errorText = eventData[P_ERRORTEXT].GetString();
  344. ATOMIC_LOGERRORF("\n%s\n", errorText.CString());
  345. ATOMIC_LOGERRORF("NETBuild Error for project, will not play.");
  346. String myerror; // too much error for the dialog! scrape off just the error summary
  347. unsigned where = errorText.Find("Errors:", 0, true);
  348. if ( where != String::NPOS)
  349. myerror = errorText.Substring (where, errorText.Length() - where);
  350. else myerror = errorText; // failed to find summary, send the whole text
  351. using namespace AtomicEditor;
  352. VariantMap errorData;
  353. errorData[EditorModal::P_TYPE] = EDITOR_MODALERROR;
  354. errorData[EditorModal::P_TITLE] = String("NETBuild Error for project");
  355. errorData[EditorModal::P_MESSAGE] = myerror;
  356. SendEvent(E_EDITORMODAL, errorData);
  357. }
  358. }
  359. /// used to ensure the project can be run from the editor
  360. bool NETProjectSystem::BuildAndRun()
  361. {
  362. bool shouldRebuild = CheckForRebuild();
  363. if (shouldRebuild)
  364. {
  365. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  366. if (!fileSystem->FileExists(solutionPath_))
  367. {
  368. if (!GenerateSolution())
  369. {
  370. ATOMIC_LOGERRORF("NETProjectSystem::BuildAtomicProject - solutionPath does not exist: %s", solutionPath_.CString());
  371. return false;
  372. }
  373. }
  374. Project* project = GetSubsystem<ToolSystem>()->GetProject();
  375. NETBuildSystem* buildSystem = GetSubsystem<NETBuildSystem>();
  376. if (buildSystem)
  377. {
  378. NETBuild* build = buildSystem->BuildAtomicProject(project);
  379. if (build)
  380. {
  381. build->SubscribeToEvent(E_NETBUILDRESULT, ATOMIC_HANDLER(NETProjectSystem, HandleNETBuildPlay));
  382. }
  383. }
  384. }
  385. return shouldRebuild;
  386. }
  387. /// interrogate sources to see if any are dirty compared to the editor project.dll
  388. bool NETProjectSystem::CheckForRebuild()
  389. {
  390. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  391. AssetDatabase* db = GetSubsystem<AssetDatabase>();
  392. Project* project = GetSubsystem<ToolSystem>()->GetProject();
  393. unsigned dllTimestamp = 0;
  394. String mydll = project->GetResourcePath() + project->GetProjectSettings()->GetName() + ".dll";
  395. Asset* myasset = db->GetAssetByPath(mydll);
  396. if (myasset)
  397. dllTimestamp = myasset->GetFileTimestamp();
  398. else
  399. return true; // dll not there, needs to be built, or the sources dont compile, or some other error
  400. int nn=0;
  401. Asset* filex = NULL;
  402. StringVector results;
  403. String tdir = AddTrailingSlash(project->GetResourcePath());
  404. fileSystem->ScanDir(results, tdir, "*.cs", SCAN_FILES, true);
  405. for (nn=0; nn<results.Size(); nn++)
  406. {
  407. filex = db->GetAssetByPath(tdir + results[nn]);
  408. if (filex && filex->GetFileTimestamp() > dllTimestamp )
  409. return true; // some file is younger or dirtier, dll needs to be rebuilt
  410. }
  411. return false; // the dll is up to date, no need to recompile
  412. }
  413. void NETProjectSystem::Initialize()
  414. {
  415. Clear();
  416. SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(NETProjectSystem, HandleUpdate));
  417. SubscribeToEvent(E_PROJECTLOADED, ATOMIC_HANDLER(NETProjectSystem, HandleProjectLoaded));
  418. SubscribeToEvent(E_PROJECTUNLOADED, ATOMIC_HANDLER(NETProjectSystem, HandleProjectUnloaded));
  419. SubscribeToEvent(E_ASSETSCANBEGIN, ATOMIC_HANDLER(NETProjectSystem, HandleAssetScanBegin));
  420. SubscribeToEvent(E_ASSETSCANEND, ATOMIC_HANDLER(NETProjectSystem, HandleAssetScanEnd));
  421. SubscribeToEvent(E_FILECHANGED, ATOMIC_HANDLER(NETProjectSystem, HandleFileChanged));
  422. SubscribeToEvent(E_RESOURCEADDED, ATOMIC_HANDLER(NETProjectSystem, HandleResourceAdded));
  423. SubscribeToEvent(E_RESOURCEREMOVED, ATOMIC_HANDLER(NETProjectSystem, HandleResourceRemoved));
  424. SubscribeToEvent(E_ASSETNEW, ATOMIC_HANDLER(NETProjectSystem, HandleAssetNew));
  425. SubscribeToEvent(E_ASSETRENAMED, ATOMIC_HANDLER(NETProjectSystem, HandleAssetRenamed));
  426. SubscribeToEvent(E_ASSETMOVED, ATOMIC_HANDLER(NETProjectSystem, HandleAssetMoved));
  427. #ifdef ATOMIC_PLATFORM_WINDOWS
  428. // On Windows, we first check for VS2015, then VS2017 which
  429. // at the time of this comment is in RC, refactor once
  430. // in general release
  431. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  432. // Query for Visual Studio 2015 path
  433. idePath_ = Poco::Environment::get("VS140COMNTOOLS", "").c_str();
  434. if (idePath_.Length())
  435. {
  436. idePath_.Replace("Tools\\", "IDE\\devenv.exe");
  437. if (!fileSystem->FileExists(idePath_))
  438. idePath_.Clear();
  439. }
  440. // If we didn't find VS2015, look for VS2017
  441. if (!idePath_.Length())
  442. {
  443. // check for VS2017
  444. Poco::WinRegistryKey regKey("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\SxS\\VS7", true);
  445. if (regKey.exists() && regKey.exists("15.0"))
  446. idePath_ = regKey.getString("15.0").c_str();
  447. if (idePath_.Length())
  448. {
  449. // We have VS2017
  450. idePath_ += "Common7\\IDE\\devenv.exe";
  451. }
  452. }
  453. #elif defined ATOMIC_PLATFORM_OSX
  454. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  455. // first look for Visual Studio Mac
  456. idePath_ = "/Applications/Visual Studio.app/Contents/MacOS/VisualStudio";
  457. if (!fileSystem->FileExists(idePath_))
  458. {
  459. // not found, look for Xamarin Studio
  460. idePath_ = "/Applications/Xamarin Studio.app/Contents/MacOS/XamarinStudio";
  461. if (!fileSystem->FileExists(idePath_))
  462. {
  463. idePath_.Clear();
  464. }
  465. }
  466. #elif defined ATOMIC_PLATFORM_LINUX
  467. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  468. if (fileSystem->FileExists("/usr/bin/monodevelop"))
  469. {
  470. idePath_ = "/usr/bin/monodevelop";
  471. }
  472. #endif
  473. }
  474. bool AtomicNETCopyAssemblies(Context* context, const String& dstFolder)
  475. {
  476. FileSystem* fileSystem = context->GetSubsystem<FileSystem>();
  477. ToolEnvironment* tenv = context->GetSubsystem<ToolEnvironment>();
  478. StringVector results;
  479. fileSystem->ScanDir(results, tenv->GetAtomicNETCoreAssemblyDir(), "*", SCAN_FILES, true);
  480. for (unsigned i = 0; i < results.Size(); i++)
  481. {
  482. String srcFile = tenv->GetAtomicNETCoreAssemblyDir() + results[i];
  483. String dstFile = dstFolder + results[i];
  484. unsigned srcModifiedTime = 0;
  485. if (fileSystem->FileExists(srcFile))
  486. {
  487. srcModifiedTime = fileSystem->GetLastModifiedTime(srcFile);
  488. }
  489. // skip if same modified time
  490. if (srcModifiedTime && fileSystem->FileExists(dstFile))
  491. {
  492. if (srcModifiedTime == fileSystem->GetLastModifiedTime(dstFile))
  493. {
  494. ATOMIC_LOGDEBUGF("NETProjectSystem::CopyAtomicAssemblies - Skipping AtomicNET %s as %s exists and has same modified time", srcFile.CString(), dstFile.CString());
  495. continue;
  496. }
  497. ATOMIC_LOGDEBUGF("NETProjectSystem::CopyAtomicAssemblies - %u %u", srcModifiedTime, fileSystem->GetLastModifiedTime(dstFile));
  498. }
  499. String dstPath = GetPath(dstFile);
  500. if (!fileSystem->CreateDirsRecursive(dstPath))
  501. {
  502. ATOMIC_LOGERRORF("NETProjectGen::CopyAtomicAssemblies - Unable to create folder: %s", dstPath.CString());
  503. continue;
  504. }
  505. if (!fileSystem->Copy(srcFile, dstFile))
  506. {
  507. ATOMIC_LOGERRORF("NETProjectGen::CopyAtomicAssemblies - Unable to copy file from: %s to %s", srcFile.CString(), dstPath.CString());
  508. continue;
  509. }
  510. // Update time so we don't needlessly copy
  511. if (srcModifiedTime)
  512. fileSystem->SetLastModifiedTime(dstFile, srcModifiedTime);
  513. ATOMIC_LOGDEBUGF(" NETProjectSystem::CopyAtomicAssemblies - Copied AtomicNET %s to %s", srcFile.CString(), dstPath.CString());
  514. }
  515. return true;
  516. }
  517. }