xmlvisual-studiomsbuildxsd.exemsbuildcommunitytasks

Can't find xsd.exe in the path during MSBuild Community Tasks BeforeBuild step in Visual Studio


I am using MSBuild Community Tasks to run Xsd.exe as part of my build in Visual Studio as thus:

<Import Project="$(SolutionDir)Common.targets" />
<PropertyGroup>
  <MSBuildCommunityTasksPath>$(SolutionDir)TeamBuildTypes</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets" />
<UsingTask TaskName="XSD" AssemblyFile="$(VCTargetsPath)$(CPPTasks)" />
<Target Name="BeforeBuild">
  <!--Exec Command="'$(DevEnvDir)..\Tools\vsvars32.bat'" /-->
  <XSD Sources="MySchema.xsd" GenerateFromSchema="classes" Language="CS" />
</Target>

Common.targets is as per here.

However, I get the following error during build:

The "XSD" task failed unexpectedly.

Microsoft.Build.Shared.InternalErrorException: MSB0001: Internal MSBuild Error: xsd.exe unexpectedly not a rooted path

A solution has been given on MSDN forum here, which is to add a path to xsd.exe to the path environment variable. But, as indicated by Pico Ohms' answer, this is not maintainable as it is version-dependent and requires every dev to perform an extra step in order to build, just because of Xsd.exe.

I found another solution here which is to call vsvars32.bat beforehand. This is the commented-out line in the code above. This didn't work, so I found a solution here that I hoped would make it work, which was to call DEVENV with the /useenv parameter.

Then I found another solution here which is to add <xs:include schemaLocation="MSBuild\MSBuild.Community.Tasks.xsd"/> to Microsoft.Build.xsd. Didn't work either.

So, now I'm out of ideas. How do I get the MSBuild Community Tasks XSD task working without requiring devs to update their path variable, either by using the /useenv solution, or some other solution? I'm aware I can put xsd.exe in the Visual Studio solution, but this seems like a cheap workaround to me.


Solution

  • I had to do some research to remember the comment I posted on the other thread about this, but you can implement a custom search path for your task by deriving from the XSD task and overriding the GenerateFullPathToTool() method.

    xsd derivation tree

    When you look at the XSD task in DotPeek you can see it derives from Microsoft.Build.Utilities.ToolTask. ToolTask contains a method called "GenerateFullPathToTool()". This method is called to return the full path to the tool as its name implies.

    The ToolLocationHelper in Microsoft.Build.Utilities class can be used to find the appropriate location for your installed xsd.exe.

    tool location helper

    It contains a method called GetPathToDotNetFrameworkSdkFile which will give you the location of fileName for the dot net sdk. Put "xsd.exe" as the parameter and you should be good to go.

    If that method doesn't return the path you need, there are other methods for returning the various paths and its based off of the registry so its more portable than setting an explicit path.