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!
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])])
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 ⇒<<
Advantages:
m4_normalize(m4_include([VERSION]))
instead of m4_esyscmd_s([cat VERSION])
the VERSION
file is added to the DIST_COMMON
variable in the Makefile
– which means that the VERSION
file is automatically redistributed when the user launches make dist
m4_normalize(m4_include([VERSION]))
instead of m4_esyscmd_s([cat VERSION])
the VERSION
file is added to the prerequisites of make aclocal.m4
(which means that each time you modify the VERSION
file the configure
script gets automatically updated)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_quote(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 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])[))]])])]))])
which can be used for doing:
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)
so that the macros FOO_VERSION_MAJOR
, FOO_VERSION_MINOR
and FOO_VERSION_REVISION
are always available within configure.ac
.
Note: The
n4_
prefix in then4_define_substrings_as()
macro name stands for “Not m4sugar”.
If the regular expression above cannot be found in the VERSION
file, n4_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 n4_define_substrings_as()
macro can come very much in handy:
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)
The n4_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:
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)
For completeness, since the last example has only one string to capture, it can also be rewritten without using n4_define_substrings_as()
as:
AC_INIT([foo],
m4_bregexp(m4_quote(m4_include([package.json])),
["?version"?:\s*"?\s*\([0-9]+\.[0-9]+\.[0-9]+\)\s*"?],
[\1]))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With