基本问题-我正在学习用C和C++编程。我不确定如何使用这些语言来处理依赖关系管理。

我想在我的项目中添加一个静态库。指出项目需要该库并将该库包括在项目中的通常方法是什么?

我有兴趣知道如何在基于Makefile的项目中执行此操作(不适用于CMake)。对于我所看到的,CMake允许您使用ExternalProject_Add函数自动执行依赖关系管理,但是我对不使用CMake的通常方法会感兴趣。

感谢您的帮助!

最佳答案

我假设您想按原样使用第三方库,而无需任何自定义修改。我的答案也将特定于GNU / Linux,但是由于您正在编写自己的Makefile,因此我认为这是与您相关的平台。

尽管将库转储到存储库中似乎是一种方便的快速修复方法,但这不是一个好习惯。理想情况下,您的存储库应仅包含项目负责的手写文件。这样可以使关注点保持清晰分离。

如果您的项目需要libfoo,我感兴趣的另一个项目可能也需要它,因此我可能决定在系统上安装libfoo并将其用于所有需要它的项目。我可以通过手动下载,构建和安装libfoo来做到这一点,或者,如果幸运的话,可以通过我的软件包管理器来安装它。手动构建和安装时,至少有两个选择。我可以在全系统范围内或仅为我的用户本地安装。另一方面,如果我认为我不再需要libfoo或您的项目需要特定版本的libfoo,而不是我通常要使用的版本,那么我可能更喜欢在本地目录中构建libfoo而不安装一切。

综上所述,为您的用户提供的选项是。

  • 使用系统的程序包管理器在系统范围(例如/usr/lib/中)安装该库。
  • 在系统范围内手动下载,构建和安装该库(例如/usr/local/lib/)。
  • 仅手动为我的本地用户下载,构建和安装该库(例如~/lib/)。
  • 手动下载并构建该库,但不要安装它(例如~/src/foo-1.42/)。

  • 选择哪种选项的决定应留给用户。但是,尽管您不应该对他们强加任何东西,但您应该尽一切可能帮助他们。

    外部依赖项的第一件事是记录它们。在README文件中,列出项目的所有依赖项以及URL到各个项目的下载页面。如果您知道相关的操作系统已经打包了该库,请同时提及人们必须为该系统安装的软件包的名称。我知道的所有GNU / Linux发行版都有一个网站,您可以在其中搜索其软件包索引,因此您可能希望针对更流行的软件包进行索引。如果您的项目需要特定版本的库,请不要忘记突出提及。

    如果您的用户决定使用选项(1),则无需执行其他操作。他们将使用软件包管理器来安装库,并且您的Makefile将从LIBS变量(例如-lfoo)中引用它。

    如果您的用户决定(或必须这样做,因为该库未针对其系统打包)使用选项(2)或(3),则情况没有太大不同。他们将下载,构建和安装该库,一旦完成,您的Makefile将再次选择它。但是,如果他们将库安装在非标准位置,则链接器可能无法直接找到它。因此,重要的是Makefile使用LDFLAGS变量,以便用户可以向其添加相应的选项(例如-L${HOME}/lib/)。如果您的代码引用包中的 header ,则包含路径(例如-I${HOME}/include/)也是如此。这些选项属于CPPFLAGS变量。

    如果您的用户使用选项(4),则一定会使用链接器标志来找到该库。例如,如果将libfoo下载到子目录foo并构建在其中,则他们会将-L./foo/添加到LDFLAGS。但是,在这种情况下,您可以使用户的生活更加轻松。将一个小的Shell脚本放在项目的顶级目录中,该脚本可以下载并可选地配置和构建所有外部依赖项。请清楚记录哪些操作将从互联网上下载内容,并确保用户掌握了他们想要下载的内容。另外,请在执行任何操作之前,让您的脚本验证已下载软件包的校验和。否则,可能成为攻击者的潜在切入点,并且您不希望使用您的软件来利用用户。在这种情况下,您的Makefile(或更好的configure脚本)应检测到该程序包是在本地构建的,并使用该程序包(即添加相应的-I…-L…标志)。您还可以无条件添加标志,因为预处理器和链接器将静默忽略不存在的目录。

    编写帮助程序脚本并不困难。一个简单的版本可能看起来像这样。

    #! /bin/bash -eu
    
    packages=(foo bar)
    declare -A urls
    urls['foo']='https://download.foo.org/foo-1.42.tar.gz'
    urls['bar']='https://download.bar.org/bar-2.50.tar.gz'
    
    cat <<'EOF' > dependency-checksums.sha1
    0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33  foo-1.42.tar.gz
    62cdb7020ff920e5aa642c3d4066950dd1f01f4d  bar-2.50.tar.gz
    EOF
    
    for pkg in "${packages[@]}"
    do
        wget "${urls[${pkg}]}" || exit
    done
    
    sha1sum --check dependency-checksums.sha1 || {
        echo "ALERT: Verification of package integrity failed.  Stop." >&2
        exit 1
    }
    
    for pkg in "${packages[@]}"
    do
        ar="${urls[${pkg}]##*/}"
        dir="${ar%.tar*}"
        tar -xf "${ar}"
        ln -s "${dir}/" "${pkg}"
        ( cd "${dir}" && ./configure && make )
    done
    

    您可以考虑稍微抛光一下。例如,使其了解--help选项,仅提供下载但不提供软件包。当然,您不必编写shell脚本。您可以提供Perl或Python脚本,甚至可以提供Make脚本。实际上,像我在上面的示例中那样使用花哨的Bash功能是一个坏主意,因为许多人不使用现代的Bash shell。

    如果您将软件作为源文件发布,则提供已经包含所有外部依赖项的备用tarball并没有错,因此您的用户不必单独下载它们。但是,您仅应将此作为替代方法,而不能作为唯一选择。这是针对tarball的,如前所述,该存储库应尽可能远离第三方内容。

    虽然编写一个不错的“下载我的依赖项”脚本很容易,但编写灵活的configure脚本和Makefile却很乏味,您可能会弄错。您应该认真考虑为此使用自动化框架。 GNU软件包通常使用GNU Autotools,即AutoconfAutomake。如果您想了解更多有关这些工具的信息,我可以推荐John Calcote的book“Autotools – GNU Autoconf,Automake和Libtool的实用指南”。

    10-04 12:35
    查看更多