// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved // Please see LICENSE.md in repository root for license information // https://github.com/AtomicGameEngine/AtomicGameEngine #include "AtomicEditor.h" #include #include #include "../AEEditor.h" #include "../Project/AEProject.h" #include "../Project/ProjectUtils.h" #include "AEEvents.h" #include "Subprocess/AESubprocessSystem.h" #include "BuildIOS.h" #include "BuildSystem.h" #include "UI/Modal/UIModalOps.h" #include "Tools/External/AEExternalTooling.h" namespace AtomicEditor { BuildIOS::BuildIOS(Context * context) : BuildBase(context) { } BuildIOS::~BuildIOS() { } void BuildIOS::Initialize() { Editor* editor = GetSubsystem(); Project* project = editor->GetProject(); FileSystem* fileSystem = GetSubsystem(); #ifdef ATOMIC_PLATFORM_WINDOWS String bundleResources = fileSystem->GetProgramDir(); #else String bundleResources = fileSystem->GetAppBundleResourceFolder(); #endif String projectResources = project->GetResourcePath(); String coreDataFolder = bundleResources + "CoreData/"; AddResourceDir(coreDataFolder); AddResourceDir(projectResources); BuildResourceEntries(); } String BuildIOS::GenerateInfoPlist() { BuildSystem* buildSystem = GetSubsystem(); IOSBuildSettings settings = buildSystem->GetBuildSettings()->GetIOSSettings(); String plist = "\n"; plist += "\n"; plist += "\n"; plist += "\n"; plist += "CFBundleDevelopmentRegion\n"; plist += "English\n"; plist += "CFBundleExecutable\n"; plist += "AtomicPlayer\n"; plist += "CFBundleGetInfoString\n"; plist += "\"Atomic Player\"\n"; plist += "CFBundleIconFile\n"; plist += "\n"; plist += "CFBundleIdentifier\n"; plist.AppendWithFormat("%s\n", settings.package.CString()); plist += "CFBundleInfoDictionaryVersion\n"; plist += "6.0\n"; plist += "CFBundleLongVersionString\n"; plist += "\n"; plist += "CFBundleName\n"; plist += "\n"; plist += "CFBundlePackageType\n"; plist += "APPL\n"; plist += "CFBundleShortVersionString\n"; plist += "\n"; plist += "CFBundleSignature\n"; plist += "????\n"; plist += "CFBundleVersion\n"; plist += "\n"; plist += "CSResourcesFileMapped\n"; plist += "\n"; plist += "LSRequiresCarbon\n"; plist += "\n"; plist += "NSHumanReadableCopyright\n"; plist += "\"(c) Your Company\"\n"; plist += "CFBundleIconFiles\n"; plist += "\n"; plist += "\n"; plist += "\n"; plist += "\n"; plist += "\n"; return plist; } String BuildIOS::GenerateEntitlements() { BuildSystem* buildSystem = GetSubsystem(); IOSBuildSettings settings = buildSystem->GetBuildSettings()->GetIOSSettings(); String app_identifer = settings.appidPrefix + "."; app_identifer += settings.package; String team_identifier = settings.appidPrefix; String keychain_access_group = app_identifer; String entitlements = "\n"; entitlements += "\n"; entitlements += "\n"; entitlements += "\n"; entitlements += "application-identifier\n"; entitlements.AppendWithFormat("%s\n", app_identifer.CString()); entitlements += "com.apple.developer.team-identifier\n"; entitlements.AppendWithFormat("%s\n", team_identifier.CString()); entitlements += "get-task-allow\n"; entitlements += "\n"; entitlements += "keychain-access-groups\n"; entitlements += "\n"; entitlements.AppendWithFormat("%s\n", keychain_access_group.CString()); entitlements += "\n"; entitlements += "\n"; entitlements += "\n"; return entitlements; } void BuildIOS::HandleEvent(StringHash eventType, VariantMap& eventData) { if (eventType == E_SUBPROCESSOUTPUT) { } if (eventType == E_SUBPROCESSCOMPLETE) { BuildSystem* buildSystem = GetSubsystem(); int code = eventData[SubprocessComplete::P_RETCODE].GetInt(); if (!code) { // success if (currentBuildPhase_ == ConvertPList) { RunCodeSign(); } else if (currentBuildPhase_ == CodeSign) { RunDeploy(); } else if (currentBuildPhase_ == Deploy) { buildSystem->BuildComplete(AE_PLATFORM_IOS, buildPath_); } } else { buildSystem->BuildComplete(AE_PLATFORM_IOS, buildPath_, false); } } } void BuildIOS::RunConvertPList() { SubprocessSystem* subs = GetSubsystem(); Vector args = String("-convert binary1 ./AtomicPlayer.app/Info.plist").Split(' '); currentBuildPhase_ = ConvertPList; Subprocess* subprocess = subs->Launch("/usr/bin/plutil", args, buildPath_); if (!subprocess) { assert(0); // ERROR return; } SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildIOS, HandleEvent)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildIOS, HandleEvent)); UIModalOps* ops = GetSubsystem(); ops->ShowProgramOutput(subprocess); ops->PrintToProgramOutput("Starting iOS Deployment\n\n"); } void BuildIOS::RunCodeSign() { SubprocessSystem* subs = GetSubsystem(); Vector args; args.Push("--force"); args.Push("--sign"); args.Push("iPhone Developer"); args.Push("--entitlements"); args.Push("./AtomicPlayer.app.xcent"); args.Push("./AtomicPlayer.app"); Poco::Process::Env env; env["CODESIGN_ALLOCATE"] = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate"; currentBuildPhase_ = CodeSign; Subprocess* subprocess = subs->Launch("/usr/bin/codesign", args, buildPath_, env); if (!subprocess) { assert(0); // ERROR return; } SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildIOS, HandleEvent)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildIOS, HandleEvent)); UIModalOps* ops = GetSubsystem(); ops->SetProgramOutputSubprocess(subprocess); ops->PrintToProgramOutput("\n\nSigning iOS Deployment\n\n"); } void BuildIOS::RunDeploy() { SubprocessSystem* subs = GetSubsystem(); ExternalTooling* tooling = GetSubsystem(); String iosDeploy = tooling->GetToolApplicationPath(); iosDeploy += "CommandLine/ios-deploy"; Vector args; args.Push("--uninstall"); args.Push("--bundle"); args.Push("./AtomicPlayer.app"); currentBuildPhase_ = Deploy; Subprocess* subprocess = subs->Launch(iosDeploy.CString(), args, buildPath_); if (!subprocess) { assert(0); // ERROR return; } SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildIOS, HandleEvent)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildIOS, HandleEvent)); UIModalOps* ops = GetSubsystem(); ops->SetProgramOutputSubprocess(subprocess); ops->PrintToProgramOutput("\n\nDeploying to iOS Device\n\n"); } void BuildIOS::Build(const String& buildPath) { buildPath_ = buildPath + "/IOS-Build"; Initialize(); FileSystem* fileSystem = GetSubsystem(); if (fileSystem->DirExists(buildPath_)) fileSystem->RemoveDir(buildPath_, true); #ifdef ATOMIC_PLATFORM_WINDOWS String buildSourceDir = fileSystem->GetProgramDir(); #else String buildSourceDir = fileSystem->GetAppBundleResourceFolder(); #endif String buildAppSourceDir = buildSourceDir + "Deployment/IOS/AtomicPlayer.app"; fileSystem->CreateDir(buildPath_); String buildDestDist = buildPath_ + "/AtomicPlayer.app"; fileSystem->CreateDir(buildDestDist); String resourcePackagePath = buildDestDist + "/AtomicResources.pak"; GenerateResourcePackage(resourcePackagePath); fileSystem->Copy(buildAppSourceDir + "/AtomicPlayer", buildDestDist + "/AtomicPlayer"); fileSystem->Copy(buildAppSourceDir + "/PkgInfo", buildDestDist + "/PkgInfo"); BuildSystem* buildSystem = GetSubsystem(); IOSBuildSettings settings = buildSystem->GetBuildSettings()->GetIOSSettings(); fileSystem->Copy(settings.provisionFile, buildDestDist + "/embedded.mobileprovision"); String entitlements = GenerateEntitlements(); String plist = GenerateInfoPlist(); File file(context_, buildPath_ + "/AtomicPlayer.app.xcent", FILE_WRITE); if (file.IsOpen()) { file.Write(entitlements.CString(), entitlements.Length()); file.Close(); } File pfile(context_, buildDestDist + "/Info.plist", FILE_WRITE); if (pfile.IsOpen()) { pfile.Write(plist.CString(), plist.Length()); pfile.Close(); } RunConvertPList(); } }