DynamicExecute Class

MSBuild Extension Pack

DynamicExecute Class MSBuild Extension Pack Help 4.0.12.0
Valid TaskActions are:

Define (Required: Code Optional: Inputs, Outputs, References, UsingNamespaces, NoDefaultParameters, NoDefaultReferences, NoDefaultUsingNamespaces Output: OutputMethodId). Defines and compiles a new method, which can then be used to create a closure.

Create (Required: MethodId Output: OutputClosureId). Creates a new closure. All input and output arguments for this closure are set to their default values.

SetInput (Required: ClosureId, Name, InputValue). Sets an argument value for a closure.

Invoke (Required: ClosureId). Invokes a closure.

GetOutput (Required: ClosureId, Name Output: OutputValue). Retrieves a result value (output parameter value) from a closure.

Destroy (Required: ClosureId). Disposes of a closure. The closure ID is no longer valid after this task action.

Call (Required: MethodId Optional: Input1, Input2, Input3 Output: Output1, Output2, Output3). Calls a method with up to three inputs, returning up to three outputs. Internally, creates a closure, sets the input parameters, invokes it, retrieves the output parameters, and destroys it.

Run (Required: Code Optional: Inputs, Outputs, References, UsingNamespaces, NoDefaultParameters, NoDefaultReferences, NoDefaultUsingNamespaces, Input1, Input2, Input3 Output: Output1, Output2, Output3, OutputMethodId). Defines a method and runs it. The task outputs include the outputs from the method as well as the method identifier.

Remote Execution Support: None.

Inheritance Hierarchy

SystemObject  Microsoft.Build.UtilitiesTask
    MSBuild.ExtensionPackBaseTask
      MSBuild.ExtensionPack.FrameworkDynamicExecute

Namespace: MSBuild.ExtensionPack.Framework
Assembly: MSBuild.ExtensionPack (in MSBuild.ExtensionPack.dll) Version: 4.0.0.0

The DynamicExecute type exposes the following members.

Constructors

  NameDescription
Public methodDynamicExecute
Top
Properties

  NameDescription
Public propertyClosureId
The identifier of the closure instance.
Public propertyCode
The actual method code for dynamic execution.
Public propertyInput1
The value for the first input parameter.
Public propertyInput2
The value for the second input parameter.
Public propertyInput3
The value for the third input parameter.
Public propertyInputs
Specifies the inputs for Code. Each input has a type and a name.
Public propertyInputValue
The value to set.
Public propertyMethodId
The identifier of the method definition.
Public propertyName
The name of the input argument to set, or the output argument to retrieve.
Public propertyNoDefaultParameters
Specifies to not define the default parameters for Code.
Public propertyNoDefaultReferences
Specifies to not include the default references for Code.
Public propertyNoDefaultUsingNamespaces
Specifies to not include the default "using namespaces" for Code.
Public propertyOutput1
The value of the first closure output.
Public propertyOutput2
The value of the second closure output.
Public propertyOutput3
The value of the third closure output.
Public propertyOutputClosureId
The ID of a closure instance.
Public propertyOutputMethodId
The ID of a defined method.
Public propertyOutputs
Specifies the outputs for Code. Each output has a type and a name.
Public propertyOutputValue
The value of a closure output.
Public propertyReferences
Specifies additional references for Code.
Public propertyUsingNamespaces
Specifies additional "using namespaces" for Code. These are namespaces that are brought into the code's scope.
Top
Remarks

The DynamicExecute task allows defining and executing code at build time. The code is not interpreted; rather, it is compiled and then loaded into the MSBuild process.

Currently, the only supported language is C#.

Code, Methods, and Closures

"Code" is the actual source code to be executed. Code may be executed directly by the Run task action, or code may be used to define a method by the Define task action.

A "method" is a piece of defined code. Methods are compiled and loaded into the MSBuild process dynamically. A method may be executed by the Call task action, or used to create a closure by the Create task action.

A "closure" is a reference to a method along with values for all the method's input and output parameters.

Using Closures

A closure contains values for all inputs and outputs of a particular method. Generally, a closure is created, its input values are set, it is invoked, its output values are retrieved, and finally the closure is destroyed.

When a closure is created, all input and output values are set to their default values. It is possible to call a method without specifying input values; in this case, the default values are used. It is also possible to re-use a closure instead of destroying it; however, this may cause confusion since the output values are not reset before invoking the method again.

