Bad error when CodeTaskFactory has a mismatch between TaskName and class name
Issue Description
This was an experiment to use the inline task and in-project C# files (net6.0) to process and provide property information during the build process.
With an inline task, and using the RoslynCodeTaskFactory, the task's file-based source code (a class) appears to cause an error on build using Visual Studio 2022 (Debug--AnyCPU).
Error: https://docs.microsoft.com/en-us/visualstudio/msbuild/errors/msb4175
The task factory "RoslynCodeTaskFactory" could not be loaded from the assembly "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64\Microsoft.Build.Tasks.Core.dll". The task factory must return a value for the "TaskType" property.
No other error messages are provided in VS or build output.
There are other ways to process and provide property information (like version details), however, this was only intended as a experiment to verify the capabilities and features discussed in the msbuild docs.
Steps to Reproduce
Using the generated console project (net6.0) with two files--the Program.cs and BuildInformation.cs.
Also added project references Microsoft.Build.Framework and Microsoft.Build.Utilities.Core from NuGet.
There is only the one console project in the test solution.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="17.2.0" CopyLocal="false" Publish="false" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.2.0" CopyLocal="false" Publish="false" ExcludeAssets="runtime" />
</ItemGroup>
<!-- inline task. -->
<UsingTask
TaskName="GetCalculatedVersion"
TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<BuildVersion ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Using Namespace="MyInlineCode" />
<Code Type="Class" Source="BuildInformation.cs" Language="cs"></Code>
</Task>
</UsingTask>
<Target Name="GetBuildVersion" BeforeTargets="BeforeBuild">
<Message Importance="high" Text="Running Target GetBuildVersion..." />
<GetCalculatedVersion>
<Output TaskParameter="BuildVersion" PropertyName="MyNewVersion" />
</GetCalculatedVersion>
<Message Importance="high" Text="... Finished Target GetBuildVersion -- '$(MyNewVersion)'." />
</Target>
</Project>
// BuildInformation.cs
namespace MyInlineCode
{
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
/// <summary>
/// Represents the project's build information.
/// </summary>
public class BuildInformation : Microsoft.Build.Utilities.Task
{
#region static properties
public static readonly Int32 Major = 0;
public static readonly Int32 Minor = 1;
public static readonly Int32 Build = (DateTime.Now - new DateTime(2000, 01, 01)).Days;
public static readonly Int32 Revision = (Int32)((DateTime.Now.TimeOfDay.TotalSeconds) / 2);
public static readonly Version Version = new(Major, Minor, Build, Revision);
#endregion static properties
public override bool Execute()
{
// task output
String BuildVersion = Version.ToString();
Log.LogMessage(MessageImportance.High, $"Build version set to {BuildVersion}");
return !Log.HasLoggedErrors;
}
}
}
// Program.cs
namespace BuildExperiment
{
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("This is a build experiment.");
}
}
}
Assessment
This appears to be unrelated to issue #7730, as this source file provides a class and is not a code fragment.
Expected Behavior
The expected behavior is a successful build with the text provided by the inline task and the target displayed via output messages to verify a working experiment.
Versions & Configurations
OS is Windows 10 Pro 21H2 19044.1766; Windows Feature Experience Pack 120.2212.4180.0
and using ...
Microsoft.NET SDK Version: 6.0.302
Microsoft (R) Build Engine version 17.2.1+52cd2da31 for .NET Framework 17.2.1.25201
Microsoft Visual Studio Community 2022 (64-bit) - Current - Version 17.2.6
Vanilla community installation with no special configuration, etc.
@Mal-Ayers I investigated this issue. My findings follow:
- The error message ('The task factory "RoslynCodeTaskFactory" could not be loaded...') is incorrect and misleading and the real error is not reported.
- The class name and the task name must match. The
UsingTaskuses the task name ofGetCalculatedVersionto look up the task and fails because the task is namedBuildInformation. (That the class and task names must match is noted in the documentation for the Code element.) - The
UsingTaskdefines an output parameter and the matching property is not provided on the class.
I modified your code to test.
In the project file, change the task name:
<UsingTask
TaskName="BuildInformation"
<BuildInformation>
<Output TaskParameter="BuildVersion" PropertyName="MyNewVersion" />
</BuildInformation>
In BuildInformation.cs, add the output property:
public string BuildVersion { get; set; }
public override bool Execute()
{
// task output
BuildVersion = Version.ToString();
There should be a separate issue specifically for the error handling and messaging. (Edit: An issues exists. See #6419.)
- The error message ('The task factory "RoslynCodeTaskFactory" could not be loaded...') is incorrect and misleading and the real error is not reported.
I just expanded #6419 to cover this case (I agree it's the same root cause with a different task factory).
I'm also changing this bug to be about the suberror The task factory must return a value for the "TaskType" property., which wouldn't be clear even if we correctly said "The CodeTaskFactory failed with error X".
@rainersigwald Please assign this issue to me.