msbuild和visual studio项目配置问题

时间:2009-11-27 06:23:54

标签: c# visual-studio-2008 msbuild

我有一个包含很多项目和安装程序项目的解决方案。一个项目使用第三方包。该软件包附带一个本机DLL和一个.net包装器DLL。为了使代码工作,.net包装器DLL需要在运行时中找到本机DLL。但代码永远不会在编译时直接引用本机DLL(代码在编译时与.net包装器DLL对话)。

现在,我必须选择正确的方式来部署本机DLL,在程序员机器的编译期间以及在用户计算机上安装时间。

基本上我有两个选项,要么将本机DLL放到Windows系统文件夹中,要么将本机DLL放在包含exe文件的本地文件夹中。

要将本机DLL放入系统文件夹,我需要一个构建后的脚本,在构建解决方案后将文件xcopy写入目录。我还需要在安装程序项目中创建一个系统文件夹输出,以便安装程序在客户端计算机上工作。我不知道是否将文件复制到系统文件夹是个好主意。对于此解决方案,每当有人(大团队中的其他人)创建新安装程序时,他或她必须记住创建系统文件夹输出并在配置下添加本机DLL,否则他或她的安装程序将无法安装可用的部分用户机器上的软件。

要将本机DLL放到本地文件夹,我有以下两种方法: 1.使用后构建脚本。这个解决方案,我必须找到我的大解决方案中的每个可执行项目,它使用本机DLL引用项目,并将构建后脚本链接到每个这样的可执行项目。在将来,当某人(大团队中的其他人)创建一个具有相同类型的新可执行项目时,他或她必须记住链接相同的构建后脚本,否则可执行文件将无法找到本机DLL 。这是我真的不喜欢的,人们往往会忘记。

  1. 我可以将本机DLL添加到使用它的项目中,并在DLL的属性配置中将其设置为“Copy If Newer”。这样,本机DLL将被复制到项目的输出文件夹和每个引用该项目的项目中。这样,我不必记住任何事情。本机DLL将被复制到每个依赖项目的本地文件夹中。
  2. 这似乎是一个很好的解决方案。但是msbuild命令似乎无法巧妙地处理这种情况。例如,假设项目A直接使用本机DLL,我将本机DLL添加到项目A.项目B指项目A,项目C指项目B和项目A.如果使用msbuild构建解决方案,本机DLL将是重复复制到项目C的输出文件夹3次,一次用于参考项目A,一次用于参考项目B,一次用于参考项目B到项目A.在我的大解决方案中,在依赖关系链接的末尾,无论“Copy If Newer”设置如何,本机DLL都将以指数方式多次复制到同一输出文件夹。这需要花费大量时间来构建整个解决方案。

    现在我完全不知道对我的情况最好的解决方案是什么。对于使用本机DLL的任何人,如何部署DLL以便1.在开发人员计算机(编译和运行)和用户计算机(安装和运行)上进行部署都很方便.2。大团队中的开发人员没有在他或她向解决方案添加新项目/安装程序时记住任何内容,3。足够聪明的构建方式,以避免不必要的冗余操作。感谢您提前提供任何提示,网上教程,建议或聪明的教学。

6 个答案:

答案 0 :(得分:3)

我们一般所做的是将所有\ bin目录放入符号链接到公共目录。这样可以避免VS为所有引用执行的所有传递副本,并且可以加速大型解决方案的干净构建,最高可达10-20倍。

我们使用以下powershell脚本来设置符号链接:

param($linkTarget={throw "Link target must be specified"}, $rootDir=".")

ls $rootDir -Recurse -Include *.csproj | % { $_.DirectoryName } | % { Join-Path $_ -ChildPath "bin" } | % {

  Write-Host "Creating link from $_ to $linkTarget"
  if (Test-Path $_)
  {
    Remove-Item -Force -Recurse $_
    cmd /c rd $_
  }

  cmd /c mklink /D $_ $linkTarget
}

符号链接需要Vista或Win7;如果在XP上,可以通过调用junction.exe替换对mklink的调用来代替联结。

默认情况下,必须以管理员身份运行。

答案 1 :(得分:1)

首先,除非没有其他选择,否则请避免使用post build事件。构建后事件非托管性质往往会导致构建失败并且可维护性较低。

“Copy If Newer”标志可能具有蹩脚的效果,但它会向您保证:1)所需的依赖项将被复制到输出2)它不会失败构建3)几乎没有可维护性。

其次,您可以通过使用“Copy If Newer”标志仅设置构建链中的最后一个项目来减少冗余复制,例如:项目A直接引用DLL但将其复制到输出,项目C通过项目B间接指向项目A,并且具有对DLL的虚拟引用,并启用“Copy If Newer”。

答案 2 :(得分:1)

