I just installed the nuget package for Jon Skeet's Unconstrained Melody project, but when I attempt to use it, I get an error when I compile:
Type parameter 'T' inherits conflicting constraints 'UnconstrainedMelody.IEnumConstraint' and 'System.ValueType'
Function definition:
public void SetEnum<T>() where T : struct, IEnumConstraint {}
Am I missing something? Should I not use the nuget package?
I could be wrong, but it appears that while this library uses IEnumConstraint internally, and gets it to work with the postbuild steps described in the article, it does not provide any magic for you to just consume IEnumConstraint directly for your own methods.
The GetValues<T> method described in the post is one of several methods provided from the UnconstrainedMelody.Enums class. There are other objects and methods available as well.
If you wanted to constrain your own generic methods to enums, you could follow the same steps Jon used to build this library, but on your own library. There was also this example in the comments of how to do this with PostSharp.
The ideas of the "unconstrained melody" have been integrated into
ExtraConstraints
It comes with the necessary tooling to incorporate the IL weaving into the build process and is being actively maintained. Therefore, it should be preferrable to use this over the code from "unconstrained melody". For Enums, Enums.NET also offers additional improvements.
ExtraConstraints also supports adding constraints to delegates.
I haven't tried this, but it appears you can use this MSBuild task to accomplish the same thing as ConstraintChanger. You have to include a copy of the DelegateConstraint.cs and IEnumConstraint.cs code files in your project. After the build task is applied to the project, the constraints are swapped out, and your other projects which reference this project will be able to see the constraints.
So its essentially useful for creating a common library project within your solution, and can include things like your own custom generic extension methods which are type constrained to System.Enum and System.Delegate.
https://code.google.com/p/unconstrained-melody/issues/detail?id=13
All credit to: j...#friesen.us
I've found this project to be useful but wanted to have the build-time steps in MSBuild. Adding this to your *.csproj files in which you use the constraints should accomplish the same thing as the Constraintchanger app. Note the following:
1. I've added the two constraint types to my classes root namespace and the substitution looks for the types in the assembly's root namespace.
2. To make Resharper happier I've added the IEnumConstraint, DelegateConstraint type args into my project inside an #if UNCONSTRAINED ... #endif block like so:
public static T Parse<T>(string val) where T : struct
#if UNCONSTRAINED
, IEnumConstraint
#endif
{
}
This is purely optional but keeps resharper from complaining about the constraint not matching when using the project's code from another project in a common solution.
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
SwapConstraints
</BuildDependsOn>
</PropertyGroup>
<ItemGroup>
<PreprocessorDefines Include="UNCONSTRAINED" />
</ItemGroup>
<UsingTask TaskName="FileReplace" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<FileName ParameterType="System.String" Required="true" />
<Source ParameterType="System.String" Required="true" />
<Replacement ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs"><![CDATA[
string content = File.ReadAllText(FileName);
content = content.Replace(Source, Replacement);
File.WriteAllText(FileName, content);
]]></Code>
</Task>
</UsingTask>
<Target Name="SwapConstraints">
<GetFrameworkPath>
<Output TaskParameter="Path" PropertyName="FW" />
</GetFrameworkPath>
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="SDK" />
</GetFrameworkSdkPath>
<PropertyGroup>
<ILDASM>"$(SDK)bin\NETFX 4.0 Tools\ildasm.exe"</ILDASM>
<ILASM>"$(FW)\ilasm.exe"</ILASM>
<IlFile>$(OutputPath)$(AssemblyName).il</IlFile>
<DllFile>$(OutputPath)$(AssemblyName).dll</DllFile>
</PropertyGroup>
<Exec Command="$(ILDASM) /OUT=$(IlFile) $(DllFile)" WorkingDirectory="$(ProjectDir)" />
<FileReplace FileName="$(IlFile)" Source="($(RootNamespace).DelegateConstraint)" Replacement="([mscorlib]System.Delegate)" />
<FileReplace FileName="$(IlFile)" Source="([mscorlib]System.ValueType, $(RootNamespace).IEnumConstraint)" Replacement="([mscorlib]System.Enum)" />
<FileReplace FileName="$(IlFile)" Source="($(RootNamespace).IEnumConstraint), [mscorlib]System.ValueType" Replacement="([mscorlib]System.Enum)" />
<FileReplace FileName="$(IlFile)" Source="($(RootNamespace).IEnumConstraint)" Replacement="([mscorlib]System.Enum)" />
<Exec Command="$(ILASM) /OUTPUT=$(DllFile) /DLL $(IlFile)" WorkingDirectory="$(ProjectDir)" />
</Target>
Nov 22, 2013
#1 j...#friesen.us
Sorry, didn't mean to say #if UNCONSTRAINED ... #endif should be around type args for DelegateConstraint. I've not messed with delegates to this point but I doubt that it would be necessary for them.
Related
I'm creating a .NET MAUI application using the current preview 11.
When I tried to implement Push notifications I added the Xamarin.Firebase.Messaging package and just adding this package causes a crash when starting the app with the exception:
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/datatransport/TransportFactory;
at com.google.firebase.datatransport.TransportRegistrar.getComponents(TransportRegistrar.java:32)
at com.google.firebase.components.ComponentRuntime.discoverComponents(ComponentRuntime.java:109)
at com.google.firebase.components.ComponentRuntime.(ComponentRuntime.java:91)
at com.google.firebase.components.ComponentRuntime.(ComponentRuntime.java:45)
at com.google.firebase.components.ComponentRuntime$Builder.build(ComponentRuntime.java:360)
at com.google.firebase.FirebaseApp.(FirebaseApp.java:427)
at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:299)
at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:267)
at com.google.firebase.FirebaseApp.initializeApp(FirebaseApp.java:252)
at com.google.firebase.provider.FirebaseInitProvider.onCreate(FirebaseInitProvider.java:51)
at android.content.ContentProvider.attachInfo(ContentProvider.java:2388)
at android.content.ContentProvider.attachInfo(ContentProvider.java:2358)
at com.google.firebase.provider.FirebaseInitProvider.attachInfo(FirebaseInitProvider.java:45)
at android.app.ActivityThread.installProvider(ActivityThread.java:7239)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:6780)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6697)
at android.app.ActivityThread.access$1300(ActivityThread.java:237)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1913)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.android.datatransport.TransportFactory" on path: DexPathList[[zip file "/data/app/.../base.apk"],nativeLibraryDirectories=[/data/app/.../lib/x86_64, /data/app/.../base.apk!/lib/x86_64, /system/lib64, /system_ext/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:207)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 24 more
During the build I get the following warnings:
Warning in obj\Debug\net6.0-android\lp\153\jl\classes.jar:com/google/firebase/datatransport/TransportRegistrar.class:
Type com.google.android.datatransport.runtime.TransportRuntime was not found, it is required for default or static interface methods desugaring of Lcom/google/firebase/datatransport/TransportRegistrar;lambda$getComponents$0(Lcom/google/firebase/components/ComponentContainer;)Lcom/google/android/datatransport/TransportFactory;
Via trail and error I found out this happens for all Xamarin.Firebase.* packages, but not when referencing e.g. Xamarin.Google.Android.DataTransport.TransportRuntime directly.
How can I fix this?
I found a workaround:
Basically the newest version of the firebase packages are broken. Using the following packages worked:
<ItemGroup>
<PackageReference Include="Xamarin.Build.Download" Version="0.11.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
<PackageReference Include="Xamarin.Google.Dagger" Version="2.39.1" />
<PackageReference Include="Xamarin.Firebase.Messaging" Version="122.0.0.2" />
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="117.6.0.2" />
</ItemGroup>
As soon as I use the versions ending with .3 instead of .2 for the last two its broken...
For an MSBuild project, I would like to output some kind of a .config file that would be redistributed along the generated binary so the parameters used at build time can be checked by the users of the binary, programmatically.
Output file format:
PropertyName1=ValueA
PropertyName2=ValueB
...
Ideally, the list of properties to write would contain just their names. Maybe like:
<ItemGroup>
<MyExposedDictionary Include="Configuration" />
<MyExposedDictionary Include="Platform" />
<MyExposedDictionary Include="PropertyName1" />
...
</ItemGroup>
With MyExposedDictionary being the argument to give to some DotConfigFileWriter task, as well as the path of the destination file.
I found several ways to write down values to a file, including a sub-target with some C# code in it, but I'm new to MSBuild and I'm not sure how I can merge those requirements into a single Target to make it re-usable.
In case someone comes here with the same requirement, here is what I ended up with. Not really happy with the result as I was hoping for something more generic but at least it does the job and blends well in my project:
<Target Name="WriteBuildProperties" BeforeTargets="PreBuildEvent">
<WriteLinesToFile File="$(DotConfigFile)" Overwrite="true" Lines="" />
<WriteLinesToFile File="$(DotConfigFile)" Lines="ProjectName=$(ProjectName)" />
<WriteLinesToFile File="$(DotConfigFile)" Lines="Configuration=$(Configuration)" />
...
</Target>
If someone happen to have a more elegant solution, please jump in!
I am not sure where your problem is located. I have a similar requirement that a file is created by the program which just was compiled. I edited the properties of the project: in the build events enter a Post-build action like
REM create special file
"$(ProjectDir)$(OutDir)MyProgram.exe" /WriteFile MyFile.xml
Of course, you must also change your program such that it does the right thing when called with that parameter (and stops after having completed that action - does not show a GUI or start as a Windows Service).
I have the following code contract:
public void F(string x)
{
Contract.Requires(!string.IsNullOrWhiteSpace(x));
throw new NotImplementedException();
}
When compiling, I get the following warning:
warning CC1036: Detected call to method 'System.String.IsNullOrWhiteSpace(System.String)' without [Pure] in contracts of method [...]
How to deal with it?
What's odd, is that I'm also using string.IsNullOrEmpty, which isn't marked as [Pure] as well, in other contracts and the rewriter does not have a problem with that.
My Contract Rewriter's Version is 1.9.10714.2.
This is the relevant part from the implementation of String class I'm using (retrieved from metadata):
#region Assembly mscorlib.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
#endregion
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
namespace System
{
// Summary:
// Represents text as a series of Unicode characters.To browse the .NET Framework
// source code for this type, see the Reference Source.
[Serializable]
[ComVisible(true)]
public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<string>, IEnumerable<char>, IEquatable<string>
{
// [...]
//
// Summary:
// [...]
public static bool IsNullOrEmpty(string value);
//
// Summary:
// [...]
public static bool IsNullOrWhiteSpace(string value);
Why is the [Pure] attribute missing?
Here we have two points:
1. Why is the [Pure] attribute missing in string class for IsNullorWhiteSpace function?
2. How to resolve the CC1030 warning issue?
I will try to discuss both.
1. Why is the [Pure] attribute missing? It's not missing, metadata does not seem to be showing this.
This may not be marked as Pure in previous version of .NET FX as, they were saying:
Yes, we need to make our checker sensitive to the disable pragma...
Sigh.
We currently don't have that implemented, but I've added it to our
work list.
Refer to the 5 year old discussion here.
But this has been marked as Pure in latest FX (4.6.1), Refer to .NET Framework 4.6.1, the new string class code.
[Pure]
public static bool IsNullOrWhiteSpace(String value) {
if (value == null) return true;
for(int i = 0; i < value.Length; i++) {
if(!Char.IsWhiteSpace(value[i])) return false;
}
return true;
}
Then Why CC1036?
This warning "CC1036" is from CodeContracts, developers have open this issue yesterday only (refer here).
Now why metadata is not spitting up Pure attributes, this is a different question, like for Equals method, Pure is added but only SecuritySafeCritical is displayed in metadata code.
[SecuritySafeCritical]
public static bool Equals(String a, String b, StringComparison comparisonType);
The same problem applies to Invariant(). Given the following code, the
same warnings are displayed:
private string testString = "test";
[ContractInvariantMethod]
private void TestInvariant()
{
Contract.Invariant(!string.IsNullOrWhiteSpace(testString));
}
How to resolve?
As others are also suggesting, create another method, mark it as Pure and call this in your contract condition.
Going through a pure delegate will make the warning go away. Predicate<T> is already marked pure, so you can just use that to work around the bug:
// Workaround for https://github.com/Microsoft/CodeContracts/issues/339
public Predicate<string> IsNullOrWhiteSpace = string.IsNullOrWhiteSpace;
public void F(string x)
{
Contract.Requires(!IsNullOrWhiteSpace(x));
throw new NotImplementedException();
}
Although kind of ugly, you can wrap the function string.IsNullOrWhiteSpace with an extension method and mark this new function as Pure.
I just encountered the exact same problem. I'm using VS2015, so it does not seem to be related to VS version. I also tested the exact same code on .NET 4.0, 4.5.1 and 4.6, without getting the warning.
Like others have commented before me, the IsNullOrWhiteSpace is marked as [Pure] in .NET 4.6.1, and additionally should by default be considered pure by Code Contracts because it is in the System.String namespace. This makes it look like a bug, so I have submitted an issue to Code Contracts about this, so with some luck we will see an official answer soon.
While we wait for an answer, it is possible (like #Jaco suggests) to wrap it in an extension method and mark it as Pure yourself. Optionally, you can suppress the warning for that particular method like this:
[SuppressMessage("Microsoft.Contracts", "CC1036", Justification = "string.IsNullOrWhiteSpace is Pure")]
... but note that this will also suppress this warning from other Contract definitions in the same method.
Actually, this is a problem with the way .NET 4.6+ is compiled. See this GitHub pull request.
I was able to work around this by modifying the following file(s):
For Visual Studio 2013:
C:\Program Files (x86)\Microsoft\Contracts\MsBuild\v12.0\Microsoft.CodeContracts.Targets
For Visual Studio 2015:
C:\Progarm Files (x86)\Microsoft\Contracts\MsBuild\v14.0\Microsoft.CodeContracts.Targets
In both files, ensure the <Otherwise> child element of the first <Choose> element has the following content shown below:
...
<Choose>
<When Condition="'$(TargetFrameworkIdentifier)' == 'Silverlight'">
...
</When>
<Otherwise>
<Choose>
<When Condition="'$(TargetFrameworkVersion)' == 'v4.0">
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.0</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</When>
<When Condition="'$(TargetFrameworkVersion)' == 'v4.5'">
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</When>
<When Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'">
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</When>
<When Condition="'$(TargetFrameworkVersion)' == 'v4.5.2'">
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</When>
<When Condition="'$(TargetFrameworkVersion)' == 'v4.6'">
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</When>
<When Condition="'$(TargetFrameworkVersion)' == 'v4.6.1'">
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v4.5</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<CodeContractsReferenceAssemblyLibPath>$(CodeContractsInstallDir)Contracts\.NETFramework\v3.5</CodeContractsReferenceAssemblyLibPath>
</PropertyGroup>
</Otherwise>
</Choose>
</Otherwise>
</Chose>
...
After making these changes to these files (per the GitHub pull request referenced above), I no longer received Code Contracts static analysis warnings for the use of String.IsNullOrWhiteSpace.
It should be noted that the referenced pull request has been merged into the main code for Code Contracts up on GitHub; they just haven't made a new release containing these changes yet.
Also, for those concerned about changing "system files", don't be. When the next version of Code Contracts is released, it will install updated versions of these files--and hopefully the changes will be included, and all will be right with the world. (Unless, of course, the changes aren't included--in which case, you'll be coming back here to reference this post to make those changes again ;) lol.)
I would like to do the following :
(project is a User Control library for WPF)
add a bunch of .FX (shader source code) files to the project as resources (Build action)
transform each to a .PS file (compiled shader) by invoking FXC.EXE utility
use the resulting file in place of the inputted file
I have been looking to write a CustomTool, unfortunately the tool is never seen by Visual Studio as it's mentioned in the article. In the article it is said that sometimes it is not seen but in my case it translates to every time.
I also looked at MSBuild Transforms but I'm not really sure if it would be appropriate for the task.
The goal of this is to include shader files source code and transform them at build time instead of manually building them from command line and dropping them every time to the project.
Do you know how one can achieve this ? Any methods are welcome
EDIT
Answer thanks to #Luaan :
public class CompileEffectTask : Task
{
public string[] Files { get; set; }
public override bool Execute()
{
if (Files != null)
{
foreach (string file in Files)
{
if (file != null)
{
Log.LogMessage(MessageImportance.High, file);
string s = #"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\fxc.exe";
string changeExtension = Path.ChangeExtension(file, "ps");
string arguments = string.Format("/T ps_3_0 /Fo \"{0}\"" + " " + "\"{1}\"", changeExtension,
file);
Log.LogMessage(MessageImportance.High, arguments);
var process = new Process
{
StartInfo = new ProcessStartInfo(s, arguments)
};
process.Start();
process.WaitForExit();
}
}
}
return true;
}
}
And the MSBuild part :
<UsingTask TaskName="CompileEffectTask" AssemblyFile="D:\HLSLCompiler.dll" />
<PropertyGroup>
<BuildDependsOn>
MyCustomTarget1;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTarget1">
<Message Text="CompileEffectTask started" Importance="high" />
<Message Text="Compiling FX files ..." Importance="high" />
<CompileEffectTask Files="#(CompileEffectTask)"/>
<Message Text="Adding resulting .PS files as resources ..." Importance="high" />
<ItemGroup>
<Resource Include="**\*.ps" />
</ItemGroup>
</Target>
<Target Name="AfterBuild">
<CreateItem Include="**\*.ps">
<Output TaskParameter="Include" ItemName="DeleteAfterBuild" />
</CreateItem>
<Delete Files="#(DeleteAfterBuild)" />
</Target>
(still needs some cleaning but it works :D)
Custom tools do work, in fact, but they're rather tricky to setup - they're COM extensions to Visual Studio. However, the better solution for your case would be a custom build target or a pre-build event anyway - custom tools (code generators) are better suited for generating code (text) rather than binary files.
So, the pre-build event is the simple one. It's just some script that's run before the project starts building. You can find it in project properties. The simplest way would be to have all your .fx files in one directory, and in the pre-build event, you'd just call fxc.exe on each of them.
Now, build targets are cooler. They allow you to add your own build actions to files, among other things. So you'd just select CompileEffect in Build action of your files, and magic happens.
The target file can be quite simple:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AvailableItemName Include="CompileEffect"></AvailableItemName>
</ItemGroup>
</Project>
Or you can just put the ItemGroup part inside of your project file directly (otherwise you'd want to include this target file).
Next, you want to set the task as part of your build:
<PropertyGroup>
<BuildDependsOn>
MyCompileTarget;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
This basically says "run my build target first, and after that whatever you'd want".
Now, for the building:
<Target Name="MyCompileTarget">
<CompileEffectTask
ProjectDirectory="$(ProjectDir)"
Files="#(CompileEffect)"
RootNamespace="$(RootNamespace)">
</CompileEffectTaskTask>
</Target>
How does Visual Studio know what CompileEffectTask is?
<UsingTask TaskName="MyAssembly.CompileEffectTask"
AssemblyFile="C:\MyAssembly.dll"/>
And then you just need to implement the compiler task itself.
Now, if you only want to call an executable or a batch script, you don't even need that custom task, because there's a lot of built-in tasks in MSBuild (and even more in MSBuild Community Tasks). Exec task should work:
<Target Name="MyCompileTarget">
<Exec Command="fxc.exe #(CompileEffect)" />
</Target>
You might have to write a for cycle there, I'm not entirely sure. There's a lot of things you can do to customize project builds, http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx (especially the Task refecence part) is a rather good start.
I thought this would be quite simple but then realised that I couldnt find any information on it anywhere.
I have a custom task like so:
public class MyCustomTask : Task
{
[Required]
public string[] SomeStrings {get;set;}
public override bool Execute()
{
// Do something with strings...
}
}
The matching MSBuild stuff is basically like so:
<UsingTask TaskName="MyCustomTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<SomeStrings ParameterType="System.String[]" Required="true" />
</ParameterGroup>
<Task>
...
</Task>
</UsingTask>
<Target Name="DoSomething">
<MyCustomTask SomeStrings="????" />
</Target>
Dont have any idea of what to put in the SomeStrings parameter, thought maybe it would understand if I did "xxx,xxx,xxx" so can anyone shed any light on this. The basic scenario is alot like tokenizing so I require a list of strings then some comparison strings so I need to pass in 2 lists/arrays, but just stumped.
#BrianKretzler is exactly dead on in using ITaskItem, since it's what MSBuild uses when you declare an <ItemGroup>.
I just wanted to flush out the answer with a full working example, since I found this post while I was trying to accomplish the same thing and it helped me out. (It's very hard to search for these problems, because the keywords are used in different contexts, so hopefully this will help someone else).
<UsingTask TaskName="MyCustomTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<SomeStrings ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Class" Language="cs"><![CDATA[
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class MyCustomTask : Task
{
public ITaskItem[] SomeStrings { get; set; }
public override bool Execute()
{
foreach (var item in SomeStrings)
{
Log.LogMessage(MessageImportance.High,
"Got item {0}",
item.ItemSpec);
Log.LogMessage(" -> {0} -> {1}",
item.GetMetadata("Comparison"),
item.GetMetadata("MoreDetail"));
}
return true;
}
}
]]></Code>
</Task>
</UsingTask>
Now you can call this task with:
<Target Name="DoSomething">
<ItemGroup>
<SomeStrings Include="first string">
<Comparison>first</Comparison>
</SomeStrings>
<SomeStrings Include="second string">
<Comparison>2nd</Comparison>
<MoreDetail>this is optional</MoreDetail>
</SomeStrings>
</ItemGroup>
<MyCustomTask SomeStrings="#(SomeStrings)" />
</Target>
and the output is:
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.269]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 2012-10-19 5:41:22 PM.
Got first string
-> first ->
Got second string
-> 2nd -> this is optional
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.12
You can of course also use something like <ItemGroup><SomeStrings Include="**\*.txt" /></ItemGroup> and you'll get the list of filenames that are matched, and of course you can use GetMetadata() to access the well-known file metadata
It isn't quite clear what you are trying to do; you have the C# code for a custom task, but also the MSBuild code for the same task as an inline task -- you do realize you only need to do one of those, correct? If you are trying to create a task in an assembly, the <UsingTask> in your MSBuild should be an empty element, without the <ParameterGroup> and <Task> children. If you are trying to use an inline task, you don't need the C# code and need to specify your own assembly as the AssemblyFile, and not specify the TaskFactory as you have.
I'd declare the parameter as type ITaskItem[], so you can then pass in the value(s) as,
<MyCustomTask SomeStrings="#(SomeStrings)" />
You could set up the comparison strings as a second item array in a second parameter, or as metadata on the first parameter, e.g.
<ItemGroup>
<SomeStrings Include="first string">
<Comparison>first</Comparison>
</SomeStrings>
<SomeStrings Include="second string">
<Comparison>2nd</Comparison>
</SomeStrings>
</ItemGroup>
If you are using inline code, you'll need to <Reference> the proper MSBuild assemblies and fully qualify the ParameterType. Get it working in a compiled assembly first even if your eventual intent is to use inline code.
Since this is currently the first hit on Google, here's the other way of doing it (as alluded to by #alastair-maw's comment) as answered in another SO thread:
MSBuild tasks can accept ITaskItem, primitives, string or an array of any of those for parameters. You declare the type in your task and then the values will be converted before passed to the task. If the value cannot convert to the type, then an exception will be raised and the build will be stopped.
For example, if you have a task which accepts an int[] named Values then you could do:
<Target Name="MyTarget">
<MyTask Values="1;45;657" />
<!-- or you can do -->
<ItemGroup>
<SomeValues Include="7;54;568;432;79" />
</ItemGroup>
<MyTask Values="#(SomeValues) />
</Target>