// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnrealBuildBase;
namespace UnrealBuildTool
{
///
/// Single
///
internal class ClangSpecificFileAction : Action, ISpecificFileAction
{
DirectoryReference SourceDir;
DirectoryReference OutputDir;
IEnumerable RspLines;
int SingleFileCounter;
internal ClangSpecificFileAction(DirectoryReference Source, DirectoryReference Output, Action Action, IEnumerable ContentLines) : base(Action)
{
ProducedItems.Clear();
DependencyListFile = null;
SourceDir = Source;
OutputDir = Output;
RspLines = ContentLines;
}
public ClangSpecificFileAction(BinaryArchiveReader Reader) : base(Reader)
{
SourceDir = Reader.ReadCompactDirectoryReference();
OutputDir = Reader.ReadCompactDirectoryReference();
RspLines = Reader.ReadList(() => Reader.ReadString())!;
}
public new void Write(BinaryArchiveWriter Writer)
{
base.Write(Writer);
Writer.WriteCompactDirectoryReference(SourceDir);
Writer.WriteCompactDirectoryReference(OutputDir);
Writer.WriteList(RspLines.ToList(), (Str) => Writer.WriteString(Str));
}
public DirectoryReference RootDirectory { get => SourceDir; }
public IExternalAction? CreateAction(FileItem SourceFile, ILogger Logger)
{
string DummyName = "SingleFile.cpp";
string UniqueDummyName = $"SingleFile{SingleFileCounter}.cpp";
++SingleFileCounter;
int FileNameIndex = CommandArguments.IndexOf(DummyName);
string DummyPath = CommandArguments.Substring(2, FileNameIndex + DummyName.Length - 2);
if (SourceFile.HasExtension(".h"))
{
FileItem DummyFile = FileItem.GetItemByFileReference(FileReference.Combine(OutputDir, "SingleFile", SourceFile.Name));
Directory.CreateDirectory(DummyFile.Directory.FullName);
File.WriteAllText(DummyFile.FullName, $"#include \"{SourceFile.FullName.Replace('\\', '/')}\"");
SourceFile = DummyFile;
}
List NewRspLines = new();
foreach (string L in RspLines)
{
string Line = L;
if (Line.Contains(".cpp.o") || Line.Contains(".cpp.d"))
{
Line = Line.Replace("SingleFile.cpp", UniqueDummyName);
}
else
{
Line = Line.Replace(DummyPath, SourceFile.FullName.Replace('\\', '/'));
}
NewRspLines.Add(Line);
}
Action Action = new Action(this);
Action.CommandArguments = CommandArguments.Replace(DummyName, UniqueDummyName);
Action.DependencyListFile = null;
Action.StatusDescription = SourceFile.Name;
// We have to add a produced item so this action is not skipped.
// Note we on purpose use a different extension than what the compiler produce because otherwise up-to-date checker might see it as up-to-date
// even though we want it to always be built
FileItem ProducedItem = FileItem.GetItemByFileReference(FileReference.Combine(OutputDir, UniqueDummyName + ".n"));
Action.ProducedItems.Add(ProducedItem);
FileItem ResponseFile = FileItem.GetItemByPath(Action.CommandArguments.Substring(1).Trim('"'));
File.WriteAllLines(ResponseFile.FullName, NewRspLines);
return Action;
}
}
class ClangSpecificFileActionSerializer : ActionSerializerBase
{
///
public override ClangSpecificFileAction Read(BinaryArchiveReader Reader)
{
return new ClangSpecificFileAction(Reader);
}
///
public override void Write(BinaryArchiveWriter Writer, ClangSpecificFileAction Action)
{
Action.Write(Writer);
}
}
class ClangSpecificFileActionGraphBuilder : ForwardingActionGraphBuilder
{
public ClangSpecificFileActionGraphBuilder(ILogger Logger) : base(new NullActionGraphBuilder(Logger))
{
}
public override void CreateIntermediateTextFile(FileItem Location, IEnumerable ContentLines, bool AllowAsync = true)
{
this.ContentLines = ContentLines;
}
public IEnumerable ContentLines = Enumerable.Empty();
}
}