自动工具中的源外构建失败

Out of source build fails in autotools

这可能是一个菜鸟问题,但我最近才开始使用 autotools 来简化我对事物的要求。也就是说,我喜欢这样的想法,即最终用户真的不需要去获取任何东西来在他们的本地系统上构建我的项目。

为了保持我的源目录干净,我在 config 目录中保存了额外的配置文件,我想 mkdir 一个构建目录来保持我的构建配置文件紧贴保持距离。这是我当前的项目树:

.
├── aclocal.m4
├── AUTHORS
├── autom4te.cache
│   ├── output.0
│   ├── output.1
│   ├── requests
│   ├── traces.0
│   └── traces.1
├── bin
├── build
├── Changelog
├── config
│   ├── compile
│   ├── install-sh
│   └── missing
├── config.h.in
├── configure
├── configure.ac
├── doc
├── lib
├── Makefile.am
├── Makefile.in
├── NEWS
├── README
├── README.html
├── README.md
├── src
│   ├── Makefile.am
│   ├── main.c
└── test

Makefile.am

SUBDIRS = src
dist_doc_data = README

src/Makefile.am

bin_PROGRAMS = testprog
testprog_SOURCES = main.c

configure.ac

AC_PREREQ(2.59)

AC_INIT([testprog], [1.0], [myemail@example.com])

AC_CONFIG_AUX_DIR([config])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])

AC_CONFIG_HEADERS([config.h])

AC_PROG_CC

AC_CONFIG_FILES([Makefile])
AC_OUTPUT

最终构建结果:

mkdir build
cd build
../configure
make

配置输出

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... /usr/bin/clang
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether /usr/bin/clang accepts -g... yes
checking for /usr/bin/clang option to accept ISO C89... none needed
checking whether /usr/bin/clang understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of /usr/bin/clang... none
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: executing depfiles commands

进行输出

CDPATH="${ZSH_VERSION+.}:" && cd .. && /bin/sh /home/me/projects/testprog/config/missing aclocal-1.15 
 cd .. && /bin/sh /home/me/projects/testprog/config/missing automake-1.15 --foreign
CDPATH="${ZSH_VERSION+.}:" && cd .. && /bin/sh /home/me/projects/testprog/config/missing autoconf
/bin/sh ./config.status --recheck
running CONFIG_SHELL=/bin/sh /bin/sh ../configure CC=/usr/bin/clang --no-create --no-recursion
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... /usr/bin/clang
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether /usr/bin/clang accepts -g... yes
checking for /usr/bin/clang option to accept ISO C89... none needed
checking whether /usr/bin/clang understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of /usr/bin/clang... none
checking that generated files are newer than configure... done
configure: creating ./config.status
 /bin/sh ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
(CDPATH="${ZSH_VERSION+.}:" && cd .. && /bin/sh /home/me/projects/testprog/config/missing autoheader)
rm -f stamp-h1
touch ../config.h.in
cd . && /bin/sh ./config.status config.h
config.status: creating config.h
config.status: config.h is unchanged
make  all-recursive
make[1]: Entering directory '/home/me/projects/testprog/build'
Making all in src
/bin/sh: line 20: cd: src: No such file or directory
make[1]: *** [Makefile:352: all-recursive] Error 1
make[1]: Leaving directory '/home/me/projects/testprog/build'
make: *** [Makefile:293: all] Error 2

我在 gnu.org 上购买了 Autotools as well as followed the Automake 佳能的副本。我不确定去哪里,这些资源都没有帮助我解决它。

从表面上看,它正在 build 目录中寻找 src,当然它不存在。但是我查看的所有资源外构建似乎都没有表明这个问题甚至会发生。我做错了什么?

我找到了这个特定问题的解决方案,但我不想给自己任何分数,因为我希望更有经验的人来更好地解释它。

解决方案比较晦涩:

AC_PREREQ(2.59)

