Skip to content
This repository was archived by the owner on Sep 17, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 27 additions & 0 deletions Samples/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,31 @@
</Task>
</UsingTask>

<!-- A simple in-line class task which inherits parameters from its base class -->
<UsingTask TaskName="InterceptingCopy"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(RoslynCodeTaskFactory)"
Condition=" '$(RoslynCodeTaskFactory)' != '' ">
<Task>
<Reference Include="Microsoft.Build.Tasks.Core"/>
<Code Type="Class" Language="cs">
<![CDATA[
using Microsoft.Build.Framework;
using System;

public class InterceptingCopy : Microsoft.Build.Tasks.Copy
{
public override bool Execute()
{
Log.LogMessage(MessageImportance.High, "Copying '{0}' to '{1}'", String.Join(", ", (object[])SourceFiles), String.Join(", ", (object[])DestinationFiles));
return base.Execute();
}
}
]]>
</Code>
</Task>
</UsingTask>

<Target Name="RunSampleTask" AfterTargets="Build">
<!-- Call the compiled task -->
<MySample Parameter1="A value for parameter 1" Parameter2="A value for parameter 2">
Expand All @@ -84,6 +109,8 @@
</PathGetFileName>

<Message Text="File name: '$(MyFileName)'" Importance="High" />

<InterceptingCopy SourceFiles="Class1.cs" DestinationFiles="$(OutputPath)CopyOfClass1.cs" />
</Target>

</Project>
21 changes: 20 additions & 1 deletion src/RoslynCodeTaskFactory.UnitTests/CodeTaskFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,21 @@ public void CodeLanguageFromTaskBody()
TryLoadTaskBodyAndExpectSuccess("<Code Language=\"ViSuAl BaSic\">code</Code>", expectedCodeLanguage: "VB");
}

[Test]
public void CodeTypeClassIgnoresParameterGroupWarning()
{
TryLoadTaskBodyAndExpectSuccess(
taskBody: "<Code Type=\"Class\">code</Code>",
parameters: new[]
{
new TaskPropertyInfo("P", typeof(string), false, false),
},
expectedWarningMessages: new[]
{
"Parameters are discovered through reflection for Type=\"Class\". Values specified in <ParameterGroup/> will be ignored.",
});
}

[Test]
public void CodeTypeFromTaskBody()
{
Expand Down Expand Up @@ -466,7 +481,8 @@ private void TryLoadTaskBodyAndExpectSuccess(
ISet<string> expectedNamespaces = null,
string expectedCodeLanguage = null,
CodeTaskFactoryCodeType? expectedCodeType = null,
string expectedSourceCode = null)
string expectedSourceCode = null,
IReadOnlyList<string> expectedWarningMessages = null)
{
MockBuildEngine buildEngine = new MockBuildEngine();

Expand All @@ -478,6 +494,7 @@ private void TryLoadTaskBodyAndExpectSuccess(
bool success = CodeTaskFactory.TryLoadTaskBody(log, TaskName, taskBody, parameters ?? new List<TaskPropertyInfo>(), out TaskInfo taskInfo);

buildEngine.Errors.ShouldBe(new string[0]);
buildEngine.Warnings.ShouldBe(expectedWarningMessages ?? new string[0]);

Assert.True(success);

Expand Down Expand Up @@ -523,6 +540,8 @@ private sealed class MockBuildEngine : IBuildEngine

public string ProjectFileOfTaskNode { get; } = String.Empty;

public IEnumerable<string> Warnings => Events.OfType<BuildWarningEventArgs>().Select(i => i.Message);

public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
{
throw new NotSupportedException();
Expand Down
29 changes: 29 additions & 0 deletions src/RoslynCodeTaskFactory/CodeTaskFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,28 @@ public bool Initialize(string taskName, IDictionary<string, TaskPropertyInfo> pa
TaskType = assembly.GetExportedTypes().FirstOrDefault(type => type.Name.Equals(taskName));
}

if (TaskType != null)
{
// Perform automatic parameter detection if the user supplied a class.
// This reduces the burden of the developer by not requiring them to
// manually specify <ParameterGroup/>.
//
if (taskInfo.CodeType == CodeTaskFactoryCodeType.Class)
{
PropertyInfo[] properties = TaskType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
_parameters = new TaskPropertyInfo[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
PropertyInfo property = properties[i];
_parameters[i] = new TaskPropertyInfo(
property.Name,
property.PropertyType,
property.GetCustomAttribute<OutputAttribute>() != null,
property.GetCustomAttribute<RequiredAttribute>() != null);
}
}
}

AppDomain.CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve;

// Initialization succeeded if we found a type matching the task name from the compiled assembly
Expand Down Expand Up @@ -395,6 +417,13 @@ internal static bool TryLoadTaskBody(TaskLoggingHelper log, string taskName, str
taskInfo.CodeType = codeType;
}

// Warn that <ParameterGroup/> is ignored if any parameters are supplied when Type="Class".
//
if (taskInfo.CodeType == CodeTaskFactoryCodeType.Class && parameters.Any())
{
log.LogWarningWithCodeFromResources("CodeTaskFactory_ParameterGroupIgnoredForCodeTypeClass");
}

if (languageAttribute != null)
{
if (String.IsNullOrWhiteSpace(languageAttribute.Value))
Expand Down
9 changes: 9 additions & 0 deletions src/RoslynCodeTaskFactory/Properties/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/RoslynCodeTaskFactory/Properties/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@
<value>MSB3428: You must specify source code within the Code element or a path to a file containing source code.</value>
<comment>{StrBegin="MSB3428: "}</comment>
</data>
<data name="CodeTaskFactory_ParameterGroupIgnoredForCodeTypeClass" xml:space="preserve">
<value>Parameters are discovered through reflection for Type="Class". Values specified in &lt;ParameterGroup/&gt; will be ignored.</value>
</data>
<data name="CodeTaskFactory_WaitingForDebugger" xml:space="preserve">
<value>Waiting for debugger to attach ({0} PID {1}). Press enter to continue...</value>
</data>
Expand Down