如何处理 `Makefile.am` 中的用户定义文件

How to deal with user defined files in `Makefile.am`

我过去常常依靠 GNU 扩展从头开始编写 makefile,现在我正尝试转向 autotools,但我遇到了一些问题:在我正在处理的代码中,用户通常会贡献和编写他们可以将自己的一些代码放在定义的目录中(可能替换现有文件)。

在手写的 Makefile 中,我可以处理 wildcarddir 和 [=15 的负载(即不知道用户想要做什么) =](因为目录可以嵌套)。如果用户可以定义他们自己的问题,定义在一个名为 PROBLEM 的唯一变量中,该变量将位于 src/ 中,我会想出类似的东西:

# Define all global objects
SRCDIR = src
SRCFILES = $(wildcard $(SRCDIR)/*f90)
SRCOBJ = $(notdir $(SRCFILES:.f90=.o))
# Define user defined objects
PRBDIR = src/$(PROBLEM)
PRBFILES = $(wildcard $(PRBDIR)/*f90)
PRBOBJ = $(notdir $(PRBFILES:.f90=.o))

ALLOBJ = $(PRBOBJ) $(SRCOBJ)

# Generic rule
%.o: %.f90
        $(F90) $(FCFLAGS) -c $< -o $@ 

# Compile main executable
main: $(ALLOBJ)
        $(F90) $(FCFLAGS) -o main $@

如果我或多或少将这段代码放在 Makefile.am 中,它会抱怨 wildcardnotdirnon-POSIX variable name (probably a GNU make extension),我被卡住了。 我想出的第一个解决方案是手动明确定义所有源文件……这很好,没有其他人添加他们自己的东西。 我得到的第二个解决方案是在 configure.ac 中定义变量,其中将包含我正在寻找的信息,但这意味着使用 shell 命令,我发现它相当不优雅:

AC_SUBST([PRBFILES], [$(ls -p src/$PROBLEM | grep -v /)])
AC_MSG_RESULT([$PRBFILES])

AC_SUBST([PRBSUBDIRS], [$(ls -p src/$PROBLEM | grep /)])
AC_MSG_RESULT([$PRBSUBDIRS])

这样做,我想我可以在 Makefile.am 中对 $PRBFILES 使用常规替换来检索 PRBOBJ,就像我在前面的示例中所做的那样。

有更好的方法吗?

为了通用性,我想转向 autotools(处理外部库绝对很棒),现在我觉得我在这方面倒退了,但这可能是因为我错过了一些东西远.

一般

您正在遇到摩擦,因为您的项目风格模糊了 Autotools 设计所围绕的角色区别。自动工具假设有

  • 项目维护者,负责准备和分发自动工具源包;
  • 构建者,他们获取源包并在本地系统上构建和安装它们;和
  • 用户,谁运行安装了软件。

当然,这些之间可能存在重叠。这里的要点是,任何为项目贡献源代码的人都在扮演维护者的角色,维护构建系统是担任该角色的职责之一。您希望以某种方式让维护人员(在这个意义上)免除 build-system 维护责任。这没问题 objective,但 Autotools 没有提供任何专门针对此的内容。

在该问题的范围内,您还 运行反对 Autotools 支持 UNIX-like 系统之间最大可移植性的设计理念。该理念的结果之一是 Autotools 不鼓励甚至可能主动拒绝依赖 make 扩展的 makefile 代码,包括 GNU make.

提供的那些

这并不意味着您不能采用 Autotools 构建系统,但这确实意味着这样做会比平时更复杂。我看到三个主要的选择。

方案一:方便项目维护者activity

在依赖 Automake 的构建系统中,所有源文件 应该 在 Automake 输入中明确指定(Makefile.am).此外,如果 Automake 输入被修改,那么 Automake 和其他自动工具当然需要 re-run。 autoreconf 程序通常足以满足此要求,但对于具有更复杂需求的项目,通常会提供名为 autogen.sh 的 shell 脚本。 您可以提供一个 autogen.sh 来扫描用户贡献/修改的来源并更新适当的 Automake 输入,然后 运行s autoreconf 这将仍然让你编写 shell 命令,但也许你会发现它在显式 shell 脚本的上下文中更优雅。

此替代方案要求构建者安装 Autotools,并且可能对他们拥有的各种工具的版本敏感。

方案二:build-time决心

如果您不希望您的用户负责重建构建系统,和/或您不希望 build-time 依赖于自动工具,那么您只能确定 user-varying 工程配置时的源文件。您在问题中提供的“不雅”方法就是一个例子。我在 configure.ac 中编写 shell 代码完全没有问题,你需要一些没有提供宏的东西,但我可能会选择稍微不同的实现:

AC_MSG_CHECKING([for problem files])
PRBFILES=$(find "src/$PROBLEM" -maxdepth 1 -type f)
AC_SUBST([PRBFILES])
AC_MSG_RESULT([$PRBFILES])

AC_MSG_CHECKING([for problem subdirectories])
PRBSUBDIRS=$(find "src/$PROBLEM" -mindepth 1 -maxdepth 1 -type d)
AC_SUBST([PRBSUBDIRS])
AC_MSG_RESULT([$PRBSUBDIRS])

请注意,与您的版本不同,$PRBSUBDIRS.

中的目录名称不会添加尾部斜杠

备选方案 3:项目重新设计

您描述的代码似乎不是一个完整的项目,因为普通的使用模式涉及第三方将其与自己的代码一起构建以创建自定义可执行文件。但这与 或 Fortran 模块 有很多相似之处。也许更好地将您提供的位与您的用户提供的位分开对您有利,从而回避这个问题。在没有任何用户贡献的情况下将您的项目构建为库或模块。如果你想为用户提供类似级别的支持,将其与他们自己的代码结合起来,然后为此编写和分发一个工具,也许是围绕 GNU makefile 的脚本形式,比如你习惯提供的。