AC_INIT([testprog], [1.0], [myemail@example.com])

AC_CONFIG_AUX_DIR([config])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])

AC_CONFIG_HEADERS([config.h])

AC_PROG_CC

AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT

AC_CONFIG_FILES 中缺少 src/Makefile。使用它构建,并在构建目录中创建一个包含二进制文件的 src 目录。现在弄清楚如何将二进制文件放入 bin 而不是 src...

既然你邀请更有经验的人来解释,我将首先重述我对你的回答的评论:你需要告诉 Autoconf configure 脚本应该创建哪些文件,这是AC_CONFIG_FILES 宏的功能。您没有告诉它您想要一个 src/Makefile 是源内和源外构建的错误。

但是您似乎想要更多地解释幕后发生的事情,所以开始吧。

您项目的 源代码树 是以包含顶级 configure 脚本的目录为根的目录树。 构建树 根植于您运行 configure 所在的工作目录。当构建树与源树不同时,您正在执行源外构建configure 将其所有输出发送到构建树中,从其测试创建的临时文件到其日志文件,再到通过 AC_CONFIG_FILES 宏指定的输出文件。在适当的时候,它还会创建为输出文件指定的任何子目录,例如 src 用于 src/Makefile。它不会从源目录 本身 复制任何内容,也不会创建比放置其输出文件所需更多的目录。

外源构建的魔力发生在 make。它依赖于某些 make 实现的“VPATH”功能,包括 GNU Make,通过它可以为 make 提供构建规则的先决条件和目标的搜索路径。在咨询 VPATH 之前,Make 总是首先查找与其(当时的)工作目录相关的这些文件,但是在新的源外构建中,它最初可以找到的唯一东西是由 configure 创建的文件。 (随着构建的进行,它也可能会找到之前构建的文件。)

但是 VPATH 与递归 make 正交,例如您通过 Automake 的 SUBDIRS 功能利用。事实上,递归 make 根本不是 make 的显式特征,而是普通 make 行为的特例(尽管 GNU make 至少识别它并提供一些特殊支持)。递归 make 只需将 make 命令放入常规 make 规则的配方中即可运行,但 VPATH 与配方中的命令没有任何(直接)关系.

特别是,如果 make 试图递归到构建树中不存在的目录,VPATH 就没有帮助。对于所有 Makefile 都由 configure 生成的正确的 Autotools 构建系统来说,这不是问题,因为 make 将仅递归到(它期望)具有 Makefile 的目录,并且 configure 将创建这些目录以及其中的 Makefile。 make,然而,只知道 Makefile 本身,而不是它的起源。如果它试图递归到一个不存在的目录,它所知道的就是它试图执行的命令因此失败。

这就是为什么您收到的错误消息对您的案例没有帮助。还因为外源构建是一种在您的构建已经在源内运行后才可以参与的能力。您会从源代码构建中获得更有用的错误消息,并且基于 Automake 的 Makefile 非常适合清理。 make clean 和/或 make distclean 和/或 make maintainer-clean 按照宣传的方式工作。

奖金

Now to figure out how to make it put the binary in bin instead of src...

为什么?说真的,这有什么意义,尤其是当您已经在执行外源构建时? make 知道在您准备好安装(或清理)二进制文件时在哪里可以找到它们。当然,make 最初放置它们的地方并不是它们的安装位置。

但是,Automake 可以做到这一点。你只需要告诉它去做。使用您呈现的布局,其主要部分如下所示:

src/Makefile.am

bin_PROGRAMS = ../bin/testprog
___bin_testprog_SOURCES = main.c

您可能还需要引入顶级 Makefile 以确保 bin/ 目录在递归到 src/ 之前存在。如果确实需要,那么我认为您可以通过以下变体实现它:

Makefile.am

SUBDIRS = . src
dist_doc_data = README

all-local:
    mkdir -p bin

clean-local:
    -rm -rf bin

另见: