|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem;
#nullable disable
namespace Microsoft.Build.BackEnd.Logging
{
/// <summary>
/// A logging context for building a specific target within a project.
/// </summary>
internal class TargetLoggingContext : BuildLoggingContext
{
/// <summary>
/// Should target outputs be logged also.
/// </summary>
private static bool s_enableTargetOutputLogging = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDTARGETOUTPUTLOGGING"));
/// <summary>
/// The project to which this target is attached.
/// </summary>
private ProjectLoggingContext _projectLoggingContext;
/// <summary>
/// The target being built.
/// </summary>
private ProjectTargetInstance _target;
/// <summary>
/// Creates a new target logging context from an existing project context and target.
/// </summary>
internal TargetLoggingContext(ProjectLoggingContext projectLoggingContext, string projectFullPath, ProjectTargetInstance target, string parentTargetName, TargetBuiltReason buildReason)
: base(projectLoggingContext, CreateInitialContext(projectLoggingContext, projectFullPath, target, parentTargetName, buildReason))
{
_projectLoggingContext = projectLoggingContext;
_target = target;
this.IsValid = true;
}
private static BuildEventContext CreateInitialContext(ProjectLoggingContext projectLoggingContext,
string projectFullPath, ProjectTargetInstance target, string parentTargetName,
TargetBuiltReason buildReason)
{
BuildEventContext buildEventContext = projectLoggingContext.LoggingService.LogTargetStarted(
projectLoggingContext.BuildEventContext, target.Name, projectFullPath, target.Location.File,
parentTargetName, buildReason);
return buildEventContext;
}
/// <summary>
/// Constructor used to support out-of-proc task host (proxy for in-proc logging service.)
/// </summary>
internal TargetLoggingContext(ILoggingService loggingService, BuildEventContext outOfProcContext)
: base(loggingService, outOfProcContext, true)
{
this.IsValid = true;
}
/// <summary>
/// Should target outputs be logged also.
/// </summary>
internal static bool EnableTargetOutputLogging
{
get { return s_enableTargetOutputLogging; }
set { s_enableTargetOutputLogging = value; }
}
/// <summary>
/// Retrieves the project logging context.
/// </summary>
internal ProjectLoggingContext ProjectLoggingContext
{
get
{
return _projectLoggingContext;
}
}
/// <summary>
/// Retrieves the target.
/// </summary>
internal ProjectTargetInstance Target
{
get
{
return _target;
}
}
/// <summary>
/// Log that a target has finished
/// </summary>
internal void LogTargetBatchFinished(string projectFullPath, bool success, IEnumerable<TaskItem> targetOutputs)
{
this.CheckValidity();
TargetOutputItemsInstanceEnumeratorProxy targetOutputWrapper = null;
// Only log target outputs if we are going to log a target finished event and the environment variable is set and the target outputs are not null
if (!LoggingService.OnlyLogCriticalEvents && s_enableTargetOutputLogging && targetOutputs != null)
{
targetOutputWrapper = new TargetOutputItemsInstanceEnumeratorProxy(targetOutputs);
}
LoggingService.LogTargetFinished(BuildEventContext, _target.Name, projectFullPath, _target.Location.File, success, targetOutputWrapper);
this.IsValid = false;
}
/// <summary>
/// Log that a task is about to start
/// </summary>
internal TaskLoggingContext LogTaskBatchStarted(string projectFullPath, ProjectTargetInstanceChild task, string taskAssemblyLocation)
{
this.CheckValidity();
return new TaskLoggingContext(this, projectFullPath, task, taskAssemblyLocation);
}
/// <summary>
/// An enumerable wrapper for items that clones items as they are requested,
/// so that writes have no effect on the items.
/// </summary>
/// <remarks>
/// This class is designed to be passed to loggers.
/// The expense of copying items is only incurred if and when
/// a logger chooses to enumerate over it.
/// </remarks>
internal class TargetOutputItemsInstanceEnumeratorProxy : IEnumerable<TaskItem>
{
/// <summary>
/// Enumerable that this proxies
/// </summary>
private IEnumerable<TaskItem> _backingItems;
/// <summary>
/// Constructor
/// </summary>
/// <param name="backingItems">Enumerator this class should proxy</param>
internal TargetOutputItemsInstanceEnumeratorProxy(IEnumerable<TaskItem> backingItems)
{
_backingItems = backingItems;
}
// For performance reasons we need to expose the raw items to BinaryLogger
// as we know we're not going to mutate anything. This allows us to bypass DeepClone
// for each item
internal IEnumerable<TaskItem> BackingItems => _backingItems;
/// <summary>
/// Returns an enumerator that provides copies of the items
/// in the backing store.
/// Each dictionary entry has key of the item type and value of an ITaskItem.
/// Type of the enumerator is imposed by backwards compatibility for ProjectStartedEvent.
/// </summary>
public IEnumerator<TaskItem> GetEnumerator()
{
foreach (TaskItem item in _backingItems)
{
yield return item.DeepClone();
}
}
/// <summary>
/// Returns an enumerator that provides copies of the items
/// in the backing store.
/// </summary>
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
}
}
}
|