-->

Does Visual Studio support adding MSBuild tasks to

2019-08-28 21:58发布

问题:

I'm trying to add some simple MSBuild tasks to a Visual Studio project (VS 2012 Express) - specifically, to create a subdirectory then copy some files to a subdirectory of the output directory ready for packaging.

I see that VS supports custom build steps, which are command-line invocations. However, since VS is based on MSBuild it should be possible to add these directly as MSBuild tasks like the Copy Task in the AfterBuild pre-defined target.

What I can't find is any way to actually add such tasks within the framework of Visual Studio. The documentation only talks about it from an MSBuild perspective, not how it works within Visual Studio's UI. It also doesn't seem to discuss the properties that refer to build output etc there; presumably they're just those used by msbuild its self.

Is there support for MSBuild task management in Visual Studio's UI and it's just crippled out of my Express edition? Or do I have to go hack the project file XML to add MSBuild tasks? Is that supported and the way it's supposed to be done?

I'm used to working with Eclipse and Ant or Maven, where all this is supported within the IDE, though of course you can hack the XML directly. Finding no UI at all for MSBuild task management in Visual Studio is quite confusing. Am I missing the obvious or crippled by using the freebie edition?

回答1:

For C++ projects, you can use the property

<CppCleanDependsOn>DeleteOutputs;$(CppCleanDependsOn)</CppCleanDependsOn>

instead of defining the BeforeClean target like you did.

From what I read, CallTarget is to be avoided. In your example, you should use DependsOnTargets to do that, as you see in many dummy targets in the MS supplied files. The analogous mechanism of a function where a target just "calls" other targets is done with DependsOnTargets. The flow is not really the same as procedural programming.

Intellisense: I never use it. Is that true for conditional AdditionalIncludeDirectories in the props file only? Go ahead and edit the entry in the proj file where the IDE put it, if you edit the property in the IDE with just one configuration chosen.



回答2:

I'm using VS2010 Pro, and it doesn't expose the AfterBuild target, at least in C++ projects which is what I'm doing. As you see, it does have the "Events", which according to what I've read are for backward compatibility with converted projects from VSBuild. I agree, a MSBuild task rather than a command script is the way to go.

Forget the UI. It's made to support free editing of the XML files, and continue using the UI too as it respects what you had in there and uses labels for its own stuff so it can find it to update it.

But to keep it neat, you could use a property page; a stand-alone XML file with *.props name, and put what you want in it. Then add that props file to the projects using the UI. You won't hand-edit the project file that the UI is maintaining, and it won't touch the props file unless you go through the property manager view and open it explicitly.

Oh, I also recall seeing additional standard targets something like Package and Publish. Maybe those are not used on your project type, but you could use those entry points anyway.



回答3:

(After a bunch more reading I found out how this works):

Visual Studio doesn't seem to expose advanced MSBuild project editing, even though modern vcxproj files are just MSBuild project files with a bunch of extra labeled properties and other entries for Visual Studio IDE specifics. So you have to hack the project XML.

To make it cleaner, only add one line to your actual vcxproj file - an include of a .targets file that contains the rest of your build customisations. e.g, just before the end of the project file, insert:

  <Import Project="pg_sysdatetime.targets" />
</Build>

Now create your .targets file with the same structure as any other MSBuild project. Here's mine from the project I've been working on:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- MSBuild extension targets for Visual Studio build -->
  <PropertyGroup>
    <DistDir>pg_sysdatetime_pg$(PGMAJORVERSION)-$(Configuration)-$(Platform)</DistDir>
  </PropertyGroup>
  <ItemGroup>
    <DocFiles Include="README.md;LICENSE"/>
    <ExtensionSourceFiles Include="pg_sysdatetime--1.0.sql;pg_sysdatetime.control"/>
    <ExtensionDll Include="$(TargetDir)\pg_sysdatetime.dll"/>
  </ItemGroup>
  <Target Name="CopyOutputs">
    <Message Text="Copying build product to $(DistDir)" Importance="high" />
    <Copy
      SourceFiles="@(DocFiles)"
      DestinationFolder="$(DistDir)"
      />
    <Copy
      SourceFiles="@(ExtensionDll)"
      DestinationFolder="$(DistDir)\lib"
      />
    <Copy
      SourceFiles="@(ExtensionSourceFiles)"
      DestinationFolder="$(DistDir)\share\extension"
      />
  </Target>
  <Target Name="DeleteOutputs">
    <Message Text="Deleting $(DistDir)" Importance="normal" />
    <Delete Files="$(DistDir)"/>
  </Target>
  <!-- Attach to Visual Studio build hooks -->
  <Target Name="BeforeClean">
    <CallTarget Targets="DeleteOutputs"/>
  </Target>
  <Target Name="AfterBuild">
    <CallTarget Targets="CopyOutputs"/>
  </Target>
</Project>

This can contain whatver MSBuild tasks you want, grouped into targets. It can also have property groups, item groups, and whatever else MSBuild supports.

To integrate into Visual Studio you add specially named targets that invoke what you want. Here you can see I've defined the BeforeClean and AfterBuild targets. You can get the supported targets from the VS integration docs.

Now, when I build or rebuild, a new directory containing the product DLL and a bunch of static files is automatically created, ready to zip up. If I wanted I could add the Nuget package for MSBuild Community Extensions and use the Zip task to bundle the whole thing into a zip file at the end too.

BTW, while you can define properties in your .targets files it's better to define them in property sheets instead. That way they're visible in the UI.