一个我正在寻找的例子是WPF代码后台文件。这些文件在Visual Studio项目视图中不显示,但与项目一起编译,并在IntelliSense中提供。 WPF代码后台文件(例如Window1.g.i.cs)由自定义工具生成。
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);AddToolOutput</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="HiddenFile.cs" />
</ItemGroup>
</Target>
</Project>
之前),即使在Visual Studio中没有显示,您的“HiddenFile.cs”也会被包含在编译中。
使用单独的.targets文件
与其直接放置在.csproj文件中,通常应将其放置在单独的.targets文件中,并用以下代码括起来:<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
</Project>
将其导入到您的.csproj中,使用<Import Project="MyTool.targets">
。建议使用.targets文件,即使只需要一次性使用,因为它可以将自定义代码与由Visual Studio维护的.csproj中的内容分开。
构建生成的文件名
如果您正在创建通用工具和/或使用单独的.targets文件,则可能不想明确列出每个隐藏文件。相反,您希望从项目中的其他设置生成隐藏文件名。例如,如果您想要所有资源文件在“obj”目录中有相应的工具生成文件,则您的目标应该是:
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Resource->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>
<Target Name="AddToolOutput">
<ItemGroup>
<MyToolFiles Include="@(Page);@(Resource)" Condition="'%(Extension)'=='.xyz' />
<Compile Include="@(MyToolFiles->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')"/>
</ItemGroup>
</Target>
<ItemGroup>
<AvailableItemName Include="Xyz" />
</ItemGroup>
之后,Visual Studio就会允许你在文件属性的“构建操作”中选择“Xyz”,这将添加以下内容到你的.csproj文件:
<ItemGroup>
<Xyz Include="Something.xyz" />
</ItemGroup>
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>
当使用自定义项目类型时,您可以通过将其映射到另一种项目类型(也称为“生成操作”)来使您的项目也由内置机制处理。如果您的“Xyz”文件实际上是.cs文件或.xaml文件,或者如果它们需要成为嵌入式资源,则此功能非常有用。例如,您可以使所有“生成操作”为Xyz的文件都被编译:
<ItemGroup>
<Compile Include="@(Xyz)" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="@(Xyz)" />
</ItemGroup>
public class GenerateCodeFromXyzFiles : ITask
{
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
public ITaskItem[] InputFiles { get; set; }
public ITaskItem[] OutputFiles { get; set; }
public bool Execute()
{
for(int i=0; i<InputFiles.Length; i++)
File.WriteAllText(OutputFiles[i].ItemSpec,
ProcessXyzFile(
File.ReadAllText(InputFiles[i].ItemSpec)));
}
private string ProcessXyzFile(string xyzFileContents)
{
// Process file and return generated code
}
}
以下是UsingTask元素和调用它的Target:
<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />
<Target Name="GenerateToolOutput">
<GenerateCodeFromXyzFiles
InputFiles="@(Xyz)"
OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<Output TaskParameter="OutputFiles" ItemGroup="Compile" />
</GenerateCodeFromXyzFiles>
</Target>
你可能希望在其他时间运行生成器,比如当某个环境变量改变时,或者强制它同步运行而不是后台运行。
为了让生成器即使没有更改输入文件也重新运行,通常最好的方法是将一个虚拟的输入文件添加到目标中,该文件存储在“obj”目录中。然后,每当环境变量或某些外部设置发生更改应该强制重新运行生成器工具时,只需触摸此文件(即创建它或更新其修改日期)。
要强制生成器同步运行而不是等待 IntelliSense 在后台运行它,只需使用 MSBuild 构建特定的目标。这可能就像执行“MSBuild /t:GenerateToolOutput”一样简单,或者 VSIP 可能提供了调用自定义构建目标的内置方法。或者,您可以简单地调用 Build 命令并等待它完成。
请注意,本节中的“输入文件”是指在 Target 元素的“Inputs”属性中列出的任何内容。
最后说明
您可能会收到来自 Visual Studio 的警告,表示它不知道是否信任您的自定义工具 .targets 文件。要解决此问题,请将其添加到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\MSBuild\SafeImports 注册表键中。
以下是一个实际的 .targets 文件,其中包含所有必要的部分:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateToolOutput</CoreCompileDependsOn>
</PropertyGroup>
<UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />
<Target Name="GenerateToolOutput" Inputs="@(Xyz)" Outputs="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<GenerateCodeFromXyzFiles
InputFiles="@(Xyz)"
OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">
<Output TaskParameter="OutputFiles" ItemGroup="Compile" />
</GenerateCodeFromXyzFiles>
</Target>
</Project>
如果您有任何问题或对此处的内容不理解,请告诉我。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\MSBuild\SafeImports
。 - devstuffVisible
的元数据属性。据说InProject
元数据也可以实现同样的效果。<ItemGroup>
<Compile Include="$(AssemblyInfoPath)">
<!-- either: -->
<InProject>false</InProject>
<!-- or: -->
<Visible>false</Visible>
</Compile>
</ItemGroup>
我知道的唯一方法是将生成的文件添加为依赖项,以便在proj文件中隐藏想要隐藏的文件。
例如:
<ItemGroup>
<Compile Include="test.cs" />
<Compile Include="test.g.i.cs">
<DependentUpon>test.cs</DependentUpon>
</Compile>
</ItemGroup>
我想您想在这里查看:http://msdn.microsoft.com/en-us/library/ms171453.aspx。
具体来说,是"创建执行期间的项目"部分。