基本问题-我正在学习用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,即Autoconf和Automake。如果您想了解更多有关这些工具的信息,我可以推荐John Calcote的book“Autotools – GNU Autoconf,Automake和Libtool的实用指南”。