diff --git a/Makefile b/Makefile index baad4d8..3b14ea6 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ TOOL_NAME = UnityBuildKit -VERSION = 1.1.1 +VERSION = 1.1.2 PREFIX = /usr/local INSTALL_PATH = $(PREFIX)/bin/$(TOOL_NAME) diff --git a/README.md b/README.md index f5aa855..af357b6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

- + @@ -44,12 +44,12 @@ mkdir ExampleProject cd ExampleProject ``` -2. Run the following to generate the `ubconfig.json` file where you can specify project information +2. Run the following to generate the `ubconfig.json` file where you can specify project information (see more information [here](https://github.com/handsomecode/UnityBuildKit/wiki/Configuration-File)) ``` UnityBuildKit config ``` -3. After filling out the config file information, run +3. After filling out the config file, run ``` $ UnityBuildKit generate ``` diff --git a/Sources/UBKit/Files/Config/configFile.swift b/Sources/UBKit/Files/Config/configFile.swift index 413bbba..679ce7e 100644 --- a/Sources/UBKit/Files/Config/configFile.swift +++ b/Sources/UBKit/Files/Config/configFile.swift @@ -31,13 +31,13 @@ extension File { "ios": { "projectName": "ExampleProject", "bundleId": "com.example.ExampleProject", - "projectPath": "" // Defaults to iOS/ + "projectPath": "" // e.g. iOS/ }, "unity": { "projectName": "ExampleProject", - "applicationPath": "", // e.g /Applications/Unity/Unity.app/Contents/MacOS/Unity - "version": "", // e.g. 2017.1.f1 - "projectPath": "", // Defaults to Unity/ + "applicationPath": "", // e.g. /Applications/Unity/Unity.app/Contents/MacOS/Unity + "version": "", // e.g. 2017.2.1.f1 + "projectPath": "", // e.g. Unity/ "sceneNames": [ "ExampleScene" ] diff --git a/Sources/UBKit/Files/Unity/UnityFrameworksScript.swift b/Sources/UBKit/Files/Unity/UnityFrameworksScript.swift new file mode 100644 index 0000000..227dfd3 --- /dev/null +++ b/Sources/UBKit/Files/Unity/UnityFrameworksScript.swift @@ -0,0 +1,72 @@ +// +// UnityProjectScript.swift +// UnityBuildKitPackageDescription +// +// Copyright (c) 2017 Handsome +// +// 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. + +import Foundation + +extension File { + + class func unityFrameworksScriptFile(projectName: String, iOSProjectPath: String) -> Data? { + let file = """ + using System.Collections; + using System.IO; + using UnityEngine; + using UnityEditor; + using UnityEditor.SceneManagement; + using UnityEngine.SceneManagement; + using UnityEditor.iOS.Xcode; + + public class XcodeFrameworks: MonoBehaviour { + + private const string iOSProjectRoot = \"\(iOSProjectPath)\"; + private const string iOSProjectName = \"\(projectName)\"; + private const string PbxFilePath = iOSProjectName + ".xcodeproj/project.pbxproj"; + + public static void Perform () { + var pbx = new PBXProject(); + var pbxPath = Path.Combine(iOSProjectRoot, PbxFilePath); + pbx.ReadFromFile(pbxPath); + + var targetGuid = pbx.TargetGuidByName(iOSProjectName); + pbx.AddFrameworkToProject(targetGuid, "GameKit.framework", true); + pbx.AddFrameworkToProject(targetGuid, "CoreGraphics.framework", false); + pbx.AddFrameworkToProject(targetGuid, "AVFoundation.framework", false); + pbx.AddFrameworkToProject(targetGuid, "CoreVideo.framework", false); + pbx.AddFrameworkToProject(targetGuid, "CoreMedia.framework", false); + pbx.AddFrameworkToProject(targetGuid, "SystemConfiguration.framework", false); + pbx.AddFrameworkToProject(targetGuid, "CoreLocation.framework", false); + pbx.AddFrameworkToProject(targetGuid, "MediaPlayer.framework", false); + pbx.AddFrameworkToProject(targetGuid, "CFNetwork.framework", false); + pbx.AddFrameworkToProject(targetGuid, "AudioToolbox.framework", false); + pbx.AddFrameworkToProject(targetGuid, "OpenAL.framework", false); + pbx.AddFrameworkToProject(targetGuid, "QuartzCore.framework", false); + pbx.AddFrameworkToProject(targetGuid, "Foundation.framework", false); + pbx.AddFrameworkToProject(targetGuid, "MediaToolbox.framework", false); + + pbx.WriteToFile(pbxPath); + } + } + """.data(using: .utf8) + return file + } +} diff --git a/Sources/UBKit/Files/Xcode/SpecFile.swift b/Sources/UBKit/Files/Xcode/SpecFile.swift index b86df0d..d337ea2 100644 --- a/Sources/UBKit/Files/Xcode/SpecFile.swift +++ b/Sources/UBKit/Files/Xcode/SpecFile.swift @@ -51,7 +51,7 @@ extension File { UNITY_SCRIPTING_BACKEND: il2cpp UNITY_IOS_EXPORT_PATH: ${SRCROOT}/../Unity/\(projectName)/ios_build GCC_PREFIX_HEADER: $(UNITY_IOS_EXPORT_PATH)/Classes/Prefix.pch - OTHER_LDFLAGS: -weak-lSystem -weak_framework CoreMotion -weak_framework GameKit -weak_framework iAd -framework CoreGraphics -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework SystemConfiguration -framework CoreLocation -framework MediaPlayer -framework CFNetwork -framework AudioToolbox -framework OpenAL -framework QuartzCore -framework OpenGLES -framework UIKit -framework Foundation -framework MediaToolbox -liconv.2 -liPhone-lib + OTHER_LDFLAGS: -weak-lSystem -liconv.2 -liPhone-lib -weak_framework CoreMotion -weak_framework iAd -framework OpenGLES HEADER_SEARCH_PATHS: $(UNITY_IOS_EXPORT_PATH)/Classes $(UNITY_IOS_EXPORT_PATH)/Classes/Native $(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include LIBRARY_SEARCH_PATHS: $(UNITY_IOS_EXPORT_PATH)/Libraries $(UNITY_IOS_EXPORT_PATH)/Libraries/libil2cpp/include ENABLE_BITCODE: NO diff --git a/Sources/UBKit/Models/Config.swift b/Sources/UBKit/Models/Config.swift index a2d5161..3051534 100644 --- a/Sources/UBKit/Models/Config.swift +++ b/Sources/UBKit/Models/Config.swift @@ -64,10 +64,18 @@ struct iOSConfig: Decodable { } let projectPath: String - if let path = try? container.decode(String.self, forKey: .projectPath), !path.isEmpty { - projectPath = path - } else { - projectPath = FileManager.default.currentDirectoryPath.appending("/iOS/") + do { + let path = try container.decode(String.self, forKey: .projectPath) + if path.isEmpty { + throw UBKitError.invalidConfigArgument(Keys.projectPath.rawValue) + } + projectPath = FileManager.default.currentDirectoryPath.appending("/").appending(path) + } catch { + throw UBKitError.invalidConfigArgument(Keys.projectPath.rawValue) + } + + if !projectPath.hasSuffix("/") { + throw UBKitError.invalidConfigArgument("Project Path must end with a \"/\"") } self.projectName = projectName @@ -113,10 +121,18 @@ struct UnityConfig { } let projectPath: String - if let path = try? container.decode(String.self, forKey: .projectPath), !path.isEmpty { - projectPath = path - } else { - projectPath = FileManager.default.currentDirectoryPath.appending("/Unity/") + do { + let path = try container.decode(String.self, forKey: .projectPath) + if path.isEmpty { + throw UBKitError.invalidConfigArgument(Keys.projectPath.rawValue) + } + projectPath = FileManager.default.currentDirectoryPath.appending("/").appending(path) + } catch { + throw UBKitError.invalidConfigArgument(Keys.projectPath.rawValue) + } + + if !projectPath.hasSuffix("/") { + throw UBKitError.invalidConfigArgument("Project Path must end with a \"/\"") } let sceneNames = try container.decode([String].self, forKey: .sceneNames) diff --git a/Sources/UBKit/Models/UnityCommandLine.swift b/Sources/UBKit/Models/UnityCommandLine.swift index b20bfd7..76a867f 100644 --- a/Sources/UBKit/Models/UnityCommandLine.swift +++ b/Sources/UBKit/Models/UnityCommandLine.swift @@ -29,6 +29,7 @@ import Foundation struct UnityCommandLine { static let buildAction = "iOSBuilder.Perform" static let refreshAction = "XcodeRefresher.Refresh" + static let frameworksAction = "XcodeFrameworks.Perform" struct Arguments { static let projectPath = "-projectPath" diff --git a/Sources/UBKit/Workers/FileCopier.swift b/Sources/UBKit/Workers/FileCopier.swift index 8c8187b..51cbc6c 100644 --- a/Sources/UBKit/Workers/FileCopier.swift +++ b/Sources/UBKit/Workers/FileCopier.swift @@ -80,6 +80,11 @@ class FileCopier { return unityFilesResult } + let addFrameworksResult = addXcodeFrameworks() + guard addFrameworksResult == .success else { + return addFrameworksResult + } + return .success } } @@ -113,25 +118,6 @@ private extension FileCopier { return .success } - func x() -> Result { - guard let project = project else { - return .failure(UBKitError.invalidXcodeProject("Failed to find project file")) - } - - guard let mainTarget = project.pbxproj.objects.nativeTargets.filter({ $0.value.name == config.iOS.projectName }).first else { - return .failure(UBKitError.invalidXcodeProject("Missing main target")) - } - - for phase in mainTarget.value.buildPhases { - if phase.starts(with: "RBP_") { - - break - } - } - - return .success - } - func changeUnityFiles() -> Result { let mainFilePath = workingPath.appending(config.unity.projectName).appending("/ios_build/Classes/main.mm") guard fileManager.fileExists(atPath: mainFilePath) else { @@ -165,12 +151,13 @@ private extension FileCopier { func addUnityFiles() -> Result { let semaphore = DispatchSemaphore(value: 0) var statusCode: Int32 = 999 + let projectPath = workingPath.appending(config.unity.projectName).appending("/ios_build") shell.perform( config.unity.applicationPath, UnityCommandLine.Arguments.batchmode, UnityCommandLine.Arguments.buildPath, - workingPath.appending(config.unity.projectName).appending("/ios_build"), + projectPath, UnityCommandLine.Arguments.executeMethod, UnityCommandLine.refreshAction, UnityCommandLine.Arguments.quit, @@ -192,6 +179,37 @@ private extension FileCopier { } } + func addXcodeFrameworks() -> Result { + let semaphore = DispatchSemaphore(value: 0) + var statusCode: Int32 = 999 + let projectPath = workingPath.appending(config.unity.projectName) + + shell.perform( + config.unity.applicationPath, + UnityCommandLine.Arguments.batchmode, + UnityCommandLine.Arguments.projectPath, + projectPath, + UnityCommandLine.Arguments.executeMethod, + UnityCommandLine.frameworksAction, + UnityCommandLine.Arguments.quit, + terminationHandler: { (process) in + statusCode = process.terminationStatus + semaphore.signal() + }) + + let timeout = semaphore.wait(timeout: DispatchTime.now()+60.0) + switch timeout { + case .success: + if statusCode == 0 { + return .success + } else { + return .failure(UBKitError.shellCommand("Initializing Unity Project")) + } + case .timedOut: + return .failure(UBKitError.waitTimedOut) + } + } + func saveProject() -> Result { guard let project = project else { return .failure(UBKitError.invalidXcodeProject("Failed to find project file")) diff --git a/Sources/UBKit/Workers/UnityProject.swift b/Sources/UBKit/Workers/UnityProject.swift index e6d6d29..e41abbb 100644 --- a/Sources/UBKit/Workers/UnityProject.swift +++ b/Sources/UBKit/Workers/UnityProject.swift @@ -47,6 +47,7 @@ class UnityProject { return .failure(UBKitError.invalidFolder(unityAppPath)) } + print("Unity Project Path: \(workingPath)\n") let unityFolderResult = createUnityFolder() guard unityFolderResult == .success else { return unityFolderResult @@ -59,7 +60,7 @@ class UnityProject { return projectGenerationResult } - print("\nGenerating Unity Editor scripts") + print("Generating Unity Editor scripts") let editorScriptsResult = createUnityEditorScripts() guard editorScriptsResult == .success else { return editorScriptsResult @@ -144,12 +145,19 @@ private extension UnityProject { } guard fileManager.createFile( - atPath: editorFilePath.appending("ProjectScript.cs"), + atPath: editorFilePath.appending("XcodeProjectRefresher.cs"), contents: File.unityProjectScriptFile(projectName: projectName, iOSProjectPath: config.iOS.projectPath), attributes: nil) else { return .failure(UBKitError.unableToCreateFile("Unity Project Script")) } + guard fileManager.createFile( + atPath: editorFilePath.appending("XcodeFrameworks.cs"), + contents: File.unityFrameworksScriptFile(projectName: projectName, iOSProjectPath: config.iOS.projectPath), + attributes: nil) else { + return .failure(UBKitError.unableToCreateFile("Xcode Frameworks Script")) + } + return .success } @@ -185,7 +193,7 @@ private extension UnityProject { if statusCode == 0 { return .success } else { - return .failure(UBKitError.shellCommand("Initializing Unity Project")) + return .failure(UBKitError.shellCommand("Initializing Unity Project: \(statusCode)")) } case .timedOut: return .failure(UBKitError.waitTimedOut) diff --git a/Sources/UBKit/Workers/XcodeProject.swift b/Sources/UBKit/Workers/XcodeProject.swift index d201ccf..96efbc5 100644 --- a/Sources/UBKit/Workers/XcodeProject.swift +++ b/Sources/UBKit/Workers/XcodeProject.swift @@ -54,6 +54,7 @@ class XcodeProject { } func create() -> Result { + print("iOS Project Path: \(workingPath)") let iOSFolderResult = createiOSFolder() guard iOSFolderResult == .success else { return iOSFolderResult diff --git a/UnityBuildKit.xcodeproj/project.pbxproj b/UnityBuildKit.xcodeproj/project.pbxproj index 8b94e02..015a8e9 100644 --- a/UnityBuildKit.xcodeproj/project.pbxproj +++ b/UnityBuildKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + D4C15EC42023E6C3002B8938 /* UnityFrameworksScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C15EC32023E6C3002B8938 /* UnityFrameworksScript.swift */; }; + D4C15EC52023E6C3002B8938 /* UnityFrameworksScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C15EC32023E6C3002B8938 /* UnityFrameworksScript.swift */; }; OBJ_204 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; OBJ_210 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_78 /* Package.swift */; }; OBJ_216 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_134 /* Package.swift */; }; @@ -428,6 +430,7 @@ /* Begin PBXFileReference section */ "AEXML::AEXML::Product" /* AEXML.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AEXML.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D4C15EC32023E6C3002B8938 /* UnityFrameworksScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnityFrameworksScript.swift; sourceTree = ""; }; "JSONUtilities::JSONUtilities::Product" /* JSONUtilities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JSONUtilities.framework; sourceTree = BUILT_PRODUCTS_DIR; }; OBJ_100 /* PBXHeadersBuildPhase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXHeadersBuildPhase.swift; sourceTree = ""; }; OBJ_101 /* PBXLegacyTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBXLegacyTarget.swift; sourceTree = ""; }; @@ -714,6 +717,7 @@ children = ( OBJ_14 /* UnityEditorBuildScript.swift */, OBJ_15 /* UnityProjectScript.swift */, + D4C15EC32023E6C3002B8938 /* UnityFrameworksScript.swift */, ); path = Unity; sourceTree = ""; @@ -1448,6 +1452,7 @@ buildActionMask = 0; files = ( OBJ_204 /* Package.swift in Sources */, + D4C15EC42023E6C3002B8938 /* UnityFrameworksScript.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1526,6 +1531,7 @@ OBJ_291 /* SpecFile.swift in Sources */, OBJ_292 /* ViewControllerFile.swift in Sources */, OBJ_293 /* Config.swift in Sources */, + D4C15EC52023E6C3002B8938 /* UnityFrameworksScript.swift in Sources */, OBJ_294 /* Result.swift in Sources */, OBJ_295 /* Shell.swift in Sources */, OBJ_296 /* UBKitError.swift in Sources */,