在Visual Studio中的MSBuild社区任务BeforeBuild步骤期间找不到路径中的xsd.exe

问题描述 投票:-1回答:2

我正在使用MSBuild社区任务来运行Xsd.exe作为我在Visual Studio中构建的一部分,因为:

<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是根据here

但是,我在构建期间收到以下错误:

“XSD”任务意外失败。

Microsoft.Build.Shared.InternalErrorException:MSB0001:内部MSBuild错误:xsd.exe意外不是root路径

已经在MSDN论坛here上给出了一个解决方案,即将xsd.exe的路径添加到路径环境变量中。但是,正如Pico Ohms的回答所指出的那样,这是不可维护的,因为它依赖于版本,并且需要每个开发人员为了构建而执行额外的步骤,仅仅因为Xsd.exe。

我找到了另一个解决方案here,它预先调用vsvars32.bat。这是上面代码中注释掉的行。这没有用,所以我找到了一个解决方案here,我希望它可以使它工作,这是用/useenv参数调用DEVENV。

然后我找到了另一个解决方案here,它将<xs:include schemaLocation="MSBuild\MSBuild.Community.Tasks.xsd"/>添加到Microsoft.Build.xsd。也没用。

所以,现在我没有想法了。如何使用/useenv解决方案或其他解决方案,无需开发人员更新其路径变量,从而使MSBuild社区任务XSD任务正常工作?我知道我可以将xsd.exe放在Visual Studio解决方案中,但这对我来说似乎是一种廉价的解决方法。

xml visual-studio msbuild xsd.exe msbuildcommunitytasks
2个回答
1
投票

我不得不做一些研究来记住我在另一个帖子上发布的comment,但你可以通过从XSD任务派生并覆盖GenerateFullPathToTool()方法为你的任务实现自定义搜索路径。

xsd derivation tree

当您查看DotPeek中的XSD任务时,您可以看到它派生自Microsoft.Build.Utilities.ToolTask​​。 ToolTask​​包含一个名为“GenerateFullPathToTool()”的方法。调用此方法以返回工具的完整路径,顾名思义。

Microsoft.Build.Utilities类中的ToolLocationHelper可用于查找已安装的xsd.exe的相应位置。

tool location helper

它包含一个名为GetPathToDotNetFrameworkSdkFile的方法,它将为您提供dot net sdk的fileName位置。把“xsd.exe”作为参数,你应该好好去。

如果该方法没有返回您需要的路径,则还有其他方法可以返回各种路径,并且它基于注册表,因此它比设置显式路径更具可移植性。


0
投票

我多年来一直使用的解决方案是使用inline task来设置项目文件中的路径。

 <ItemGroup>   
    <!-- Configure XSD files by using the XsdClasses custom item type.  All files of this type are converted by the GenerateXsdClasses target. --> 
    <XsdClasses Include="Data\XsdFile1.xsd">
      <SubType>Designer</SubType>
    </XsdClasses>
     <XsdClasses Include="Data\XsdFile2.xsd">
      <SubType>Designer</SubType>
    </XsdClasses>
  </ItemGroup>


  <!-- In-line task to add a folder to the current path if not already in it. -->
  <UsingTask TaskName="AddToPath" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <FolderPath ParameterType="System.String" Required="true" />
    </ParameterGroup>

    <Task>
      <Code Type="Fragment" Language="c#"><![CDATA[
            const string PathEnvironmentVariableName = "PATH";
            string currentPath = Environment.GetEnvironmentVariable(PathEnvironmentVariableName);
            if(!currentPath.ToLower().Contains(FolderPath.ToLower()))
            {
                currentPath = currentPath.TrimEnd(';');
                string newPath = string.Format("{0};{1}", currentPath, FolderPath);
                Environment.SetEnvironmentVariable(PathEnvironmentVariableName, newPath);
            }
]]></Code>
    </Task>   
  </UsingTask>

  <!-- Reference the Xsd task. -->
  <UsingTask TaskName="XSD" AssemblyFile="$(VCTargetsPath)Microsoft.Build.CppTasks.Common.dll" />

  <!-- Set the default path to Xsd.exe - this will be added to the current path using the above inline task.  Can be overridden per client by setting a user or machine environment variable with the same name. -->
  <PropertyGroup>
    <NetFxToolsPath Condition="'$(NetFxToolsPath)' == ''">C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7 Tools\</NetFxToolsPath>
  </PropertyGroup>


  <!-- Generate classes from xsd files configured with 'XsdClasses' item type. -->
  <Target Name="GenerateXsdClasses" Inputs="@(XsdClasses)" Outputs="@(XsdClasses->'%(RelativeDir)%(Filename).cs')">
    <Message Importance="high" Text="Building %(XsdClasses.Filename)" />
    <AddToPath FolderPath="$(NetFxToolsPath)" />
    <!-- Note the trailing . in the outputdir because the folder ends with a / -->
    <XSD MinimalRebuildFromTracking="true" Sources="%(XsdClasses.Identity)" GenerateFromSchema="classes" Namespace="XsdClassesNamespace" SuppressStartupBanner="true" AdditionalOptions="/outputdir:&quot;%(XsdClasses.RelativeDir).&quot;" />
  </Target>

  <!-- Configure class generation from xsd to occur before build. -->
  <Target Name="BeforeBuild" DependsOnTargets="GenerateXsdClasses"/>
© www.soinside.com 2019 - 2024. All rights reserved.