基本问题 - 我正在学习用C和C ++编程。我不确定如何处理这些语言中的依赖关系管理。
我想在项目中添加一个静态库。通常的方式是说明项目需要该库,并将库包含在项目中?
我很想知道如何在基于makefile的项目中做到这一点(不是用CMake)。对于我所看到的,CMake允许您使用ExternalProject_Add函数自动化依赖关系管理,但我感兴趣的是在不使用CMake的情况下执行此操作的常用方法。
感谢您的帮助!
答案 0 :(得分:1)
我假设您希望按原样使用第三方库,而无需进行任何自定义修改。我的回答也将特定于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/
)。如果您的代码引用了包中的标头,则包含路径(例如-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而言, repository - 如前所述 - 应该尽可能地保留第三方内容。
虽然编写一个体面的“下载我的依赖项”脚本很容易,但编写灵活的configure
脚本和Makefile
是很乏味的,你可能会弄错它。您应该认真考虑使用自动化框架。 GNU包通常使用GNU Autotools,即Autoconf和Automake。如果您想了解有关这些工具的更多信息,我可以推荐John Calcote的book“Autotools - GNU Autoconf,Automake和Libtool的实用工具指南”。