从 configure.ac 中的文件读取版本号

Posted

技术标签:

【中文标题】从 configure.ac 中的文件读取版本号【英文标题】:Read a version number from a file in configure.ac 【发布时间】:2012-01-23 11:20:33 【问题描述】:

出于某些原因,我在纯文本文件而不是 configure.ac 中定义了我的项目版本号。我想创建一个语句来读取版本号并在编译期间存储它。

现在我的 configure.ac 看起来像这样:

AC_INIT([my program],[999.9.9])

我想要类似的东西:

AC_INIT([my program],[ $(cat VERSION) ])

这当然行不通。这里的诀窍是什么? (我知道我失去了一些便携性 - 我现在不在乎)。谢谢!

【问题讨论】:

【参考方案1】:

试试:

AC_INIT([my program], m4_esyscmd([tr -d '\n' < VERSION]))

使用 cmets 中建议的修复进行了编辑。

我还能够使用以下方法删除不可移植的 tr 调用:

AC_INIT([my program], [m4_translit(m4_esyscmd([cat VERSION]),m4_newline)])

这似乎和 Enrico 在下面的 cmets 中建议的解决方案一样有效:

AC_INIT([my program], [m4_esyscmd_s([cat VERSION])])

【讨论】:

得到了 configure.ac:5: warning: AC_INIT: not a literal: m4_esyscmd([echo 9.9| tr -d '\n']) 但它有效!谢谢。 @lzap:您可以删除 m4_esyscmd(...) 周围的引号,以便在调用 AC_INIT 之前对其进行评估。这样AC_INIT 得到一个文字。为了提高效率,我还将删除cat 的调用:AC_INIT([my program], m4_esyscmd([tr -d '\n' &lt;VERSION])) 您可以使用m4_esyscmd_s 而不是m4_esyscmd 来删除尾随换行符而不使用tr 这是 autoconf 本身用来动态生成自己的版本号的方法。见this mailing list post 和the source code。 此解决方案仍会为我生成警告。我正在提取一个哈希值并查看:configure.ac:3: warning: AC_INIT: not a literal: a0aabf81【参考方案2】:

您可以简单地使用本机宏 m4_include()(而不是按照 ldav1s 的建议通过 m4_esyscmd_s() 调用 trcat),

AC_INIT([foo], m4_normalize(m4_include([VERSION])))

这也是GNU M4的官方指南suggests类似案例:

$ cat examples/incl.m4
⇒Include file start
⇒foo
⇒Include file end

[…]

includesinclude 扩展为 file 可用于定义对整个文件进行操作的宏。这里 是一个例子,它定义了‘bar’来扩展为 incl.m4:

