// // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include #include #include #include "../ToolSystem.h" #include "../ToolEnvironment.h" #include "../Project/Project.h" #include "../Project/ProjectBuildSettings.h" #include "../Assets/AssetDatabase.h" #include "../Subprocess/SubprocessSystem.h" #include "BuildIOS.h" #include "BuildEvents.h" #include "BuildSystem.h" namespace ToolCore { BuildIOS::BuildIOS(Context * context, Project *project) : BuildBase(context, project, PLATFORMID_IOS) { } BuildIOS::~BuildIOS() { } String BuildIOS::GenerateInfoPlist() { ToolSystem* tsystem = GetSubsystem(); Project* project = tsystem->GetProject(); ProjectBuildSettings* buildSettings = project->GetBuildSettings(); IOSBuildSettings* iosSettings = buildSettings->GetIOSBuildSettings(); 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", iosSettings->GetPackageName().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() { ToolSystem* tsystem = GetSubsystem(); Project* project = tsystem->GetProject(); ProjectBuildSettings* buildSettings = project->GetBuildSettings(); IOSBuildSettings* iosSettings = buildSettings->GetIOSBuildSettings(); String app_identifer = iosSettings->GetAppIDPrefix() + "."; app_identifer += iosSettings->GetPackageName(); String team_identifier = iosSettings->GetAppIDPrefix(); 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::Initialize() { ToolSystem* tsystem = GetSubsystem(); Project* project = tsystem->GetProject(); Vector defaultResourcePaths; GetDefaultResourcePaths(defaultResourcePaths); for (unsigned i = 0; i < defaultResourcePaths.Size(); i++) { AddResourceDir(defaultResourcePaths[i]); } BuildDefaultResourceEntries(); // TODO: smart filtering of cache String projectResources = project->GetResourcePath(); AddProjectResourceDir(projectResources); AssetDatabase* db = GetSubsystem(); String cachePath = db->GetCachePath(); AddProjectResourceDir(cachePath); BuildProjectResourceEntries(); } 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) { FailBuild("BuildFailed::RunConvertPList"); return; } VariantMap buildOutput; buildOutput[BuildOutput::P_TEXT] = "\n\nStarting iOS Deployment\n\n"; SendEvent(E_BUILDOUTPUT, buildOutput); SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildIOS, HandleConvertPListComplete)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildBase, HandleSubprocessOutputEvent)); } void BuildIOS::HandleConvertPListComplete(StringHash eventType, VariantMap& eventData) { int code = eventData[SubprocessComplete::P_RETCODE].GetInt(); if (!code) { RunCodeSign(); } else { BuildSystem* buildSystem = GetSubsystem(); buildSystem->BuildComplete(PLATFORMID_IOS, buildPath_, false); } } 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) { FailBuild("BuildFailed::RunCodeSign"); return; } VariantMap buildOutput; buildOutput[BuildOutput::P_TEXT] = "\n\nCode Signing iOS Deployment\n\n"; SendEvent(E_BUILDOUTPUT, buildOutput); SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildIOS, HandleCodeSignComplete)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildBase, HandleSubprocessOutputEvent)); } void BuildIOS::HandleCodeSignComplete(StringHash eventType, VariantMap& eventData) { int code = eventData[SubprocessComplete::P_RETCODE].GetInt(); if (!code) { RunDeploy(); } else { BuildSystem* buildSystem = GetSubsystem(); buildSystem->BuildComplete(PLATFORMID_IOS, buildPath_, false); } } void BuildIOS::RunDeploy() { SubprocessSystem* subs = GetSubsystem(); ToolEnvironment* tenv = GetSubsystem(); String iosDeploy = tenv->GetIOSDeployBinary(); Vector args; // https://github.com/phonegap/ios-deploy/issues/152 // args.Push("--uninstall"); args.Push("--bundle"); args.Push("./AtomicPlayer.app"); currentBuildPhase_ = Deploy; Subprocess* subprocess = subs->Launch(iosDeploy.CString(), args, buildPath_); if (!subprocess) { FailBuild("BuildFailed::RunDeploy"); return; } VariantMap buildOutput; buildOutput[BuildOutput::P_TEXT] = "\n\nDeploying to iOS Device\n\n"; SendEvent(E_BUILDOUTPUT, buildOutput); SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(BuildIOS, HandleDeployComplete)); SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(BuildBase, HandleSubprocessOutputEvent)); } void BuildIOS::HandleDeployComplete(StringHash eventType, VariantMap& eventData) { int code = eventData[SubprocessComplete::P_RETCODE].GetInt(); BuildSystem* buildSystem = GetSubsystem(); buildSystem->BuildComplete(PLATFORMID_IOS, buildPath_, true); } void BuildIOS::Build(const String& buildPath) { FileSystem* fileSystem = GetSubsystem(); ToolEnvironment* tenv = GetSubsystem(); ToolSystem* tsystem = GetSubsystem(); Project* project = tsystem->GetProject(); ProjectBuildSettings* buildSettings = project->GetBuildSettings(); IOSBuildSettings* iosSettings = buildSettings->GetIOSBuildSettings(); buildPath_ = AddTrailingSlash(buildPath) + GetBuildSubfolder(); Initialize(); if (fileSystem->DirExists(buildPath_)) fileSystem->RemoveDir(buildPath_, true); String buildSourceDir = tenv->GetToolDataDir(); String buildAppSourceDir = buildSourceDir + "Deployment/IOS/AtomicPlayer.app"; fileSystem->CreateDir(buildPath_); String buildDestDist = buildPath_ + "/AtomicPlayer.app"; fileSystem->CreateDir(buildDestDist); String resourcePackagePath = buildDestDist + "/AtomicResources" + PAK_EXTENSION; GenerateResourcePackage(resourcePackagePath); fileSystem->Copy(buildAppSourceDir + "/AtomicPlayer", buildDestDist + "/AtomicPlayer"); fileSystem->Copy(buildAppSourceDir + "/PkgInfo", buildDestDist + "/PkgInfo"); fileSystem->Copy(iosSettings->GetProvisionFile(), 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(); } }