使用msbuild执行文件系统发布配置文件

99

我有一个使用VS2010创建并现在正在使用VS2012访问的C# .Net 4.0项目。

我正试图将此网站中仅需要的文件发布到目标位置(C:\builds\MyProject[Files])

我的文件结构: ./ProjectRoot/MyProject.csproj ./ProjectRoot/Properties/PublishProfiles/FileSystemDebug.pubxml

我通过MSBuild运行以下命令:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe ./ProjectRoot/MyProject.csproj /p:DeployOnBuild=true /p:PublishProfile=./ProjectRoot/Properties/PublishProfiles/FileSystemDebug.pubxml

这是FileSystemDebug.pubxml中的xml:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>FileSystem</WebPublishMethod>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <publishUrl>C:\builds\MyProject\</publishUrl>
    <DeleteExistingFiles>True</DeleteExistingFiles>
  </PropertyGroup>
</Project>

结果如下:

  • ./ProjectRoot/obj/Debug/Package/MyProject.zip创建了一个zip文件
  • 没有部署到<publishUrl>C:\builds\MyProject\</publishUrl>(啥情况)
  • 创建的zip文件杂乱无章,包含了不需要的应用程序文件。

当我通过Visual Studio运行此发布配置文件时,会在*C:\builds\MyProject*创建一个文件夹,并包含我想要的确切工件。

我如何让msbuild获得这个简单的结果?

8个回答

61

提醒您:我在使用Visual Studio 2015时也遇到了相同的问题,在尝试了多个小时后,现在我可以执行msbuild myproject.csproj /p:DeployOnBuild=true /p:PublishProfile=myprofile

为了让它正常工作,我不得不编辑我的.csproj文件。其中包含了类似这样的一行:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" 
  Condition="false" />

我将这行改为以下内容:

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0\WebApplications\Microsoft.WebApplication.targets" />

(我将10.0更改为14.0,不确定这是否必要。但我肯定必须删除条件部分。)


(I changed 10.0 to 14.0, not sure whether this was necessary. But I definitely had to remove the condition part.)

1
具有“Condition =“false””的条件导入是为了向后兼容而存在的。即使由于假条件而被跳过,VS2010也要求存在此导入。如果再仔细看,则会发现csproj包含另一个导入“$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets”,该导入解析为当前版本Visual Studio的目标文件。 - Steven Liekens
3
注意在路径中战略性地使用 $(MSBuildToolsVersion),以便考虑正确的 VS 版本: <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(MSBuildToolsVersion)\WebApplications\Microsoft.WebApplication.targets" Condition="false" />。这在我的 VS2015 Update 1 上起作用。 - Al Dass
这对于 .NET Core 5.0+ 应用程序(非 Web)不起作用。 - Martin Braun

45

在这里找到了答案:http://www.digitallycreated.net/Blog/59/locally-publishing-a-vs2010-asp.net-web-application-using-msbuild

该链接介绍了如何使用MSBuild本地发布VS2010 ASP.NET Web应用程序。
Visual Studio 2010有出色的Web应用程序项目发布功能,使您可以轻松地通过点击按钮发布您的Web应用程序项目。在幕后,Web.config转换和包构建由一个庞大的MSBuild脚本完成,该脚本被导入到您的项目文件中(位于:C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets)。不幸的是,该脚本非常复杂、混乱且未经记录(除了文件中一些常常拼错并且大多无用的注释之外)。那个文件的大流程图以及如何连接它的一些文档会很好,但似乎缺乏(或者至少我找不到)。
不幸的是,这意味着通过命令行执行发布比需要更加难以理解。我对这个领域缺乏文档感到惊讶,因为现在许多商店使用持续集成服务器,有些甚至进行自动化部署(VS2010发布功能可以帮助很多),因此我认为启用这个(容易!)应该是特性的一个相当主要的要求。
无论如何,在Microsoft.Web.Publishing.targets文件中挖掘几个小时并且在试错墙上撞了几次头之后,我设法弄清楚了Visual Studio似乎如何执行其魔术一键“发布到文件系统”和“构建部署包”功能。我将进入一些MSBuild脚本,因此如果您不熟悉MSBuild,建议您查看这个快速入门MSDN页面。
发布到文件系统
VS2010发布到文件系统对话框发布到文件系统需要一段时间才能理解,因为我期望发生一些明智的MSBuild使用。相反,VS2010做了一些相当奇怪的事情:它调用MSBuild执行一种半部署,该半部署在项目的obj文件夹中准备Web应用程序的文件,然后似乎手动将这些文件(即在MSBuild之外)复制到目标发布文件夹中。这真的很奇怪,因为MSBuild旨在复制文件(和其他与构建相关的内容),因此如果整个过程只是一个VS2010调用的MSBuild目标,而不是一个目标然后手动复制,那么这将是有意义的。
这意味着通过命令行使用MSBuild执行此操作并不像调用具有特定目标和设置某些属性的项目文件那样简单。您需要做的是VS2010应该完成的:创建一个目标自己执行半部署,然后将结果复制到目标文件夹。要编辑项目文件,请右键单击VS2010中的项目,然后单击卸载项目,然后再次右键单击并单击“编辑”。向下滚动,直到找到导入Web应用程序目标(Microsoft.WebApplication.targets)的Import元素;该文件本身导入了前面提到的Microsoft.Web.Publishing.targets文件。在这条线下面,我们将添加我们的新目标,称为PublishToFileSystem:
<Target Name="PublishToFileSystem"
        DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
    <Error Condition="'$(PublishDestination)'==''"
           Text="The PublishDestination property must be set to the intended publishing destination." />
    <MakeDir Condition="!Exists($(PublishDestination))"
             Directories="$(PublishDestination)" />

    <ItemGroup>
        <PublishFiles Include="$(_PackageTempDir)\**\*.*" />
    </ItemGroup>

    <Copy SourceFiles="@(PublishFiles)"
          DestinationFiles="@(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')"
          SkipUnchangedFiles="True" />
