【问题标题】:VS2010 and MSBuild get different results on combined C++/C# solutionVS2010 和 MSBuild 在 C++/C# 组合解决方案上得到不同的结果
【发布时间】:2011-02-25 20:16:00
【问题描述】:

我有一个很大的 VS2010 解决方案,其中包含一堆 C# 项目。其中一个项目通过 P/Invoke 使用 C++(本机,也称为非托管)库。为了确保一切都正确构建,我在同一个解决方案中包含了上述 C++ 项目。现在,问题就从这里开始了。

简而言之:MSBuild 神秘地删除了一些输出文件,而 VS2010 可以正确构建。
.

长篇大论
以前(VS2005/2008),我会使用称为“项目依赖项”的漂亮功能。这使您可以选择给定项目所依赖的特定项目,以便环境确保首先构建这些项目。

然而,VS2010 已经朝着 MSBuild 的方向发展,现在项目依赖项根本不起作用。他们只是没有。 (例如见this question) 现在,为了确保我的 C++ 项目在使用它之前构建,我必须“添加引用”。所以我做到了。一切似乎都很好。

然后,我转到命令行并启动 MSBuild 以构建相同的解决方案。一切都很好,再次。但是当我查看输出文件夹时,没有 C++ 项目的输出!

MSBuild 控制台输出清楚地表明 C++ 项目在某个时间点确实已构建。我什至在一些项目中插入了一些“dir bin\MYPROJNAME.dll”语句作为后期构建步骤,以查看文件是否存在 - 它们是Here 是命令行窗口的截图。红色圈出的是文件存在的时刻(顶部),然后是文件丢失的时刻(底部)。

另一个奇怪的事情是,该项目显然被构建了两次。请参阅屏幕截图中的红色下划线 - 这是关于构建同一项目的第二条消息(第一条消息以及所有编译器输出都在屏幕上方)。

看起来确实是第二次构建事件导致文件被删除:当我完全禁用构建这个项目时(通过解决方案属性),它只构建了一次,并且文件最终存在。我本可以将其称为“解决方案”,但随后它在 Visual Studio 本身中中断:VS 只是不构建项目。

解决此问题的另一种方法是从正在使用的 C# 项目中删除“项目引用”。然后 MSBuild 将只构建 C++ 项目一次,并且文件将在那里。但随后它又出现在另一个地方:对 C++ 项目的更改不会触发正在使用的 C# 项目的重建。

所以问题是:如何让 MSBuild 不删除该死的文件?

【问题讨论】:

  • 查看解决方案中使用的平台目标。这在 VS2010 中可能会变得很混乱,特别是如果您从早期版本导入它。将输出详细程度也设置为 11,以更详细地了解它正在考虑的内容。您可以将其带到 connect.microsoft.com,但您需要更好地记录它。至少包括项目和解决方案文件。
  • @Hans,谢谢。我已使用您的建议并使用/v:diag 运行 MSBuild。这让我有了更多的了解,但我仍然不知所措。下面是 MSBuild 输出的相关部分:apreleva.com/trs/missingdll.txt 注意“构建”目标是如何被跳过的(第 89 行),因为“之前构建成功”。然而,所有各种“清理”目标都未能被跳过。有什么想法吗?
  • 哦,我还必须补充一点,我正在使用/t:Rebuild 构建,因此“清理”目标实际上正在正确执行。这里的“构建”目标有问题:它不应该被跳过。
  • 我是个白痴,但 32/64 位混合是您的目标平台、开发环境或引用库中的一个因素吗?对我来说,VS2010 对这类问题的直言不讳比以前的版本少。
  • @Kynth,这对我来说似乎不太可能。是的,我的工作机器是 64 位的,而 MSBuild 是 64 位进程,VS2010 是 32 位,这可能会造成不一致。但是我的构建服务器是 32 位机器,所以 MSBuild 在那里作为 32 位进程运行,但仍然产生相同的结果。

标签: visual-studio-2010 msbuild build


【解决方案1】:

对这个问题的简短回答:

您在 PostBuildEvents 中有目录。如果您添加属性 +r 它不会删除您的文件:

<Exec Command="attrib +r $(TargetPath)"/>

(TargetPath 应该是 dll 文件的位置...)

