为多个项目和配置有效地使用 Visual Studio 项目属性

Posted

技术标签:

【中文标题】为多个项目和配置有效地使用 Visual Studio 项目属性【英文标题】:Using Visual Studio project properties effectively for multiple projects and configurations 【发布时间】:2011-03-30 23:21:51 【问题描述】:

我一直使用内置 GUI 支持的 Visual Studios 来配置我的项目,通常使用属性表,以便多个项目使用一个公共集。

我对此的主要抱怨之一是管理多个项目、配置和平台。如果你只是用主 GUI 做所有事情(右键单击项目 -> 属性),它很快就会变得一团糟,难以维护并且容易出现错误(例如未能正确定义某些宏,或使用错误的运行时库等)。处理不同的人将依赖库放在不同的地方(例如我的都住在“C:\Libs\[C,C++]\[lib-name]\”)的事实,然后经常管理这些库的不同版本同样不同(发布、调试、x86、x64 等)也是一个大问题,因为它使在新系统上设置它的时间大大复杂化,然后存在版本控制问题和保持每个人的路径分开.. .

属性表使这更好一些,但我不能让一张表具有针对不同配置和平台的单独设置(下拉框显示为灰色),导致我有很多表,如果以正确的顺序继承会做什么我想要(“x86”、“x64”、“debug”、“release”、“common”、“directories”(通过定义像 BoostX86LibDir 这样的用户宏来处理前面提到的依赖问题)等)如果继承错误顺序(例如“x64”和“debug”之前的“common”)会导致尝试链接不正确的库版本或错误命名输出等问题...

我想要的是一种处理所有这些分散依赖项并设置一组“规则”的方法,这些“规则”供我在解决方案中的所有项目使用,例如将输出库命名为“mylib-[vc90,vc100] -[x86,x64][-d].lib",无需为每个单独的项目、配置和平台组合执行所有这些操作,然后让它们全部正确同步。

我知道迁移到创建所需文件的完全不同的系统(例如 CMake),但是这会使其他地方的事情变得复杂,因此即使是简单的任务(例如向项目中添加新文件)也需要在其他地方进行额外的更改,这不是两者我都非常满意,除非有一些与 VS2010 集成的东西可以跟踪这些变化。

【问题讨论】:

我感受到您的痛苦:我们的产品中有超过 600 个 vcproj 正在工作。 :( 【参考方案1】:

我刚刚发现了一些我认为不可能的东西(它没有被 GUI 公开),这有助于使属性表更加有用。项目属性文件中许多标签的“条件”属性,也可以在.props文件中使用!

我只是将以下内容放在一起作为测试,它运行良好,完成了 5 个(通用、x64、x86、调试、发布)单独的属性表的任务!

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Label="UserMacros">
    <!--debug suffix-->
    <DebugSuffix Condition="'$(Configuration)'=='Debug'">-d</DebugSuffix>
    <DebugSuffix Condition="'$(Configuration)'!='Debug'"></DebugSuffix>
    <!--platform-->
    <ShortPlatform Condition="'$(Platform)' == 'Win32'">x86</ShortPlatform>
    <ShortPlatform Condition="'$(Platform)' == 'x64'">x64</ShortPlatform>
    <!--toolset-->
    <Toolset Condition="'$(PlatformToolset)' == 'v90'">vc90</Toolset>
    <Toolset Condition="'$(PlatformToolset)' == 'v100'">vc100</Toolset>
  </PropertyGroup>
  <!--target-->
  <PropertyGroup>
    <TargetName>$(ProjectName)-$(Toolset)-$(ShortPlatform)$(DebugSuffix)</TargetName>
  </PropertyGroup>
</Project>

唯一的问题是属性 GUI 无法处理它,使用上述属性表的项目只会报告目标的默认继承值,例如“$(ProjectName)”。

【讨论】:

是的,属性表在 VS2k10 中要强大得多。可惜没有通过 IDE 暴露出来。我做过类似的事情,它确实简化了事情。 如果您需要更高级的 String 函数,可以使用 System.String 方法:msdn.microsoft.com/en-us/library/… 示例:`$(Configuration.ToLower().LastIndexOf('rel')) 如何应用此解决方案?我应该把它放在哪里,如何让 Visual Studio 知道我希望它以默认方式使用? 我从来没有将它作为“全局默认值”,因为它可能会与不是我自己的项目(例如 3rd 方库和示例项目)混淆,尽管默认属性表存储在某处(忘记)你可以编辑。因此,我只是将它们放在我的解决方案目录中,然后在 Visual Studio“属性管理器”窗口中,您可以使用“添加现有属性表”(您也可以在那里创建新的,但 GUI 仍然无法提供属性表上的基本平台/配置条件)。这也适用于多个开发人员/源代码控制。【参考方案2】:

我做了一些改进,可能对某人有用

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Label="UserMacros">
    <!--IsDebug: search for 'Debug' in Configuration-->
    <IsDebug>$([System.Convert]::ToString( $([System.Text.RegularExpressions.Regex]::IsMatch($(Configuration), '[Dd]ebug'))))</IsDebug>

    <!--ShortPlatform-->
    <ShortPlatform Condition="'$(Platform)' == 'Win32'">x86</ShortPlatform>
    <ShortPlatform Condition="'$(Platform)' == 'x64'">x64</ShortPlatform>

    <!--build parameters-->
    <BUILD_DIR>$(registry:HKEY_CURRENT_USER\Software\MyCompany\@BUILD_DIR)</BUILD_DIR>
  </PropertyGroup>

  <Choose>
    <When Condition="$([System.Convert]::ToBoolean($(IsDebug)))">
      <!-- debug macroses -->
      <PropertyGroup Label="UserMacros">
        <MyOutDirBase>Debug</MyOutDirBase>
        <DebugSuffix>-d</DebugSuffix>
      </PropertyGroup>
    </When>
    <Otherwise>
      <!-- other/release macroses -->
      <PropertyGroup Label="UserMacros">
        <MyOutDirBase>Release</MyOutDirBase>
        <DebugSuffix></DebugSuffix>
      </PropertyGroup>
    </Otherwise>
  </Choose>

  <Choose>
    <When Condition="Exists($(BUILD_DIR))">
      <PropertyGroup Label="UserMacros">
        <MyOutDir>$(BUILD_DIR)\Bin\$(MyOutDirBase)_$(ShortPlatform)\</MyOutDir>
        <MyIntDir>$(BUILD_DIR)\Build\$(Configuration)_$(ShortPlatform)_$(PlatformToolset)\$(ProjectGuid)\</MyIntDir>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup Label="UserMacros">
        <MyOutDir>$(SolutionDir)\Bin\$(MyOutDirBase)_$(ShortPlatform)\</MyOutDir>
        <MyIntDir>$(SolutionDir)\Build\$(Configuration)_$(ShortPlatform)_$(PlatformToolset)\$(ProjectGuid)\</MyIntDir>
      </PropertyGroup>
    </Otherwise>
  </Choose>

  <PropertyGroup>
    <OutDir>$(MyOutDir)</OutDir>
    <IntDir>$(MyIntDir)</IntDir>
<!-- some common for projects
    <CharacterSet>Unicode</CharacterSet>
    <LinkIncremental>false</LinkIncremental>
--> 
  </PropertyGroup>
</Project>

玩得开心!

【讨论】:

何时评估?加载项目时?我想将自定义构建后复制规则添加到属性表中,以仅复制实际链接到项目的 DLL。你认为这可能吗? Bim,我认为它是在构建之前评估的,因为它从注册表中读取变量(参见 $(registry:key))。【参考方案3】:

我以前对我公司的产品(200 多个项目)也有同样的痛苦。我解决它的方法是建立一个很好的属性表层次结构。

项目通过其输出类型继承属性表,例如 x64.Debug.Dynamic.Library.vsprops。这个 vsprops 文件只是使用 InheritedPropertySheets 属性继承其他属性表

<VisualStudioPropertySheet
    ProjectType="Visual C++"
    Version="8.00"
    Name="x64.Debug.Dynamic.Binary"
    InheritedPropertySheets=".\Common.vsprops;.\x64.vsprops;.\Debug.vsprops;.\Runtime.Debug.Dynamic.vsprops;.\Output.x64.Library.vsprops"
    >

您还可以在属性表中使用变量(即 UserMacro,其值可以是绝对变量甚至是环境变量)来根据您的需要自定义很多东西。比如在Debug.vsprops中定义一个BIN变量

<UserMacro name="BIN" Value="Debug" />

那么当您在一系列 vsprops 中设置输出名称时,例如 Output.x64.Library.vsprops

<VisualStudioPropertySheet
    ProjectType="Visual C++"
    Version="8.00"
    OutputDirectory="$(BIN)"
>

$(BIN) 变量将扩展为已设置的内容(在本例中为 Debug)。使用此技术,您可以轻松构建良好的属性表层次结构以满足您的需求。

现在您可能还想做一件事:构建您自己的项目模板,使用您的属性表集。真正困难的部分是强制正确使用模板和属性表。我个人的经验是,即使一切都设置好了,仍然有人会忘记使用模板来创建新项目...

【讨论】:

VS 2015 无法识别“VisualStudioPropertySheet”元素:【参考方案4】:

可以为每个配置创建单独的属性表。为此:

    创建特定于配置的属性表 打开属性管理器 右键单击要修改的配置(不是项目) 点击“添加现有属性表”并添加您的表

这使您无需将条件插入到单个工作表中以进行多种配置。如果您有一些想要在配置之间共享的公共属性,则创建一个层次结构。顶层工作表可用于所有配置,嵌套工作表将仅包含特定于配置的属性

【讨论】:

完美!谢谢。因此,您还可以在每个配置节点中创建新页面。很容易陷入编辑共享页面的陷阱,即使在每个配置节点下访问它也会影响所有配置。【参考方案5】:

就输出库而言,您可以选择所有项目,然后打开属性页,选择所有配置、所有平台,然后将目标名称设置为:

$(ProjectName)-$(PlatformToolset)-$(PlatformShortName)-$(Configuration)

会给出类似 mylib-v100-x86-Debug.lib 的输出

我们对附加库目录也做了类似的事情,使用$(PlatformName)#(Configuration) 来选择正确的库路径,尽管这确实意味着在库的初始设置上有些混乱。例如,我们将其库提升安装到boost/lib.Win32boost/lib.x64


关于图书馆,以及人们将它们安装在不同的地方,有几种选择。如果你有一个非常强大的源代码控制系统,你可以把所有东西都放在源代码控制中,放在源代码旁边的 libs 文件夹中。如果您使用多个库,或者它们特别大,那么这可能不起作用。

想到的另一个选项是在每台用户计算机上设置一个指向其库文件夹根目录的环境变量,例如LIB_ROOT=c:\libraries,然后您可以在 Visual Studio 中以$(LIB_ROOT) 访问它。

【讨论】:

我已经做了一些类似的事情,但是有些库在他们的名字中使用了其他东西(例如 opt 代替 release,-d 代替 -Debug)等,这仍然需要很多特殊规则(我的 debug.props 和 release.props 设置了用户宏来帮助解决这个问题,而 common.props 实际设置了 include/lib 路径)。没有考虑过 boost,好主意(如果自动链接包括 not 对 x86 和 x64 构建使用相同的名称,但 boost.build 人们似乎不认为这是一个问题,那就更好了) 我猜你的 PlatformShortName 来自必须正确继承的 x86.props 和 x64.props 或者它只是没有记录? @Fire Lancer:老实说,我不确定 PlatformShortName 的来源。我认为它一定是内置的,因为我们的属性表中没有任何特别之处。 @FireLancer:我们破解了 boost 构建脚本,将平台包含在 lib-name 中。它确实需要我们有一个自定义的boost_version.h,但自动链接就像一个魅力,我们只需要将boost.props 文件添加到项目中,我们就可以开始了。当然,如果我们知道 props-files 中的条件可能是更好的方法..绝对不那么痛苦..【参考方案6】:

听起来构建工具值得一试——在我这里,我们使用定制的工具来监视文件和项目的变化,并计算依赖关系和编译顺序。添加新文件没什么大不了的 - 使用 msbuild 完成编译。

如果我必须编译多个项目,我会使用类似 nant 的东西: http://nant.sourceforge.net/

【讨论】:

【参考方案7】:

如果你想定义一些对整个解决方案都有效的 UserMacros:

    在您的解决方案根文件夹中创建一个solution-global.props 文件

    在那里定义你的 UserMacros:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup Label="UserMacros" >
        <CommonUtilsDir>$(SolutionDir)Common\Utils\</CommonUtilsDir>
        <DriverBaseDir>$(SolutionDir)Common\DriverBase\</DriverBaseDir>
      </PropertyGroup>
    </Project>
    

    在您想在其中使用自定义宏的每个 *.vcxproj 文件中插入这一行:

    &lt;Import Project="$(SolutionDir)solution-global.props" /&gt;

    使用您的自定义宏:

    &lt;AdditionalIncludeDirectories&gt;$(CommonUtilsDir)\Source;%(AdditionalIncludeDirectories)&lt;/AdditionalIncludeDirectories&gt;

适用于 VS2015

【讨论】:

以上是关于为多个项目和配置有效地使用 Visual Studio 项目属性的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio - 根据选择的配置为多个平台构建

Visual Studio中怎样的配置只对单个项目(工程)有效?怎样的配置对所有项目(工程)都有效?

在 Visual Studio 中为单个解决方案编译和使用多个项目

MyBatis 安装和配置

Visual Studio 中的多个调试和发布配置选项

第一次使用 OpenGL:我应该如何配置 Visual Studio Express 2015 来构建和运行 OpenGL 项目?