</Target>

这个目标依赖于PipelinePreDeployCopyAllFilesToOneFolder目标,这是VS2010在进行手动复制之前调用的。在Microsoft.Web.Publishing.targets中查找,可以发现调用此目标会导致将项目文件放置到由属性_PackageTempDir指定的目录中。

我们在目标中调用的第一个任务是Error任务,在其上放置了一个条件,以确保只有在未设置PublishDestination属性时才会发生该任务。如果您忘记指定PublishDestination属性,它将捕获您并使构建失败。然后,我们调用MakeDir任务来创建PublishDestination目录(如果不存在)。

然后,我们定义了一个名为PublishFiles的项,它表示在_PackageTempDir文件夹下找到的所有文件。然后调用Copy任务,将所有这些文件复制到Publish Destination文件夹中。Copy元素上的DestinationFiles属性有点复杂;它执行项目的转换,并将其路径转换为以PublishDestination文件夹为根的新路径(请查看Well-Known Item Metadata以了解那些%()s的含义)。

要从命令行调用此目标,现在我们只需执行以下命令(显然更改项目文件名称和属性以适合您):

msbuild Website.csproj "/p:Platform=AnyCPU;Configuration=Release;PublishDestination=F:\Temp\Publish" /t:PublishToFileSystem

3
我无法理解新目标的代码片段(它显示01 02 03 ...)。你能否请编辑一下?请注意不要改变原本的意思,同时使内容更加通俗易懂。 - fan711
2
我同意fan711的观点。虽然,解决方案已经在链接中描述了 - 那么复制它是为了什么呢? - Anton Kuryan
4
链接通常会在一段时间后失效,这就是为什么stackoverflow.com上的问题和答案应该始终是自包含的,不依赖于外部资源。 - Oliver
结果看起来像是MSBuild根本没有使用发布配置文件,而是在执行打包操作(可能是默认的?)。 你下面的解决方案所做的就是复制配置文件所设置的内容,这是由Microsoft.Web.Publishing.targets处理的,它从其部署文件夹中选择正确的类型(对于FileSystem)。因此,看起来你在重新发明轮子,而不是解决问题。但如果没有你的MSBuild日志,就无法确定。我已经让我的工作了,详细信息请参见我的答案。 - GregS
这对我来说可行,但会复制所有项目文件,而不仅仅是在 Web 服务器上运行应用程序所需的文件。希望看到一个解决方案,它只复制当我运行发布配置文件时 Visual Studio 所做的文件。 - NightWatchman
1
GregS的解决方案对我无效。构建没有问题,但是没有文件被复制到发布目录中。 - NightWatchman

20

尝试了以上所有答案后,仍然遇到麻烦(我使用的是Visual Studio 2013)。没有任何内容被复制到发布文件夹中。

关键在于,如果我运行MSBuild时选择单个项目而不是整个解决方案,我必须添加一个额外的参数来指定Visual Studio版本:

/p:VisualStudioVersion=12.0

