// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Reflection;
using EpicGames.Core;
using Microsoft.Extensions.Logging;
using UnrealBuildBase;
namespace UnrealBuildTool
{
///
/// Interface for toolchain operations that produce output
///
interface IActionGraphBuilder
{
///
/// Adds an action to this graph
///
/// Action to add
void AddAction(IExternalAction Action);
///
/// Creates a response file for use in the action graph
///
/// Location of the response file
/// Contents of the file
/// Allows the backend to write the file in a separate task.
/// New file item
void CreateIntermediateTextFile(FileItem Location, string Contents, bool AllowAsync = true);
///
/// Creates a response file for use in the action graph, with a newline between each string in ContentLines
///
/// Location of the response file
/// Contents of the file
/// Allows the backend to write the file in a separate task.
/// New file item
void CreateIntermediateTextFile(FileItem Location, IEnumerable ContentLines, bool AllowAsync = true);
///
/// Adds a file which is in the non-unity working set
///
/// The file to add to the working set
void AddFileToWorkingSet(FileItem File);
///
/// Adds a file which is a candidate for being in the non-unity working set
///
/// The file to add to the working set
void AddCandidateForWorkingSet(FileItem File);
///
/// Adds a source directory. These folders are scanned recursively for C++ source files.
///
/// Base source directory
void AddSourceDir(DirectoryItem SourceDir);
///
/// Adds the given source files as dependencies
///
/// Source directory containing files to build
/// Contents of the directory
void AddSourceFiles(DirectoryItem SourceDir, FileItem[] SourceFiles);
///
/// Adds a list of known header files
///
/// List of header files to track
void AddHeaderFiles(FileItem[] HeaderFiles);
///
/// Sets the output items which belong to a particular module
///
/// Name of the module
/// Array of output items for this module
void SetOutputItemsForModule(string ModuleName, FileItem[] OutputItems);
///
/// Adds a diagnostic message
///
/// Message to display
void AddDiagnostic(string Message);
}
///
/// Implementation of IActionGraphBuilder which discards all unnecessary operations
///
sealed class NullActionGraphBuilder : IActionGraphBuilder
{
private readonly ILogger Logger;
///
/// Constructor
///
///
public NullActionGraphBuilder(ILogger InLogger)
{
Logger = InLogger;
}
///
public void AddAction(IExternalAction Action)
{
}
///
public void CreateIntermediateTextFile(FileItem FileItem, string Contents, bool AllowAsync = true)
{
Utils.WriteFileIfChanged(FileItem, Contents, Logger);
}
///
public void CreateIntermediateTextFile(FileItem FileItem, IEnumerable ContentLines, bool AllowAsync = true)
{
Utils.WriteFileIfChanged(FileItem, ContentLines, Logger);
}
///
public void AddSourceDir(DirectoryItem SourceDir)
{
}
///
public void AddSourceFiles(DirectoryItem SourceDir, FileItem[] SourceFiles)
{
}
///
public void AddHeaderFiles(FileItem[] HeaderFiles)
{
}
///
public void AddFileToWorkingSet(FileItem File)
{
}
///
public void AddCandidateForWorkingSet(FileItem File)
{
}
///
public void AddDiagnostic(string Message)
{
}
///
public void SetOutputItemsForModule(string ModuleName, FileItem[] OutputItems)
{
}
}
///
/// Implementation of IActionGraphBuilder which forwards calls to an underlying implementation, allowing derived classes to intercept certain calls
///
class ForwardingActionGraphBuilder : IActionGraphBuilder
{
///
/// The inner graph builder
///
IActionGraphBuilder Inner;
///
/// Constructor
///
/// Builder to pass all calls to
public ForwardingActionGraphBuilder(IActionGraphBuilder Inner)
{
this.Inner = Inner;
}
///
public virtual void AddAction(IExternalAction Action)
{
Inner.AddAction(Action);
}
///
public virtual void CreateIntermediateTextFile(FileItem FileItem, string Contents, bool AllowAsync = true)
{
Inner.CreateIntermediateTextFile(FileItem, Contents, AllowAsync);
}
///
public virtual void CreateIntermediateTextFile(FileItem FileItem, IEnumerable ContentLines, bool AllowAsync = true)
{
Inner.CreateIntermediateTextFile(FileItem, ContentLines, AllowAsync);
}
///
public virtual void AddSourceDir(DirectoryItem SourceDir)
{
Inner.AddSourceDir(SourceDir);
}
///
public virtual void AddSourceFiles(DirectoryItem SourceDir, FileItem[] SourceFiles)
{
Inner.AddSourceFiles(SourceDir, SourceFiles);
}
///
public virtual void AddHeaderFiles(FileItem[] HeaderFiles)
{
Inner.AddHeaderFiles(HeaderFiles);
}
///
public virtual void AddFileToWorkingSet(FileItem File)
{
Inner.AddFileToWorkingSet(File);
}
///
public virtual void AddCandidateForWorkingSet(FileItem File)
{
Inner.AddCandidateForWorkingSet(File);
}
///
public virtual void AddDiagnostic(string Message)
{
Inner.AddDiagnostic(Message);
}
///
public virtual void SetOutputItemsForModule(string ModuleName, FileItem[] OutputItems)
{
Inner.SetOutputItemsForModule(ModuleName, OutputItems);
}
}
///
/// Extension methods for IActionGraphBuilder classes
///
static class ActionGraphBuilderExtensions
{
///
/// Creates a new action to be built as part of this target
///
/// Graph to add the action to
/// Type of action to create
/// New action
public static Action CreateAction(this IActionGraphBuilder Graph, ActionType Type)
{
Action Action = new Action(Type);
Graph.AddAction(Action);
return Action;
}
///
/// Creates an action which copies a file from one location to another
///
/// The action graph
/// The source file location
/// The target file location
/// File item for the output file
public static Action CreateCopyAction(this IActionGraphBuilder Graph, FileItem SourceFile, FileItem TargetFile)
{
Action CopyAction = Graph.CreateAction(ActionType.BuildProject);
CopyAction.CommandDescription = "Copy";
CopyAction.CommandPath = BuildHostPlatform.Current.Shell;
if (BuildHostPlatform.Current.ShellType == ShellType.Cmd)
{
CopyAction.CommandArguments = String.Format("/C \"copy /Y \"{0}\" \"{1}\" 1>nul\"", SourceFile.AbsolutePath, TargetFile.AbsolutePath);
}
else
{
CopyAction.CommandArguments = String.Format("-c \"cp -f \\\"{0}\\\" \\\"{1}\\\"\"", SourceFile.AbsolutePath, TargetFile.AbsolutePath);
}
CopyAction.WorkingDirectory = Unreal.EngineSourceDirectory;
CopyAction.PrerequisiteItems.Add(SourceFile);
CopyAction.ProducedItems.Add(TargetFile);
CopyAction.DeleteItems.Add(TargetFile);
CopyAction.StatusDescription = TargetFile.Location.GetFileName();
CopyAction.bCanExecuteRemotely = false;
return CopyAction;
}
///
/// Creates an action which copies a file from one location to another
///
/// List of actions to be executed. Additional actions will be added to this list.
/// The source file location
/// The target file location
/// File item for the output file
public static FileItem CreateCopyAction(this IActionGraphBuilder Graph, FileReference SourceFile, FileReference TargetFile)
{
FileItem SourceFileItem = FileItem.GetItemByFileReference(SourceFile);
FileItem TargetFileItem = FileItem.GetItemByFileReference(TargetFile);
Graph.CreateCopyAction(SourceFileItem, TargetFileItem);
return TargetFileItem;
}
///
/// Creates an action which calls UBT recursively
///
/// The action graph
/// Type of the action
/// Arguments for the action
/// New action instance
public static Action CreateRecursiveAction(this IActionGraphBuilder Graph, ActionType Type, string Arguments) where T : ToolMode
{
ToolModeAttribute? Attribute = typeof(T).GetCustomAttribute();
if (Attribute == null)
{
throw new BuildException("Missing ToolModeAttribute on {0}", typeof(T).Name);
}
Action NewAction = Graph.CreateAction(Type);
NewAction.CommandPath = Unreal.DotnetPath;
NewAction.WorkingDirectory = Unreal.EngineSourceDirectory;
NewAction.CommandArguments = $"\"{Unreal.UnrealBuildToolDllPath}\" -Session=\"{UnrealBuildTool.SessionIdentifier}\" -Mode={Attribute.Name} {Arguments}";
NewAction.CommandDescription = Attribute.Name;
NewAction.bCanExecuteRemotely = false;
NewAction.bCanExecuteRemotelyWithSNDBS = false;
NewAction.bCanExecuteInUBA = false;
return NewAction;
}
///
/// Creates a text file with the given contents. If the contents of the text file aren't changed, it won't write the new contents to
/// the file to avoid causing an action to be considered outdated.
///
/// The action graph
/// Path to the intermediate file to create
/// Contents of the new file
/// Allows the backend to write the file in a separate task.
/// File item for the newly created file
public static FileItem CreateIntermediateTextFile(this IActionGraphBuilder Graph, FileReference AbsolutePath, string Contents, bool AllowAsync = true)
{
FileItem FileItem = FileItem.GetItemByFileReference(AbsolutePath);
Graph.CreateIntermediateTextFile(FileItem, Contents, AllowAsync);
return FileItem;
}
///
/// Creates a text file with the given contents. If the contents of the text file aren't changed, it won't write the new contents to
/// the file to avoid causing an action to be considered outdated.
///
/// The action graph
/// Path to the intermediate file to create
/// Contents of the new file
/// Allows the backend to write the file in a separate task.
/// File item for the newly created file
public static FileItem CreateIntermediateTextFile(this IActionGraphBuilder Graph, FileReference AbsolutePath, IEnumerable ContentLines, bool AllowAsync = true)
{
FileItem FileItem = UnrealBuildBase.FileItem.GetItemByFileReference(AbsolutePath);
Graph.CreateIntermediateTextFile(FileItem, ContentLines, AllowAsync);
return FileItem;
}
}
}