Most of the time, the Call or Run task actions are used. These create a closure to do their work, destroying it when they are done. These task actions are much more compact than using the closure-based task actions such as SetInput and GetOutput.

However, Call and Run do have limitations. Closures allow any number of input and output values, instead of just three. Also, input and output values are set and retrieved by name when using closures directly; Run and Call can only set and retrieve by position.

Code Context

The actual code is compiled into a static method of a class. Enclosing Code in curly braces is not necessary.

There are three types of parameters passed to the method: default parameters, input parameters, and output parameters.

Currently, there is only one default parameter, named "@this". Its type is Microsoft.Build.Utilities.Task, and it may be used to access task-level properties such as Log and BuildEngine2. Default parameters may be disabled by specifying NoDefaultParameters.

<Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
        <TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
    </PropertyGroup>
    <Import Project="$(TPath)"/>
    <Target Name="Default">
        <!-- A very simple example, showing the default parameter (currently, there is only one) -->
        <!-- Output: Hi! -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Code="@this.Log.LogMessage(%22Hi!%22);"
                                                        />
    </Target>
</Project>

Input parameters are strongly-typed, as defined by Inputs.

Output parameters are likewise strongly-typed, as defined by Outputs. They are compiled as ref parameters, so all method outputs are optional by definition.

The compiled method returns void, so code containing a simple return will compile, but any code attempting to return a value will not. All method outputs must be assigned to an output parameter before returning.

Assembly references and using namespaces may be augmented or replaced; see Advanced Code Options below for more details.

Specifying Inputs and Outputs

Each input or output has a type and a name. The name must be a legal C# parameter name, and cannot start with "_" or be equal to "@this". Input and output names must be unique; an input cannot have the same name as an output.

If NoDefaultParameters is specified, then input and output names may start with "_" or be equal to "@this".

Type and name pairs may be specified one of three ways. The first (and most compact) way is to pass a comma-delimited string of type and name pairs. This is the most familiar syntax to C#.

The second way is to pass an array of task items, with the identity of each item set to its type and name separated by at least one space.

The third way is to pass an array of task items, with the type looked up from the item's "Type" metadata. The name may be specified by the item's "Name" metadata or its identity.

<Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
        <TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
    </PropertyGroup>
    <Import Project="$(TPath)"/>
    <Target Name="Default">
        <!-- These are equivalent -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="string format"
                                                        Outputs="string result"
                                                        Input1="yyyy-MM-dd"
                                                        Code="result = DateTime.Now.ToString(format);"
                                                        >
            <Output TaskParameter="Output1" PropertyName="FormattedDate"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <Message Text="Formatted date: $(FormattedDate)"/>
        <ItemGroup>
            <FormatInputs Include="string format"/>
            <FormatOutputs Include="result">
                <Type>string</Type>
            </FormatOutputs>
        </ItemGroup>
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="@(FormatInputs)"
                                                        Outputs="@(FormatOutputs)"
                                                        Input1="yyyy-MM-dd"
                                                        Code="result = DateTime.Now.ToString(format);"
                                                        >
            <Output TaskParameter="Output1" PropertyName="FormattedDate"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <Message Text="Formatted date: $(FormattedDate)"/>
    </Target>
</Project>

Supported Input and Output Types

Input and output types must be one of the following:

Group A - ITaskItem[] or ITaskItem. This is the MSBuild task item group / task item, which may be used to access metadata.

Group B - The CLR types convertible to and from string. This includes the types string, char, bool, byte, sbyte, short, ushort, int, uint, long, ulong, float, double, Decimal, and DateTime.

Group C - Any nullable type whose underlying type is in Group B. This includes the types char?, bool?, byte?, sbyte?, short?, ushort?, int?, uint?, long?, ulong?, float?, double?, Decimal?, and DateTime?.

Group D - An array of any type from Group B. Each element of the array must contain a value; it is not valid to pass or return arrays if one of the elements in the array is null.

Invalid input and output types are not detected at compile time. They are only detected if a value fails to convert to or from the specified type.

Conversion of Input Parameters

An input argument value passes through two conversions. The first is the default MSBuild conversion, and the second is performed by the SetInput, Call, or Run task action.

The MSBuild conversion always converts to ITaskItem[], because this is the type of the InputValue, Input1, Input2, and Input3 properties.

