问题
I define my project version number in a plain text file instead of configure.ac for some reasons. I would like to create a statement that would read the version number and store it during compilation time.
Right now my configure.ac looks like this:
AC_INIT([my program],[999.9.9])
I would like to have something like:
AC_INIT([my program],[ $(cat VERSION) ])
This wont work of course. What is the trick here? (I know I am loosing some portability - I don't care at the moment). Thanks!
回答1:
Try:
AC_INIT([my program], m4_esyscmd([tr -d '\n' < VERSION]))
Edited with fixes suggested in the comments.
I was also able to remove the non-portable tr
invocation using:
AC_INIT([my program], [m4_translit(m4_esyscmd([cat VERSION]),m4_newline)])
which seems to work just as well, as does the solution suggested by Enrico in the comments below:
AC_INIT([my program], [m4_esyscmd_s([cat VERSION])])
回答2:
You can simply use the native macro m4_include()
(instead of invoking tr
or cat
via m4_esyscmd_s()
as suggested by ldav1s),
AC_INIT([foo], m4_normalize(m4_include([VERSION])))
which is also what the official guide of GNU M4 suggests fo similar cases:
$ cat examples/incl.m4 ⇒Include file start ⇒foo ⇒Include file end
[…]
The fact that
include
andsinclude
expand to the contents of the file can be used to define macros that operate on entire files. Here is an example, which defines ‘bar
’ to expand to the contents ofincl.m4
:$ m4 -I examples define(`bar', include(`incl.m4')) ⇒ This is `bar': >>bar<< ⇒This is bar: >>Include file start ⇒foo ⇒Include file end ⇒<<
GNU M4 offers also support for regular expressions, so if you want to make sure that the version string always follows a particular pattern – or if the VERSION
file contains more text than just the version string – you can use m4_bregexp()
to find what you are looking for:
AC_INIT([foo], m4_bregexp(m4_include([VERSION]), [[0-9]+\.[0-9]+\.[0-9]+], [\&]))
This is also the safest approach, since if the regular expression above cannot be found in the VERSION
file the second argument of AC_INIT()
simply expands to an empty string and the following error message is thrown by Autoconf:
error: AC_INIT should be called with package and version arguments
A typical case where it can be useful to invoke m4_bregexp()
to process the content of a VERSION
file is when this contains a three-number version string (MAJOR.MINOR.REVISION
) but you only want a two-number version string (MAJOR.MINOR
) as the expansion of your AC_PACKAGE_VERSION
macro.
If you are familiar with regular expressions and capturing parentheses and you want to be able to do more complex tasks, I had written this general purpose variadic macro (you can paste it at the beginning of your configure.ac
),
dnl NA_DEFINE_SUBSTRINGS_AS(string, regexp, macro0[, macro1[, ... macroN ]])
dnl ***************************************************************************
dnl
dnl Searches for the first match of `regexp` in `string`. For both the entire
dnl regular expression `regexp` (`\0`) and each sub-expression within capturing
dnl parentheses (`\1`, `\2`, `\3`, ... , `\N`) a macro expanding to the
dnl corresponding matching text will be created, named according to the
dnl argument `macroN` passed. If a `macroN` argument is omitted or empty, the
dnl corresponding parentheses in the regular expression will be considered as
dnl non-capturing. If `regexp` cannot be found in `string` no macro will be
dnl defined. If `regexp` can be found but some of its capturing parentheses
dnl cannot, the macro(s) corresponding to the latter will be defined as empty
dnl strings.
dnl
dnl Source: https://github.com/madmurphy/not-autotools
dnl
dnl ***************************************************************************
AC_DEFUN([NA_DEFINE_SUBSTRINGS_AS], [
m4_if(m4_eval([$# > 2]), [1], [
m4_if(m4_normalize(m4_argn([$#], $*)), [], [],
[m4_bregexp([$1], [$2], [m4_define(m4_normalize(m4_argn([$#], $*)), \]m4_if([$#], [3], [&], m4_eval([$# - 3]))[)])])
m4_if(m4_eval([$# > 3]), [1], [NA_DEFINE_SUBSTRINGS_AS(m4_reverse(m4_shift(m4_reverse($@))))])
])
])
which can be used for doing:
NA_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)
so that the macros FOO_VERSION_MAJOR
, FOO_VERSION_MINOR
and FOO_VERSION_REVISION
are always available within configure.ac
.
Note: The
NA_
prefix in theNA_DEFINE_SUBSTRINGS_AS()
macro name stands for “Not Autotools”.
If the regular expression above cannot be found in the VERSION
file, NA_DEFINE_SUBSTRINGS_AS()
safely does not define the corresponding macro names. This allows to generate an error for this particular case (the following line must be pasted immediately after AC_INIT()
):
m4_ifndef([FOO_VERSION_STRING], [AC_MSG_ERROR([invalid version format in `VERSION` file])])
For as trivial as it can look to read a simple VERSION
file, things get trickier if you want to retrive the version string from a package.json
file. Here the NA_DEFINE_SUBSTRINGS_AS()
macro can come very much in handy:
NA_DEFINE_SUBSTRINGS_AS(
m4_join([|], m4_unquote(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)
Note:
.json
files might contain commas and square brackets (which are not so friendly towards GNU m4-ish), and these need to be removed/replaced before processing a JSON string. In the code above the macrom4_unquote()
removes all first-level square brackets possibly present inpackage.json
– if the latter contains nested arrays,m4_unquote()
must be invoked on itself as many times as the maximum level of array nesting gets – then the macrom4_join()
replaces all commas with'|'
.
The NA_DEFINE_SUBSTRINGS_AS()
macro accepts also empty arguments, so if you prefer you may replace the [JSON_ENTRY]
argument with []
, since probably you are never going to use the JSON source string "version": "999.9.9"
.
If you only need to retrieve the full version string from a package.json
file but you don't need to use FOO_VERSION_MAJOR
, FOO_VERSION_MINOR
and FOO_VERSION_REVISION
, you can get rid of some of the capturing parentheses in the regular expression above, as in the following example:
NA_DEFINE_SUBSTRINGS_AS(
m4_join([|], m4_unquote(m4_include([package.json]))),
["?version"?:\s*"?\s*\([0-9]+\.[0-9]+\.[0-9]+\)\s*"?],
[], [FOO_VERSION_STRING]
)
AC_INIT([foo], FOO_VERSION_STRING)
For completeness, since the last example has only one string to capture, it can also be rewritten without using NA_DEFINE_SUBSTRINGS_AS()
as:
AC_INIT([foo], m4_bregexp(m4_join([|], m4_unquote(m4_include([package.json]))), ["?version"?:\s*"?\s*\([0-9]+\.[0-9]+\.[0-9]+\)\s*"?], [\1]))
来源:https://stackoverflow.com/questions/8559456/read-a-version-number-from-a-file-in-configure-ac