12.0是针对VS2013的版本,替换为您使用的版本。一旦我添加了这个参数,它就可以正常工作。

完整的命令行如下:


The complete command line looks like this:

MSBuild C:\PathToMyProject\MyProject.csproj /p:DeployOnBuild=true /p:PublishProfile=MyPublishProfile /p:VisualStudioVersion=12.0

我在这里找到了它:

http://www.asp.net/mvc/overview/deployment/visual-studio-web-deployment/command-line-deployment

他们说:

如果您指定单个项目而不是解决方案,则必须添加一个参数来指定 Visual Studio 版本。


12

我认为你的发布配置文件没有被使用,而是进行了一些默认的打包。Microsoft Web Publish目标会执行你上面所做的所有操作,并基于配置选择正确的目标。

我从TeamCity MSBuild步骤中轻松实现了我的工作,但我确实指定了配置文件的显式路径,你只需要按名称调用它,不要在后面加上 .pubxml (例如 FileSystemDebug)。只要在标准文件夹中(你的已经在其中),就会找到它。

示例:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe ./ProjectRoot/MyProject.csproj /p:DeployOnBuild=true /p:PublishProfile=FileSystemDebug

请注意,这是使用 Visual Studio 2012 版本的 Microsoft Web Publish 目标完成的,通常位于 "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web"。查看特定部署类型的目标的 deploy 文件夹。


自几年前发布以来,微软已经进行了许多改进,感谢更新的编程? - P. Roe

5

实际上,我将你的所有答案合并到了我的解决方案中,以解决上述问题:

  1. 我根据自己的需求创建了 pubxml 文件。
  2. 然后,我将 pubxml 文件中的所有参数复制到自己的参数列表 "/p:foo=bar" 中,以供 msbuild.exe 使用。
  3. 我丢弃了 pubxml 文件。

结果如下所示:

msbuild /t:restore /t:build /p:WebPublishMethod=FileSystem /p:publishUrl=C:\builds\MyProject\ /p:DeleteExistingFiles=True /p:LastUsedPlatform="Any CPU" /p:Configuration=Release


4

提醒:在构建服务器上运行时出现了同样的问题(Jenkins安装了msbuild 15,由VS 2017驱动.NET Core 2.1 Web项目)。

在我的情况下,使用msbuild的“publish”目标忽略了配置文件。

因此,我的msbuild命令以以下方式开始:

msbuild /t:restore;build;publish

这个命令正确触发了发布过程,但无论如何组合或变化,使用“/p:PublishProfile=FolderProfile”都无法选择我想要使用的配置文件(“FolderProfile”)。
当我停止使用发布目标时:
msbuild /t:restore;build /p:DeployOnBuild=true /p:PublishProfile=FolderProfile

我(愚蠢地)认为这不会有任何影响,但是一旦我使用了DeployOnBuild开关,它就正确地识别了配置文件。


2

从项目文件夹运行

msbuild /p:DeployOnBuild=true /p:PublishProfile="release-file.pubxml" /p:AspnetMergePath="C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" /p:Configuration=Release

这将处理web.config转换和AspnetMergePath。


1

首先检查开发者电脑上的Visual Studio版本,该版本可以发布解决方案(项目)。 如图所示是针对VS 2013的。

 /p:VisualStudioVersion=12.0

将上述命令行添加到指定使用哪个版本的Visual Studio构建项目。 如先前的答案所述,当我们尝试发布一个项目而不是整个解决方案时,可能会发生这种情况。
因此,完整的代码应该像这样:
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" "C:\Program Files (x86)\Jenkins\workspace\Jenkinssecondsample\MVCSampleJenkins\MVCSampleJenkins.csproj" /T:Build;Package /p:Configuration=DEBUG /p:OutputPath="obj\DEBUG" /p:DeployIisAppPath="Default Web Site/jenkinsdemoapp" /p:VisualStudioVersion=12.0

1
抱歉,shammakalubo,你完全误解了这个问题。 - P. Roe
1
@shammakalubo 答案是正确的,但它并没有完全说明。需要将此参数添加到 OP 提到的命令中,该命令将变为:MSBuild C:\PathToMyProject\MyProject.csproj /p:DeployOnBuild=true /p:PublishProfile=MyPublishProfile /p:VisualStudioVersion=12.0 这个参数就是我缺失的,解决了我的问题。您只需要完整地提及答案即可! - Syed Waqas
@WaqasShah 谢谢,正如你所提到的,我已经编辑了我的回答并发布了完整的代码。 - Shammie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接