【问题标题】:Ensuring internal shared library versions are correct确保内部共享库版本正确
【发布时间】:2014-06-18 12:42:58
【问题描述】:

我目前的任务是在我们的内部库方面尝试改进我们公司的版本控制实践。我已经阅读了 Semantic Versioning 2.0.0,这对于我们当前的问题来说似乎是一个非常好的、简单和优雅的解决方案,因为我们当前的版本控制实践(例如 library_v01)并没有真正告诉我们每个库版本的太多信息。

理想情况下,应用程序会在编译时检查正在使用的库的版本,以便在部署和/或测试之前发现任何不匹配的情况。我们的应用程序是用 Visual Studio 编写的,我们很快就会升级到 C++11 编译器友好的 VS2013,这就是为什么我的第一个想法是使用 static_asserts 以便在编译时检查版本,例如:

// Defined in library code and accessible by applications
struct LibraryVersion {
    static const unsigned int LIB_VERSION_MAJOR = 1;
    static const unsigned int LIB_VERSION_MINOR = 0;
    static const unsigned int LIB_VERSION_PATCH = 0;
}

// Application code
static const unsigned int EXPECTED_LIB_VERSION_MAJOR = 1;
static const unsigned int EXPECTED_LIB_VERSION_MINOR = 0;
static const unsigned int EXPECTED_LIB_VERSION_PATCH = 0;

void lib_version_check() {
    static_assert(LibraryVersion::LIB_VERSION_MAJOR == EXPECTED_LIB_VERSION_MAJOR, "Error Major");
    static_assert(LibraryVersion::LIB_VERSION_MINOR == EXPECTED_LIB_VERSION_MINOR, "Error Minor");
    static_assert(LibraryVersion::LIB_VERSION_PATCH == EXPECTED_LIB_VERSION_PATCH, "Error Patch");
}

此外,最好在库 prop 文件中包含某种预处理器定义,该文件可用于包含路径,以便每个应用程序始终包含正确的库文件,例如

$(LIB_DIR)="C:\Libs\"
$(LIB_VERS)="v1.0.0"
Include_Path = $(LIB_DIR)\$(LIB_VERS);

文件结构类似于:

C:\Libs\v1.0.0\include\...
C:\Libs\v1.0.1\include\...
C:\Libs\v1.0.2\include\...
C:\Libs\v1.2.0\include\...

然而,这对我来说似乎不是最优雅的解决方案,并且必须跟踪两个版本变量(预处理器值和代表 MAJOR、MINOR 和 PATCH 版本的无符号整数)似乎有点多。

所以基本上我想问一下其他人是否遇到过类似的事情,他们是否对什么是优雅的解决方案有任何见解,或者使用上述解决方案是否有任何我可能没有想到的陷阱的?也许具有像上述那样的文件结构是有问题的和/或有更简单的方法?对于任何反馈,我们都表示感谢。非常感谢。

【问题讨论】:

    标签: c++ design-patterns c++11 shared-libraries


    【解决方案1】:

    我们只是使用 git 标签作为主要、次要和补丁生成带有模板的标题。我们做你上面所做的,但它都是自动化的。这并不能解决编译依赖问题。

    然而,需要什么库版本的依赖被提升到我们自定义的waf 构建系统的包依赖中。例如:

    # We need boost
    conf.env.BOOST_MINIMUM_VERSION = (1, 50)
    conf.load('boost', tooldir=vcawaflib.dir)
    
    # VCA project dependencies
    conf.env.VCA_TEST_MINIMUM_VERSION = (0, 2, 3)
    conf.project_uselib('sdk/test', 'vca_test')
    

    构建系统与系统包管理器接口(在 Windows 上它使用我们编写的自定义 python 包管理器)并解决所有依赖关系。

    话虽如此,建立我们的构建系统需要做很多工作。对于您的情况,您可以允许编译器计算出库依赖项,但我会使用 std::tuple,因为在版本比较方面这样做是正确的

    include/my_awesome_library/version.h

    namespace my_awesome_library {
      static constexpr auto version = std::make_tuple(1, 0, 0);
    }  // namespace my_awesome_library
    

    src/my_program_or_library/dependencies.c

    static_assert(my_awesome_library::version >= std::make_tuple(1, 0, 0), "\nmy_awesome_library is old");
    

    这可以被定义为:

    #define VERSION_CHECK(version, ...) \
      static_assert(version >= std::make_tuple(__VA_ARGS__), #version " is old");
    
    VERSION_CHECK(my_awesome_library::version, 1, 0, 0);
    

    您可以扩展它以进行最小和最大检查等。

    Microsoft 在新版本 Visual Studio 中变得更加符合标准方面做得很好,但您必须检查上述内容是否适用于您使用的任何 MSVC 编译器。

    【讨论】:

    • 感谢您,这看起来是个不错的解决方案。文件结构如何?像 library\version_number\include 这样的东西有意义吗?是你做的事情还是我没有想到的其他明显的事情?
    • 我们的 Windows 包管理器执行以下操作:third_party/prebuilt/ntlm-1.4-windows-7-x64-gcc-4.8-posix-dwarf/。所以[library]-[version]-[os]-[release]-[arch]-[compiler]-[version]-[threading]-[exceptions] 包含文件夹和库位于该文件夹下。 MSVC 看起来像:third_party/prebuilt/ntlm-1.4-windows-7-x64-msvc-10.0/。这是因为编译器对每个版本(以及异常模型/线程模型)都有不同的 ABI。除非您将其全部自动化(脚本化),否则它将变得无法维护。
    • 是的,我知道手动维护是个问题。如此有效(只是为了确保我正确掌握了这一点)一个后期构建脚本用于从存储库中提取所需的正确版本的库?
    • 不,有一个配置步骤。很像 autotools 和 cmake(我不知道您对 Visual Studio 以外的其他构建系统了解多少)。这解决了共享工具链文件夹中的所有依赖项。您可以编写一个预构建批处理脚本来做同样的事情,不过这将是一个怪物。
    • 我对它们的了解很模糊,因为我在学习等时接触过它们,但在商业层面上没有什么,因为它一直是我使用的 Visual Studio。我想我现在对一个好的版本控制过程有了更好的理解,所以这应该给我一个很好的工作平台,谢谢你的帮助:)
    猜你喜欢
    • 2014-09-02
    • 1970-01-01
    • 2019-06-27
    • 2017-07-10
    • 2010-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    相关资源
    最近更新 更多