Udostępnij za pośrednictwem


MSBuild Transforms

A transform is a one-to-one conversion of one item collection to another. In addition to enabling a project to convert item collections, a transform enables a target to identify a direct mapping between its inputs and outputs. This topic explains transforms and how MSBuild uses them to build projects more efficiently.

Transform Modifiers

Transforms are not arbitrary, but are limited by special syntax in which all transform modifiers must be in the format %(ItemMetaDataName). Any item metadata can be used as a transform modifier. This includes the well-known item metadata that is assigned to every item when it is created. For a list of well-known item metadata, see MSBuild Well-known Item Metadata.

In the following example, a list of .resx files is transformed into a list of .resources files. The %(filename) transform modifier specifies that each .resources file has the same file name as the corresponding .resx file.

@(RESXFile->'%(filename).resources')

Note

You can specify a custom separator for a transformed item collection in the same way you specify a separator for a standard item collection. For example, to separate a transformed item collection by using a comma (,) instead of the default semicolon (;), use the following XML.

@(RESXFile->'Toolset\%(filename)%(extension)', ',')

For example, if the items in the @(RESXFile) item collection are Form1.resx, Form2.resx, and Form3.resx, the outputs in the transformed list will be Form1.resources, Form2.resources, and Form3.resources.

Using Multiple Modifiers

A transform expression can contain multiple modifiers, which can be combined in any order and can be repeated. In the following example, the name of the directory that contains the files is changed but the files retain the original name and file name extension.

@(RESXFile->'Toolset\%(filename)%(extension)')

For example, if the items that are contained in the RESXFile item collection are Project1\Form1.resx, Project1\Form2.resx, and Project1\Form3.text, the outputs in the transformed list will be Toolset\Form1.resx, Toolset\Form2.resx, and Toolset\Form3.text.

Dependency Analysis

Transforms guarantee a one-to-one mapping between the transformed item collection and the original item collection. Therefore, if a target creates outputs that are transforms of the inputs, MSBuild can analyze the timestamps of the inputs and outputs, and decide whether to skip, build, or partially rebuild a target.

In the Copy Task in the following example, every file in the BuiltAssemblies item collection maps to a file in the destination folder of the task, specified by using a transform in the Outputs attribute. If a file in the BuiltAssemblies item collection changes, the Copy task will be run only for the changed file and all other files will be skipped. For more information about dependency analysis and how to use transforms, see How to: Build Incrementally.

<Target Name="CopyOutputs"
    Inputs="@(BuiltAssemblies)"
    Outputs="@(BuiltAssemblies -> '$(OutputPath)%(Filename)%(Extension)')">

    <Copy
        SourceFiles="@(BuiltAssemblies)"
        DestinationFolder="$(OutputPath)"/>

</Target>

Example

Description

The following example shows an MSBuild project file that uses transforms. This example assumes that there is just one .xsd file in the c:\sub0\sub1\sub2\sub3 directory, and that the working directory is c:\sub0.

Code

<Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <Schema Include="sub1\**\*.xsd"/>
    </ItemGroup>

    <Target Name="Messages">
        <Message Text="rootdir: @(Schema->'%(rootdir)')"/>
        <Message Text="fullpath: @(Schema->'%(fullpath)')"/>
        <Message Text="rootdir + directory + filename + extension: @(Schema->'%(rootdir)%(directory)%(filename)%(extension)')"/>
        <Message Text="identity: @(Schema->'%(identity)')"/>
        <Message Text="filename: @(Schema->'%(filename)')"/>
        <Message Text="directory: @(Schema->'%(directory)')"/>
        <Message Text="relativedir: @(Schema->'%(relativedir)')"/>
        <Message Text="extension: @(Schema->'%(extension)')"/>
    </Target>
</Project>

Comments

This example produces the following output.

rootdir: C:\
fullpath: C:\xmake\sub1\sub2\sub3\myfile.xsd
rootdir + directory + filename + extension: C:\xmake\sub1\sub2\sub3\myfile.xsd
identity: sub1\sub2\sub3\myfile.xsd
filename: myfile
directory: xmake\sub1\sub2\sub3\
relativedir: sub1\sub2\sub3\
extension: .xsd

See Also

Tasks

How to: Build Incrementally

Other Resources

MSBuild Concepts

MSBuild Reference