using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using UnityEditor.Build.Content; using UnityEditor.Build.Pipeline.Utilities; [assembly: InternalsVisibleTo("Unity.Addressables.Editor.Tests")] namespace UnityEditor.Build.Pipeline.Interfaces { /// /// Describes the level of a log entry /// public enum LogLevel { /// /// The entry is reporting an error. /// Error, /// /// The entry is reporting an warning. /// Warning, /// /// The entry is reporting general information. /// Info, /// /// The entry is reporting verbose information. /// Verbose } /// /// Interface for monitoring the build process. Several tasks will log details of their progress through this interface. /// See the [Build Logging](https://docs.unity3d.com/Packages/com.unity.scriptablebuildpipeline@latest/index.html?subfolder=/manual/BuildLogger.html) documentation for more details. /// public interface IBuildLogger : IContextObject { /// /// Adds details to the active build step /// /// The log level of this entry. /// The message to add. void AddEntry(LogLevel level, string msg); /// /// Should be called when beginning a build step. /// /// The log level of this step. /// A name associated with the step. It is recommended that this name does not include specific context about the step; dynamic context should be added under the step as an entry. /// True if within this build step the IBuildLogger will be used on multiple threads. void BeginBuildStep(LogLevel level, string stepName, bool subStepsCanBeThreaded); /// /// Ends the build step. /// void EndBuildStep(); } internal enum DeferredEventType { Begin, End, Info } internal struct DeferredEvent { public LogLevel Level; public DeferredEventType Type; public double Time; public string Name; public string Context; } internal interface IDeferredBuildLogger { void HandleDeferredEventStream(IEnumerable events); } /// /// Helper class to define a scope with a using statement /// public struct ScopedBuildStep : IDisposable { IBuildLogger m_Logger; internal ScopedBuildStep(LogLevel level, string stepName, IBuildLogger logger, bool multiThreaded, string context) { m_Logger = logger; m_Logger?.BeginBuildStep(level, stepName, multiThreaded); if (!string.IsNullOrEmpty(context)) m_Logger?.AddEntrySafe(level, context); } /// void IDisposable.Dispose() { m_Logger?.EndBuildStep(); } } #if UNITY_2020_2_OR_NEWER || ENABLE_DETAILED_PROFILE_CAPTURING internal struct ProfileCaptureScope : IDisposable { IBuildLogger m_Logger; public ProfileCaptureScope(IBuildLogger logger, ProfileCaptureOptions options) { m_Logger = ScriptableBuildPipeline.useDetailedBuildLog ? logger : null; ContentBuildInterface.StartProfileCapture(options); } public void Dispose() { ContentBuildProfileEvent[] events = ContentBuildInterface.StopProfileCapture(); if (m_Logger == null) return; IDeferredBuildLogger dLog = (IDeferredBuildLogger)m_Logger; IEnumerable dEvents = events.Select(i => { var e = new DeferredEvent(); e.Level = LogLevel.Verbose; BuildLoggerExternsions.ConvertNativeEventName(i.Name, out e.Name, out e.Context); e.Time = (double)i.TimeMicroseconds / (double)1000; e.Type = BuildLoggerExternsions.ConvertToDeferredType(i.Type); return e; }); dLog.HandleDeferredEventStream(dEvents); } } #endif /// /// Contains extension methods for the IBuildLogger interface /// public static class BuildLoggerExternsions { /// /// Adds details to the active build step /// /// The build log. /// The log level of this entry. /// The message to add. public static void AddEntrySafe(this IBuildLogger log, LogLevel level, string msg) { if (log != null) { log.AddEntry(level, msg); } } /// /// Begins a new build step and returns an ScopedBuildStep which will end the build step when disposed. It is recommended to use this in conjunction with the using statement. /// /// The build log. /// The log level of this step. /// A name associated with the step. /// True if within this build step the IBuildLogger will be used on multiple threads. /// Returns a ScopedBuildStep that will end the build step when it is disposed. public static ScopedBuildStep ScopedStep(this IBuildLogger log, LogLevel level, string stepName, bool multiThreaded = false) { return new ScopedBuildStep(level, stepName, log, multiThreaded, null); } /// /// Begins a new build step and returns an ScopedBuildStep which will end the build step when disposed. It is recommended to use this in conjunction with the using statement. /// /// The build log. /// The log level of this step. /// A name associated with the step. /// Adds an entry message the build step. This allows attaching specific context data without changing the stepName. /// Returns a ScopedBuildStep that will end the build step when it is disposed. public static ScopedBuildStep ScopedStep(this IBuildLogger log, LogLevel level, string stepName, string context) { return new ScopedBuildStep(level, stepName, log, false, context); } #if UNITY_2020_2_OR_NEWER || ENABLE_DETAILED_PROFILE_CAPTURING internal static DeferredEventType ConvertToDeferredType(ProfileEventType type) { if (type == ProfileEventType.Begin) return DeferredEventType.Begin; if (type == ProfileEventType.End) return DeferredEventType.End; if (type == ProfileEventType.Info) return DeferredEventType.Info; throw new Exception("Unknown type"); } const string k_WriteFile = "Write file:"; const string k_WriteObject = "Write object - "; internal static void ConvertNativeEventName(string nativeName, out string eventName, out string eventContext) { eventName = nativeName; eventContext = ""; if (nativeName.StartsWith(k_WriteFile, StringComparison.Ordinal)) { eventName = "Write File"; eventContext = nativeName.Substring(k_WriteFile.Length); } else if (nativeName.StartsWith(k_WriteObject, StringComparison.Ordinal)) { eventName = "Write Object"; eventContext = nativeName.Substring(k_WriteObject.Length); } if (eventContext.Any(c => c == '"')) eventContext = eventContext.Replace("\"", "\\\""); } #endif } }