diff --git a/src/Cake.Unity.FSharp.Tests/Cake.Unity.FSharp.Tests.fsproj b/src/Cake.Unity.FSharp.Tests/Cake.Unity.FSharp.Tests.fsproj
index 89a6f3b..1ee943f 100644
--- a/src/Cake.Unity.FSharp.Tests/Cake.Unity.FSharp.Tests.fsproj
+++ b/src/Cake.Unity.FSharp.Tests/Cake.Unity.FSharp.Tests.fsproj
@@ -7,6 +7,7 @@
+
@@ -14,6 +15,7 @@
+
diff --git a/src/Cake.Unity.FSharp.Tests/InfoPlistParserTests.fs b/src/Cake.Unity.FSharp.Tests/InfoPlistParserTests.fs
new file mode 100644
index 0000000..728c399
--- /dev/null
+++ b/src/Cake.Unity.FSharp.Tests/InfoPlistParserTests.fs
@@ -0,0 +1,91 @@
+module Cake.Unity.Tests.InfoPlistParserTests
+
+open FsUnit.TopLevelOperators
+open NUnit.Framework
+open Cake.Unity.SeekersOfEditors
+open System.IO
+open System.Text
+
+[]
+let ``Parse Info.plist`` () =
+
+ // arrange
+ let xml = @"
+
+
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ Unity Asset Store
+ CFBundleURLSchemes
+
+ com.unity3d.kharma
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeExtensions
+
+ unity
+ unityPackage
+
+ CFBundleTypeIconFile
+ UnityDocumentIcon
+ CFBundleTypeOSTypes
+
+ ????
+
+ CFBundleTypeRole
+ Editor
+
+
+ CFBundleExecutable
+ Unity
+ CFBundleGetInfoString
+ Unity version 2017.4.25f1 (9cba1c3a94f1). (c) 2019 Unity Technologies ApS. All rights reserved.
+ CFBundleIconFile
+ UnityAppIcon
+ CFBundleIdentifier
+ com.unity3d.UnityEditor5.x
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Unity
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ Unity version 2017.4.25f1
+ CFBundleSignature
+ UNED
+ CFBundleVersion
+ 2017.4.25f1
+ NSMainNibFile
+ MainMenuNew
+ NSPrincipalClass
+ EditorApplicationPrincipalClass
+ UnityBuildNumber
+ 9cba1c3a94f1
+ LSMinimumSystemVersion
+ 10.9.0
+
+
+"
+ let xmlStream = new MemoryStream(Encoding.UTF8.GetBytes(xml))
+ let parser = new InfoPlistParser()
+
+ // act
+ let version = parser.UnityVersionFromInfoPlist(xmlStream)
+
+ // assert
+ version |> should equal "2017.4.25f1"
diff --git a/src/Cake.Unity.FSharp.Tests/UnityVersionTests.fs b/src/Cake.Unity.FSharp.Tests/UnityVersionTests.fs
index 094049d..723fed0 100644
--- a/src/Cake.Unity.FSharp.Tests/UnityVersionTests.fs
+++ b/src/Cake.Unity.FSharp.Tests/UnityVersionTests.fs
@@ -29,3 +29,20 @@ let ``Unity version without suffix should be in Unknown stage`` () =
[]
let ``Unity version with unknown suffix should be in Unknown stage`` () =
(2022, 2, 2, 'x', 2) |> UnityVersion |> stage |> should equal UnityReleaseStage.Unknown
+
+
+[]
+let ``Parse UnityVersion from string`` () =
+
+ //arrange
+ let stringVersion = "2017.4.25f1"
+
+ //act
+ let unityVersion = UnityVersion.Parse(stringVersion)
+
+ //assert
+ unityVersion.Year |> should equal 2017
+ unityVersion.Stream |> should equal 4
+ unityVersion.Update |> should equal 25
+ unityVersion.SuffixCharacter |> should equal 'f'
+ unityVersion.SuffixNumber |> should equal 1
diff --git a/src/Cake.Unity.sln b/src/Cake.Unity.sln
index c1ccc47..b62cbda 100644
--- a/src/Cake.Unity.sln
+++ b/src/Cake.Unity.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28803.202
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.271
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.Unity", "Cake.Unity\Cake.Unity.csproj", "{0222DDD2-3716-4E19-854F-EB36D04268D0}"
EndProject
diff --git a/src/Cake.Unity/SeekersOfEditors/OSXSeekerOfEditors.cs b/src/Cake.Unity/SeekersOfEditors/OSXSeekerOfEditors.cs
index 3dc5c62..69c76aa 100644
--- a/src/Cake.Unity/SeekersOfEditors/OSXSeekerOfEditors.cs
+++ b/src/Cake.Unity/SeekersOfEditors/OSXSeekerOfEditors.cs
@@ -1,17 +1,24 @@
-using System.Linq;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
using System.Xml.Linq;
using Cake.Core;
using Cake.Core.Diagnostics;
using Cake.Core.IO;
using Cake.Unity.Version;
+[assembly: InternalsVisibleTo("Cake.Unity.FSharp.Tests")]
namespace Cake.Unity.SeekersOfEditors
{
internal class OSXSeekerOfEditors : SeekerOfEditors
{
- public OSXSeekerOfEditors(ICakeEnvironment environment, IGlobber globber, ICakeLog log)
+ private readonly IFileSystem fileSystem;
+
+ public OSXSeekerOfEditors(ICakeEnvironment environment, IGlobber globber, ICakeLog log, IFileSystem fileSystem)
: base(environment, globber, log)
- { }
+ {
+ this.fileSystem = fileSystem;
+ }
protected override string SearchPattern => "/Applications/**/Unity*.app/Contents/MacOS/Unity";
@@ -19,7 +26,9 @@ protected override UnityVersion DetermineVersion(FilePath editorPath)
{
log.Debug($"Determining version of Unity Editor at path {editorPath}...");
- var version = UnityVersionFromInfoPlist(editorPath);
+ string version;
+ using (var stream = fileSystem.GetFile(PlistPath(editorPath)).OpenRead())
+ version = new InfoPlistParser().UnityVersionFromInfoPlist(stream);
if (version == null)
{
@@ -33,16 +42,19 @@ protected override UnityVersion DetermineVersion(FilePath editorPath)
return unityVersion;
}
- private static string UnityVersionFromInfoPlist(FilePath editorPath) =>
- XElement
- .Load(PlistPath(editorPath))
+ private static string PlistPath(FilePath editorPath) =>
+ editorPath.FullPath.Replace("/MacOS/Unity", "/Info.plist");
+ }
+
+ internal class InfoPlistParser
+ {
+ public string UnityVersionFromInfoPlist(Stream infoPlistStream) =>
+ XDocument
+ .Load(infoPlistStream)
.Descendants()
.SkipWhile((element) => element.Value != "CFBundleVersion")
.Skip(1)
.FirstOrDefault()
?.Value;
-
- private static string PlistPath(FilePath editorPath) =>
- editorPath.FullPath.Replace("/MacOS/Unity", "/Info.plist");
}
}
diff --git a/src/Cake.Unity/SeekersOfEditors/SeekerOfEditors.cs b/src/Cake.Unity/SeekersOfEditors/SeekerOfEditors.cs
index 82cd382..0aaace0 100644
--- a/src/Cake.Unity/SeekersOfEditors/SeekerOfEditors.cs
+++ b/src/Cake.Unity/SeekersOfEditors/SeekerOfEditors.cs
@@ -14,13 +14,13 @@ internal abstract class SeekerOfEditors
private readonly IGlobber globber;
protected readonly ICakeLog log;
- public static SeekerOfEditors GetSeeker(ICakeEnvironment environment, IGlobber globber, ICakeLog log)
+ public static SeekerOfEditors GetSeeker(ICakeEnvironment environment, IGlobber globber, ICakeLog log, IFileSystem fileSystem)
{
if (environment.Platform.Family == PlatformFamily.Windows)
return new WindowsSeekerOfEditors(environment, globber, log);
if (environment.Platform.Family == PlatformFamily.OSX)
- return new OSXSeekerOfEditors(environment, globber, log);
+ return new OSXSeekerOfEditors(environment, globber, log, fileSystem);
throw new NotSupportedException("Cannot locate Unity Editors. Only Windows and OSX platform is supported.");
}
diff --git a/src/Cake.Unity/UnityAliases.cs b/src/Cake.Unity/UnityAliases.cs
index e9b91b4..d27efce 100644
--- a/src/Cake.Unity/UnityAliases.cs
+++ b/src/Cake.Unity/UnityAliases.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using Cake.Core;
using Cake.Core.Annotations;
using Cake.Core.Diagnostics;
@@ -114,6 +115,120 @@ public static void UnityEditor(this ICakeContext context,
new UnityEditor(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log)
.Run(unityEditor, arguments, settings ?? new UnityEditorSettings());
+ ///
+ /// Executes Unity Editor via command-line interface.
+ /// Determines Unity Editor location automatically by specified version.
+ ///
+ /// Year part of Unity version aka major version.
+ /// Stream part of Unity version aka minor version.
+ /// Update part of Unity version.
+ /// Stage part of Unity version. "a" - mean Alpha, "b" - Beta, "p" - Patch, "f" - Final
+ /// SuffixNumber part of Unity version.
+ /// Unity Editor command-line arguments.
+ /// Optional settings which affect how Unity Editor should be executed.
+ ///
+ ///
+ /// UnityEditor(
+ /// 2018, 3, 14, 'f', 1,
+ /// new UnityEditorArguments
+ /// {
+ /// ProjectPath = "A:/UnityProject",
+ /// BuildWindowsPlayer = "A:/Build/game.exe",
+ /// LogFile = "A:/Build/unity.log",
+ /// },
+ /// new UnityEditorSettings
+ /// {
+ /// RealTimeLog = true,
+ /// });
+ ///
+ ///
+ [CakeMethodAlias]
+ [CakeAliasCategory("Execute")]
+ [CakeNamespaceImport("Cake.Unity.Arguments")]
+ public static void UnityEditor(this ICakeContext context,
+ int versionYear, int versionStream, int versionUpdate, char versionSuffixCharacter, int versionSuffixNumber, UnityEditorArguments arguments, UnityEditorSettings settings = null) =>
+ new UnityEditor(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log)
+ .Run(
+ context.FindUnityEditor(versionYear, versionStream, versionUpdate, versionSuffixCharacter, versionSuffixNumber)
+ ?? throw new Exception($"Failed to locate Unity Editor {versionYear}.{versionStream}.{versionUpdate}{versionSuffixCharacter}{versionSuffixNumber}. Try to specify it's path explicitly."),
+ arguments,
+ settings ?? new UnityEditorSettings());
+
+ ///
+ /// Executes Unity Editor via command-line interface.
+ /// Determines Unity Editor location automatically by specified version.
+ ///
+ /// Year part of Unity version aka major version.
+ /// Stream part of Unity version aka minor version.
+ /// Update part of Unity version.
+ /// Stage part of Unity version. "a" - mean Alpha, "b" - Beta, "p" - Patch, "f" - Final
+ /// Unity Editor command-line arguments.
+ /// Optional settings which affect how Unity Editor should be executed.
+ ///
+ ///
+ /// UnityEditor(
+ /// 2018, 3, 14, 'f',
+ /// new UnityEditorArguments
+ /// {
+ /// ProjectPath = "A:/UnityProject",
+ /// BuildWindowsPlayer = "A:/Build/game.exe",
+ /// LogFile = "A:/Build/unity.log",
+ /// },
+ /// new UnityEditorSettings
+ /// {
+ /// RealTimeLog = true,
+ /// });
+ ///
+ ///
+ [CakeMethodAlias]
+ [CakeAliasCategory("Execute")]
+ [CakeNamespaceImport("Cake.Unity.Arguments")]
+ public static void UnityEditor(this ICakeContext context,
+ int versionYear, int versionStream, int versionUpdate, char versionSuffixCharacter, UnityEditorArguments arguments, UnityEditorSettings settings = null) =>
+ new UnityEditor(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log)
+ .Run(
+ context.FindUnityEditor(versionYear, versionStream, versionUpdate, versionSuffixCharacter)
+ ?? throw new Exception($"Failed to locate Unity Editor {versionYear}.{versionStream}.{versionUpdate}{versionSuffixCharacter}. Try to specify it's path explicitly."),
+ arguments,
+ settings ?? new UnityEditorSettings());
+
+ ///
+ /// Executes Unity Editor via command-line interface.
+ /// Determines Unity Editor location automatically by specified version.
+ ///
+ /// Year part of Unity version aka major version.
+ /// Stream part of Unity version aka minor version.
+ /// Update part of Unity version.
+ /// Unity Editor command-line arguments.
+ /// Optional settings which affect how Unity Editor should be executed.
+ ///
+ ///
+ /// UnityEditor(
+ /// 2018, 3, 14,
+ /// new UnityEditorArguments
+ /// {
+ /// ProjectPath = "A:/UnityProject",
+ /// BuildWindowsPlayer = "A:/Build/game.exe",
+ /// LogFile = "A:/Build/unity.log",
+ /// },
+ /// new UnityEditorSettings
+ /// {
+ /// RealTimeLog = true,
+ /// });
+ ///
+ ///
+ [CakeMethodAlias]
+ [CakeAliasCategory("Execute")]
+ [CakeNamespaceImport("Cake.Unity.Arguments")]
+ public static void UnityEditor(this ICakeContext context,
+ int versionYear, int versionStream, int versionUpdate, UnityEditorArguments arguments, UnityEditorSettings settings = null) =>
+ new UnityEditor(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log)
+ .Run(
+ context.FindUnityEditor(versionYear, versionStream, versionUpdate)
+ ?? throw new Exception($"Failed to locate Unity Editor {versionYear}.{versionStream}.{versionUpdate}. Try to specify it's path explicitly."),
+ arguments,
+ settings ?? new UnityEditorSettings());
+
///
/// Executes Unity Editor via command-line interface.
/// Determines Unity Editor location automatically by specified version.
@@ -210,14 +325,42 @@ public static void UnityEditor(this ICakeContext context,
[CakeAliasCategory("Execute")]
[CakeNamespaceImport("Cake.Unity.Arguments")]
public static void UnityEditor(this ICakeContext context,
- UnityEditorArguments arguments, UnityEditorSettings settings = null) =>
+ UnityEditorArguments arguments, UnityEditorSettings settings = null)
+ {
+ UnityEditorDescriptor unityEditor;
+
+ UnityVersion version = null;
+ if (arguments.ProjectPath != null)
+ version = context.GetUnityVersionFromProjectPath(arguments.ProjectPath);
+
+ if (version != null)
+ {
+ context.Log.Information("Detected Unity version from project: {0}, attempting to find UnityEditor.", version);
+
+ unityEditor =
+ context.FindUnityEditor(version.Year, version.Stream, version.Update, version.SuffixCharacter.Value, version.SuffixNumber.Value)
+ ?? context.FindUnityEditor(version.Year, version.Stream, version.Update, version.SuffixCharacter.Value)
+ ?? context.FindUnityEditor(version.Year, version.Stream, version.Update)
+ ?? context.FindUnityEditor(version.Year, version.Stream)
+ ?? context.FindUnityEditor(version.Year)
+ ?? throw new Exception("Failed to locate Unity Editor. Try to specify it's path explicitly.");
+
+ if (!unityEditor.Version.Equals(version))
+ context.Log.Warning("Can't locate Unity {0} exactly, using {1} instead", version, unityEditor.Version);
+ }
+ else
+ {
+ unityEditor = context.FindUnityEditor();
+ if (unityEditor == null)
+ throw new Exception("Failed to locate Unity Editor. Try to specify it's path explicitly.");
+ }
+
new UnityEditor(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools, context.Log)
.Run(
- context.FindUnityEditor()
- ?? throw new Exception("Failed to locate Unity Editor. Try to specify it's path explicitly."),
+ unityEditor,
arguments,
settings ?? new UnityEditorSettings());
-
+ }
///
@@ -246,7 +389,8 @@ from editor in context.FindUnityEditors()
ReleaseStagePriority(version.Stage),
version.Year descending,
version.Stream descending,
- version.Update descending
+ version.Update descending,
+ version.SuffixNumber descending
select editor
);
@@ -279,7 +423,8 @@ from editor in context.FindUnityEditors()
orderby
ReleaseStagePriority(version.Stage),
version.Stream descending,
- version.Update descending
+ version.Update descending,
+ version.SuffixNumber descending
select editor
);
@@ -313,7 +458,114 @@ from editor in context.FindUnityEditors()
version.Stream == stream
orderby
ReleaseStagePriority(version.Stage),
- version.Update descending
+ version.Update descending,
+ version.SuffixNumber descending
+ select editor
+ );
+
+ ///
+ /// Locates installed Unity Editor by version (year, stream and update).
+ /// If more than one Unity Editor satisfies specified version, then latest one is returned.
+ ///
+ /// Year part of Unity version aka major version.
+ /// Stream part of Unity version (aka minor version). Usually 1, 2 and 3 mean tech stream while 4 is long term support.
+ /// Update part of Unity version.
+ /// Descriptor of Unity Editor or null.
+ ///
+ ///
+ /// var editor = FindUnityEditor(2018, 3);
+ /// if (editor != null)
+ /// Information("Found Unity Editor {0} at path {1}", editor.Version, editor.Path);
+ /// else
+ /// Warning("Cannot find Unity Editor 2018.3");
+ ///
+ ///
+ [CakeMethodAlias]
+ [CakeAliasCategory("Locate")]
+ [CakeNamespaceImport("Cake.Unity.Version")]
+ public static UnityEditorDescriptor FindUnityEditor(this ICakeContext context, int year, int stream, int update) =>
+ Enumerable.FirstOrDefault
+ (
+ from editor in context.FindUnityEditors()
+ let version = editor.Version
+ where
+ version.Year == year &&
+ version.Stream == stream &&
+ version.Update == update
+ orderby
+ ReleaseStagePriority(version.Stage),
+ version.SuffixNumber descending
+ select editor
+ );
+
+ ///
+ /// Locates installed Unity Editor by version (year, stream, update and stage).
+ /// If more than one Unity Editor satisfies specified version, then latest one is returned.
+ ///
+ /// Year part of Unity version aka major version.
+ /// Stream part of Unity version (aka minor version). Usually 1, 2 and 3 mean tech stream while 4 is long term support.
+ /// Update part of Unity version.
+ /// Stage part of Unity version. "a" - mean Alpha, "b" - Beta, "p" - Patch, "f" - Final
+ /// Descriptor of Unity Editor or null.
+ ///
+ ///
+ /// var editor = FindUnityEditor(2018, 3, 14, 'f');
+ /// if (editor != null)
+ /// Information("Found Unity Editor {0} at path {1}", editor.Version, editor.Path);
+ /// else
+ /// Warning("Cannot find Unity Editor 2018.3");
+ ///
+ ///
+ [CakeMethodAlias]
+ [CakeAliasCategory("Locate")]
+ [CakeNamespaceImport("Cake.Unity.Version")]
+ public static UnityEditorDescriptor FindUnityEditor(this ICakeContext context, int year, int stream, int update, char suffixCharacter) =>
+ Enumerable.FirstOrDefault
+ (
+ from editor in context.FindUnityEditors()
+ let version = editor.Version
+ where
+ version.Year == year &&
+ version.Stream == stream &&
+ version.Update == update &&
+ version.SuffixCharacter == suffixCharacter
+ orderby
+ version.SuffixNumber descending
+ select editor
+ );
+
+ ///
+ /// Locates installed Unity Editor by full version.
+ ///
+ /// Year part of Unity version aka major version.
+ /// Stream part of Unity version (aka minor version). Usually 1, 2 and 3 mean tech stream while 4 is long term support.
+ /// Update part of Unity version.
+ /// Stage part of Unity version. "a" - mean Alpha, "b" - Beta, "p" - Patch, "f" - Final
+ /// SuffixNumber part of Unity version.
+ /// Descriptor of Unity Editor or null.
+ ///
+ ///
+ /// var editor = FindUnityEditor(2018, 3, 14, 'f', 1);
+ /// if (editor != null)
+ /// Information("Found Unity Editor {0} at path {1}", editor.Version, editor.Path);
+ /// else
+ /// Warning("Cannot find Unity Editor 2018.3");
+ ///
+ ///
+ [CakeMethodAlias]
+ [CakeAliasCategory("Locate")]
+ [CakeNamespaceImport("Cake.Unity.Version")]
+ public static UnityEditorDescriptor FindUnityEditor(this ICakeContext context, int year, int stream, int update, char suffixCharacter, int suffixNumber) =>
+ Enumerable.FirstOrDefault
+ (
+ from editor in context.FindUnityEditors()
+ let version = editor.Version
+ where
+ version.Year == year &&
+ version.Stream == stream &&
+ version.Update == update &&
+ version.SuffixCharacter == suffixCharacter &&
+ version.SuffixNumber == suffixNumber
select editor
);
@@ -341,7 +593,7 @@ public static IReadOnlyCollection FindUnityEditors(this I
return
unityEditorsCache =
- SeekerOfEditors.GetSeeker(context.Environment, context.Globber, context.Log)
+ SeekerOfEditors.GetSeeker(context.Environment, context.Globber, context.Log, context.FileSystem)
.Seek();
}
@@ -363,5 +615,34 @@ private static int ReleaseStagePriority(UnityReleaseStage stage)
return 2;
}
}
+
+ private static UnityVersion GetUnityVersionFromProjectPath(this ICakeContext context, DirectoryPath projectPath)
+ {
+ context.Log.Debug("Determining Unity version for project: {0}", projectPath);
+ try
+ {
+ var filePath =
+ projectPath
+ .Combine("ProjectSettings")
+ .CombineWithFilePath("ProjectVersion.txt");
+
+ string version =
+ context
+ .FileSystem
+ .GetFile(filePath)
+ .ReadLines(Encoding.Default)
+ .First()
+ .Split(' ')
+ [1]
+ .Trim();
+
+ return UnityVersion.Parse(version);
+ }
+ catch (Exception e)
+ {
+ context.Log.Warning("Can't determine unity version from project path. Error: {0}", e.Message);
+ return null;
+ }
+ }
}
}
diff --git a/src/Cake.Unity/Version/UnityVersion.cs b/src/Cake.Unity/Version/UnityVersion.cs
index 4fba3dc..475a212 100644
--- a/src/Cake.Unity/Version/UnityVersion.cs
+++ b/src/Cake.Unity/Version/UnityVersion.cs
@@ -63,5 +63,12 @@ public static UnityVersion Parse(string s)
return i;
return null;
}
+
+ public bool Equals(UnityVersion other) =>
+ Year == other.Year &&
+ Stream == other.Stream &&
+ Update == other.Update &&
+ SuffixCharacter == other.SuffixCharacter &&
+ SuffixNumber == other.SuffixNumber;
}
}