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