Once the input value has been converted to an array of task items, the DynamicExecute task action performs a second conversion. This is designed to work similarly to the MSBuild conversions and default C# conversions to prevent unexpected behavior. The exact steps taken are dependent on the group the input type belongs to; see Supported Types above for more information about the type grouping.

Group A - No actual conversion is performed. If the method expects a single ITaskItem, then the task action ensures that the input value contains only a single task item.

Groups B and C - The task ensures that the input value contains only a single task item. Then, the task item's ItemSpec is used as a string value, and this string is converted to the expected type.

Group D - Each task item's ItemSpec is used as a string value, and this string is converted to the expected type. The result is an array with the same number of elements as the array of task items.

If an input argument value is null, then no conversions are performed; the method is passed a null value.

Special conversions exist if the input parameter is of bool type. Valid values include "true", "false", "yes", "no", "on", and "off", all case-insensitive. In addition, these values may be prefixed with the logical "not" operator ("!"). These conversions are supported because they are MSBuild conventions.

Conversion of Strings

String input parameters may cause problems if the argument value contains semicolons. In this case, the default MSBuild conversion will split the string into an array of ITaskItem, using the semicolons as separators.

To prevent this behavior, one may first escape the string by using the TextStringReplace task action, as this example illustrates:

<Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
        <TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
    </PropertyGroup>
    <Import Project="$(TPath)"/>
    <Target Name="Default">
        <PropertyGroup>
            <String>This semicolon does not separate items; it is used in a grammatical sense.</String>
        </PropertyGroup>
        <!-- Semicolons normally act as item separators; to prevent this treatment, escape them first -->
        <MSBuild.ExtensionPack.Framework.TextString TaskAction="Replace"
                                                    OldString="$(String)"
                                                    OldValue=";"
                                                    NewValue="%3B"
                                                    >
            <Output TaskParameter="NewString" PropertyName="EscapedString"/>
        </MSBuild.ExtensionPack.Framework.TextString>
        <!-- $(String) would be treated as a vector argument (2 elements), but $(EscapedString) is a scalar argument -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="string test"
                                                        Outputs="string result"
                                                        Code="result = test;"
                                                        Input1="$(EscapedString)"
                                                        >
            <Output TaskParameter="Output1" PropertyName="Result"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Converting the result to an item group shows that the semicolon is still not used as a separator -->
        <ItemGroup>
            <!-- Only one item will exist in this item group -->
            <ResultItemGroup Include="$(Result)"/>
        </ItemGroup>
        <!-- Result:  This semicolon does not separate items; it is used in a grammatical sense.  -->
        <Message Text="Result: @(ResultItemGroup->' %(Identity) ')"/>
    </Target>
</Project>

Conversion of Output Parameters

An output argument value passes through two conversions. The first is performed by the GetOutput, Call, or Run task action. The second is the default MSBuild conversion.

The DynamicExecute task action performs the first conversion. This is always a conversion to ITaskItem[], because that is the type of the OutputValue, Output1, Output2, and Output3 properties.

This conversion is designed to work similarly to the MSBuild conversions and default C# conversions to prevent unexpected behavior. The exact steps taken are dependent on the group the output type belongs to; see Supported Types above for more information about the type grouping.

Group A - The actual objects returned must be of type TaskItem or TaskItem[] (returning an instance of another type implementing ITaskItem is not supported). No actual conversion is performed. If the method produces a single TaskItem, then the task action creates an array of task items containing only the single element.

Groups B and C - An array of task items is returned containing a single element. The ItemSpec of that single element is the output value converted to a string. Note that null values are treated specially (see below).

Group D - An array of task items is returned, with the same number of items as the output array. For each corresponding array item, the ItemSpec of the task item array element is set to the string representation of the output array element.

If an output argument value is null, then no conversions are performed by the task action. MSBuild will convert a null value to an empty string or empty item group if necessary.

The default MSBuild conversion will convert from ITaskItem[] to an item group or string as necessary.

Advanced Code Options

When a method is compiled, it is given some assembly references by default. See References for a list of the default references. Specify NoDefaultReferences to prevent default references from being used.

The method is also given some using namespace declarations by default. See UsingNamespaces for a list of the default "using namespaces". Specify NoDefaultUsingNamespaces to prevent default "using namespaces" from being used.

