├── Builder.cs ├── BundleVersionResolver.cs ├── CommandLineBuild.cs ├── LICENSE ├── README.md └── Reporters ├── BuildReporter.cs ├── TeamCityReporter.cs └── UnityReporter.cs /Builder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Nordeus.Build.Reporters; 5 | using UnityEditor; 6 | using UnityEngine; 7 | using System.Collections; 8 | 9 | namespace Nordeus.Build 10 | { 11 | public static class Builder 12 | { 13 | /// 14 | /// Builds an APK at the specified path with the specified texture compression. 15 | /// 16 | /// Path for the output APK. 17 | /// If not null, will override the texture compression subtarget. 18 | public static void BuildAndroid(string buildPath, MobileTextureSubtarget? textureCompression = null) 19 | { 20 | if (textureCompression != null) 21 | { 22 | EditorUserBuildSettings.androidBuildSubtarget = textureCompression.Value; 23 | } 24 | 25 | string buildMessage = BuildPipeline.BuildPlayer(GetEnabledScenePaths().ToArray(), buildPath, BuildTarget.Android, BuildOptions.None); 26 | 27 | if (string.IsNullOrEmpty(buildMessage)) BuildReporter.Current.IndicateSuccessfulBuild(); 28 | else 29 | { 30 | BuildReporter.Current.Log(buildMessage, BuildReporter.MessageSeverity.Error); 31 | } 32 | } 33 | 34 | /// 35 | /// Builds an XCode project at the specified path. 36 | /// 37 | /// Path for the XCode project. 38 | public static void BuildIos(string buildPath) 39 | { 40 | string buildMessage = BuildPipeline.BuildPlayer(GetEnabledScenePaths().ToArray(), buildPath, BuildTarget.iOS, BuildOptions.None); 41 | 42 | if (string.IsNullOrEmpty(buildMessage)) BuildReporter.Current.IndicateSuccessfulBuild(); 43 | else 44 | { 45 | BuildReporter.Current.Log(buildMessage, BuildReporter.MessageSeverity.Error); 46 | } 47 | } 48 | 49 | /// 50 | /// Returns a list of all the enabled scenes. 51 | /// 52 | private static List GetEnabledScenePaths() 53 | { 54 | List scenePaths = new List(); 55 | 56 | foreach (var scene in EditorBuildSettings.scenes) 57 | { 58 | scenePaths.Add(scene.path); 59 | } 60 | 61 | return scenePaths; 62 | } 63 | 64 | /// 65 | /// Builds the specified build target. 66 | /// 67 | /// Build target to build. 68 | /// Output path for the build. 69 | /// Texture compression subtarget for Android. 70 | public static void Build(BuildTarget parsedBuildTarget, string buildPath, MobileTextureSubtarget? parsedTextureSubtarget = null) 71 | { 72 | Directory.CreateDirectory(buildPath); 73 | 74 | switch (parsedBuildTarget) 75 | { 76 | case BuildTarget.Android: 77 | BuildAndroid(buildPath, parsedTextureSubtarget); 78 | break; 79 | case BuildTarget.iOS: 80 | BuildIos(buildPath); 81 | break; 82 | default: 83 | throw new ArgumentException(parsedBuildTarget + " is not a supported build target."); 84 | } 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /BundleVersionResolver.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | 3 | namespace Nordeus.Build 4 | { 5 | /// 6 | /// Sets up the appropriate player settings to properly represent the game version and build number for different platforms and versions of Unity. 7 | /// 8 | public static class BundleVersionResolver 9 | { 10 | #if !UNITY_5 11 | private const BuildTarget IosTarget = BuildTarget.iPhone; 12 | #else 13 | private const BuildTarget IosTarget = BuildTarget.iOS; 14 | #endif 15 | 16 | /// 17 | /// Pretty version of the game, for example 0.123f 18 | /// 19 | public static string PrettyVersion { get; set; } 20 | 21 | /// 22 | /// Number of the build. Corresponds to bundle version code for Android and build number for iOS. 23 | /// 24 | public static int? BuildNumber { get; set; } 25 | 26 | /// 27 | /// Setups player settings with the specified pretty version and build number. 28 | /// 29 | /// 30 | public static void Setup(BuildTarget target) 31 | { 32 | if (target == BuildTarget.Android) 33 | { 34 | SetupAndroid(); 35 | } 36 | else if (target == IosTarget) 37 | { 38 | SetupIos(); 39 | } 40 | } 41 | 42 | #if !UNITY_5_2 43 | private static void SetupIos() 44 | { 45 | if (BuildNumber != null) 46 | { 47 | PlayerSettings.bundleVersion = BuildNumber.Value.ToString(); 48 | } 49 | } 50 | #else 51 | private static void SetupIos() 52 | { 53 | if (PrettyVersion != null) 54 | { 55 | PlayerSettings.bundleVersion = PrettyVersion; 56 | } 57 | 58 | if (BuildNumber != null) 59 | { 60 | PlayerSettings.iOS.buildNumber = BuildNumber.Value.ToString(); 61 | } 62 | } 63 | #endif 64 | 65 | private static void SetupAndroid() 66 | { 67 | if (PrettyVersion != null) 68 | { 69 | PlayerSettings.bundleVersion = PrettyVersion; 70 | } 71 | 72 | if (BuildNumber != null) 73 | { 74 | PlayerSettings.Android.bundleVersionCode = BuildNumber.Value; 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /CommandLineBuild.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Nordeus.Build.Reporters; 4 | using UnityEditor; 5 | 6 | namespace Nordeus.Build 7 | { 8 | public static class CommandLineBuild 9 | { 10 | #region Constants 11 | 12 | private const string PublishPathCommand = "-out"; 13 | private const string BuildNumberCommand = "-buildNumber"; 14 | private const string BuildVersionCommand = "-buildVersion"; 15 | private const string BuildReporterCommand = "-reporter"; 16 | private const string BuildTargetCommand = "-target"; 17 | private const string AndroidTextureCompressionCommand = "-textureCompression"; 18 | 19 | private const char CommandStartCharacter = '-'; 20 | 21 | #endregion 22 | 23 | #region Methods 24 | 25 | /// 26 | /// Performs the command line build by using the passed command line arguments. 27 | /// 28 | private static void Build() 29 | { 30 | string publishPath, buildNumber, buildVersion, buildReporter, buildTarget, androidTextureCompression; 31 | 32 | Dictionary commandToValueDictionary = GetCommandLineArguments(); 33 | 34 | // Extract our arguments from dictionary 35 | commandToValueDictionary.TryGetValue(PublishPathCommand, out publishPath); 36 | commandToValueDictionary.TryGetValue(BuildNumberCommand, out buildNumber); 37 | commandToValueDictionary.TryGetValue(BuildVersionCommand, out buildVersion); 38 | commandToValueDictionary.TryGetValue(BuildReporterCommand, out buildReporter); 39 | commandToValueDictionary.TryGetValue(BuildTargetCommand, out buildTarget); 40 | commandToValueDictionary.TryGetValue(AndroidTextureCompressionCommand, out androidTextureCompression); 41 | 42 | 43 | if (!string.IsNullOrEmpty(buildReporter)) BuildReporter.Current = BuildReporter.CreateReporterByName(buildReporter); 44 | 45 | try 46 | { 47 | if (!string.IsNullOrEmpty(buildNumber)) 48 | { 49 | BundleVersionResolver.BuildNumber = int.Parse(buildNumber); 50 | } 51 | if (!string.IsNullOrEmpty(buildVersion)) 52 | { 53 | BundleVersionResolver.PrettyVersion = buildVersion; 54 | } 55 | 56 | if (string.IsNullOrEmpty(buildTarget)) 57 | { 58 | BuildReporter.Current.Log("No target was specified for this build.", BuildReporter.MessageSeverity.Error); 59 | } 60 | else 61 | { 62 | BuildTarget parsedBuildTarget = (BuildTarget)Enum.Parse(typeof(BuildTarget), buildTarget); 63 | MobileTextureSubtarget? parsedTextureSubtarget = null; 64 | if (!string.IsNullOrEmpty(androidTextureCompression)) parsedTextureSubtarget = (MobileTextureSubtarget)Enum.Parse(typeof(MobileTextureSubtarget), androidTextureCompression); 65 | 66 | BundleVersionResolver.Setup(parsedBuildTarget); 67 | 68 | Builder.Build(parsedBuildTarget, publishPath, parsedTextureSubtarget); 69 | } 70 | } 71 | catch (Exception e) 72 | { 73 | BuildReporter.Current.Log(e.Message, BuildReporter.MessageSeverity.Error); 74 | } 75 | } 76 | 77 | 78 | /// 79 | /// Gets all the command line arguments relevant to the build process. All commands that don't have a value after them have their value at string.Empty. 80 | /// 81 | private static Dictionary GetCommandLineArguments() 82 | { 83 | Dictionary commandToValueDictionary = new Dictionary(); 84 | 85 | string[] args = System.Environment.GetCommandLineArgs(); 86 | 87 | for (int i = 0; i < args.Length; i++) 88 | { 89 | if (args[i].StartsWith(CommandStartCharacter.ToString())) 90 | { 91 | string command = args[i]; 92 | string value = string.Empty; 93 | 94 | if (i < args.Length - 1 && !args[i + 1].StartsWith(CommandStartCharacter.ToString())) 95 | { 96 | value = args[i + 1]; 97 | i++; 98 | } 99 | 100 | if (!commandToValueDictionary.ContainsKey(command)) 101 | { 102 | commandToValueDictionary.Add(command, value); 103 | } 104 | else 105 | { 106 | BuildReporter.Current.Log("Duplicate command line argument " + command, BuildReporter.MessageSeverity.Warning); 107 | } 108 | } 109 | } 110 | 111 | return commandToValueDictionary; 112 | } 113 | 114 | #endregion 115 | } 116 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nordeus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Build Pipeline 2 | 3 | This is a simplified version of the build pipeline that we use at Nordeus. It is capable of building Android and iOS and reporting build progress back to TeamCity. When building, Unity should be started with the following parameters: 4 | ```sh 5 | unity.exe -projectPath "pathToYourProject" -batchMode -quit -executeMethod Nordeus.Build.CommandLineBuild.Build 6 | ``` 7 | 8 | Aditional build parameters can be: 9 | - -out - Output path for the built project 10 | - -target - Platform for the build, it can be iOS or Android 11 | - -textureCompression - Texture compression subtarget for Android (ETC1, ATC, ETC2...) 12 | - -buildNumber - Integer value for the number of the build. It is copied to Android's bundle version code and iOS's build number (Unity 5.2+ only) 13 | - -buildVersion - Pretty build version string (eg. 1.2.3f). It is copied to bundle version on both Android and iOS. 14 | - -reporter - Build report to use. By default UnityReporter is used which reprots all emssages to Unity's console. TeamCityReporter also reports errors as service messages to TeamCity. 15 | -------------------------------------------------------------------------------- /Reporters/BuildReporter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Nordeus.Build.Reporters 4 | { 5 | /// 6 | /// Base class for reporting messages to the build environment. 7 | /// 8 | public abstract class BuildReporter 9 | { 10 | public enum MessageSeverity 11 | { 12 | Debug, 13 | Info, 14 | Warning, 15 | Error 16 | } 17 | 18 | /// 19 | /// Gets the current global build reporter. By default, it's the Unity (Debug.Log) reporter. 20 | /// 21 | public static BuildReporter Current 22 | { 23 | get 24 | { 25 | if (currentReporter == null) 26 | { 27 | currentReporter = new UnityReporter(); 28 | } 29 | 30 | return currentReporter; 31 | } 32 | set 33 | { 34 | currentReporter = value; 35 | } 36 | } 37 | 38 | private static BuildReporter currentReporter; 39 | 40 | /// 41 | /// Minimum severity the build reporter should report. 42 | /// 43 | public MessageSeverity MinimumSeverity { get; set; } 44 | 45 | /// 46 | /// Reports a message to the build environment. 47 | /// 48 | public virtual void Log(string message, MessageSeverity severity = MessageSeverity.Info) 49 | { 50 | if (severity >= MinimumSeverity) LogInternal(message, severity); 51 | } 52 | 53 | /// 54 | /// Indicates a successful build to the build environment. 55 | /// 56 | public abstract void IndicateSuccessfulBuild(); 57 | 58 | protected abstract void LogInternal(string message, MessageSeverity severity); 59 | 60 | /// 61 | /// Creates a build reporter by name. Returns null in case it cannot create one. 62 | /// 63 | public static BuildReporter CreateReporterByName(string name) 64 | { 65 | switch (name) 66 | { 67 | case "Unity": 68 | return new UnityReporter(); 69 | case "TeamCity": 70 | return new TeamCityReporter(); 71 | default: 72 | return null; 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Reporters/TeamCityReporter.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | namespace Nordeus.Build.Reporters 5 | { 6 | /// 7 | /// Reports messages to TeamCity. 8 | /// 9 | public class TeamCityReporter : UnityReporter 10 | { 11 | protected override void LogInternal(string message, MessageSeverity severity = MessageSeverity.Info) 12 | { 13 | base.LogInternal(message, severity); 14 | 15 | if (severity == MessageSeverity.Error) 16 | { 17 | Debug.Log("##teamcity[message text='" + message + "'" + "status='ERROR']"); 18 | } 19 | } 20 | 21 | public override void IndicateSuccessfulBuild() 22 | { 23 | base.IndicateSuccessfulBuild(); 24 | 25 | // Magic string to indicate successful build. 26 | Debug.Log("Successful build ~0xDEADBEEF"); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Reporters/UnityReporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Nordeus.Build; 3 | using Nordeus.Build.Reporters; 4 | using UnityEngine; 5 | using System.Collections; 6 | 7 | namespace Nordeus.Build.Reporters 8 | { 9 | /// 10 | /// Reports messages to Unity console. 11 | /// 12 | public class UnityReporter : BuildReporter 13 | { 14 | public override void IndicateSuccessfulBuild() {} 15 | 16 | protected override void LogInternal(string message, MessageSeverity severity = MessageSeverity.Info) 17 | { 18 | switch (severity) 19 | { 20 | case MessageSeverity.Debug: 21 | Debug.Log(message); 22 | break; 23 | case MessageSeverity.Info: 24 | Debug.Log(message); 25 | break; 26 | case MessageSeverity.Warning: 27 | Debug.LogWarning(message); 28 | break; 29 | case MessageSeverity.Error: 30 | Debug.LogError(message); 31 | break; 32 | default: 33 | throw new ArgumentOutOfRangeException("Severity out of bounds"); 34 | } 35 | } 36 | } 37 | } --------------------------------------------------------------------------------