JD2022-TU1/main/build/sharpmake/Tools/Helpers/CopyLocalOptimizer.cs

146 lines
No EOL
5.6 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using Sharpmake;
using Configuration = Sharpmake.Project.Configuration;
namespace JustDance.Tools.Helpers
{
/// <summary>
/// Creates ntfs hard link to references instead of copying them locally,
/// in order to improve the build time and reduces the output size.
/// </summary>
/// <remarks>Should be called after all the projects have been resolved.</remarks>
internal static class CopyLocalOptimizer
{
public static void ConvertCopiesIntoHardlinks(this Project project)
{
foreach (var configuration in project.Configurations)
{
configuration.ConvertCopiesIntoHardlinks();
}
}
private static void ConvertCopiesIntoHardlinks(this Configuration configuration)
{
var dependencies = new HashSet<Configuration>();
var mklinker = Path.Combine(Globals.BranchRoot, @"extern\mklinker\mklinker.exe");
CollectDependenciesRecursive(dependencies, configuration);
var filesToCopy = dependencies.SelectMany(GetFilesToCopy).Concat(GetReferencesByPath(configuration));
foreach (var toCopy in filesToCopy)
{
var sourceFile = toCopy.Source;
var targetFile = Path.Combine(configuration.TargetPath, toCopy.RelativePath);
// C# supports a single declaration of post build events, we must replace the target
// configuration with its corresponding macro.
var postBuild = string.Format("\"{0}\" \"{1}\" \"{2}\"", mklinker, targetFile, sourceFile);
postBuild = postBuild.Replace(string.Format("\\{0}\\", configuration.Target.Name), "\\$(ConfigurationName)\\");
configuration.EventPostBuild.Add(postBuild);
}
}
private static IEnumerable<FileToCopy> GetFilesToCopy(Configuration dependency)
{
var toCopy = new List<FileToCopy>();
toCopy.AddRange(GetCustomBuildActionFiles(dependency));
toCopy.AddRange(GetReferencesByPath(dependency));
toCopy.Add(FileToCopy.Create(Path.Combine(dependency.TargetPath, dependency.TargetFileFullName + GetAssemblyExtension(dependency))));
toCopy.Add(FileToCopy.Create(Path.Combine(dependency.TargetPath, dependency.TargetFileFullName + ".pdb")));
return toCopy;
}
private static IEnumerable<FileToCopy> GetReferencesByPath(Configuration dependency)
{
return dependency.ReferencesByPath.Select(file => Util.PathGetAbsolute(dependency.TargetPath, file)).Select(FileToCopy.Create);
}
/// <summary>
/// Returns the files in the project that are copied to output. They should be relative to their folder.
/// </summary>
private static IEnumerable<FileToCopy> GetCustomBuildActionFiles(Configuration dependency)
{
var project = dependency.Project as CSharpProject;
if (project != null)
{
return from additionalFile in project.AdditionalContentAlwaysCopy.Concat(project.AdditionalContentCopyIfNewer)
let absolutePath = Path.Combine(project.SourceRootPath, additionalFile)
from file in ExpandWildcards(absolutePath)
select new FileToCopy
{
Source = file,
RelativePath = Util.PathGetRelative(project.SourceRootPath, file)
};
}
return new FileToCopy[0];
}
/// <summary>
/// Expands the * into their files.
/// </summary>
private static IEnumerable<string> ExpandWildcards(string sourceFile)
{
if (sourceFile.EndsWith("*"))
{
var directory = sourceFile.TrimEnd('*');
if (Directory.Exists(directory))
{
foreach (var file in Directory.GetFiles(directory, "*", SearchOption.AllDirectories))
{
yield return file;
}
}
}
else
{
yield return sourceFile;
}
}
private static void CollectDependenciesRecursive(HashSet<Configuration> configurations, Configuration configuration)
{
foreach (var dependency in configuration.ResolvedDependencies)
{
if (configurations.Add(dependency))
{
CollectDependenciesRecursive(configurations, dependency);
}
}
}
private static string GetAssemblyExtension(Configuration dependency)
{
return (dependency.Output == Configuration.OutputType.Exe ||
dependency.Output == Configuration.OutputType.DotNetWindowsApp ||
dependency.Output == Configuration.OutputType.DotNetConsoleApp) ? ".exe" : ".dll";
}
private struct FileToCopy
{
/// <summary>
/// The full path of the sourceFolder file.
/// </summary>
public string Source { get; set; }
/// <summary>
/// The destination relative to the output.
/// </summary>
public string RelativePath { get; set; }
public static FileToCopy Create(string source)
{
return new FileToCopy
{
Source = source,
RelativePath = Path.GetFileName(source)
};
}
}
}
}