You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
========================== MAJOR FEATURES + CHANGES ========================== Change 2864843 on 2016/02/12 by Ben.Marsh Add individual 'status', 'outcome', and 'error_code' fields to parsed jobsteps. Should fix grid view not being able to display 'pending' icons. Change 2865161 on 2016/02/12 by Ben.Marsh Stop storing a reference to UEBuildTarget from UEBuildModule. It creates an awkward cyclic data dependency, and makes it easy for people to write lazy code that just reaches into the internal state of the build. Change 2865643 on 2016/02/12 by Ben.Marsh Rename UEBuildModuleType to UHTModuleType, and move implementation into ExternalExecution. Change 2874408 on 2016/02/19 by Ben.Marsh Automatically sort nodes in the dashboard grid view by a weight derived from the node's order in the build graph, summed across all the jobs in which it was present. Change 2879572 on 2016/02/24 by Ben.Marsh Allow spoofing a Git merge from a given commit, using a changelist description containing the tag "git merge <branch> <changelist>", where <branch> is the name of a branch on Git (eg. master, 4.11, etc..), and <changelist> is the changelist being merged in. Change 2883216 on 2016/02/26 by Ben.Marsh Prevent Jira tickets being incorrectly updated with 'Main CL' fields which are after the 'Fix CL' fields. Change 2883755 on 2016/02/26 by Ben.Marsh Fix solution files having a Shipping configuration, even when -NoShippingConfigs is passed on the command line. Change 2886223 on 2016/02/29 by Ben.Marsh Ignore SignTool errors - we can recover from them. Change 2887414 on 2016/03/01 by Ben.Marsh Dump all the *.crash files produced while running commandlets, to make it easier to diagnose build system crashes cooking on Mac. Change 2888235 on 2016/03/01 by Ben.Marsh Add overloads for methods in FileFilter which take FileReference and DirectoryReference objects. Change 2889602 on 2016/03/02 by Ben.Marsh Treat shaders as code in UGS. Don't sync them as part of content-only syncs, and don't allow syncing past them without updated binaries. Change2889610on 2016/03/02 by Ben.Marsh Fix setting for using incremental builds not being saved. Also hide command to do incremental builds if the 'use incremental builds' option is not checked. Change 2891866 on 2016/03/03 by Matthew.Griffin Removed Rocket specific batch files and made sure installed build won't try to include them Removed last use of RocketGenerateProjectFiles.sh by using UBT directly instead Change2893349on 2016/03/03 by Ben.Marsh Add derived ReplicatedBranch to support mirroring the VR editor branch to GitHub. Change 2894703 on 2016/03/04 by Ben.Marsh Include *.usf when looking for the last code changelist. Also update version to 1.68. Change 2897991 on 2016/03/07 by Ben.Marsh Copy the changelist number to the clipboard when the user presses Ctrl-C. Update version number to 1.69. Change 2898005 on 2016/03/07 by Ben.Marsh Minor changes to support BuildGraph: * UE4Build now has a static function that can update version files. * Adding FileReference/DirectoryReference methods to FileFilter and CommandUtils. * FileFilter treats any pattern containing a slash as implictly starting from the root directory, unless it begins with "...". Change 2898095 on 2016/03/07 by Ben.Marsh UAT - Don't retry builds if we're using local executor; we don't encounter failures due to timeouts. Change 2898248 on 2016/03/07 by Ben.Marsh UBT - Add the standard game include paths back in to plugin modules. Existing game code relies on this. Change 2898615 on 2016/03/08 by Matthew.Griffin Removed last uses of RunningRocket function All seemed to be overly cautious about people using an Installed build to do non standard things, don't see any ill effects in the most common circumstances. Change 2898681 on 2016/03/08 by Matthew.Griffin Removed Automation.RunningRocket function as there are no more uses Changed the majority of comments referencing Rocket mode that are now either about the engine being installed or from the Launcher etc. #lockdown Nick.Penwarden [CL 2898813 by Matthew Griffin in Main branch]
329 lines
12 KiB
C#
329 lines
12 KiB
C#
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.CodeDom.Compiler;
|
|
using Microsoft.CSharp;
|
|
using System.Reflection;
|
|
using System.Diagnostics;
|
|
using Tools.DotNETCommon;
|
|
|
|
namespace UnrealBuildTool
|
|
{
|
|
public class DynamicCompilation
|
|
{
|
|
/// File information for UnrealBuildTool.exe, cached at program start
|
|
private static FileInfo UBTExecutableFileInfo = new FileInfo(Assembly.GetEntryAssembly().GetOriginalLocation());
|
|
|
|
/*
|
|
* Checks to see if the assembly needs compilation
|
|
*/
|
|
private static bool RequiresCompilation(List<FileReference> SourceFileNames, FileReference AssemblySourceListFilePath, FileReference OutputAssemblyPath)
|
|
{
|
|
// Check to see if we already have a compiled assembly file on disk
|
|
FileInfo OutputAssemblyInfo = new FileInfo(OutputAssemblyPath.FullName);
|
|
if (OutputAssemblyInfo.Exists)
|
|
{
|
|
// Check the time stamp of the UnrealBuildTool.exe file. If Unreal Build Tool was compiled more
|
|
// recently than the dynamically-compiled assembly, then we'll always recompile it. This is
|
|
// because Unreal Build Tool's code may have changed in such a way that invalidate these
|
|
// previously-compiled assembly files.
|
|
if (UBTExecutableFileInfo.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc)
|
|
{
|
|
// UnrealBuildTool.exe has been recompiled more recently than our cached assemblies
|
|
Log.TraceVerbose("UnrealBuildTool.exe has been recompiled more recently than " + OutputAssemblyInfo.Name);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Make sure we have a manifest of source files used to compile the output assembly. If it doesn't exist
|
|
// for some reason (not an expected case) then we'll need to recompile.
|
|
FileInfo AssemblySourceListFile = new FileInfo(AssemblySourceListFilePath.FullName);
|
|
if (!AssemblySourceListFile.Exists)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Make sure the source files we're compiling are the same as the source files that were compiled
|
|
// for the assembly that we want to load
|
|
List<FileReference> ExistingAssemblySourceFileNames = new List<FileReference>();
|
|
{
|
|
using (FileStream Reader = AssemblySourceListFile.OpenRead())
|
|
{
|
|
using (StreamReader TextReader = new StreamReader(Reader))
|
|
{
|
|
for (string ExistingSourceFileName = TextReader.ReadLine(); ExistingSourceFileName != null; ExistingSourceFileName = TextReader.ReadLine())
|
|
{
|
|
FileReference FullExistingSourceFileName = new FileReference(ExistingSourceFileName);
|
|
|
|
ExistingAssemblySourceFileNames.Add(FullExistingSourceFileName);
|
|
|
|
// Was the existing assembly compiled with a source file that we aren't interested in? If so, then it needs to be recompiled.
|
|
if (!SourceFileNames.Contains(FullExistingSourceFileName))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test against source file time stamps
|
|
foreach (FileReference SourceFileName in SourceFileNames)
|
|
{
|
|
// Was the existing assembly compiled without this source file? If so, then we definitely need to recompile it!
|
|
if (!ExistingAssemblySourceFileNames.Contains(SourceFileName))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FileInfo SourceFileInfo = new FileInfo(SourceFileName.FullName);
|
|
|
|
// Check to see if the source file exists
|
|
if (!SourceFileInfo.Exists)
|
|
{
|
|
throw new BuildException("Could not locate source file for dynamic compilation: {0}", SourceFileName);
|
|
}
|
|
|
|
// Ignore temp files
|
|
if (!SourceFileInfo.Extension.Equals(".tmp", StringComparison.CurrentCultureIgnoreCase))
|
|
{
|
|
// Check to see if the source file is newer than the compiled assembly file. We don't want to
|
|
// bother recompiling it if it hasn't changed.
|
|
if (SourceFileInfo.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc)
|
|
{
|
|
// Source file has changed since we last compiled the assembly, so we'll need to recompile it now!
|
|
Log.TraceVerbose(SourceFileInfo.Name + " has been modified more recently than " + OutputAssemblyInfo.Name);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// File doesn't exist, so we'll definitely have to compile it!
|
|
Log.TraceVerbose(OutputAssemblyInfo.Name + " doesn't exist yet");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Compiles an assembly from source files
|
|
*/
|
|
private static Assembly CompileAssembly(FileReference OutputAssemblyPath, List<FileReference> SourceFileNames, List<string> ReferencedAssembies, List<string> PreprocessorDefines = null, bool TreatWarningsAsErrors = false)
|
|
{
|
|
TempFileCollection TemporaryFiles = new TempFileCollection();
|
|
|
|
// Setup compile parameters
|
|
CompilerParameters CompileParams = new CompilerParameters();
|
|
{
|
|
// Always compile the assembly to a file on disk, so that we can load a cached version later if we have one
|
|
CompileParams.GenerateInMemory = false;
|
|
|
|
// This is the full path to the assembly file we're generating
|
|
CompileParams.OutputAssembly = OutputAssemblyPath.FullName;
|
|
|
|
// We always want to generate a class library, not an executable
|
|
CompileParams.GenerateExecutable = false;
|
|
|
|
// Never fail compiles for warnings
|
|
CompileParams.TreatWarningsAsErrors = false;
|
|
|
|
// Set the warning level so that we will actually receive warnings -
|
|
// doesn't abort compilation as stated in documentation!
|
|
CompileParams.WarningLevel = 4;
|
|
|
|
// Always generate debug information as it takes minimal time
|
|
CompileParams.IncludeDebugInformation = true;
|
|
#if !DEBUG
|
|
// Optimise the managed code in Development
|
|
CompileParams.CompilerOptions += " /optimize";
|
|
#endif
|
|
Log.TraceVerbose("Compiling " + OutputAssemblyPath);
|
|
|
|
// Keep track of temporary files emitted by the compiler so we can clean them up later
|
|
CompileParams.TempFiles = TemporaryFiles;
|
|
|
|
// Warnings as errors if desired
|
|
CompileParams.TreatWarningsAsErrors = TreatWarningsAsErrors;
|
|
|
|
// Add assembly references
|
|
{
|
|
if (ReferencedAssembies == null)
|
|
{
|
|
// Always depend on the CLR System assembly
|
|
CompileParams.ReferencedAssemblies.Add("System.dll");
|
|
}
|
|
else
|
|
{
|
|
// Add in the set of passed in referenced assemblies
|
|
CompileParams.ReferencedAssemblies.AddRange(ReferencedAssembies.ToArray());
|
|
}
|
|
|
|
// The assembly will depend on this application
|
|
Assembly UnrealBuildToolAssembly = Assembly.GetExecutingAssembly();
|
|
CompileParams.ReferencedAssemblies.Add(UnrealBuildToolAssembly.Location);
|
|
}
|
|
|
|
// Add preprocessor definitions
|
|
if (PreprocessorDefines != null && PreprocessorDefines.Count > 0)
|
|
{
|
|
CompileParams.CompilerOptions += " /define:";
|
|
for (int DefinitionIndex = 0; DefinitionIndex < PreprocessorDefines.Count; ++DefinitionIndex)
|
|
{
|
|
if (DefinitionIndex > 0)
|
|
{
|
|
CompileParams.CompilerOptions += ";";
|
|
}
|
|
CompileParams.CompilerOptions += PreprocessorDefines[DefinitionIndex];
|
|
}
|
|
}
|
|
|
|
// @todo: Consider embedding resources in generated assembly file (version/copyright/signing)
|
|
}
|
|
|
|
// Create the output directory if it doesn't exist already
|
|
DirectoryInfo DirInfo = new DirectoryInfo(OutputAssemblyPath.Directory.FullName);
|
|
if (!DirInfo.Exists)
|
|
{
|
|
try
|
|
{
|
|
DirInfo.Create();
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
throw new BuildException(Ex, "Unable to create directory '{0}' for intermediate assemblies (Exception: {1})", OutputAssemblyPath, Ex.Message);
|
|
}
|
|
}
|
|
|
|
// Compile the code
|
|
CompilerResults CompileResults;
|
|
try
|
|
{
|
|
// Enable .NET 4.0 as we want modern language features like 'var'
|
|
Dictionary<string, string> ProviderOptions = new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } };
|
|
CSharpCodeProvider Compiler = new CSharpCodeProvider(ProviderOptions);
|
|
CompileResults = Compiler.CompileAssemblyFromFile(CompileParams, SourceFileNames.Select(x => x.FullName).ToArray());
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
throw new BuildException(Ex, "Failed to launch compiler to compile assembly from source files '{0}' (Exception: {1})", SourceFileNames.ToString(), Ex.Message);
|
|
}
|
|
|
|
// Display compilation warnings and errors
|
|
if (CompileResults.Errors.Count > 0)
|
|
{
|
|
Log.TraceInformation("Messages while compiling {0}:", OutputAssemblyPath);
|
|
foreach (CompilerError CurError in CompileResults.Errors)
|
|
{
|
|
if (CurError.IsWarning)
|
|
{
|
|
Log.TraceWarning(CurError.ToString());
|
|
}
|
|
else
|
|
{
|
|
Log.TraceError(CurError.ToString());
|
|
}
|
|
}
|
|
if (CompileResults.Errors.HasErrors || TreatWarningsAsErrors)
|
|
{
|
|
throw new BuildException("UnrealBuildTool encountered an error while compiling source files");
|
|
}
|
|
}
|
|
|
|
// Grab the generated assembly
|
|
Assembly CompiledAssembly = CompileResults.CompiledAssembly;
|
|
if (CompiledAssembly == null)
|
|
{
|
|
throw new BuildException("UnrealBuildTool was unable to compile an assembly for '{0}'", SourceFileNames.ToString());
|
|
}
|
|
|
|
// Clean up temporary files that the compiler saved
|
|
TemporaryFiles.Delete();
|
|
|
|
return CompiledAssembly;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dynamically compiles an assembly for the specified source file and loads that assembly into the application's
|
|
/// current domain. If an assembly has already been compiled and is not out of date, then it will be loaded and
|
|
/// no compilation is necessary.
|
|
/// </summary>
|
|
/// <param name="SourceFileNames">List of source file name</param>
|
|
/// <param name="OutputAssemblyPath">Full path to the assembly to be created</param>
|
|
/// <returns>The assembly that was loaded</returns>
|
|
public static Assembly CompileAndLoadAssembly(FileReference OutputAssemblyPath, List<FileReference> SourceFileNames, List<string> ReferencedAssembies = null, List<string> PreprocessorDefines = null, bool DoNotCompile = false, bool TreatWarningsAsErrors = false)
|
|
{
|
|
// Check to see if the resulting assembly is compiled and up to date
|
|
FileReference AssemblySourcesListFilePath = FileReference.Combine(OutputAssemblyPath.Directory, Path.GetFileNameWithoutExtension(OutputAssemblyPath.FullName) + "SourceFiles.txt");
|
|
bool bNeedsCompilation = false;
|
|
if (!DoNotCompile)
|
|
{
|
|
bNeedsCompilation = RequiresCompilation(SourceFileNames, AssemblySourcesListFilePath, OutputAssemblyPath);
|
|
}
|
|
|
|
// Load the assembly to ensure it is correct
|
|
Assembly CompiledAssembly = null;
|
|
if (!bNeedsCompilation)
|
|
{
|
|
try
|
|
{
|
|
// Load the previously-compiled assembly from disk
|
|
CompiledAssembly = Assembly.LoadFile(OutputAssemblyPath.FullName);
|
|
}
|
|
catch (FileLoadException Ex)
|
|
{
|
|
Log.TraceInformation(String.Format("Unable to load the previously-compiled assembly file '{0}'. Unreal Build Tool will try to recompile this assembly now. (Exception: {1})", OutputAssemblyPath, Ex.Message));
|
|
bNeedsCompilation = true;
|
|
}
|
|
catch (BadImageFormatException Ex)
|
|
{
|
|
Log.TraceInformation(String.Format("Compiled assembly file '{0}' appears to be for a newer CLR version or is otherwise invalid. Unreal Build Tool will try to recompile this assembly now. (Exception: {1})", OutputAssemblyPath, Ex.Message));
|
|
bNeedsCompilation = true;
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
throw new BuildException(Ex, "Error while loading previously-compiled assembly file '{0}'. (Exception: {1})", OutputAssemblyPath, Ex.Message);
|
|
}
|
|
}
|
|
|
|
// Compile the assembly if me
|
|
if (bNeedsCompilation)
|
|
{
|
|
CompiledAssembly = CompileAssembly(OutputAssemblyPath, SourceFileNames, ReferencedAssembies, PreprocessorDefines, TreatWarningsAsErrors);
|
|
|
|
// Save out a list of all the source files we compiled. This is so that we can tell if whole files were added or removed
|
|
// since the previous time we compiled the assembly. In that case, we'll always want to recompile it!
|
|
{
|
|
FileInfo AssemblySourcesListFile = new FileInfo(AssemblySourcesListFilePath.FullName);
|
|
using (StreamWriter Writer = AssemblySourcesListFile.CreateText())
|
|
{
|
|
SourceFileNames.ForEach(x => Writer.WriteLine(x));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load the assembly into our app domain
|
|
try
|
|
{
|
|
AppDomain.CurrentDomain.Load(CompiledAssembly.GetName());
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
throw new BuildException(Ex, "Unable to load the compiled build assembly '{0}' into our application's domain. (Exception: {1})", OutputAssemblyPath, Ex.Message);
|
|
}
|
|
|
|
return CompiledAssembly;
|
|
}
|
|
}
|
|
}
|