有效使用Visual Studio项目属性来管理多个项目和配置

70

我一直使用Visual Studio内置的GUI支持配置我的项目,通常使用属性表来让几个项目使用相同的设置。

但我最大的抱怨之一是管理多个项目、配置和平台。如果你只用主要的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集成可以跟踪这些更改,否则我也不是完全满意。


2
我感同身受:我们在工作中的产品中有超过600个vcproj。 :( - C.J.
7个回答

82

我刚刚发现一件我认为不可能的事情(它没有在GUI中公开),这可以帮助使属性表格变得更加有用。 项目属性文件中许多标记的“条件”属性可以在.props文件中使用!

我刚刚编写了下面的测试,它非常有效,并完成了5个(common,x64,x86,debug,release)单独属性表格的任务!

<?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公开它的任何内容。我做过类似的事情,它确实简化了很多事情。 - jalf
4
如果您需要更高级的字符串处理功能,可以使用System.String方法:http://msdn.microsoft.com/en-us/library/system.string_methods%28v=vs.110%29.aspx 例如:<ShortHasRelPos>$(Configuration.ToLower().LastIndexOf('rel'))</ShortHasRelPos> - Oliver Zendel
我该如何应用这个解决方案?我应该把它放在哪里,如何让Visual Studio知道我想要默认使用它? - user2023370
1
我从来没有将其作为“全局默认值”运行,因为这可能会影响那些不属于我的项目(例如第三方库和示例项目),尽管有默认属性表存储在某个地方(忘记了),你可以进行编辑。因此,我只是将它们放在我的解决方案目录中,然后在Visual Studio的“属性管理器”窗口中,您可以使用“添加现有属性表”(您也可以在那里创建新的属性表,但GUI仍然没有提供任何基本平台/配置条件)。这也可以与多个开发人员/源控件正确配合使用。 - Fire Lancer

28

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

<?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
Bim,我认为它是在构建之前进行评估的,因为它从注册表中读取变量(请参见$(registry:key))。 - lunicon

12

之前我在公司的产品中(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”元素: - Heri

5

可以为每个配置创建单独的属性表。操作步骤如下:

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

这样,您就无需在单个表格中插入多个配置条件了。如果您有一些通用属性希望在多个配置之间共享,那么就将其创建成层次结构。顶层表格可用于所有配置,而嵌套表格仅包含特定于配置的属性。


非常好!谢谢。因此,您还可以在每个配置节点中创建新页面。很容易陷入编辑共享页面的陷阱,即使在每个配置节点下访问它,也会影响所有配置。 - Justin

4
就输出库而言,您可以选择所有项目,然后打开属性页面,选择所有配置、所有平台,然后将目标名称设置为:$(ProjectName)-$(PlatformToolset)-$(PlatformShortName)-$(Configuration),这将产生类似于mylib-v100-x86-Debug.lib的输出。
我们对附加库目录也执行类似操作,使用$(PlatformName)$(Configuration)来选择正确的库路径,尽管这意味着需要对库的初始设置进行一些调整。例如,我们将boost安装其库文件到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,好主意(如果其中包括自动链接,而不是为 x86 和 x64 构建使用相同的名称,那将更好,但 boost.build 的人似乎认为这不是问题)。 - Fire Lancer
我猜你的PlatformShortName是来自于x86.props和x64.props,这些文件必须正确继承或者说是未经记录的吗? - Fire Lancer
@Fire Lancer:说实话,我不确定PlatformShortName是从哪里来的。我认为它一定是内置的,因为在我们的属性表中没有任何特殊的东西。 - ngoozeff
1
@FireLancer:我们黑掉了boost的构建脚本,以便在库名称中包含平台信息。这确实需要我们有一个自定义的boost_version.h文件,但自动链接非常完美,我们只需要将boost.props文件添加到项目中,就可以开始使用了。当然,如果我们知道props文件中的条件语句,那可能是更好的选择...绝对不会那么痛苦... - Andreas Magnusson

1

如果您想定义一些用户宏,这些宏应该对整个解决方案有效:

  1. Create a solution-global.props file in your solution root folder

  2. Define there your 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>
    
  3. insert in each *.vcxproj file in which you want to use your custom macros this line:

    <Import Project="$(SolutionDir)solution-global.props" />

  4. Use your custom macro:

    <AdditionalIncludeDirectories>$(CommonUtilsDir)\Source;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

适用于VS2015


0

听起来值得尝试一下构建工具 - 在我的公司,我们使用一个自定义的工具,它会监视文件和项目的更改,并确定依赖关系和编译顺序。添加新文件并不是什么大问题 - 编译是通过msbuild完成的。

如果我需要编译超过一堆项目,我会使用类似nant这样的东西: http://nant.sourceforge.net/


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