史蒂夫,

我一直在为我的项目添加本机dll,并且我没有将dll作为内容添加到项目中。您实际上可以将必要的dll添加为项目链接而不是实际内容。这样,dll就不会被复制到项目文件夹中以及目标文件夹中。

以下是devx的解释:

  

要添加共享文件,请打开该对话框   选择一个现有的文件   项目|添加现有项目菜单项   并选择您想要的文件   包括。然后,而不是点击   打开按钮,单击上的箭头   在该按钮的左侧,然后单击“链接”   下拉列表中的文件。   这样您就可以链接到原始版本   文件,而不是它的本地副本。

您是否将所有项目构建到公共解决方案目标目录中,即.. \ bin \ debug?这可能会减少不必要的副本。

将项目链接添加到dll肯定比在构建事件中复制更容易。

答案 3 :(得分:0)

在项目设置中必须有一些“狡猾的”(如循环引用)才能产生无限的复制循环 - 但找到它可能会非常棘手和/或耗费时间。

我会尝试两件事:

  • 关闭“较新”标志,看看它是否对您的问题有任何影响。复制单个dll不太可能影响构建时间,并且当文件日期戳混淆时,此选项可能会出现问题。 (我不希望这会导致重复的复制尝试)

  • 尝试使用带有xcopy的后期构建事件来复制文件,而不是依赖于自动系统。然后你将完全控制一个简单的系统,而不是想知道为什么一个聪明的系统不起作用。

答案 4 :(得分:0)

就个人而言,我将本机DLL和程序集DLL视为一个实体。添加链接到文件是一种方法。或者,您可以使用单独的主构建脚本来按摩项目和安装程序。

例如,我有一个Library.msbuild文件,它运行源.csproj的MsBuild任务作为我的Build目标。 My Deploy目标使用复制任务将dll从source \ library \ LibraryName \ bin \ Configuration * .dll复制到library \ LibraryName。我有一个库位置用于我需要构建的所有源文件,另一个是我的主项目引用的所有bin。

这是整个Library.msbuild脚本:`

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
        <ProjectDirectory>library\AspNetMvc\DataAnnotationsModelBinder\src</ProjectDirectory>
        <LibraryDirectory>..\library\AspNetMvc</LibraryDirectory>
    </PropertyGroup>
    <ItemGroup>
       <CompiledBinaries Include="$(ProjectDirectory)\bin\$(Configuration)\*.dll" />
    </ItemGroup>

    <Target Name="Clean">
        <MSBuild Projects="$(ProjectDirectory)\Microsoft.Web.Mvc.DataAnnotations.csproj"
        Targets="Clean" Properties="Configuration=$(Configuration)" />
    </Target>

    <Target Name="PreBuild" DependsOnTargets="Clean">
    </Target>

    <Target Name="Build" DependsOnTargets="PreBuild">
        <MSBuild Projects="$(ProjectDirectory)\Microsoft.Web.Mvc.DataAnnotations.csproj"
        Targets="Build" Properties="Configuration=$(Configuration)" />
    </Target>

    <Target Name="Deploy" DependsOnTargets="Build">
        <Copy SourceFiles="@(CompiledBinaries)" DestinationFolder="$(LibraryDirectory)"/>
    </Target>
</Project>  

` 在您的实例中,我将Deploy复制任务移动到PreBuild或完全删除它以依赖于复制dll的Add Existing方法。目标是当你运行\ debug你的项目时,一切都将是bin相对的。如果您要将本机与.NET DLL分离,您甚至可以使用bin \ win32。

现在,每个构建都是从.build.cmd批处理文件(一键构建)完成的,就是这样:C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild Library.msbuild /t:Deploy。您可以在链中添加更多目标,例如在构建之后但在部署之前添加。

如果您使用的是WiX或任何其他类型的安装程序,则应该能够为文件本身使用相对路径。我有一个顶级的staging \目录,我没有问题从源\安装\(.. \ .. \ staging \ icky进入,是但工作)。最糟糕的情况是,您可以将临时目录放在安装路径中的某个位置。

在大多数情况下,您可以远离.csproj或.wixproj文件而不必抽象MsBuild。在After / AfterBuild遇到的天花板非常低,但我发现自己现在几乎在所有情况下都选择了这种方法。

答案 5 :(得分:0)

请参阅此帖子:http://blog.alexyakunin.com/2009/09/making-msbuild-visual-studio-to.html

它解释了如何自动将N级依赖项复制到Bin文件夹中。

如果您有一些程序集,甚至没有间接引用,您可以创建另一个程序集(例如MyProject.ThirdPartyMagnet),从该项目引用此程序集并从您需要的项目中引用MyProject.ThirdPartyMagnet将所有程序集复制到。