【讨论】:

  • 好的,这会起作用。一次。但是当我需要重建时,我该怎么办?
  • 虽然,我想我可以让它们再次成为可读性,作为在它们之前构建的其他项目的一部分......
  • 好的,这看起来确实有效。 (顺便说一句,奇怪的是,构建后事件在 C++ 项目本身上不起作用)。尽管确实感觉不舒服,但至少现在我有一个工作版本。我想在没有其他“真实”答案的情况下,这可能是我最初问题的答案......告诉你:我会给你绿色的复选标记,但只有在赏金到期后。看起来公平吗?
【解决方案2】:

听起来您有 2 个项目,都依赖于第三个项目。如果这不是真的,那么您可以忽略整个 anwser :)

因为编译器是线程化的,所以您需要确保这两个项目在第三个项目完成之前不会尝试构建。

所以 projectA 和 projectB 都有一个依赖构建 projectC。 projectA 启动,看到 projectC 的输出不存在并调用 Rebuild。在构建过程中,另一个编译线程出现,它开始构建项目 B。它没有看到 projectC 的输出(它还没有完成构建),所以它调用 Rebuild,再次清理项目。依赖构建在项目开始时检查,而不是在需要时检查。因此,如果 projectB 在其依赖链中到达 projectC 之前必须构建 4 个其他项目,则可能需要一段时间才能在 projectC 上调用第二次 Rebuild。

有几种方法可以解决它。

  1. 尝试解析您的依赖构建,以便 projectA 依赖于 projectC。当然,这可能并不理想。

  2. 我觉得VS2010还是有构建顺序的,所以可以设置项目内置的顺序。不仅仅是依赖。确保 projectC 列在 projectA 和 projectB 之前。

  3. 最简单的方法是进行 2 次构建调用。首先调用msbuild &lt;project&gt; /target:Clean 然后调用msbuild &lt;project&gt; /target:Build 不是重建,只是构建。这样 projectA 和 projectB 都可以看到 projectC 的输出,并且不会尝试构建它。

【讨论】:

  • 在我看来,MSBuild 的创建者不太可能忽略竞争条件的问题。但即使这是真的,- 不,我没有两个项目依赖于 C++ 一个。
  • 我也有同样的想法,直到我亲眼看到。它在 VS2008 中仍然没有修复,但我还没有在 VS2010 中对其进行测试。无论如何,选项 3 可能仍然适合您。
【解决方案3】:

找出导致它第一次构建的原因可能是要走的路。即使您删除了对它的引用,某些东西也会在早期触发第一次构建。您可以使用 /v:diag 发布完整的 msbuild 日志吗?您发布的部分开始于我感兴趣的部分之后 :)

【讨论】:

  • 恐怕你误解了我的描述。第一个构建实际上是正确的。它是第一次构建的,因为另一个项目依赖于它(即“引用它”)。这是不应该发生的第二次。即使是这样,它也不应该删除文件 - 或者我天真地认为:-)
  • 删除引用确实会阻止它在早期构建。如果我删除了引用,那么 C++ 项目只会构建一次。只有它发生在依赖项目之后,这几乎破坏了整个想法。
  • @Fyodor Soikin - 是的,我误解了。尽管如此,完整的构建日志对于查看作为项目第一次构建的一部分实际运行的目标很有用。目标只运行一次,这就是为什么“构建”目标不会在第二次通过项目时运行。该项目的第一次构建有一些可疑之处,这使 MSBuild 认为它尚未被清理,因此出现并稍后清理它。
【解决方案4】:

确保您正在为正确的配置和平台进行构建。如果不指定平台,它将在没有平台的情况下构建,这是一种Mixed 平台,具体取决于 msbuild 文件的构建方式。 Visual Studio 始终使用 UI 中指定的平台构建,这可能就是它与命令行不同的原因。

如果您没有找到解决方案,也许一种可行的解决方法是创建一个基于属性的条件,该条件删除在命令行中导致问题的任何内容(例如两次构建项目)并使用 /p 设置属性: 从命令行。

【讨论】:

  • Ooooo-kaaaay...错误的平台如何导致文件被完全删除?
【解决方案5】:

解决方案:为每个项目使用不同的中间目录

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多