From 9e3d7c0c4c7000ef51c2959b4e04c2323d1dac31 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 2 Jan 2022 13:13:10 -0800 Subject: Move to a single tracking file for all bind inputs and outputs A single tracking file can be enhanced more easily than continuing to introduce specialized tracking files. In this case, we now include output files that are copied and can differentiate them from the built output files. --- .../Data/ILayoutContext.cs | 14 +-- .../Data/TrackedFileType.cs | 12 ++- src/wix/WixToolset.BuildTasks/ReadTracking.cs | 97 +++++++++++++++++++ src/wix/WixToolset.BuildTasks/WixBuild.cs | 10 +- .../WixToolset.Core.Burn/Bind/BindBundleCommand.cs | 4 +- .../Bind/BindDatabaseCommand.cs | 4 +- .../Bind/CreateCabinetsCommand.cs | 2 +- .../Bind/ProcessUncompressedFilesCommand.cs | 2 +- .../WixToolset.Core/CommandLine/BuildCommand.cs | 34 ++----- .../ExtensibilityServices/TrackedFile.cs | 2 +- src/wix/WixToolset.Core/LayoutContext.cs | 6 +- src/wix/WixToolset.Core/LayoutCreator.cs | 97 ++++--------------- src/wix/WixToolset.Sdk/tools/wix.targets | 104 +++++++++------------ .../WixToolsetTest.Sdk/WixToolsetTest.Sdk.csproj | 3 +- 14 files changed, 185 insertions(+), 206 deletions(-) create mode 100644 src/wix/WixToolset.BuildTasks/ReadTracking.cs diff --git a/src/api/wix/WixToolset.Extensibility/Data/ILayoutContext.cs b/src/api/wix/WixToolset.Extensibility/Data/ILayoutContext.cs index b11b4d13..981ed38e 100644 --- a/src/api/wix/WixToolset.Extensibility/Data/ILayoutContext.cs +++ b/src/api/wix/WixToolset.Extensibility/Data/ILayoutContext.cs @@ -31,25 +31,15 @@ namespace WixToolset.Extensibility.Data /// IReadOnlyCollection FileTransfers { get; set; } - /// - /// File to capture list of content files. - /// - string ContentsFile { get; set; } - - /// - /// File to capture list of output files. - /// - string OutputsFile { get; set; } - /// /// Intermediate folder. /// string IntermediateFolder { get; set; } /// - /// List of built output files. + /// File to capture list of content, built output and copied output files. /// - string BuiltOutputsFile { get; set; } + string TrackingFile { get; set; } /// /// Reset ACLs on file transfers. diff --git a/src/api/wix/WixToolset.Extensibility/Data/TrackedFileType.cs b/src/api/wix/WixToolset.Extensibility/Data/TrackedFileType.cs index e7f53842..904a990f 100644 --- a/src/api/wix/WixToolset.Extensibility/Data/TrackedFileType.cs +++ b/src/api/wix/WixToolset.Extensibility/Data/TrackedFileType.cs @@ -25,9 +25,15 @@ namespace WixToolset.Extensibility.Data Intermediate, /// - /// Final output (like a .msi, .cab or .wixpdb). - /// These are the whole point of the build process. + /// Output created by the build process itself (like a .msi, .cab or .wixpdb). + /// These files can be recreated in the final output location by building again. /// - Final, + BuiltOutput, + + /// + /// Output copied by the build process (like external files in an .msi). + /// These files are not created by the build process but are copied to the final output location. + /// + CopiedOutput, } } diff --git a/src/wix/WixToolset.BuildTasks/ReadTracking.cs b/src/wix/WixToolset.BuildTasks/ReadTracking.cs new file mode 100644 index 00000000..1ce039f6 --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/ReadTracking.cs @@ -0,0 +1,97 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.BuildTasks +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// + /// Read the contents of the tracking file produced by the build command. + /// + public class ReadTracking : Task + { + private const string TrackedTypeMetadataName = "TrackedType"; + private static readonly char[] TrackedLineTypePathSeparator = new[] { '\t' }; + + /// + /// The path to the tracking file. + /// + [Required] + public ITaskItem File { get; set; } + + /// + /// All tracked files. + /// + [Output] + public ITaskItem[] All { get; private set; } + + /// + /// The tracked built outputs. + /// + [Output] + public ITaskItem[] BuiltOutputs { get; private set; } + + /// + /// The tracked copied outputs. + /// + [Output] + public ITaskItem[] CopiedOutputs { get; private set; } + + /// + /// The tracked inputs. + /// + [Output] + public ITaskItem[] Inputs { get; private set; } + + /// + /// All tracked outputs. + /// + [Output] + public ITaskItem[] Outputs { get; private set; } + + /// + /// Gets a complete list of external cabs referenced by the given installer database file. + /// + /// True upon completion of the task execution. + public override bool Execute() + { + var all = new List(); + var path = this.File.ItemSpec; + + if (System.IO.File.Exists(path)) + { + var lines = System.IO.File.ReadAllLines(path); + + foreach (var line in lines) + { + var split = line.Split(TrackedLineTypePathSeparator, 2, StringSplitOptions.RemoveEmptyEntries); + + if (split.Length == 2) + { + all.Add(new TaskItem(split[1], new Dictionary() { [TrackedTypeMetadataName] = split[0] })); + } + else + { + this.Log.LogError($"Failed to parse tracked line: {line}"); + } + } + } + + this.All = all.ToArray(); + this.BuiltOutputs = all.Where(t => FilterByTrackedType(t, "BuiltOutput")).ToArray(); + this.CopiedOutputs = all.Where(t => FilterByTrackedType(t, "CopiedOutput")).ToArray(); + this.Inputs = all.Where(t => FilterByTrackedType(t, "Input")).ToArray(); + this.Outputs = all.Where(t => FilterByTrackedType(t, "BuiltOutput") || FilterByTrackedType(t, "CopiedOutput")).ToArray(); + + return true; + } + + private static bool FilterByTrackedType(ITaskItem item, string type) + { + return item.GetMetadata(TrackedTypeMetadataName).Equals(type, StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/src/wix/WixToolset.BuildTasks/WixBuild.cs b/src/wix/WixToolset.BuildTasks/WixBuild.cs index dbff7e81..3d4617f6 100644 --- a/src/wix/WixToolset.BuildTasks/WixBuild.cs +++ b/src/wix/WixToolset.BuildTasks/WixBuild.cs @@ -52,11 +52,7 @@ namespace WixToolset.BuildTasks public bool BindFiles { get; set; } - public ITaskItem BindContentsFile { get; set; } - - public ITaskItem BindOutputsFile { get; set; } - - public ITaskItem BindBuiltOutputsFile { get; set; } + public ITaskItem BindTrackingFile { get; set; } public string CabinetCachePath { get; set; } @@ -96,9 +92,7 @@ namespace WixToolset.BuildTasks commandLineBuilder.AppendSwitchIfNotNull("-usf ", this.UnreferencedSymbolsFile); commandLineBuilder.AppendSwitchIfNotNull("-cc ", this.CabinetCachePath); commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory); - commandLineBuilder.AppendSwitchIfNotNull("-contentsfile ", this.BindContentsFile); - commandLineBuilder.AppendSwitchIfNotNull("-outputsfile ", this.BindOutputsFile); - commandLineBuilder.AppendSwitchIfNotNull("-builtoutputsfile ", this.BindBuiltOutputsFile); + commandLineBuilder.AppendSwitchIfNotNull("-trackingfile ", this.BindTrackingFile); commandLineBuilder.AppendSwitchIfNotNull("-defaultcompressionlevel ", this.DefaultCompressionLevel); base.BuildCommandLine(commandLineBuilder); diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index cd00232a..7b74ce5e 100644 --- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs @@ -483,7 +483,7 @@ namespace WixToolset.Core.Burn command.Execute(); fileTransfers.Add(command.Transfer); - trackedFiles.Add(this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final)); + trackedFiles.Add(this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.BuiltOutput)); } #if TODO // does this need to come back, or do they only need to be in TrackedFiles? @@ -504,7 +504,7 @@ namespace WixToolset.Core.Burn } else { - var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.Final); + var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.BuiltOutput); trackedFiles.Add(trackPdb); wixout = WixOutput.Create(trackPdb.Path); diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 9f36cd78..f2a8b9c6 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs @@ -478,7 +478,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind { this.Messaging.Write(VerboseMessages.GeneratingDatabase()); - var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); + var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.BuiltOutput); trackedFiles.Add(trackMsi); var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false); @@ -595,7 +595,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else { - var trackPdb = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.Final); + var trackPdb = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.BuiltOutput); trackedFiles.Add(trackPdb); wixout = WixOutput.Create(trackPdb.Path); diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs index 83a4949e..4ac248cd 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs @@ -225,7 +225,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else { - var trackDestination = this.BackendHelper.TrackFile(Path.Combine(cabinetDir, mediaSymbol.Cabinet), TrackedFileType.Final, mediaSymbol.SourceLineNumbers); + var trackDestination = this.BackendHelper.TrackFile(Path.Combine(cabinetDir, mediaSymbol.Cabinet), TrackedFileType.BuiltOutput, mediaSymbol.SourceLineNumbers); this.trackedFiles.Add(trackDestination); var transfer = this.BackendHelper.CreateFileTransfer(resolvedCabinet.Path, trackDestination.Path, resolvedCabinet.BuildOption == CabinetBuildOption.BuildAndMove, mediaSymbol.SourceLineNumbers); diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs index 039ba495..9aad3537 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs @@ -109,7 +109,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind // because if the source and destination of the transfer is the same, we // don't want to clean the file because we'd be deleting the original // (and that would be bad). - var tracked = this.BackendHelper.TrackFile(transfer.Destination, TrackedFileType.Final, facade.SourceLineNumber); + var tracked = this.BackendHelper.TrackFile(transfer.Destination, TrackedFileType.BuiltOutput, facade.SourceLineNumber); tracked.Clean = !transfer.Redundant; trackedFiles.Add(tracked); diff --git a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs index 5f618b81..47b7afa8 100644 --- a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs @@ -52,11 +52,7 @@ namespace WixToolset.Core.CommandLine private CompressionLevel? DefaultCompressionLevel { get; set; } - private string ContentsFile { get; set; } - - private string OutputsFile { get; set; } - - private string BuiltOutputsFile { get; set; } + private string TrackingFile { get; set; } public Task ExecuteAsync(CancellationToken cancellationToken) { @@ -78,11 +74,7 @@ namespace WixToolset.Core.CommandLine this.Platform = this.commandLine.Platform; - this.ContentsFile = this.commandLine.ContentsFile; - - this.OutputsFile = this.commandLine.OutputsFile; - - this.BuiltOutputsFile = this.commandLine.BuiltOutputsFile; + this.TrackingFile = this.commandLine.TrackingFile; this.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel; @@ -371,9 +363,7 @@ namespace WixToolset.Core.CommandLine context.TrackedFiles = bindResult.TrackedFiles; context.FileTransfers = bindResult.FileTransfers; context.IntermediateFolder = intermediateFolder; - context.ContentsFile = this.ContentsFile; - context.OutputsFile = this.OutputsFile; - context.BuiltOutputsFile = this.BuiltOutputsFile; + context.TrackingFile = this.TrackingFile; context.ResetAcls = this.commandLine.ResetAcls; context.CancellationToken = cancellationToken; @@ -537,11 +527,7 @@ namespace WixToolset.Core.CommandLine public CompressionLevel? DefaultCompressionLevel { get; private set; } - public string ContentsFile { get; private set; } - - public string OutputsFile { get; private set; } - - public string BuiltOutputsFile { get; private set; } + public string TrackingFile { get; private set; } public List Ices { get; } = new List(); @@ -610,16 +596,8 @@ namespace WixToolset.Core.CommandLine parser.GetNextArgumentOrError(arg, this.Cultures); return true; - case "contentsfile": - this.ContentsFile = parser.GetNextArgumentAsFilePathOrError(arg); - return true; - - case "outputsfile": - this.OutputsFile = parser.GetNextArgumentAsFilePathOrError(arg); - return true; - - case "builtoutputsfile": - this.BuiltOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg); + case "trackingfile": + this.TrackingFile = parser.GetNextArgumentAsFilePathOrError(arg); return true; case "d": diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/TrackedFile.cs b/src/wix/WixToolset.Core/ExtensibilityServices/TrackedFile.cs index 028cddbf..570fb029 100644 --- a/src/wix/WixToolset.Core/ExtensibilityServices/TrackedFile.cs +++ b/src/wix/WixToolset.Core/ExtensibilityServices/TrackedFile.cs @@ -12,7 +12,7 @@ namespace WixToolset.Core.ExtensibilityServices this.Path = path; this.Type = type; this.SourceLineNumbers = sourceLineNumbers; - this.Clean = (type == TrackedFileType.Intermediate || type == TrackedFileType.Final); + this.Clean = (type == TrackedFileType.Intermediate || type == TrackedFileType.BuiltOutput || type == TrackedFileType.CopiedOutput); } public bool Clean { get; set; } diff --git a/src/wix/WixToolset.Core/LayoutContext.cs b/src/wix/WixToolset.Core/LayoutContext.cs index 4b8c7b99..3def3006 100644 --- a/src/wix/WixToolset.Core/LayoutContext.cs +++ b/src/wix/WixToolset.Core/LayoutContext.cs @@ -27,11 +27,7 @@ namespace WixToolset.Core public string IntermediateFolder { get; set; } - public string ContentsFile { get; set; } - - public string OutputsFile { get; set; } - - public string BuiltOutputsFile { get; set; } + public string TrackingFile { get; set; } public bool ResetAcls { get; set; } diff --git a/src/wix/WixToolset.Core/LayoutCreator.cs b/src/wix/WixToolset.Core/LayoutCreator.cs index 0c5aaf63..7a143680 100644 --- a/src/wix/WixToolset.Core/LayoutCreator.cs +++ b/src/wix/WixToolset.Core/LayoutCreator.cs @@ -16,6 +16,8 @@ namespace WixToolset.Core /// internal class LayoutCreator : ILayoutCreator { + private const string TrackedLineTypePathSeparator = "\t"; + internal LayoutCreator(IServiceProvider serviceProvider) { this.Messaging = serviceProvider.GetService(); @@ -51,22 +53,9 @@ namespace WixToolset.Core } finally { - if (context.TrackedFiles != null) + if (context.TrackedFiles != null && !String.IsNullOrEmpty(context.TrackingFile)) { - if (!String.IsNullOrEmpty(context.ContentsFile)) - { - this.CreateContentsFile(context.ContentsFile, context.TrackedFiles); - } - - if (!String.IsNullOrEmpty(context.OutputsFile)) - { - this.CreateOutputsFile(context.OutputsFile, context.TrackedFiles); - } - - if (!String.IsNullOrEmpty(context.BuiltOutputsFile)) - { - this.CreateBuiltOutputsFile(context.BuiltOutputsFile, context.TrackedFiles); - } + this.CreateTrackingFile(context.TrackingFile, context.TrackedFiles); } } @@ -78,72 +67,15 @@ namespace WixToolset.Core } /// - /// Writes the paths to the content files to a text file. - /// - /// Path to write file. - /// Collection of paths to content files that will be written to file. - private void CreateContentsFile(string path, IEnumerable trackedFiles) - { - var uniqueInputFilePaths = new SortedSet(trackedFiles.Where(t => t.Type == TrackedFileType.Input).Select(t => t.Path), StringComparer.OrdinalIgnoreCase); - - if (!uniqueInputFilePaths.Any()) - { - return; - } - - var directory = Path.GetDirectoryName(path); - Directory.CreateDirectory(directory); - - using (var contents = new StreamWriter(path, false)) - { - foreach (var inputPath in uniqueInputFilePaths) - { - contents.WriteLine(inputPath); - } - } - } - - /// - /// Writes the paths to the output files to a text file. - /// - /// Path to write file. - /// Collection of files that were transferred to the output directory. - private void CreateOutputsFile(string path, IEnumerable trackedFiles) - { - var uniqueOutputPaths = new SortedSet(trackedFiles.Where(t => t.Clean).Select(t => t.Path), StringComparer.OrdinalIgnoreCase); - - if (!uniqueOutputPaths.Any()) - { - return; - } - - var directory = Path.GetDirectoryName(path); - Directory.CreateDirectory(directory); - - using (var outputs = new StreamWriter(path, false)) - { - //// Don't list files where the source is the same as the destination since - //// that might be the only place the file exists. The outputs file is often - //// used to delete stuff and losing the original source would be bad. - //var uniqueOutputPaths = new SortedSet(fileTransfers.Where(ft => !ft.Redundant).Select(ft => ft.Destination), StringComparer.OrdinalIgnoreCase); - - foreach (var outputPath in uniqueOutputPaths) - { - outputs.WriteLine(outputPath); - } - } - } - - /// - /// Writes the paths to the built output files to a text file. + /// Writes the paths of the track files to a text file. /// /// Path to write file. - /// Collection of files that were transferred to the output directory. - private void CreateBuiltOutputsFile(string path, IEnumerable trackedFiles) + /// Collection of files that were tracked. + private void CreateTrackingFile(string path, IEnumerable trackedFiles) { - var uniqueBuiltPaths = new SortedSet(trackedFiles.Where(t => t.Type == TrackedFileType.Final).Select(t => t.Path), StringComparer.OrdinalIgnoreCase); + var uniqueTrackingLines = new SortedSet(trackedFiles.Where(t => t.Type != TrackedFileType.Temporary).Select(TrackedFileLine), StringComparer.OrdinalIgnoreCase); - if (!uniqueBuiltPaths.Any()) + if (!uniqueTrackingLines.Any()) { return; } @@ -151,11 +83,11 @@ namespace WixToolset.Core var directory = Path.GetDirectoryName(path); Directory.CreateDirectory(directory); - using (var outputs = new StreamWriter(path, false)) + using (var stream = new StreamWriter(path, false)) { - foreach (var builtPath in uniqueBuiltPaths) + foreach (var trackingLine in uniqueTrackingLines) { - outputs.WriteLine(builtPath); + stream.WriteLine(trackingLine); } } } @@ -219,5 +151,10 @@ namespace WixToolset.Core } } } + + private static string TrackedFileLine(ITrackedFile trackedFile) + { + return trackedFile.Type + TrackedLineTypePathSeparator + trackedFile.Path; + } } } diff --git a/src/wix/WixToolset.Sdk/tools/wix.targets b/src/wix/WixToolset.Sdk/tools/wix.targets index 7497a9f8..0029e217 100644 --- a/src/wix/WixToolset.Sdk/tools/wix.targets +++ b/src/wix/WixToolset.Sdk/tools/wix.targets @@ -112,18 +112,20 @@ $(TargetDir) - $(PdbOutputDir)\ - - - $([System.IO.Path]::GetFullPath(`$([System.IO.Path]::Combine(`$(MSBuildProjectDirectory)`, `$(PdbOutputDir)`))`)) - - + $([MSBuild]::NormalizeDirectory($(MSBuildProjectDirectory), $(PdbOutputDir))) $(TargetName).wixpdb - - $(TargetPdbDir)$(TargetPdbFileName) + + $(WixBinDir) + + + + $(MSBuildProjectFile).BindTracking + .txt + + + $(BindTrackingFilePrefix)- @@ -615,18 +610,17 @@ Inputs="@(Compile); @(Content); @(_WixLocalizationFile); - @(WixObject); @(_WixReferencedProjectOutputs); @(_ResolvedWixLibraryPaths); @(_ResolvedWixExtensionPaths); @(_BindInputs)" - Outputs="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(BindBuiltOutputsFile);@(_BindBuiltOutputs)" + Outputs="$(IntermediateOutputPath)$(BindTrackingFilePrefix)%(CultureGroup.Identity)$(BindTrackingFileExtension);@(_BindBuiltOutputs)" DependsOnTargets="$(CoreCompileDependsOn)" Condition=" '@(Compile)' != '' "> - $([System.IO.Path]::GetFullPath($(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetFileName))) - $(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetPdbFileName) + $(IntermediateOutputPath)cabcache\ + <_WixBuildCabinetCachePath Condition=" '$(CabinetCachePath)'!='' ">$([MSBuild]::NormalizeDirectory($(CabinetCachePath), %(CultureGroup.OutputFolder))) - - - - - - - - - - + + + + - - - - + + + + @@ -930,17 +912,17 @@ true true - $([System.IO.Path]::GetFullPath($(IntermediateOutputPath))) + $([MSBuild]::NormalizeDirectory($(IntermediateOutputPath))) - - - - + + + + - <_FullPathToCopy Include="$(OutputFile)" Condition=" '@(_FullPathToCopy)'=='' " /> - <_RelativePath Include="$([MSBuild]::MakeRelative($(FullIntermediateOutputPath), %(_FullPathToCopy.Identity)))" /> + <_FullPathToCopy Include="$(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetFileName)" Condition=" '@(_FullPathToCopy)'=='' " /> + <_RelativePath Include="$([MSBuild]::MakeRelative($(FullIntermediateOutputPath), %(_FullPathToCopy.FullPath)))" /> - - + -- cgit v1.2.3-55-g6feb