Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/Tasks.UnitTests/CodeTaskFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ namespace Microsoft.Build.UnitTests
#if FEATURE_CODETASKFACTORY

using System.CodeDom.Compiler;
using System.IO.Compression;
using Microsoft.Build.Logging;
using Microsoft.Build.Tasks.UnitTests;
using Shouldly;

public sealed class CodeTaskFactoryTests
{
Expand Down Expand Up @@ -1200,6 +1203,83 @@ public void EmbedsGeneratedFileInBinlogWhenFailsToCompile()
CodeTaskFactoryEmbeddedFileInBinlogTestHelper.BuildAndCheckForEmbeddedFileInBinlog(
FactoryType.CodeTaskFactory, "HelloTask", taskXml, false);
}

[Fact]
public void ShouldEmitSingleGeneratedFileIntoBinlog()
{
using var env = TestEnvironment.Create();

// Define task XML for Import.targets
string taskXml = @"
<Project>
<UsingTask
TaskName=""CustomTask""
TaskFactory=""CodeTaskFactory""
AssemblyFile=""$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"">
<ParameterGroup>
<InputParameter ParameterType=""System.String"" />
<OutputParameter ParameterType=""System.String"" Output=""True"" />
</ParameterGroup>
<Task>
<Using Namespace=""System"" />
<Code Type=""Fragment"" Language=""cs"">
<![CDATA[
Console.WriteLine(this.InputParameter);
this.OutputParameter = ""Hello "" + this.InputParameter;
]]>
</Code>
</Task>
</UsingTask>
</Project>";

TransientTestFile importTargetsFile = env.CreateFile("Import.targets", taskXml);

// Define Another.proj content
string anotherContent = $@"<Project>
<Import Project=""{importTargetsFile.Path.Replace("\\", "/")}"" />
Comment thread
GangWang01 marked this conversation as resolved.
Outdated
<Target Name=""AnotherTarget"">
<CustomTask InputParameter=""Foo"">
<Output PropertyName=""TaskOutput"" TaskParameter=""OutputParameter"" />
</CustomTask>
<Message Text=""Output: $(TaskOutput)"" />
</Target>
</Project>";

TransientTestFile anotherProjFile = env.CreateFile("Another.proj", anotherContent);

// Define main.csproj content
string projectFileContent = $@"<Project>
<Import Project=""{importTargetsFile.Path.Replace("\\", "/")}"" />
<Target Name=""Build"">
<MSBuild Projects=""{anotherProjFile.Path.Replace("\\", "/")}"" Targets=""AnotherTarget"" />
<CustomTask InputParameter=""Bar"" />
</Target>
</Project>";

TransientTestFile binlog = env.ExpectFile(".binlog");

var binaryLogger = new BinaryLogger()
{
Parameters = $"LogFile={binlog.Path}",
CollectProjectImports = BinaryLogger.ProjectImportsCollectionMode.ZipFile,
};

Helpers.BuildProjectWithNewOMAndBinaryLogger(projectFileContent, binaryLogger, out bool result, out string projectDirectory);

Assert.True(result);

string projectImportsZipPath = Path.ChangeExtension(binlog.Path, ".ProjectImports.zip");
using var fileStream = new FileStream(projectImportsZipPath, FileMode.Open);
using var zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read);

// A path like "C:\path" in ZipArchive is saved as "C\path"
// For unix-based systems path uses '/'
projectDirectory = NativeMethodsShared.IsWindows ? projectDirectory.Replace(":\\", "\\") : projectDirectory.Replace("/", "\\");
Comment thread
GangWang01 marked this conversation as resolved.
Outdated

// check to make sure that only 1 tmp file is created
var tmpFiles = zipArchive.Entries.Where(zE => zE.Name.EndsWith("CustomTask-compilation-file.tmp")).ToList();
tmpFiles.Count.ShouldBe(1, $"Expected exactly one file ending with 'CustomTask-compilation-file.tmp' in ProjectImports.zip, but found {tmpFiles.Count}.");
}
}
#else
public sealed class CodeTaskFactoryTests
Expand Down
8 changes: 4 additions & 4 deletions src/Tasks/CodeTaskFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -791,16 +791,16 @@ private Assembly CompileInMemoryAssembly()
// Our code generation is complete, grab the source from the builder ready for compilation
string fullCode = codeBuilder.ToString();

// Embed generated file in the binlog
string fileNameInBinlog = $"{Guid.NewGuid()}-{_nameOfTask}-compilation-file.tmp";
_log.LogIncludeGeneratedFile(fileNameInBinlog, fullCode);

var fullSpec = new FullTaskSpecification(finalReferencedAssemblies, fullCode);
if (!s_compiledTaskCache.TryGetValue(fullSpec, out Assembly existingAssembly))
{
// Invokes compilation.
CompilerResults compilerResults = provider.CompileAssemblyFromSource(compilerParameters, fullCode);

// Embed generated file in the binlog
string fileNameInBinlog = $"{Guid.NewGuid()}-{_nameOfTask}-compilation-file.tmp";
_log.LogIncludeGeneratedFile(fileNameInBinlog, fullCode);

string outputPath = null;
if (compilerResults.Errors.Count > 0 || Environment.GetEnvironmentVariable("MSBUILDLOGCODETASKFACTORYOUTPUT") != null)
{
Expand Down