Finally, the method is given some default parameters. Currently, the only default parameter is "@this", but all parameter names beginning with an underscore ("_") are reserved for future default parameters. Specify NoDefaultParameters to prevent default parameters from being used.

Limitations

One defined method may not call another defined method.

There is no facility for a method storing data in a way that it could be retrieved by a future call of the method (or another method). A workaround is to convert any such data to a string representation and pass that as an input and / or output parameter.

Examples

<Project ToolsVersion="4.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <TPath>$(MSBuildProjectDirectory)\..\MSBuild.ExtensionPack.tasks</TPath>
        <TPath Condition="Exists('$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks')">$(MSBuildProjectDirectory)\..\..\Common\MSBuild.ExtensionPack.tasks</TPath>
    </PropertyGroup>
    <Import Project="$(TPath)"/>
    <Target Name="Default">
        <!-- A very simple example, using a default parameter -->
        <!-- Output: Hi! -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Code="@this.Log.LogMessage(%22Hi!%22);"
                                                        />
        <!-- An example that takes a string argument and returns a string result -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="string format"
                                                        Outputs="string result"
                                                        Input1="yyyy-MM-dd"
                                                        Code="result = DateTime.Now.ToString(format);"
                                                        >
            <Output TaskParameter="Output1" PropertyName="FormattedDate"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output (varies by time): 2009-06-10 -->
        <Message Text="Formatted date: $(FormattedDate)"/>
        <!-- An example that shows the more advanced conversions available for boolean arguments -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="bool test"
                                                        Outputs="bool result"
                                                        Input1="!no"
                                                        Code="result = test;"
                                                        >
            <Output TaskParameter="Output1" PropertyName="ConversionTestResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: Converts '!no' to: True -->
        <Message Text="Converts '!no' to: $(ConversionTestResult)"/>
        <!-- Take two array arguments and return an array -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="string[] first, string[] second"
                                                        Outputs="string[] result"
                                                        Input1="1;2;3"
                                                        Input2="10;10;10"
                                                        Code="result = new string[first.Length];   for (int i = 0; i != first.Length; ++i)   result[i] = first[i] + second[i];"
                                                        >
            <Output TaskParameter="Output1" PropertyName="ArrayTestResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: Array test result: 110;210;310 -->
        <Message Text="Array test result: $(ArrayTestResult)"/>
        <!-- Take two array arguments of non-string type and return an array -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="int[] first, int[] second"
                                                        Outputs="int[] result"
                                                        Input1="1;2;3"
                                                        Input2="10;10;10"
                                                        Code="result = new int[first.Length];   for (int i = 0; i != first.Length; ++i)   result[i] = first[i] + second[i];"
                                                        >
            <Output TaskParameter="Output1" PropertyName="ArrayTestResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: Array test result: 11;12;13 -->
        <Message Text="Array test result: $(ArrayTestResult)"/>
        <!-- A much more complex example: defining a more reusable DynamicTask, that performs a cross product on item groups -->
        <PropertyGroup>
            <CrossProductCode>
                &lt;![CDATA[
                    if (string.IsNullOrEmpty(separator))
                        separator = ";";
                    result = new TaskItem[itemGroup1.Length * itemGroup2.Length];
                    int i = 0;
                    foreach (ITaskItem item1 in itemGroup1)
                    {
                        foreach (ITaskItem item2 in itemGroup2)
                        {
                            // Determine metadata
                            Dictionary<string, stringmetadata = newDictionary&lt;stringstring>();
                            // Copy all metadata from the first item
                            if (string.IsNullOrEmpty(prefix1))
                            {
                                foreach (string name in item1.MetadataNames)
                                    metadata.Add(name, item1.GetMetadata(name));
                            }
                            else
                            {
                                foreach (string name in item1.MetadataNames)
                                    metadata.Add(prefix1 + name, item1.GetMetadata(name));
                            }
                            // Copy all metadata from the second item
                            if (string.IsNullOrEmpty(prefix2))
                            {
                                foreach (string name in item2.MetadataNames)
                                    if (!metadata.ContainsKey(name))
                                        metadata.Add(name, item2.GetMetadata(name));
                            }
                            else
                            {
                                foreach (string name in item2.MetadataNames)
                                    if (!metadata.ContainsKey(prefix2 + name))
                                        metadata.Add(prefix2 + name, item2.GetMetadata(name));
                            }
                            // Create an output item with a (hopefully unique) itemspec.
                            result[i++] = new TaskItem(item1.ItemSpec + separator + item2.ItemSpec, metadata);
                        }
                    }
                ]]&gt;
            </CrossProductCode>
        </PropertyGroup>
        <ItemGroup>
            <!-- Parameters and results: these are used in the method definition -->
            <CrossProductParameters Include="itemGroup1;itemGroup2">
                <Type>ITaskItem[]</Type>
            </CrossProductParameters>
            <CrossProductParameters Include="separator;prefix1;prefix2">
                <Type>string</Type>
            </CrossProductParameters>
            <CrossProductResults Include="result">
                <Type>ITaskItem[]</Type>
            </CrossProductResults>
            <!-- Arguments: these are used by the closure -->
            <CrossProductArguments1 Include="x;y;z">
                <M1>Meta1</M1>
            </CrossProductArguments1>
            <CrossProductArguments2 Include="1;2">
                <M2>Meta2</M2>
            </CrossProductArguments2>
            <CrossProductArguments2 Include="3">
                <M1>Meta1 that is overwritten</M1>
                <M2>A different Meta2</M2>
            </CrossProductArguments2>
        </ItemGroup>
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="@(CrossProductParameters)"
                                                        Outputs="@(CrossProductResults)"
                                                        Input1="@(CrossProductArguments1)"
                                                        Input2="@(CrossProductArguments2)"
                                                        Code="$(CrossProductCode)"
                                                        >
            <Output TaskParameter="Output1" ItemName="CrossProductResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: Cross product:  x;1 { M1=Meta1, M2=Meta2 } ; x;2 { M1=Meta1, M2=Meta2 } ; x;3 { M1=Meta1, M2=A different Meta2 } ; y;1 { M1=Meta1, M2=Meta2 } ; y;2 { M1=Meta1, M2=Meta2 } ; y;3 { M1=Meta1, M2=A different Meta2 } ; z;1 { M1=Meta1, M2=Meta2 } ; z;2 { M1=Meta1, M2=Meta2 } ; z;3 { M1=Meta1, M2=A different Meta2 } -->
        <Message Text="Cross product: @(CrossProductResult->' %(Identity) { M1=%(M1), M2=%(M2) } ')"/>
        <!-- The "Run" and "Call" TaskActions are limited to 3 inputs and 3 outputs (currently), but by separating out each step,
                any number of inputs and outputs may be specified, and they may be specified by name -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Define"
                                                        Inputs="@(CrossProductParameters)"
                                                        Outputs="@(CrossProductResults)"
                                                        Code="$(CrossProductCode)"
                                                        >
            <Output TaskParameter="OutputMethodId" PropertyName="CrossProductMethodId"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Create"
                                                        MethodId="$(CrossProductMethodId)"
                                                        >
            <Output TaskParameter="OutputClosureId" PropertyName="CrossProductClosureId"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="SetInput"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        Name="itemGroup1"
                                                        InputValue="@(CrossProductArguments1)"
                                                        />
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="SetInput"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        Name="itemGroup2"
                                                        InputValue="@(CrossProductArguments2)"
                                                        />
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="SetInput"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        Name="prefix1"
                                                        InputValue="P1_"
                                                        />
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="SetInput"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        Name="prefix2"
                                                        InputValue="P2_"
                                                        />
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="SetInput"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        Name="separator"
                                                        InputValue="."
                                                        />
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Invoke"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        />
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="GetOutput"
                                                        ClosureId="$(CrossProductClosureId)"
                                                        Name="result"
                                                        >
            <Output TaskParameter="OutputValue" ItemName="ComplexCrossProductResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: Cross product with more parameters:  x.1 { P1_M1=Meta1, P2_M1=, P2_M2=Meta2 } ; x.2 { P1_M1=Meta1, P2_M1=, P2_M2=Meta2 } ; x.3 { P1_M1=Meta1, P2_M1=Meta1 that is overwritten, P2_M2=A different Meta2 } ; y.1 { P1_M1=Meta1, P2_M1=, P2_M2=Meta2 } ; y.2 { P1_M1=Meta1, P2_M1=, P2_M2=Meta2 } ; y.3 { P1_M1=Meta1, P2_M1=Meta1 that is overwritten, P2_M2=A different Meta2 } ; z.1 { P1_M1=Meta1, P2_M1=, P2_M2=Meta2 } ; z.2 { P1_M1=Meta1, P2_M1=, P2_M2=Meta2 } ; z.3 { P1_M1=Meta1, P2_M1=Meta1 that is overwritten, P2_M2=A different Meta2 } -->
        <Message Text="Cross product with more parameters: @(ComplexCrossProductResult->' %(Identity) { P1_M1=%(P1_M1), P2_M1=%(P2_M1), P2_M2=%(P2_M2) } ')"/>
        <!-- Testing nullable parameter values -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="int? arg"
                                                        Outputs="int? result"
                                                        Input1="33"
                                                        Code="result = arg + 3;"
                                                        >
            <Output TaskParameter="Output1" PropertyName="DefaultResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: 33 + 3: 36 -->
        <Message Text="33 + 3: $(DefaultResult)"/>
        <!-- Testing nullable parameter values with null argument (the output value is actually null in this case, which MSBuild converts to an empty string) -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="int? arg"
                                                        Outputs="int? result"
                                                        Code="result = arg + 3;"
                                                        >
            <Output TaskParameter="Output1" PropertyName="DefaultResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: default(int?) + 3: -->
        <Message Text="default(int?) + 3: $(DefaultResult)"/>
        <!-- Testing parameter values with default argument -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Inputs="int arg"
                                                        Outputs="int? result"
                                                        Code="result = arg + 3;"
                                                        >
            <Output TaskParameter="Output1" PropertyName="DefaultResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: default(int) + 3: 3 -->
        <Message Text="default(int) + 3: $(DefaultResult)"/>
        <!-- Defining a method once and calling it multiple times (this is more resource-efficient than always using Run) -->
        <!--   (the GUID-testing regex was taken from the Regular Expression Library, http://regexlib.com/ ) -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Define"
                                                        Inputs="string arg"
                                                        Outputs="bool result"
                                                        UsingNamespaces="System.Text.RegularExpressions"
                                                        Code="result = Regex.IsMatch(arg, @%22[{|\(]?[0-9a-fA-F]{8}[-]?([0-9a-fA-F]{4}[-]?){3}[0-9a-fA-F]{12}[\)|}]?%22);"
                                                        >
            <Output TaskParameter="OutputMethodId" PropertyName="IsGuidMethod"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- The first call -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Call"
                                                        MethodId="$(IsGuidMethod)"
                                                        Input1="{914D226A-2F5B-4944-934D-96BBE6571977}"
                                                        >
            <Output TaskParameter="Output1" PropertyName="IsGuidResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: IsGuid({914D226A-2F5B-4944-934D-96BBE6571977}): True -->
        <Message Text="IsGuid({914D226A-2F5B-4944-934D-96BBE6571977}): $(IsGuidResult)"/>
        <!-- The second call; recompiling the method is unnecessary for this call -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Call"
                                                        MethodId="$(IsGuidMethod)"
                                                        Input1="{X14D226A-2F5B-4944-934D-96BBE6571977}"
                                                        >
            <Output TaskParameter="Output1" PropertyName="IsGuidResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: IsGuid({X14D226A-2F5B-4944-934D-96BBE6571977}): False -->
        <Message Text="IsGuid({X14D226A-2F5B-4944-934D-96BBE6571977}): $(IsGuidResult)"/>
        <!-- Using a parameter to define part of the code for a Run task action. -->
        <PropertyGroup>
            <MathArgument>42 - 37</MathArgument>
        </PropertyGroup>
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        Outputs="int result"
                                                        Code="result = 2 * ($(MathArgument));"
                                                        >
            <Output TaskParameter="Output1" PropertyName="MathResult"/>
        </MSBuild.ExtensionPack.Framework.DynamicExecute>
        <!-- Output: 2 * (42 - 37) = 10 -->
        <Message Text="2 * (42 - 37) = $(MathResult)"/>
        <!-- Other assemblies may also be referenced, along with optional "using namespace" declarations -->
        <!-- Output: {"Hi from Windows Forms!" in a MessageBox} -->
        <MSBuild.ExtensionPack.Framework.DynamicExecute TaskAction="Run"
                                                        References="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                                                        UsingNamespaces="System.Windows;System.Windows.Forms"
                                                        Code="MessageBox.Show(%22Hi from Windows Forms!%22);"
                                                        />
    </Target>
</Project>
See Also

Reference