$ m4 -I examples
define(`bar', include(`incl.m4'))
⇒
This is `bar':  >>bar<<
⇒This is bar:  >>Include file start
⇒foo
⇒Include file end
⇒<<

优点:

通过使用m4_normalize(m4_include([VERSION])) 而不是m4_esyscmd_s([cat VERSION])VERSION 文件被添加到Makefile 中的DIST_COMMON 变量中——这意味着VERSION 文件在用户启动时自动重新分发make dist 通过使用m4_normalize(m4_include([VERSION])) 而不是m4_esyscmd_s([cat VERSION])VERSION 文件被添加到make aclocal.m4 的先决条件中(这意味着每次修改VERSION 文件时,configure 脚本都会自动更新)

GNU M4 还提供 support for regular expressions,因此如果您想确保版本字符串始终遵循特定模式 - 或者如果 VERSION 文件包含的文本不仅仅是版本字符串– 您可以使用m4_bregexp() 找到您要查找的内容:

AC_INIT([foo], m4_bregexp(m4_quote(m4_include([VERSION])), [[0-9]+\.[0-9]+\.[0-9]+], [\&]))

这也是最安全的方法,因为如果在VERSION 文件中找不到上面的正则表达式,AC_INIT() 的第二个参数会简单地扩展为一个空字符串,并且 Autoconf 会抛出以下错误消息

error: AC_INIT should be called with package and version arguments

调用m4_bregexp() 来处理VERSION 文件的内容很有用的典型情况是,该文件包含三个数字的版本字符串(MAJOR.MINOR.REVISION),但您只需要一个两个数字的版本字符串 (MAJOR.MINOR) 作为 AC_PACKAGE_VERSION 宏的扩展。

如果您熟悉正则表达式和捕获括号,并且希望能够完成更复杂的任务,我已经编写了这个通用的可变参数宏(您可以将其粘贴在 configure.ac 的开头),

dnl  n4_define_substrings_as(string, regexp, macro0[, macro1[, ... macroN ]])
dnl  ***************************************************************************
dnl
dnl  Searches for the first match of `regexp` in `string` and defines custom
dnl  macros accordingly
dnl
dnl  For both the entire regular expression `regexp` (`\0`) and each
dnl  sub-expression within capturing parentheses (`\1`, `\2`, `\3`, ... , `\N`)
dnl  a macro expanding to the corresponding matching text will be created,
dnl  named according to the argument `macroN` passed. If a `macroN` argument is
dnl  omitted or empty, the corresponding parentheses in the regular expression
dnl  will be considered as non-capturing. If `regexp` cannot be found in
dnl  `string` no macro will be defined. If `regexp` can be found but some of
dnl  its capturing parentheses cannot, the macro(s) corresponding to the latter
dnl  will be defined as empty strings.
dnl
dnl  Source: https://github.com/madmurphy/not-autotools
dnl
dnl  ***************************************************************************
m4_define([n4_define_substrings_as],
    [m4_bregexp([$1], [$2],
        m4_ifnblank([$3],
            [[m4_define(m4_normalize([$3]), [m4_quote(\&)])]])[]m4_if(m4_eval([$# > 3]), [1],
            [m4_for([_idx_], [4], [$#], [1],
                [m4_ifnblank(m4_quote(m4_argn(_idx_, $@)),
                    [[m4_define(m4_normalize(m4_argn(]_idx_[, $@)), m4_quote(\]m4_eval(_idx_[ - 3])[))]])])]))])

可以用来做:

n4_define_substrings_as(

    m4_include([VERSION]),

    [\([0-9]+\)\s*\.\s*\([0-9]+\)\s*\.\s*\([0-9]+\)],

    [FOO_VERSION_STRING], [FOO_VERSION_MAJOR], [FOO_VERSION_MINOR], [FOO_VERSION_REVISION]

)

AC_INIT([foo], FOO_VERSION_MAJOR[.]FOO_VERSION_MINOR[.]FOO_VERSION_REVISION)

这样FOO_VERSION_MAJORFOO_VERSION_MINORFOO_VERSION_REVISION 的宏在configure.ac 中始终可用。

注意:n4_define_substrings_as() 宏名称中的n4_ 前缀代表“Not m4sugar”。 p>

如果在VERSION 文件中找不到上面的正则表达式,n4_define_substrings_as() 安全地没有定义相应的宏名称。这允许针对这种特殊情况生成错误(必须在AC_INIT() 之后立即粘贴以下行):

m4_ifndef([FOO_VERSION_STRING], [AC_MSG_ERROR([invalid version format in `VERSION` file])])

尽管读取一个简单的VERSION 文件看起来很简单,但如果您想从package.json 文件中检索版本字符串,事情就会变得更加棘手。这里n4_define_substrings_as() 宏可以派上用场:

n4_define_substrings_as(

    m4_quote(m4_include([package.json])),

    ["?version"?:\s*"?\s*\(\([0-9]+\)\s*\.\s*\([0-9]+\)\s*\.\s*\([0-9]+\)\)\s*"?],

    [JSON_ENTRY], [FOO_VERSION_STRING], [FOO_VERSION_MAJOR], [FOO_VERSION_MINOR], [FOO_VERSION_REVISION]

)

AC_INIT([foo], FOO_VERSION_MAJOR[.]FOO_VERSION_MINOR[.]FOO_VERSION_REVISION)

n4_define_substrings_as() 宏也接受空参数,因此如果您愿意,可以将 [JSON_ENTRY] 参数替换为 [],因为您可能永远不会使用 JSON 源字符串 "version": "999.9.9"

如果您只需要从package.json 文件中检索完整版本字符串,但不需要使用FOO_VERSION_MAJORFOO_VERSION_MINORFOO_VERSION_REVISION,则可以去掉其中的一些捕获括号上面的正则表达式,如下例:

n4_define_substrings_as(

    m4_quote(m4_include([package.json])),

    ["?version"?:\s*"?\s*\([0-9]+\.[0-9]+\.[0-9]+\)\s*"?],

    [], [FOO_VERSION_STRING]

)

AC_INIT([foo], FOO_VERSION_STRING)

为了完整起见,由于最后一个示例只有一个字符串要捕获,因此也可以不使用n4_define_substrings_as() 将其重写为:

AC_INIT([foo],
    m4_bregexp(m4_quote(m4_include([package.json])),
        ["?version"?:\s*"?\s*\([0-9]+\.[0-9]+\.[0-9]+\)\s*"?],
        [\1]))

【讨论】:

以上是关于从 configure.ac 中的文件读取版本号的主要内容,如果未能解决你的问题,请参考以下文章

autotools:将常量从 configure.ac 传递给 python 脚本

configure.ac中如何处理多个版本的Automake

更改 configure.ac 中的 PYTHON_PREFIX

Linux下使用automakeautoconf生成configure文件

Linux下使用automakeautoconf生成configure文件

如果 configure.ac 可用,是不是应该分发“配置”脚本?