From a6170086bf670daefa9bb7b21a019ff0800c4228 Mon Sep 17 00:00:00 2001 From: calvin rien Date: Tue, 9 Aug 2022 20:53:01 -0700 Subject: [PATCH 1/7] Warn if files in build are not in git. --- .editorconfig | 91 +++++++++++++++++++------ .gitattributes | 94 ++++++++++++++++++++++++++ .gitignore | 34 ++++++++-- Editor/PostBuildLog.cs | 148 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 340 insertions(+), 27 deletions(-) create mode 100644 .gitattributes diff --git a/.editorconfig b/.editorconfig index b98f5d9..f15eef1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,22 +1,69 @@ -# see http://editorconfig.org/ for docs on this file - -[{*.cs,*.json}] -charset=utf-8 -end_of_line=lf -indent_size=4 -indent_style=space -insert_final_newline=true -trim_trailing_whitespace=true - -# Microsoft .NET properties -dotnet_sort_system_directives_first = true -csharp_new_line_before_catch=true -csharp_new_line_before_else=true -csharp_new_line_before_finally=true -csharp_new_line_before_members_in_object_initializers=false -csharp_new_line_before_open_brace = accessors, anonymous_methods, anonymous_types, control_blocks, events, indexers, lambdas, local_functions, methods, object_collection_array_initializers, properties, types -csharp_space_after_cast=false - -[{*.asmdef,*.meta}] -indent_size=2 -indent_style=space +root = true + +[*] +ignore_if_in_header = This code was generated by a tool| +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.cs] +; uncomment to enable full formatting of c# files +formatters = generic, uncrustify + +[*.asmdef] +scrape_api = true + +[**/Tests/**.asmdef] +scrape_api = false + +[*.Tests.asmdef] +scrape_api = false + +[*.md] +indent_size = 2 +; trailing whitespace is unfortunately significant in markdown +trim_trailing_whitespace = false +; uncomment to enable basic formatting of markdown files +formatters = generic + + +[{Makefile,makefile}] +; tab characters are part of the Makefile format +indent_style = tab + +[*.asmdef] +indent_size = 4 + +[*.json] +indent_size = 2 + +[*.{vcproj,bat,cmd,xaml,tt,t4,ttinclude}] +end_of_line = crlf + +; this VS-specific stuff is based on experiments to see how VS will modify a file after it has been manually edited. +; the settings are meant to closely match what VS does to minimize unnecessary diffs. +[*.{vcxproj,vcxproj.filters}] +indent_style = space +indent_size = 2 +end_of_line = crlf +charset = utf-8-bom +trim_trailing_whitespace = true +insert_final_newline = false +; must be broken out because of 51-char bug (https://github.com/editorconfig/editorconfig-visualstudio/issues/21) +[*.{csproj,pyproj,props,targets}] +indent_style = space +indent_size = 2 +end_of_line = crlf +charset = utf-8-bom +trim_trailing_whitespace = true +insert_final_newline = false +[*.{sln,sln.template}] +indent_style = tab +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4da2687 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,94 @@ +# Don't handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* -text + +*.cs text eol=lf diff=csharp +*.shader text eol=lf +*.cginc text eol=lf +*.hlsl text eol=lf +*.compute text eol=lf +*.meta text eol=lf + +# base git lfs attributes to store binary files in lfs +*.3[dD][sS] filter=lfs diff=lfs merge=lfs -text lockable +*.7[zZ] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA][aA][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA][iI] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA][iI][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA][pP][kK] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA][sS][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[aA][vV][iI] filter=lfs diff=lfs merge=lfs -text lockable +*.[bB][mM][pP] filter=lfs diff=lfs merge=lfs -text lockable +*.[bB][zZ]2 filter=lfs diff=lfs merge=lfs -text lockable +*.[cC]4[dD] filter=lfs diff=lfs merge=lfs -text lockable +*.[dD][bB] filter=lfs diff=lfs merge=lfs -text lockable +*.[dD][aA][eE] filter=lfs diff=lfs merge=lfs -text lockable +*.[dD][aA][tT] filter=lfs diff=lfs merge=lfs -text lockable +*.[dD][dD][sS] filter=lfs diff=lfs merge=lfs -text lockable +*.[dD][lL][lL] filter=lfs diff=lfs merge=lfs -text lockable +*.[dD][xX][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[eE][pP][sS] filter=lfs diff=lfs merge=lfs -text lockable +*.[eE][xX][eE] filter=lfs diff=lfs merge=lfs -text lockable +*.[eE][xX][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[fF][bB][xX] filter=lfs diff=lfs merge=lfs -text lockable +*.[fF][lL][vV] filter=lfs diff=lfs merge=lfs -text lockable +*.[gG][iI][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[gG][zZ] filter=lfs diff=lfs merge=lfs -text lockable +*.[hH][dD][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[jJ][aA][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[jJ][pP][gG] filter=lfs diff=lfs merge=lfs -text lockable +*.[lL][fF][sS].* filter=lfs diff=lfs merge=lfs -text lockable +*.[lL][iI][bB] filter=lfs diff=lfs merge=lfs -text lockable +*.[lL][wW][oO] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM]4[aA] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM]4[vV] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][aA] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][aA][xX] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][bB] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][oO][dD] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][oO][vV] filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][pP]3 filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][pP]4 filter=lfs diff=lfs merge=lfs -text lockable +*.[mM][pP][gG] filter=lfs diff=lfs merge=lfs -text lockable +*.[oO][bB][jJ] filter=lfs diff=lfs merge=lfs -text lockable +*.[oO][gG][gG] filter=lfs diff=lfs merge=lfs -text lockable +*.[oO][gG][vV] filter=lfs diff=lfs merge=lfs -text lockable +*.[oO][tT][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[pP][bB] filter=lfs diff=lfs merge=lfs -text lockable +*.[pP][dD][bB] filter=lfs diff=lfs merge=lfs -text lockable +*.[pP][dD][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[pP][nN][gG] filter=lfs diff=lfs merge=lfs -text lockable +*.[pP][sS][dD] filter=lfs diff=lfs merge=lfs -text lockable +*.[pP][vV][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[rR][aA][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[rR][aA][wW] filter=lfs diff=lfs merge=lfs -text lockable +*.[sS][oO] filter=lfs diff=lfs merge=lfs -text lockable +*.[tT][aA][rR] filter=lfs diff=lfs merge=lfs -text lockable +*.[tT][gG][aA] filter=lfs diff=lfs merge=lfs -text lockable +*.[tT][iI][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[tT][tT][fF] filter=lfs diff=lfs merge=lfs -text lockable +*.[wW][aA][vV] filter=lfs diff=lfs merge=lfs -text lockable +*.[wW][mM][vV] filter=lfs diff=lfs merge=lfs -text lockable +*.[zZ][iI][pP] filter=lfs diff=lfs merge=lfs -text lockable +*.aiff filter=lfs diff=lfs merge=lfs -text lockable +*.apng filter=lfs diff=lfs merge=lfs -text lockable +*.assetbundle filter=lfs diff=lfs merge=lfs -text lockable +*.astc filter=lfs diff=lfs merge=lfs -text lockable +*.blend filter=lfs diff=lfs merge=lfs -text lockable +*.bundle filter=lfs diff=lfs merge=lfs -text lockable +*.bytes filter=lfs diff=lfs merge=lfs -text lockable +*.caffemodel filter=lfs diff=lfs merge=lfs -text lockable +*.cubemap filter=lfs diff=lfs merge=lfs -text lockable +*.dylib filter=lfs diff=lfs merge=lfs -text lockable +*.jpeg filter=lfs diff=lfs merge=lfs -text lockable +*.lwo2 filter=lfs diff=lfs merge=lfs -text lockable +*.mjpeg filter=lfs diff=lfs merge=lfs -text lockable +*.mpeg filter=lfs diff=lfs merge=lfs -text lockable +*.svgz filter=lfs diff=lfs merge=lfs -text lockable +*.tiff filter=lfs diff=lfs merge=lfs -text lockable +*.unitypackage filter=lfs diff=lfs merge=lfs -text lockable +*.webm filter=lfs diff=lfs merge=lfs -text lockable +*.webp filter=lfs diff=lfs merge=lfs -text lockable +LightingData.asset filter=lfs diff=lfs merge=lfs -text lockable +NavMesh.asset filter=lfs diff=lfs merge=lfs -text lockable diff --git a/.gitignore b/.gitignore index 8af868e..49fa13c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # This .gitignore file should be placed at the root of your Unity project directory # -# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore # /[Ll]ibrary/ /[Tt]emp/ @@ -8,16 +8,23 @@ /[Bb]uild/ /[Bb]uilds/ /[Ll]ogs/ -/[Mm]emoryCaptures/ +/[Uu]ser[Ss]ettings/ -# Asset meta data should only be ignored when the corresponding asset is also ignored +# Never ignore Asset meta data !/[Aa]ssets/**/*.meta +# MemoryCaptures can get excessive in size. +# They also could contain extremely sensitive data +/[Mm]emoryCaptures/ + +# Recordings can get excessive in size +/[Rr]ecordings/ + # Uncomment this line if you wish to ignore the asset store tools plugin # /[Aa]ssets/AssetStoreTools* # Autogenerated Jetbrains Rider plugin -[Aa]ssets/Plugins/Editor/JetBrains* +/[Aa]ssets/Plugins/Editor/JetBrains* # Visual Studio cache directory .vs/ @@ -53,8 +60,27 @@ sysinfo.txt # Builds *.apk +*.aab *.unitypackage +*.app # Crashlytics generated file crashlytics-build.properties +# Packed Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* + +# Temporary auto-generated Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* + +# Files and Folders hidden from unity. https://docs.unity3d.com/Manual/SpecialFolders.html "Hidden Assets" +**/*~ + +# Annoying Mac Files +.DS_Store + +# Hide junk in the _Attic +**/_[aA]ttic/ +**/_[aA]ttic.meta + diff --git a/Editor/PostBuildLog.cs b/Editor/PostBuildLog.cs index 702b4e8..6aff140 100644 --- a/Editor/PostBuildLog.cs +++ b/Editor/PostBuildLog.cs @@ -1,6 +1,9 @@ +#define GIT_CHECK + using System; using System.IO; using System.Text; +using System.Collections.Generic; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; @@ -9,6 +12,11 @@ using Debug = UnityEngine.Debug; public class PostBuildLog : ScriptableObject { + private static readonly Regex ASSET_ENTRY = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); + private static readonly Regex GIT_ERROR = new Regex("^error:.*'(.*)'.*$", RegexOptions.IgnoreCase); + + private const string GIT_LS = "ls-files --error-unmatch{0}"; + private enum STATUS { FOUND, @@ -157,5 +165,143 @@ private static void WriteBuildLog(string buildPath, string target="") { Debug.LogException(e); Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, target); } - } + +#if GIT_CHECK + CheckGit(report.ToString()); +#endif + } + + private static void CheckGit(string buildReport) + { + var buildAssets = new List(); + + var unversioned = new List(); + + var lines = buildReport.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); + + var directoryHashset = new HashSet(); + + // for each line in buildreport + // see if it matches regex + // if so add the matched substring to assets + foreach (var line in lines) + { + var match = ASSET_ENTRY.Match(line); + + if (match.Groups.Count == 2) + { + var asset = match.Groups[1].Value; + + var path = Path.GetDirectoryName(asset).Replace('\\', '/'); + + if (!path.Equals("Assets", StringComparison.CurrentCultureIgnoreCase)) + { + directoryHashset.Add(string.Format("{0}.meta", path)); + } + + buildAssets.Add(asset); + } + } + + var arguments = new List(); + + var stringBuilder = new StringBuilder(); + + foreach (var asset in buildAssets) + { + // Verify the file actually exists + if (!File.Exists(asset)) + { + // Unity generates some assets that are added to the build. + // (Unity does this with movies) + + //Debug.LogWarningFormat("doesn't exist: {0}", asset); + continue; + } + + // also check for .meta files + var line = string.Format(" \"{0}\" \"{0}.meta\"", asset); + + if (line.Length + stringBuilder.Length > 2000) + { + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + } + + stringBuilder.Append(line); + } + + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + + foreach (var dir in directoryHashset) + { + var line = string.Format(" \"{0}\"", dir); + + if (line.Length + stringBuilder.Length > 2000) + { + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + } + + stringBuilder.Append(line); + } + + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + + foreach (var line in arguments) + { + using (var process = new System.Diagnostics.Process()) + { + process.StartInfo.FileName = "git"; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + //process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + process.StartInfo.Arguments = string.Format(GIT_LS, line); + process.Start(); + + process.WaitForExit(); + + if (process.ExitCode != 0) + { + var err = process.StandardError.ReadToEnd(); + + var results = err.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var result in results) + { + var match = GIT_ERROR.Match(result); + + if (match.Groups.Count == 2) + { + var unver = match.Groups[1].Value; + + //Debug.LogWarningFormat("unversioned asset in build: {0}", unver); + + unversioned.Add(unver); + } + } + } + } + } + + if (unversioned.Count == 0) + { + Debug.Log("No unversioned assets in build!"); + } + else + { + unversioned.Sort(); + + foreach (var asset in unversioned) + { + Debug.LogWarningFormat("unversioned asset in build: {0}", asset); + } + } + + // TODO: Show a dialog box and add the assets to git? + } } From ff9d6c11e2375d90f96761120ba1023cefbb83e9 Mon Sep 17 00:00:00 2001 From: calvin rien Date: Thu, 11 Aug 2022 09:52:00 -0700 Subject: [PATCH 2/7] Apply Unity coding conventions. --- Editor/PostBuildLog.cs | 536 ++++++++++++++++++++++------------------- 1 file changed, 288 insertions(+), 248 deletions(-) diff --git a/Editor/PostBuildLog.cs b/Editor/PostBuildLog.cs index 6aff140..1176db3 100644 --- a/Editor/PostBuildLog.cs +++ b/Editor/PostBuildLog.cs @@ -1,307 +1,347 @@ #define GIT_CHECK using System; +using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Text; -using System.Collections.Generic; +using System.Text.RegularExpressions; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; using UnityEngine.SceneManagement; -using System.Text.RegularExpressions; using Debug = UnityEngine.Debug; -public class PostBuildLog : ScriptableObject { - private static readonly Regex ASSET_ENTRY = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); - private static readonly Regex GIT_ERROR = new Regex("^error:.*'(.*)'.*$", RegexOptions.IgnoreCase); - - private const string GIT_LS = "ls-files --error-unmatch{0}"; - - private enum STATUS - { - FOUND, - END - } - - private static readonly Regex BUILD_REPORT = new Regex(@"^Build Report$", RegexOptions.IgnoreCase); - private static readonly Regex DASH_LINE = new Regex(@"^-+$"); - private static readonly Regex DEPENDENCIES_LIST = new Regex(@"^Mono dependencies included in the build$", RegexOptions.IgnoreCase); - - [MenuItem("Tools/Post Build Log/Test Build Report")] - private static void TestBuildReport() - { - WriteBuildLog(Application.dataPath, "android"); - } - - [PostProcessBuild] // Requires Unity 3.5+ - private static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) { - // This is a hack, but on windows you have to wait - // 1 frame after a build finishes for the - // log file to get written out. (and delayCall doesn't work). - EditorApplication.update = CallbackFunc; - - void CallbackFunc() - { - WriteBuildLog(buildPath, target.ToString()); - EditorApplication.update -= CallbackFunc; - } - } +public class PostBuildLog : ScriptableObject +{ + static readonly Regex k_AssetEntry = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); + static readonly Regex k_GitError = new Regex("^error:.*'(.*)'.*$", RegexOptions.IgnoreCase); + + const string k_GitLs = "ls-files --error-unmatch{0}"; + + enum Status + { + Found, + End + } + + static readonly Regex k_BuildReport = new Regex(@"^Build Report$", RegexOptions.IgnoreCase); + static readonly Regex k_DashLine = new Regex(@"^-+$"); + static readonly Regex k_DependenciesList = new Regex(@"^Mono dependencies included in the build$", RegexOptions.IgnoreCase); + + [MenuItem("Tools/Post Build Log/Test Build Report")] + static void TestBuildReport() + { + WriteBuildLog(Application.dataPath, "android"); + } + + [PostProcessBuild] // Requires Unity 3.5+ + static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) + { + // This is a hack, but on windows you have to wait + // 1 frame after a build finishes for the + // log file to get written out. (and delayCall doesn't work). + EditorApplication.update = CallbackFunc; + + void CallbackFunc() + { + WriteBuildLog(buildPath, target.ToString()); + EditorApplication.update -= CallbackFunc; + } + } - private static string ScenesInBuild() - { - StringBuilder scenesList = new StringBuilder(); + static string ScenesInBuild() + { + StringBuilder scenesList = new StringBuilder(); - scenesList.AppendLine("Scenes included in the build"); + scenesList.AppendLine("Scenes included in the build"); - int sceneCount = SceneManager.sceneCountInBuildSettings; - for (int i = 0; i < sceneCount; i++) - { - var sceneName = SceneUtility.GetScenePathByBuildIndex(i); + int sceneCount = SceneManager.sceneCountInBuildSettings; + for (int i = 0; i < sceneCount; i++) + { + var sceneName = SceneUtility.GetScenePathByBuildIndex(i); - scenesList.AppendLine(sceneName); - } + scenesList.AppendLine(sceneName); + } - return scenesList.ToString(); - } + return scenesList.ToString(); + } - private static void WriteBuildLog(string buildPath, string target="") { + static void WriteBuildLog(string buildPath, string target = "") + { //string editorLogFilePath = null; string[] pieces; bool winEditor = Application.platform == RuntimePlatform.WindowsEditor; - STATUS buildMarker = STATUS.END; - STATUS dependencyMarker = STATUS.END; - - if (winEditor) { - pieces = new string[] { Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Unity", "Editor", "Editor.log"}; - } else { - pieces = new string[] { Environment.GetFolderPath(Environment.SpecialFolder.Personal), - "Library","Logs","Unity","Editor.log"}; + Status buildMarker = Status.End; + Status dependencyMarker = Status.End; + + if (winEditor) + { + pieces = new string[] + { + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Unity", "Editor", "Editor.log" + }; + } + else + { + pieces = new string[] + { + Environment.GetFolderPath(Environment.SpecialFolder.Personal), + "Library", "Logs", "Unity", "Editor.log" + }; } - string editorLogFilePath = Path.Combine(pieces); + string editorLogFilePath = Path.Combine(pieces); - if (!File.Exists(editorLogFilePath)) { + if (!File.Exists(editorLogFilePath)) + { Debug.LogWarning("Editor log file could not be found at: " + editorLogFilePath); return; } StringBuilder report = new StringBuilder(); - StringBuilder assemblies = new StringBuilder(); - - using (StreamReader reader = new StreamReader(File.Open(editorLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { - string prevLine = null; - string currentLine = null; - int lineNum = 0; - while (true) { - lineNum++; - prevLine = currentLine; + StringBuilder assemblies = new StringBuilder(); + + using (StreamReader reader = new StreamReader(File.Open(editorLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + { + string currentLine = null; + int lineNum = 0; + while (true) + { + lineNum++; + var prevLine = currentLine; currentLine = reader.ReadLine(); - if (currentLine == null) { + if (currentLine == null) + { break; } - if (BUILD_REPORT.IsMatch(currentLine)) { - Debug.LogFormat("found a build report at line: {0}", lineNum); + if (k_BuildReport.IsMatch(currentLine)) + { + Debug.LogFormat("found a build report at line: {0}", lineNum); - buildMarker = STATUS.FOUND; + buildMarker = Status.Found; report.Length = 0; - report.AppendLine(prevLine); + report.AppendLine(prevLine); + } + else if (k_DependenciesList.IsMatch(currentLine)) + { + dependencyMarker = Status.Found; + assemblies.Length = 0; + assemblies.AppendLine(prevLine); + } + + if (buildMarker == Status.Found) + { + report.AppendLine(currentLine); + } + else if (dependencyMarker == Status.Found) + { + assemblies.AppendLine(currentLine); + } + + // end of report. + if (buildMarker == Status.Found && k_DashLine.IsMatch(currentLine)) + { + buildMarker = Status.End; + } + else if (dependencyMarker == Status.Found && string.IsNullOrEmpty(currentLine)) + { + dependencyMarker = Status.End; } - else if (DEPENDENCIES_LIST.IsMatch(currentLine)) - { - dependencyMarker = STATUS.FOUND; - assemblies.Length = 0; - assemblies.AppendLine(prevLine); - } - - if (buildMarker == STATUS.FOUND) - { - report.AppendLine(currentLine); - } - else if (dependencyMarker == STATUS.FOUND) - { - assemblies.AppendLine(currentLine); - } - - // end of report. - if (buildMarker == STATUS.FOUND && DASH_LINE.IsMatch(currentLine)) - { - buildMarker = STATUS.END; - } - else if (dependencyMarker == STATUS.FOUND && string.IsNullOrEmpty(currentLine)) - { - dependencyMarker = STATUS.END; - } } } - if (report.Length == 0) { - Debug.Log("no build report found in log."); - return; // No builds have been run. + if (report.Length == 0) + { + Debug.Log("no build report found in log."); + return; // No builds have been run. } string outputPath; - var filename = string.Format("build {0}.log", System.DateTime.UtcNow.ToString("s").Replace(':', '-')); + var filename = $"build {DateTime.UtcNow.ToString("s").Replace(':', '-')}.log"; - if (target.StartsWith("standalone", StringComparison.InvariantCultureIgnoreCase) || target.StartsWith("android", StringComparison.InvariantCultureIgnoreCase)) { - outputPath = Path.Combine(Path.GetDirectoryName(buildPath), filename); - } else { - outputPath = Path.Combine(buildPath, filename); + if (target.StartsWith("standalone", StringComparison.InvariantCultureIgnoreCase) || target.StartsWith("android", StringComparison.InvariantCultureIgnoreCase)) + { + outputPath = Path.Combine(Path.GetDirectoryName(buildPath), filename); + } + else + { + outputPath = Path.Combine(buildPath, filename); } - try { - var output = new StringBuilder(); - output.AppendFormat("Build Report @ {0}\n\n", System.DateTime.Now.ToString("u")); - output.Append(ScenesInBuild()); - output.Append(assemblies.ToString()); - output.Append(report.ToString()); - - File.WriteAllText(outputPath, output.ToString()); - } catch (Exception e) { - Debug.LogException(e); - Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, target); + try + { + var output = new StringBuilder(); + output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.Now); + output.Append(ScenesInBuild()); + output.Append(assemblies.ToString()); + output.Append(report); + + File.WriteAllText(outputPath, output.ToString()); + + + } + catch (Exception e) + { + Debug.LogException(e); + Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, target); } #if GIT_CHECK - CheckGit(report.ToString()); + CheckGit(new StringReader(report.ToString())); #endif - } + } + + static void CheckGit(StringReader reader) + { + // TODO: check submodules + // TODO: check Packages + + var buildAssets = new List(); - private static void CheckGit(string buildReport) - { - var buildAssets = new List(); + var unversioned = new List(); - var unversioned = new List(); + var directoryHashset = new HashSet(); - var lines = buildReport.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); + while (reader.Peek() != -1) + { + var line = reader.ReadLine(); - var directoryHashset = new HashSet(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + // var lines = buildReport.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); + + // for each line in buildreport + // see if it matches regex + // if so add the matched substring to assets + var match = k_AssetEntry.Match(line); - // for each line in buildreport - // see if it matches regex - // if so add the matched substring to assets - foreach (var line in lines) - { - var match = ASSET_ENTRY.Match(line); + if (match.Groups.Count == 2) + { + var asset = match.Groups[1].Value; + + if (string.IsNullOrEmpty(asset)) + { + continue; + } - if (match.Groups.Count == 2) - { - var asset = match.Groups[1].Value; + var path = Path.GetDirectoryName(asset).Replace('\\', '/'); + + if (!path.Equals("Assets", StringComparison.CurrentCultureIgnoreCase)) + { + directoryHashset.Add(path); + } + + buildAssets.Add(asset); + } + } + + var arguments = new List(); + + var stringBuilder = new StringBuilder(); + + foreach (var asset in buildAssets) + { + // Verify the file actually exists + if (!File.Exists(asset)) + { + // Unity generates some assets that are added to the build. + // (Unity does this with movies) + + //Debug.LogWarningFormat("doesn't exist: {0}", asset); + continue; + } + + // also check for .meta files + var line = string.Format(" \"{0}\" \"{0}.meta\"", asset); + + if (line.Length + stringBuilder.Length > 2000) + { + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + } + + stringBuilder.Append(line); + } + + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + + foreach (var dir in directoryHashset) + { + var line = $" \"{dir}.meta\""; + + if (line.Length + stringBuilder.Length > 2000) + { + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + } + + stringBuilder.Append(line); + } + + arguments.Add(stringBuilder.ToString()); + stringBuilder.Clear(); + + foreach (var line in arguments) + { + using var process = new Process(); + process.StartInfo.FileName = "git"; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + + //process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + process.StartInfo.Arguments = string.Format(k_GitLs, line); + process.Start(); + + process.WaitForExit(); + + if (process.ExitCode != 0) + { + var err = process.StandardError.ReadToEnd(); + + var results = err.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var result in results) + { + var match = k_GitError.Match(result); + + if (match.Groups.Count == 2) + { + var unver = match.Groups[1].Value; + + //Debug.LogWarningFormat("unversioned asset in build: {0}", unver); + + unversioned.Add(unver); + } + } + } + } + + if (unversioned.Count == 0) + { + Debug.Log("No unversioned assets in build!"); + } + else + { + unversioned.Sort(); + + foreach (var asset in unversioned) + { + Debug.LogWarningFormat("unversioned asset in build: {0}", asset); + } + } - var path = Path.GetDirectoryName(asset).Replace('\\', '/'); - - if (!path.Equals("Assets", StringComparison.CurrentCultureIgnoreCase)) - { - directoryHashset.Add(string.Format("{0}.meta", path)); - } - - buildAssets.Add(asset); - } - } - - var arguments = new List(); - - var stringBuilder = new StringBuilder(); - - foreach (var asset in buildAssets) - { - // Verify the file actually exists - if (!File.Exists(asset)) - { - // Unity generates some assets that are added to the build. - // (Unity does this with movies) - - //Debug.LogWarningFormat("doesn't exist: {0}", asset); - continue; - } - - // also check for .meta files - var line = string.Format(" \"{0}\" \"{0}.meta\"", asset); - - if (line.Length + stringBuilder.Length > 2000) - { - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); - } - - stringBuilder.Append(line); - } - - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); - - foreach (var dir in directoryHashset) - { - var line = string.Format(" \"{0}\"", dir); - - if (line.Length + stringBuilder.Length > 2000) - { - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); - } - - stringBuilder.Append(line); - } - - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); - - foreach (var line in arguments) - { - using (var process = new System.Diagnostics.Process()) - { - process.StartInfo.FileName = "git"; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.UseShellExecute = false; - //process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - process.StartInfo.Arguments = string.Format(GIT_LS, line); - process.Start(); - - process.WaitForExit(); - - if (process.ExitCode != 0) - { - var err = process.StandardError.ReadToEnd(); - - var results = err.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); - - foreach (var result in results) - { - var match = GIT_ERROR.Match(result); - - if (match.Groups.Count == 2) - { - var unver = match.Groups[1].Value; - - //Debug.LogWarningFormat("unversioned asset in build: {0}", unver); - - unversioned.Add(unver); - } - } - } - } - } - - if (unversioned.Count == 0) - { - Debug.Log("No unversioned assets in build!"); - } - else - { - unversioned.Sort(); - - foreach (var asset in unversioned) - { - Debug.LogWarningFormat("unversioned asset in build: {0}", asset); - } - } - - // TODO: Show a dialog box and add the assets to git? - } + // TODO: Show a dialog box and add the assets to git? + } } From b714c24252638780817b4ca69961eafe51ff695c Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Sat, 17 Sep 2022 11:01:52 -0700 Subject: [PATCH 3/7] version bump --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f753aeb..476f596 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.github.darktable.postbuildlog", - "version": "0.1.0", + "version": "1.0.0", "displayName": "Post Build Log", "description": "Save out a log file of scenes, assemblies, and assets included in build", "unity": "2018.4", @@ -12,5 +12,6 @@ "name": "Calvin Rien", "email": "", "url": "https://github.com/darktable" - } -} + }, + "type": "tool" +} \ No newline at end of file From ad19988ece71d7d8595ff4845011fbf523acdd56 Mon Sep 17 00:00:00 2001 From: calvin rien Date: Tue, 18 Oct 2022 21:56:08 -0700 Subject: [PATCH 4/7] much faster method for finding files not in git. --- Editor/PostBuildLog.cs | 287 ++++++++++++++++++------------- Editor/Unity.PostBuildLog.asmdef | 5 +- 2 files changed, 173 insertions(+), 119 deletions(-) diff --git a/Editor/PostBuildLog.cs b/Editor/PostBuildLog.cs index 1176db3..fcb5a19 100644 --- a/Editor/PostBuildLog.cs +++ b/Editor/PostBuildLog.cs @@ -1,11 +1,11 @@ -#define GIT_CHECK - using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using System.Text.RegularExpressions; +using Unity.EditorCoroutines.Editor; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; @@ -14,60 +14,73 @@ public class PostBuildLog : ScriptableObject { - static readonly Regex k_AssetEntry = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); - static readonly Regex k_GitError = new Regex("^error:.*'(.*)'.*$", RegexOptions.IgnoreCase); + // rev-parse --short HEAD // get hash of current revision + + // git diff --cached --compact-summary // staged files that haven't been committed. + // .gitmodules | 3 +++ - const string k_GitLs = "ls-files --error-unmatch{0}"; + // git ls-files --exclude-standard // everything in the repo. + // git ls-files -d // deleted + // git ls-files -m // modified + // git ls-files -o --exclude-standard // files that need to be added - enum Status + private enum Status { Found, End } - static readonly Regex k_BuildReport = new Regex(@"^Build Report$", RegexOptions.IgnoreCase); - static readonly Regex k_DashLine = new Regex(@"^-+$"); - static readonly Regex k_DependenciesList = new Regex(@"^Mono dependencies included in the build$", RegexOptions.IgnoreCase); + private const string k_GitFilename = "git"; + private const string k_IgnoredFiles = "ls-files -i -o --exclude-standard"; + private const string k_UnversionedFiles = "ls-files -o --exclude-standard"; + + private static readonly string[] k_Newlines = { "\r\n", "\r", "\n" }; + + private static readonly Regex k_AssetEntry = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); + private static readonly Regex k_BuildReport = new Regex(@"^Build Report$", RegexOptions.IgnoreCase); + private static readonly Regex k_DashLine = new Regex(@"^-+$"); + + private static readonly Regex k_DependenciesList = + new Regex(@"^Mono dependencies included in the build$", RegexOptions.IgnoreCase); [MenuItem("Tools/Post Build Log/Test Build Report")] static void TestBuildReport() { - WriteBuildLog(Application.dataPath, "android"); + EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(Application.dataPath, "android")); } - [PostProcessBuild] // Requires Unity 3.5+ - static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) + [MenuItem("Tools/Post Build Log/Find Git Unversioned")] + static void TestGitUnversioned() { - // This is a hack, but on windows you have to wait - // 1 frame after a build finishes for the - // log file to get written out. (and delayCall doesn't work). - EditorApplication.update = CallbackFunc; + var buildLog = new StringBuilder(); + AppendBuildLog(buildLog); - void CallbackFunc() - { - WriteBuildLog(buildPath, target.ToString()); - EditorApplication.update -= CallbackFunc; - } + var scenes = new StringBuilder(); + ScenesInBuild(scenes); + + EditorCoroutineUtility.StartCoroutineOwnerless(CheckGit(buildLog, scenes)); } - static string ScenesInBuild() + [PostProcessBuild] // Requires Unity 3.5+ + static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) { - StringBuilder scenesList = new StringBuilder(); + EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(buildPath, target.ToString())); + } - scenesList.AppendLine("Scenes included in the build"); + static void ScenesInBuild(StringBuilder report) + { + report.Append("Scenes included in the build\n"); int sceneCount = SceneManager.sceneCountInBuildSettings; for (int i = 0; i < sceneCount; i++) { var sceneName = SceneUtility.GetScenePathByBuildIndex(i); - scenesList.AppendLine(sceneName); + report.AppendFormat("{0}\n", sceneName); } - - return scenesList.ToString(); } - static void WriteBuildLog(string buildPath, string target = "") + private static void AppendBuildLog(StringBuilder output) { //string editorLogFilePath = null; string[] pieces; @@ -104,7 +117,8 @@ static void WriteBuildLog(string buildPath, string target = "") StringBuilder report = new StringBuilder(); StringBuilder assemblies = new StringBuilder(); - using (StreamReader reader = new StreamReader(File.Open(editorLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + using (StreamReader reader = + new StreamReader(File.Open(editorLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { string currentLine = null; int lineNum = 0; @@ -155,17 +169,33 @@ static void WriteBuildLog(string buildPath, string target = "") } } + output.Append(assemblies); + output.Append(report); + } + + private static IEnumerator WriteBuildLog(string buildPath, string target = "") + { + // This is a hack, but on windows you have to wait + // 1 frame after a build finishes for the + // log file to get written out. (and delayCall doesn't work). + yield return null; + + var report = new StringBuilder(); + + AppendBuildLog(report); + if (report.Length == 0) { Debug.Log("no build report found in log."); - return; // No builds have been run. + yield break; // No builds have been run. } string outputPath; var filename = $"build {DateTime.UtcNow.ToString("s").Replace(':', '-')}.log"; - if (target.StartsWith("standalone", StringComparison.InvariantCultureIgnoreCase) || target.StartsWith("android", StringComparison.InvariantCultureIgnoreCase)) + if (target.StartsWith("standalone", StringComparison.InvariantCultureIgnoreCase) || + target.StartsWith("android", StringComparison.InvariantCultureIgnoreCase)) { outputPath = Path.Combine(Path.GetDirectoryName(buildPath), filename); } @@ -178,35 +208,34 @@ static void WriteBuildLog(string buildPath, string target = "") { var output = new StringBuilder(); output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.Now); - output.Append(ScenesInBuild()); - output.Append(assemblies.ToString()); - output.Append(report); - File.WriteAllText(outputPath, output.ToString()); + ScenesInBuild(output); + // output.Append(ScenesInBuild()); + // output.Append(assemblies.ToString()); + output.Append(report); + File.WriteAllText(outputPath, output.ToString()); } catch (Exception e) { Debug.LogException(e); - Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, target); + Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, + target); } - -#if GIT_CHECK - CheckGit(new StringReader(report.ToString())); -#endif } - static void CheckGit(StringReader reader) + static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) { // TODO: check submodules // TODO: check Packages - var buildAssets = new List(); + var buildAssets = new HashSet(); - var unversioned = new List(); + var reader = new StringReader(report.ToString()); - var directoryHashset = new HashSet(); + var visitedDirectories = new HashSet(); + visitedDirectories.Add("Assets"); while (reader.Peek() != -1) { @@ -233,115 +262,137 @@ static void CheckGit(StringReader reader) continue; } - var path = Path.GetDirectoryName(asset).Replace('\\', '/'); + if (!File.Exists(asset)) + { + // Unity generates some assets that are added to the build. + // (Unity does this with movies) + + Debug.LogWarningFormat("doesn't exist: {0}", asset); + continue; + } + + // TODO: skip files that are under git submodules - if (!path.Equals("Assets", StringComparison.CurrentCultureIgnoreCase)) + string directoryName = asset; + while (true) { - directoryHashset.Add(path); + directoryName = Path.GetDirectoryName(directoryName); + if (string.IsNullOrEmpty(directoryName)) + { + break; + } + +#if UNITY_EDITOR_WIN + directoryName = directoryName.Replace(Path.DirectorySeparatorChar, '/'); +#endif + + if (visitedDirectories.Contains(directoryName)) + { + continue; + } + + buildAssets.Add($"{directoryName}.meta"); + visitedDirectories.Add(directoryName); } buildAssets.Add(asset); + buildAssets.Add($"{asset}.meta"); } } - var arguments = new List(); + var scenesList = scenes.ToString().Split( + k_Newlines, + StringSplitOptions.None + ); - var stringBuilder = new StringBuilder(); - - foreach (var asset in buildAssets) + foreach (var scene in scenesList) { - // Verify the file actually exists - if (!File.Exists(asset)) - { - // Unity generates some assets that are added to the build. - // (Unity does this with movies) + buildAssets.Add(scene); + buildAssets.Add($"{scene}.meta"); + } - //Debug.LogWarningFormat("doesn't exist: {0}", asset); - continue; - } + // every asset in the build (except Packages) should be included in buildAssets hashset. + + // ignored files in build: + var ignoredFilesInBuild = new HashSet(); - // also check for .meta files - var line = string.Format(" \"{0}\" \"{0}.meta\"", asset); + var output = new List(); - if (line.Length + stringBuilder.Length > 2000) + yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_IgnoredFiles, output)); + + foreach (var line in output) + { + if (buildAssets.Remove(line)) { - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); + ignoredFilesInBuild.Add(line); } - - stringBuilder.Append(line); } - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); + // unversioned files in the build: + var unversionedFilesInBuild = new HashSet(); - foreach (var dir in directoryHashset) - { - var line = $" \"{dir}.meta\""; + output.Clear(); + yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_UnversionedFiles, output)); - if (line.Length + stringBuilder.Length > 2000) + foreach (var line in output) + { + if (buildAssets.Remove(line)) { - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); + unversionedFilesInBuild.Add(line); } - - stringBuilder.Append(line); } - arguments.Add(stringBuilder.ToString()); - stringBuilder.Clear(); - - foreach (var line in arguments) + if (ignoredFilesInBuild.Count > 0) { - using var process = new Process(); - process.StartInfo.FileName = "git"; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.UseShellExecute = false; - - //process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - process.StartInfo.Arguments = string.Format(k_GitLs, line); - process.Start(); - - process.WaitForExit(); + Debug.Log($"total ignored files in build: {ignoredFilesInBuild.Count}"); + foreach (var file in ignoredFilesInBuild) + { + Debug.Log($"ignored in build: {file}"); + } + } - if (process.ExitCode != 0) + if (unversionedFilesInBuild.Count > 0) + { + Debug.Log($"total unversioned files in build: {unversionedFilesInBuild.Count}"); + foreach (var file in unversionedFilesInBuild) { - var err = process.StandardError.ReadToEnd(); + Debug.Log($"unversioned in build: {file}"); + } + } - var results = err.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); + // TODO: Show a dialog box and add the assets to git? + } - foreach (var result in results) - { - var match = k_GitError.Match(result); + private static IEnumerator RunGitCommand(string command, List output) + { + using var process = new Process(); + process.StartInfo.FileName = k_GitFilename; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; - if (match.Groups.Count == 2) - { - var unver = match.Groups[1].Value; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - //Debug.LogWarningFormat("unversioned asset in build: {0}", unver); + process.StartInfo.Arguments = command; + process.Start(); - unversioned.Add(unver); - } - } + var stopwatch = Stopwatch.StartNew(); + while (true) + { + var standardOutput = process.StandardOutput.ReadLine(); + + if (standardOutput == null) + { + yield break; } - } - if (unversioned.Count == 0) - { - Debug.Log("No unversioned assets in build!"); - } - else - { - unversioned.Sort(); + output.Add(standardOutput); - foreach (var asset in unversioned) + if (stopwatch.ElapsedMilliseconds > 500) { - Debug.LogWarningFormat("unversioned asset in build: {0}", asset); + yield return null; + stopwatch.Restart(); } } - - // TODO: Show a dialog box and add the assets to git? } -} +} \ No newline at end of file diff --git a/Editor/Unity.PostBuildLog.asmdef b/Editor/Unity.PostBuildLog.asmdef index c55b6c2..f7de413 100644 --- a/Editor/Unity.PostBuildLog.asmdef +++ b/Editor/Unity.PostBuildLog.asmdef @@ -1,6 +1,9 @@ { "name": "Unity.PostBuildLog", - "references": [], + "rootNamespace": "", + "references": [ + "Unity.EditorCoroutines.Editor" + ], "includePlatforms": [ "Editor" ], From c42dc8a90026484291620c35215992d2b2c1acb9 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 19 Oct 2022 10:49:44 -0700 Subject: [PATCH 5/7] Cleanup. Tested on windows. --- Editor/PostBuildLog.cs | 109 ++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 62 deletions(-) diff --git a/Editor/PostBuildLog.cs b/Editor/PostBuildLog.cs index fcb5a19..37e80f9 100644 --- a/Editor/PostBuildLog.cs +++ b/Editor/PostBuildLog.cs @@ -14,6 +14,7 @@ public class PostBuildLog : ScriptableObject { + // Handy git commands: // rev-parse --short HEAD // get hash of current revision // git diff --cached --compact-summary // staged files that haven't been committed. @@ -27,13 +28,20 @@ public class PostBuildLog : ScriptableObject private enum Status { Found, - End + End, } private const string k_GitFilename = "git"; private const string k_IgnoredFiles = "ls-files -i -o --exclude-standard"; private const string k_UnversionedFiles = "ls-files -o --exclude-standard"; +#if UNITY_EDITOR_WIN + private static readonly string k_LogPath = Path.Combine( + new string[] { Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Unity", "Editor", "Editor.log" }); +#else + private static readonly string k_LogPath = Path.Combine( + new string[]{ Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Logs", "Unity", "Editor.log" }); +#endif private static readonly string[] k_Newlines = { "\r\n", "\r", "\n" }; private static readonly Regex k_AssetEntry = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); @@ -44,13 +52,13 @@ private enum Status new Regex(@"^Mono dependencies included in the build$", RegexOptions.IgnoreCase); [MenuItem("Tools/Post Build Log/Test Build Report")] - static void TestBuildReport() + private static void TestBuildReport() { EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(Application.dataPath, "android")); } [MenuItem("Tools/Post Build Log/Find Git Unversioned")] - static void TestGitUnversioned() + private static void TestGitUnversioned() { var buildLog = new StringBuilder(); AppendBuildLog(buildLog); @@ -62,19 +70,19 @@ static void TestGitUnversioned() } [PostProcessBuild] // Requires Unity 3.5+ - static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) + private static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) { EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(buildPath, target.ToString())); } - static void ScenesInBuild(StringBuilder report) + private static void ScenesInBuild(StringBuilder report) { report.Append("Scenes included in the build\n"); int sceneCount = SceneManager.sceneCountInBuildSettings; - for (int i = 0; i < sceneCount; i++) + for (var i = 0; i < sceneCount; i++) { - var sceneName = SceneUtility.GetScenePathByBuildIndex(i); + string sceneName = SceneUtility.GetScenePathByBuildIndex(i); report.AppendFormat("{0}\n", sceneName); } @@ -82,50 +90,27 @@ static void ScenesInBuild(StringBuilder report) private static void AppendBuildLog(StringBuilder output) { - //string editorLogFilePath = null; - string[] pieces; - - bool winEditor = Application.platform == RuntimePlatform.WindowsEditor; - Status buildMarker = Status.End; - Status dependencyMarker = Status.End; - - if (winEditor) - { - pieces = new string[] - { - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - "Unity", "Editor", "Editor.log" - }; - } - else - { - pieces = new string[] - { - Environment.GetFolderPath(Environment.SpecialFolder.Personal), - "Library", "Logs", "Unity", "Editor.log" - }; - } + var buildMarker = Status.End; + var dependencyMarker = Status.End; - string editorLogFilePath = Path.Combine(pieces); - - if (!File.Exists(editorLogFilePath)) + if (!File.Exists(k_LogPath)) { - Debug.LogWarning("Editor log file could not be found at: " + editorLogFilePath); + Debug.LogWarning($"Editor log file could not be found at: {k_LogPath}"); return; } - StringBuilder report = new StringBuilder(); - StringBuilder assemblies = new StringBuilder(); + var report = new StringBuilder(); + var assemblies = new StringBuilder(); - using (StreamReader reader = - new StreamReader(File.Open(editorLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + using (var reader = + new StreamReader(File.Open(k_LogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { string currentLine = null; - int lineNum = 0; + var lineNum = 0; while (true) { lineNum++; - var prevLine = currentLine; + string prevLine = currentLine; currentLine = reader.ReadLine(); if (currentLine == null) @@ -179,7 +164,7 @@ private static IEnumerator WriteBuildLog(string buildPath, string target = "") // 1 frame after a build finishes for the // log file to get written out. (and delayCall doesn't work). yield return null; - + var report = new StringBuilder(); AppendBuildLog(report); @@ -210,9 +195,6 @@ private static IEnumerator WriteBuildLog(string buildPath, string target = "") output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.Now); ScenesInBuild(output); - - // output.Append(ScenesInBuild()); - // output.Append(assemblies.ToString()); output.Append(report); File.WriteAllText(outputPath, output.ToString()); @@ -225,37 +207,40 @@ private static IEnumerator WriteBuildLog(string buildPath, string target = "") } } - static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) + private static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) { // TODO: check submodules // TODO: check Packages + if (report.Length == 0) + { + Debug.Log("no build report found in log."); + yield break; // No builds have been run. + } + var buildAssets = new HashSet(); - var reader = new StringReader(report.ToString()); + var reportReader = new StringReader(report.ToString()); - var visitedDirectories = new HashSet(); - visitedDirectories.Add("Assets"); + var visitedDirectories = new HashSet { "Assets" }; - while (reader.Peek() != -1) + while (reportReader.Peek() != -1) { - var line = reader.ReadLine(); + string line = reportReader.ReadLine(); if (string.IsNullOrWhiteSpace(line)) { continue; } - // var lines = buildReport.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries); - - // for each line in buildreport + // for each line in build report // see if it matches regex // if so add the matched substring to assets var match = k_AssetEntry.Match(line); if (match.Groups.Count == 2) { - var asset = match.Groups[1].Value; + string asset = match.Groups[1].Value; if (string.IsNullOrEmpty(asset)) { @@ -300,12 +285,12 @@ static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) } } - var scenesList = scenes.ToString().Split( + string[] scenesList = scenes.ToString().Split( k_Newlines, StringSplitOptions.None ); - foreach (var scene in scenesList) + foreach (string scene in scenesList) { buildAssets.Add(scene); buildAssets.Add($"{scene}.meta"); @@ -320,7 +305,7 @@ static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_IgnoredFiles, output)); - foreach (var line in output) + foreach (string line in output) { if (buildAssets.Remove(line)) { @@ -334,7 +319,7 @@ static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) output.Clear(); yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_UnversionedFiles, output)); - foreach (var line in output) + foreach (string line in output) { if (buildAssets.Remove(line)) { @@ -345,7 +330,7 @@ static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) if (ignoredFilesInBuild.Count > 0) { Debug.Log($"total ignored files in build: {ignoredFilesInBuild.Count}"); - foreach (var file in ignoredFilesInBuild) + foreach (string file in ignoredFilesInBuild) { Debug.Log($"ignored in build: {file}"); } @@ -354,7 +339,7 @@ static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) if (unversionedFilesInBuild.Count > 0) { Debug.Log($"total unversioned files in build: {unversionedFilesInBuild.Count}"); - foreach (var file in unversionedFilesInBuild) + foreach (string file in unversionedFilesInBuild) { Debug.Log($"unversioned in build: {file}"); } @@ -379,7 +364,7 @@ private static IEnumerator RunGitCommand(string command, List output) var stopwatch = Stopwatch.StartNew(); while (true) { - var standardOutput = process.StandardOutput.ReadLine(); + string standardOutput = process.StandardOutput.ReadLine(); if (standardOutput == null) { @@ -395,4 +380,4 @@ private static IEnumerator RunGitCommand(string command, List output) } } } -} \ No newline at end of file +} From c63b87989fa5f0aafc490fcef93815dfc7310d11 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Wed, 19 Oct 2022 16:55:36 -0700 Subject: [PATCH 6/7] Switch datetime to UTC now --- Editor/PostBuildLog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/PostBuildLog.cs b/Editor/PostBuildLog.cs index 37e80f9..7c1f494 100644 --- a/Editor/PostBuildLog.cs +++ b/Editor/PostBuildLog.cs @@ -192,7 +192,7 @@ private static IEnumerator WriteBuildLog(string buildPath, string target = "") try { var output = new StringBuilder(); - output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.Now); + output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.UtcNow); ScenesInBuild(output); output.Append(report); From 18c3ee857b939bb017b801c4a735eb91abe15c11 Mon Sep 17 00:00:00 2001 From: Calvin Rien Date: Fri, 4 Nov 2022 18:09:54 -0700 Subject: [PATCH 7/7] Move git validation to separate script Refactor PostBuildLog to use IPostprocessBuildWithReport --- Editor/PostBuildGitValidation.cs | 349 ++++++++++++++++++++++++++ Editor/PostBuildGitValidation.cs.meta | 11 + Editor/PostBuildLog.cs | 304 ++++++---------------- 3 files changed, 432 insertions(+), 232 deletions(-) create mode 100644 Editor/PostBuildGitValidation.cs create mode 100644 Editor/PostBuildGitValidation.cs.meta diff --git a/Editor/PostBuildGitValidation.cs b/Editor/PostBuildGitValidation.cs new file mode 100644 index 0000000..16ce69f --- /dev/null +++ b/Editor/PostBuildGitValidation.cs @@ -0,0 +1,349 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using Unity.EditorCoroutines.Editor; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEngine; +using Debug = UnityEngine.Debug; + +public class PostBuildGitValidation : IPostprocessBuildWithReport +{ + // Handy git commands: + // rev-parse --short HEAD // get hash of current revision + + // git diff --cached --compact-summary // staged files that haven't been committed. + // .gitmodules | 3 +++ + + // git ls-files --exclude-standard // everything in the repo. + // git ls-files -d // deleted + // git ls-files -m // modified + // git ls-files -o --exclude-standard // files that need to be added + + private const string k_GitFilename = "git"; + private const string k_IgnoredFiles = "ls-files -i -o --exclude-standard"; + private const string k_UnversionedFiles = "ls-files -o --exclude-standard"; + private const string k_TestCommand = "--version"; + private const string k_DotGit = ".git"; + + private static readonly Regex k_AssetEntry = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); + +#if UNITY_EDITOR_WIN + private static readonly string k_LogPath = Path.Combine( + new string[] { Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Unity", "Editor", "Editor.log" }); +#else + private static readonly string k_LogPath = Path.Combine( + new string[]{ Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Logs", "Unity", "Editor.log" }); +#endif + + public static bool CheckGitAfterBuild { get; private set; } = true; + public int callbackOrder => int.MaxValue; + + public void OnPostprocessBuild(BuildReport report) + { + var result = report.summary.result; + + if (result == BuildResult.Failed || result == BuildResult.Cancelled) + { + return; + } + + var assetList = new List(); + if (CheckGitAfterBuild && ProjectUsesGit() && GetAssetListFromBuildReport(assetList, report)) + { + EditorCoroutineUtility.StartCoroutineOwnerless(CheckGit(assetList)); + } + } + + [MenuItem("Tools/Post Build Log/Find Unversioned Files")] + private static void TestGitUnversioned() + { + var assetList = new List(); + + if (!GetAssetListFromEditorLog(assetList, k_LogPath)) + { + string prevLogPath = Path.Combine(Path.GetDirectoryName(k_LogPath), "Editor-prev.log"); + + if (!GetAssetListFromEditorLog(assetList, prevLogPath)) + { + Debug.LogWarning("no builds have been run yet."); + return; + } + } + + PostBuildLog.ScenesInBuild(assetList); + + EditorCoroutineUtility.StartCoroutineOwnerless(CheckGit(assetList)); + } + + private static bool ProjectUsesGit() + { + // check to see if the git command is available. + using var process = CreateGitProcess(); + + process.StartInfo.Arguments = k_TestCommand; + + try + { + process.Start(); + process.WaitForExit(); + + string error = process.StandardError.ReadToEnd(); + + if (!string.IsNullOrWhiteSpace(error)) + { + return false; + } + } + catch (Win32Exception) + { + return false; + } + + // search for a .git directory or .git file in the Assets directory or above. + + string assetPath = Application.dataPath; + while (!string.IsNullOrWhiteSpace(assetPath)) + { + string gitPath = Path.Combine(assetPath, k_DotGit); + + if (Directory.Exists(gitPath) || File.Exists(gitPath)) + { + return true; + } + + assetPath = Path.GetDirectoryName(assetPath); + } + + return false; + } + + private static bool GetAssetListFromBuildReport(List assetList, BuildReport buildReport) + { + var result = buildReport.summary.result; + + if (result == BuildResult.Cancelled || result == BuildResult.Failed) + { + return false; + } + + var packedAssets = buildReport.packedAssets; + foreach (var packedAsset in packedAssets) + { + foreach (var info in packedAsset.contents) + { + assetList.Add(info.sourceAssetPath); + } + } + + return assetList.Count > 0; + } + + private static bool GetAssetListFromEditorLog(List assetList, string logPath) + { + var logBuilder = new StringBuilder(); + if (!PostBuildLog.GetBuildReportFromEditorLog(logBuilder, logPath)) + { + return false; + } + + using var reportReader = new StringReader(logBuilder.ToString()); + + while (reportReader.Peek() != -1) + { + string line = reportReader.ReadLine(); + + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var match = k_AssetEntry.Match(line); + + if (match.Groups.Count == 2) + { + string asset = match.Groups[1].Value; + + if (string.IsNullOrEmpty(asset)) + { + continue; + } + + assetList.Add(asset); + } + } + + return true; + } + + private static IEnumerator CheckGit(List assets) + { + // TODO: check submodules + // TODO: check Packages + + if (assets.Count == 0) + { + Debug.Log("no assets to check."); + yield break; // No builds have been run. + } + + var buildAssets = new HashSet(); + + var visitedDirectories = new HashSet { "Assets" }; + + foreach (var asset in assets) + { + if (string.IsNullOrWhiteSpace(asset)) + { + continue; + } + + // for each line in build report + // see if it matches regex + // if so add the matched substring to assets + if (!File.Exists(asset)) + { + // Unity generates some assets that are added to the build. + // (Unity does this with movies) + + // doesn't exist: 'Resources/unity_builtin_extra' + // doesn't exist: 'Built-in Cubemap:' + + Debug.LogWarning($"doesn't exist: '{asset}'"); + continue; + } + + // TODO: skip files that are under git submodules + + string directoryName = asset; + while (true) + { + directoryName = Path.GetDirectoryName(directoryName); + if (string.IsNullOrEmpty(directoryName)) + { + break; + } + +#if UNITY_EDITOR_WIN + directoryName = directoryName.Replace(Path.DirectorySeparatorChar, '/'); +#endif + + if (visitedDirectories.Contains(directoryName)) + { + continue; + } + + buildAssets.Add($"{directoryName}.meta"); + visitedDirectories.Add(directoryName); + } + + buildAssets.Add(asset); + buildAssets.Add($"{asset}.meta"); + } + + // every asset in the build (except Packages) should be included in buildAssets hashset. + + // ignored files in build: + var ignoredFilesInBuild = new HashSet(); + + var output = new List(); + + yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_IgnoredFiles, output)); + + foreach (string line in output) + { + if (buildAssets.Remove(line)) + { + ignoredFilesInBuild.Add(line); + } + } + + // unversioned files in the build: + var unversionedFilesInBuild = new HashSet(); + + output.Clear(); + yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_UnversionedFiles, output)); + + foreach (string line in output) + { + if (buildAssets.Remove(line)) + { + unversionedFilesInBuild.Add(line); + } + } + + var stringBuilder = new StringBuilder(); + + if (ignoredFilesInBuild.Count > 0) + { + stringBuilder.AppendLine($"total ignored files in build: {ignoredFilesInBuild.Count}"); + foreach (string file in ignoredFilesInBuild) + { + stringBuilder.AppendLine(file); + } + + Debug.Log(stringBuilder); + } + + stringBuilder.Clear(); + + if (unversionedFilesInBuild.Count > 0) + { + stringBuilder.AppendLine($"total unversioned files in build: {unversionedFilesInBuild.Count}"); + foreach (string file in unversionedFilesInBuild) + { + stringBuilder.AppendLine(file); + } + + Debug.LogWarning(stringBuilder); + } + + // TODO: Show a dialog box and add the assets to git? + } + + private static Process CreateGitProcess() + { + var process = new Process(); + process.StartInfo.FileName = k_GitFilename; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + + return process; + } + + private static IEnumerator RunGitCommand(string command, List output) + { + using var process = CreateGitProcess(); + process.StartInfo.Arguments = command; + process.Start(); + + var stopwatch = Stopwatch.StartNew(); + while (true) + { + string standardOutput = process.StandardOutput.ReadLine(); + + if (standardOutput == null) + { + yield break; + } + + output.Add(standardOutput); + + if (stopwatch.ElapsedMilliseconds > 500) + { + yield return null; + stopwatch.Restart(); + } + } + } +} diff --git a/Editor/PostBuildGitValidation.cs.meta b/Editor/PostBuildGitValidation.cs.meta new file mode 100644 index 0000000..1fbfc35 --- /dev/null +++ b/Editor/PostBuildGitValidation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 048b5c470baa4c34fb20f66ba4a9ef50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/PostBuildLog.cs b/Editor/PostBuildLog.cs index 7c1f494..70ab85a 100644 --- a/Editor/PostBuildLog.cs +++ b/Editor/PostBuildLog.cs @@ -1,40 +1,25 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Text; using System.Text.RegularExpressions; using Unity.EditorCoroutines.Editor; using UnityEditor; -using UnityEditor.Callbacks; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; using UnityEngine; using UnityEngine.SceneManagement; using Debug = UnityEngine.Debug; -public class PostBuildLog : ScriptableObject +public class PostBuildLog : IPostprocessBuildWithReport { - // Handy git commands: - // rev-parse --short HEAD // get hash of current revision - - // git diff --cached --compact-summary // staged files that haven't been committed. - // .gitmodules | 3 +++ - - // git ls-files --exclude-standard // everything in the repo. - // git ls-files -d // deleted - // git ls-files -m // modified - // git ls-files -o --exclude-standard // files that need to be added - private enum Status { Found, End, } - private const string k_GitFilename = "git"; - private const string k_IgnoredFiles = "ls-files -i -o --exclude-standard"; - private const string k_UnversionedFiles = "ls-files -o --exclude-standard"; - #if UNITY_EDITOR_WIN private static readonly string k_LogPath = Path.Combine( new string[] { Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Unity", "Editor", "Editor.log" }); @@ -44,66 +29,59 @@ private enum Status #endif private static readonly string[] k_Newlines = { "\r\n", "\r", "\n" }; - private static readonly Regex k_AssetEntry = new Regex(@"^.*% (Assets/.*)$", RegexOptions.IgnoreCase); private static readonly Regex k_BuildReport = new Regex(@"^Build Report$", RegexOptions.IgnoreCase); private static readonly Regex k_DashLine = new Regex(@"^-+$"); private static readonly Regex k_DependenciesList = new Regex(@"^Mono dependencies included in the build$", RegexOptions.IgnoreCase); - [MenuItem("Tools/Post Build Log/Test Build Report")] - private static void TestBuildReport() - { - EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(Application.dataPath, "android")); - } + public int callbackOrder => int.MaxValue; - [MenuItem("Tools/Post Build Log/Find Git Unversioned")] - private static void TestGitUnversioned() + public void OnPostprocessBuild(BuildReport report) { - var buildLog = new StringBuilder(); - AppendBuildLog(buildLog); + var result = report.summary.result; - var scenes = new StringBuilder(); - ScenesInBuild(scenes); + if (result == BuildResult.Failed || result == BuildResult.Cancelled) + { + return; + } - EditorCoroutineUtility.StartCoroutineOwnerless(CheckGit(buildLog, scenes)); + EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(report)); } - [PostProcessBuild] // Requires Unity 3.5+ - private static void OnPostProcessBuildPlayer(BuildTarget target, string buildPath) + [MenuItem("Tools/Post Build Log/Test Build Report")] + private static void TestBuildReport() { - EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(buildPath, target.ToString())); + EditorCoroutineUtility.StartCoroutineOwnerless(WriteBuildLog(null)); } - private static void ScenesInBuild(StringBuilder report) + internal static void ScenesInBuild(List sceneList) { - report.Append("Scenes included in the build\n"); - int sceneCount = SceneManager.sceneCountInBuildSettings; for (var i = 0; i < sceneCount; i++) { string sceneName = SceneUtility.GetScenePathByBuildIndex(i); - report.AppendFormat("{0}\n", sceneName); + sceneList.Add(sceneName); } } - private static void AppendBuildLog(StringBuilder output) + internal static bool GetBuildReportFromEditorLog(StringBuilder logBuilder, string logPath) { var buildMarker = Status.End; var dependencyMarker = Status.End; - if (!File.Exists(k_LogPath)) + if (!File.Exists(logPath)) { - Debug.LogWarning($"Editor log file could not be found at: {k_LogPath}"); - return; + Debug.LogWarning($"Editor log file could not be found at: {logPath}"); + return false; } var report = new StringBuilder(); var assemblies = new StringBuilder(); using (var reader = - new StreamReader(File.Open(k_LogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + new StreamReader(File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { string currentLine = null; var lineNum = 0; @@ -120,7 +98,7 @@ private static void AppendBuildLog(StringBuilder output) if (k_BuildReport.IsMatch(currentLine)) { - Debug.LogFormat("found a build report at line: {0}", lineNum); + // Debug.LogFormat("found a build report at line: {0}", lineNum); buildMarker = Status.Found; report.Length = 0; @@ -154,230 +132,92 @@ private static void AppendBuildLog(StringBuilder output) } } - output.Append(assemblies); - output.Append(report); + logBuilder.Append(assemblies); + logBuilder.Append(report); + return true; } - private static IEnumerator WriteBuildLog(string buildPath, string target = "") + private static IEnumerator WriteBuildLog(BuildReport buildReport) { // This is a hack, but on windows you have to wait // 1 frame after a build finishes for the // log file to get written out. (and delayCall doesn't work). yield return null; + BuildTarget target; + string buildPath; var report = new StringBuilder(); - AppendBuildLog(report); - - if (report.Length == 0) + if (buildReport != null) { - Debug.Log("no build report found in log."); - yield break; // No builds have been run. - } - - string outputPath; + target = buildReport.summary.platform; + buildPath = buildReport.summary.outputPath; - var filename = $"build {DateTime.UtcNow.ToString("s").Replace(':', '-')}.log"; - - if (target.StartsWith("standalone", StringComparison.InvariantCultureIgnoreCase) || - target.StartsWith("android", StringComparison.InvariantCultureIgnoreCase)) - { - outputPath = Path.Combine(Path.GetDirectoryName(buildPath), filename); + GetBuildReportFromEditorLog(report, k_LogPath); } else { - outputPath = Path.Combine(buildPath, filename); - } + // Probably running a test, so pretend we're windows. + target = BuildTarget.StandaloneWindows64; + buildPath = Application.dataPath; - try - { - var output = new StringBuilder(); - output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.UtcNow); - - ScenesInBuild(output); - output.Append(report); - - File.WriteAllText(outputPath, output.ToString()); - } - catch (Exception e) - { - Debug.LogException(e); - Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, - target); - } - } - - private static IEnumerator CheckGit(StringBuilder report, StringBuilder scenes) - { - // TODO: check submodules - // TODO: check Packages - - if (report.Length == 0) - { - Debug.Log("no build report found in log."); - yield break; // No builds have been run. - } - - var buildAssets = new HashSet(); - - var reportReader = new StringReader(report.ToString()); - - var visitedDirectories = new HashSet { "Assets" }; - - while (reportReader.Peek() != -1) - { - string line = reportReader.ReadLine(); - - if (string.IsNullOrWhiteSpace(line)) + if (!GetBuildReportFromEditorLog(report, k_LogPath)) { - continue; - } - - // for each line in build report - // see if it matches regex - // if so add the matched substring to assets - var match = k_AssetEntry.Match(line); - - if (match.Groups.Count == 2) - { - string asset = match.Groups[1].Value; + Debug.Log("No build report found. Checking previous log file..."); + string prevLogPath = Path.Combine(Path.GetDirectoryName(k_LogPath), "Editor-prev.log"); - if (string.IsNullOrEmpty(asset)) + if (!GetBuildReportFromEditorLog(report, prevLogPath)) { - continue; + Debug.Log("no build report found in log."); + yield break; // No builds have been run. } - - if (!File.Exists(asset)) - { - // Unity generates some assets that are added to the build. - // (Unity does this with movies) - - Debug.LogWarningFormat("doesn't exist: {0}", asset); - continue; - } - - // TODO: skip files that are under git submodules - - string directoryName = asset; - while (true) - { - directoryName = Path.GetDirectoryName(directoryName); - if (string.IsNullOrEmpty(directoryName)) - { - break; - } - -#if UNITY_EDITOR_WIN - directoryName = directoryName.Replace(Path.DirectorySeparatorChar, '/'); -#endif - - if (visitedDirectories.Contains(directoryName)) - { - continue; - } - - buildAssets.Add($"{directoryName}.meta"); - visitedDirectories.Add(directoryName); - } - - buildAssets.Add(asset); - buildAssets.Add($"{asset}.meta"); } } - string[] scenesList = scenes.ToString().Split( - k_Newlines, - StringSplitOptions.None - ); - - foreach (string scene in scenesList) - { - buildAssets.Add(scene); - buildAssets.Add($"{scene}.meta"); - } - - // every asset in the build (except Packages) should be included in buildAssets hashset. - - // ignored files in build: - var ignoredFilesInBuild = new HashSet(); - - var output = new List(); + string outputPath; - yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_IgnoredFiles, output)); + var filename = $"build {DateTime.UtcNow.ToString("s").Replace(':', '-')}.log"; - foreach (string line in output) + switch (target) { - if (buildAssets.Remove(line)) - { - ignoredFilesInBuild.Add(line); - } + case BuildTarget.Android: + case BuildTarget.StandaloneLinux64: + case BuildTarget.StandaloneWindows: + case BuildTarget.StandaloneWindows64: + case BuildTarget.StandaloneOSX: + outputPath = Path.Combine(Path.GetDirectoryName(buildPath), filename); + break; + default: + outputPath = Path.Combine(buildPath, filename); + break; } - // unversioned files in the build: - var unversionedFilesInBuild = new HashSet(); - - output.Clear(); - yield return EditorCoroutineUtility.StartCoroutineOwnerless(RunGitCommand(k_UnversionedFiles, output)); - - foreach (string line in output) + try { - if (buildAssets.Remove(line)) - { - unversionedFilesInBuild.Add(line); - } - } + var output = new StringBuilder(); + output.AppendFormat("Build Report @ {0:u}\n\n", DateTime.UtcNow); - if (ignoredFilesInBuild.Count > 0) - { - Debug.Log($"total ignored files in build: {ignoredFilesInBuild.Count}"); - foreach (string file in ignoredFilesInBuild) - { - Debug.Log($"ignored in build: {file}"); - } - } + var scenes = new List(); + ScenesInBuild(scenes); - if (unversionedFilesInBuild.Count > 0) - { - Debug.Log($"total unversioned files in build: {unversionedFilesInBuild.Count}"); - foreach (string file in unversionedFilesInBuild) + if (scenes.Count != 0) { - Debug.Log($"unversioned in build: {file}"); + output.Append("Scenes included in the build\n"); + foreach (var scene in scenes) + { + output.AppendFormat("{0}\n", scene); + } } - } - - // TODO: Show a dialog box and add the assets to git? - } - - private static IEnumerator RunGitCommand(string command, List output) - { - using var process = new Process(); - process.StartInfo.FileName = k_GitFilename; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.UseShellExecute = false; - - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - process.StartInfo.Arguments = command; - process.Start(); + output.Append(report); - var stopwatch = Stopwatch.StartNew(); - while (true) + File.WriteAllText(outputPath, output.ToString()); + } + catch (Exception e) { - string standardOutput = process.StandardOutput.ReadLine(); - - if (standardOutput == null) - { - yield break; - } - - output.Add(standardOutput); - - if (stopwatch.ElapsedMilliseconds > 500) - { - yield return null; - stopwatch.Restart(); - } + Debug.LogException(e); + Debug.LogErrorFormat("Build log file could not be created for writing at: {0} for target {1}", outputPath, + target); } } }