├── 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 | }
--------------------------------------------------------------------------------