BuildAndroid.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. //
  2. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  3. // LICENSE: Atomic Game Engine Editor and Tools EULA
  4. // Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
  5. // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
  6. //
  7. #include <Atomic/Core/StringUtils.h>
  8. #include <Atomic/IO/FileSystem.h>
  9. #include <Atomic/IO/File.h>
  10. #include <Atomic/IO/MemoryBuffer.h>
  11. #include "../ToolSystem.h"
  12. #include "../ToolEnvironment.h"
  13. #include "../Subprocess/SubprocessSystem.h"
  14. #include "../Project/Project.h"
  15. #include "../Project/ProjectBuildSettings.h"
  16. #include "../Platform/PlatformAndroid.h"
  17. #include "AndroidProjectGenerator.h"
  18. #include "BuildSystem.h"
  19. #include "BuildEvents.h"
  20. #include "BuildAndroid.h"
  21. namespace ToolCore
  22. {
  23. BuildAndroid::BuildAndroid(Context* context, Project* project) : BuildBase(context, project, PLATFORMID_ANDROID)
  24. {
  25. ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
  26. // this cast isn't great
  27. platformAndroid_ = (PlatformAndroid*) toolSystem->GetPlatformByID(PLATFORMID_ANDROID);
  28. }
  29. BuildAndroid::~BuildAndroid()
  30. {
  31. }
  32. void BuildAndroid::HandleADBStartActivityComplete(StringHash eventType, VariantMap& eventData)
  33. {
  34. }
  35. // adb shell am start -n com.package.name/com.package.name.ActivityName
  36. void BuildAndroid::RunADBStartActivity()
  37. {
  38. SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
  39. String adbCommand = platformAndroid_->GetADBCommand();
  40. ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
  41. Project* project = toolSystem->GetProject();
  42. AndroidBuildSettings* settings = project->GetBuildSettings()->GetAndroidBuildSettings();
  43. String stringArgs;
  44. const char* cpackage = settings->GetPackageName().CString();
  45. stringArgs.AppendWithFormat("shell am start -n %s/%s.AtomicGameEngine",cpackage, cpackage);
  46. Vector<String> args = stringArgs.Split(' ');
  47. currentBuildPhase_ = ADBStartActivity;
  48. Subprocess* subprocess = subs->Launch(adbCommand, args, buildPath_);
  49. if (!subprocess)
  50. {
  51. FailBuild("BuildFailed::RunStartActivity");
  52. return;
  53. }
  54. VariantMap buildOutput;
  55. buildOutput[BuildOutput::P_TEXT] = "\n\n<color #D4FB79>Starting Android Activity</color>\n\n";
  56. SendEvent(E_BUILDOUTPUT, buildOutput);
  57. SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildAndroid, HandleADBStartActivityComplete));
  58. SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildBase, HandleSubprocessOutputEvent));
  59. }
  60. void BuildAndroid::HandleRunADBInstallComplete(StringHash eventType, VariantMap& eventData)
  61. {
  62. int code = eventData[SubprocessComplete::P_RETCODE].GetInt();
  63. if (!code)
  64. {
  65. RunADBStartActivity();
  66. }
  67. else
  68. {
  69. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  70. buildSystem->BuildComplete(PLATFORMID_ANDROID, buildPath_, false);
  71. }
  72. }
  73. void BuildAndroid::RunADBInstall()
  74. {
  75. SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
  76. String adbCommand = platformAndroid_->GetADBCommand();
  77. Vector<String> args = String("install -r ./bin/Atomic-debug-unaligned.apk").Split(' ');
  78. currentBuildPhase_ = ADBInstall;
  79. Subprocess* subprocess = subs->Launch(adbCommand, args, buildPath_);
  80. if (!subprocess)
  81. {
  82. FailBuild("BuildFailed::RunADBInstall");
  83. return;
  84. }
  85. VariantMap buildOutput;
  86. buildOutput[BuildOutput::P_TEXT] = "\n\n<color #D4FB79>Installing on Android Device</color>\n\n";
  87. SendEvent(E_BUILDOUTPUT, buildOutput);
  88. SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildAndroid, HandleRunADBInstallComplete));
  89. SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildBase, HandleSubprocessOutputEvent));
  90. }
  91. void BuildAndroid::HandleADBListDevicesComplete(StringHash eventType, VariantMap& eventData)
  92. {
  93. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  94. int code = eventData[SubprocessComplete::P_RETCODE].GetInt();
  95. if (!code)
  96. {
  97. // check if we have any devices attached, otherwise adb install
  98. // will hang looking for devices
  99. bool noDevices = true;
  100. if (deviceListText_.Length())
  101. {
  102. MemoryBuffer reader(deviceListText_.CString(), deviceListText_.Length() + 1);
  103. while (!reader.IsEof())
  104. {
  105. String line = reader.ReadLine();
  106. if (line.Length() && line[0] >= '0' && line[0] <= '9')
  107. {
  108. noDevices = false;
  109. break;
  110. }
  111. }
  112. }
  113. if (!noDevices)
  114. RunADBInstall();
  115. else
  116. {
  117. // can't proceed, though success
  118. buildSystem->BuildComplete(PLATFORMID_ANDROID, buildPath_);
  119. }
  120. }
  121. else
  122. {
  123. buildSystem->BuildComplete(PLATFORMID_ANDROID, buildPath_, false);
  124. }
  125. }
  126. void BuildAndroid::HandleADBListDevicesOutputEvent(StringHash eventType, VariantMap& eventData)
  127. {
  128. // E_SUBPROCESSOUTPUT
  129. const String& text = eventData[SubprocessOutput::P_TEXT].GetString();
  130. deviceListText_ += text;
  131. // convert to a build output event and forward to subscribers
  132. VariantMap buildOutputData;
  133. buildOutputData[BuildOutput::P_TEXT] = text;
  134. SendEvent(E_BUILDOUTPUT, buildOutputData);
  135. }
  136. void BuildAndroid::RunADBListDevices()
  137. {
  138. ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
  139. SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
  140. String adbCommand = platformAndroid_->GetADBCommand();
  141. deviceListText_.Clear();
  142. Vector<String> args = String("devices").Split(' ');
  143. currentBuildPhase_ = ADBListDevices;
  144. Subprocess* subprocess = subs->Launch(adbCommand, args, "");
  145. if (!subprocess)
  146. {
  147. FailBuild("BuildFailed::RunADBListDevices");
  148. return;
  149. }
  150. VariantMap buildOutput;
  151. buildOutput[BuildOutput::P_TEXT] = "\n\n<color #D4FB79>Listing Android Devices</color>\n\n";
  152. SendEvent(E_BUILDOUTPUT, buildOutput);
  153. SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildAndroid, HandleADBListDevicesComplete));
  154. SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildAndroid, HandleADBListDevicesOutputEvent));
  155. }
  156. void BuildAndroid::HandleAntDebugComplete(StringHash eventType, VariantMap& eventData)
  157. {
  158. int code = eventData[SubprocessComplete::P_RETCODE].GetInt();
  159. if (!code)
  160. {
  161. RunADBListDevices();
  162. }
  163. else
  164. {
  165. BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
  166. buildSystem->BuildComplete(PLATFORMID_ANDROID, buildPath_, false);
  167. }
  168. }
  169. void BuildAndroid::RunAntDebug()
  170. {
  171. ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
  172. SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
  173. ToolPrefs* tprefs = tenv->GetToolPrefs();
  174. Poco::Process::Env env;
  175. #ifdef ATOMIC_PLATFORM_OSX
  176. String antCommand = tprefs->GetAntPath();
  177. Vector<String> args;
  178. args.Push("debug");
  179. #else
  180. // C:\ProgramData\Oracle\Java\javapath;
  181. Vector<String> args;
  182. String antCommand = "cmd";
  183. String antPath = tprefs->GetAntPath() + "/ant.bat";
  184. env["JAVA_HOME"] = tprefs->GetJDKRootPath().CString();
  185. // ant is a batch file on windows, so have to run with cmd /c
  186. args.Push("/c");
  187. args.Push("\"" + antPath + "\"");
  188. args.Push("debug");
  189. #endif
  190. currentBuildPhase_ = AntBuildDebug;
  191. Subprocess* subprocess = subs->Launch(antCommand, args, buildPath_, env);
  192. if (!subprocess)
  193. {
  194. FailBuild("BuildFailed::RunAntDebug");
  195. return;
  196. }
  197. VariantMap buildOutput;
  198. buildOutput[BuildOutput::P_TEXT] = "<color #D4FB79>Starting Android Deployment</color>\n\n";
  199. SendEvent(E_BUILDOUTPUT, buildOutput);
  200. SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildAndroid, HandleAntDebugComplete));
  201. SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildBase, HandleSubprocessOutputEvent));
  202. }
  203. void BuildAndroid::Initialize()
  204. {
  205. ToolSystem* tsystem = GetSubsystem<ToolSystem>();
  206. Project* project = tsystem->GetProject();
  207. Vector<String> defaultResourcePaths;
  208. GetDefaultResourcePaths(defaultResourcePaths);
  209. String projectResources = project->GetResourcePath();
  210. for (unsigned i = 0; i < defaultResourcePaths.Size(); i++)
  211. {
  212. AddResourceDir(defaultResourcePaths[i]);
  213. }
  214. // TODO: smart filtering of cache
  215. AddResourceDir(project->GetProjectPath() + "Cache/");
  216. AddResourceDir(projectResources);
  217. BuildResourceEntries();
  218. }
  219. void BuildAndroid::Build(const String& buildPath)
  220. {
  221. ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
  222. ToolSystem* tsystem = GetSubsystem<ToolSystem>();
  223. Project* project = tsystem->GetProject();
  224. buildPath_ = AddTrailingSlash(buildPath) + GetBuildSubfolder();
  225. Initialize();
  226. //generate manifest file
  227. String manifest;
  228. for (unsigned i = 0; i < resourceEntries_.Size(); i++)
  229. {
  230. BuildResourceEntry* entry = resourceEntries_[i];
  231. manifest += entry->packagePath_;
  232. if ( i != resourceEntries_.Size() - 1)
  233. manifest += ";";
  234. }
  235. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  236. if (fileSystem->DirExists(buildPath_))
  237. fileSystem->RemoveDir(buildPath_, true);
  238. String buildSourceDir = tenv->GetToolDataDir();
  239. String androidProject = buildSourceDir + "Deployment/Android";
  240. // Copy the base android project
  241. fileSystem->CopyDir(androidProject, buildPath_);
  242. Vector<String> defaultResourcePaths;
  243. GetDefaultResourcePaths(defaultResourcePaths);
  244. String projectResources = project->GetResourcePath();
  245. for (unsigned i = 0; i < defaultResourcePaths.Size(); i++)
  246. {
  247. fileSystem->CopyDir(defaultResourcePaths[i], buildPath_ + "/assets/" + GetFileName(RemoveTrailingSlash(defaultResourcePaths[i])));
  248. }
  249. fileSystem->CopyDir(project->GetProjectPath() + "Cache/", buildPath_ + "/assets/Cache");
  250. fileSystem->CopyDir(projectResources, buildPath_ + "/assets/AtomicResources");
  251. // write the manifest
  252. SharedPtr<File> mfile(new File(context_, buildPath_ + "/assets/AtomicManifest", FILE_WRITE));
  253. mfile->WriteString(manifest);
  254. mfile->Close();
  255. AndroidProjectGenerator gen(context_);
  256. gen.SetBuildPath(buildPath_);
  257. if (!gen.Generate())
  258. {
  259. FailBuild(gen.GetErrorText());
  260. return;
  261. }
  262. RunAntDebug();
  263. }
  264. }