Tuesday, April 25, 2017

Posted by beni in , , , , | April 25, 2017

A config m4 generator in m4


When you create a PHP extension you need to provide a config.m4 file.
There are plenty of examples how a config.m4 should look like and one can (and does) mimic these examples.

Since they are all somewhat similar I wanted to take a step back and find a way how to automatically create a valid config.m4 by using

  • a set of definitions (name of the extension, name of C file etc.)
  • a config.m4 template

    Of course there are plenty of ways to do this and I decided to explore the m4 way i.e. writing an m4 file which after being parsed by m4 would generate the config.m4 file.

    The m4 file consists of three parts (aside from documenation).

  • The first part lists some variables which need to be changed by the user according to his needs.
    The user needs to provide:
    • The name of the extension (which will name the shared library)
    • The title of the extension (more a descriptive name for humans)
    • The C file
    • The name of the external library
      The path will be provided at configuration time via --with-PHP_EXT=DIR
  • The second part creates a set of derived variables (derived from part 1)
    These variables will replace the respective content in the template.
  • The third part is the PHP template.
    The template contains code to generate a PHP interface to an external application whose library should be linked in.

    The m4 file

    I named it preconfig.m4
     divert(-1) dnl This is a generalized config.m4 file which defines all settings at the beginning dnl in order to simplify the building of php extensions dnl Run as: dnl m4 preconfig.m4 > config.m4 dnl phpize dnl ./configure --with-PHP_EXT=DIR dnl make dnl where PHP_EXT is the name of the extension and DIR is the directory of the external package dnl which should contain an include and a lib directory dnl ---------------------------------- dnl A set of definitions for the new php extension to be built incl. the C file(s) dnl 1) the name of the shared library e.g. foo dnl 2) a nickname for the share library e.g. Foo bar dnl 3) the name of the C file e.g. foo.c dnl 4) the name of the library to be linked into e.g. bar dnl (the corresponding libbar.a must be found in DIR/lib) dnl Note: DIR/include should contain the corresponding .h file dnl which needs to be called in foo.c dnl ---------------------------------- dnl The name of the php extension define(`PHP_EXT,`foo) dnl A name of the extension (for humans) define(`EXT_NAME,`Foo bar) dnl The corresponding C file (there should be an include file too like php_config.h) define(`PHP_C_FILES,`foo.c) dnl To be linked as -lbar with a corresponding libbar.a in DIR/lib define(`LIBNAME,`bar) dnl ---------------------------------- dnl Everything below here is derived from the settings above dnl ---------------------------------- dnl The name of the extension in capitals define(`PHP_EXT_UPP,translit( PHP_EXT, [a-z], [A-Z])) dnl The internal php variable to define the existance of the extension define(`PHP_VAR,`PHP_PHP_EXT_UPP) dnl The directory of lib/libbar.a define(`PHP_DIR,PHP_EXT_UPP`_DIR) dnl An autoconfig variable define(`HAVE_EXT,`HAVE_PHP_EXT_UPP) dnl An autoconfig variable define(`EXT_SHARED_ADD,PHP_EXT_UPP`_SHARED_LIBADD) dnl An autoconfig variable define(`HAVE_EXTLIB,`HAVE_PHP_EXT_UPP`LIB) dnl The filename of the library bar -> libbar.a define(`LIBS,`libLIBNAME`.a) dnl The default paths for libbar.a define(`LIB_PATHS,`/usr/local /usr) dnl ---------------------------------- dnl Here starts the actual config.m4 code dnl ---------------------------------- divert() `dnl This is the config.m4 file for extension EXT_NAME` (PHP_EXT`) `dnl The extension should be built with --with-PHP_EXT`=DIR PHP_ARG_WITH( PHP_EXT, whether to enable EXT_NAME support, [ --with-PHP_EXT=[DIR] Enable EXT_NAME support]) `dnl Check whether the extension is enabled at all if test "$PHP_VAR" != "no"; then AC_DEFINE(HAVE_EXT, 1, [Whether you have EXT_NAME]) `dnl Add include path PHP_ADD_INCLUDE($PHP_VAR/include) `dnl Check whether the lib exists in the lib directory AC_MSG_CHECKING(for LIBS in $PHP_VAR and LIB_PATHS ) for i in $PHP_VAR LIB_PATHS; do if test -r $i/lib/LIBS; then PHP_DIR=$i AC_MSG_RESULT(found in $i) fi done if test -z "$PHP_DIR"; then AC_MSG_RESULT(not found) AC_MSG_ERROR(Please reinstall the LIBS distribution - includes should be in /include and LIBS should be in /lib) fi `dnl Add lib path PHP_ADD_LIBRARY_WITH_PATH( LIBNAME, $PHP_VAR/lib, EXT_SHARED_ADD) AC_DEFINE( HAVE_EXTLIB, 1, [Whether EXT_NAME support is present and requested]) `dnl Finally, tell the build system about the extension and what files are needed PHP_SUBST(EXT_SHARED_ADD) PHP_NEW_EXTENSION( PHP_EXT, PHP_C_FILES, $ext_shared) fi 

    You should run

     m4 preconfig.m4 >config.m4 
    which will report
     m4:../s1/preconfig.m4:65: empty string treated as 0 in builtin `divert 
    which can be ignored, it is about ommitting a set of superfluous empty lines.

    The resulting config.m4

    What you can see is that all references to PHP_VAR, PHP_EXT etc. have been replaced by the proper FOO versions.

     dnl This is the config.m4 file for extension Foo bar (foo) dnl The extension should be built with --with-foo=DIR PHP_ARG_WITH( foo, whether to enable Foo bar support, [ --with-foo=[DIR] Enable Foo bar support]) dnl Check whether the extension is enabled at all if test "$PHP_FOO" != "no"; then AC_DEFINE(HAVE_FOO, 1, [Whether you have Foo bar]) dnl Add include path PHP_ADD_INCLUDE($PHP_FOO/include) dnl Check whether the lib exists in the lib directory AC_MSG_CHECKING(for libfoo.a in $PHP_FOO and /usr/local /usr ) for i in $PHP_FOO /usr/local /usr; do if test -r $i/lib/libfoo.a; then FOO_DIR=$i AC_MSG_RESULT(found in $i) fi done if test -z "$FOO_DIR"; then AC_MSG_RESULT(not found) AC_MSG_ERROR(Please reinstall the libfoo.a distribution - includes should be in /include and libfoo.a should be in /lib) fi dnl Add lib path PHP_ADD_LIBRARY_WITH_PATH( libfoo.a, $PHP_FOO/lib, FOO_SHARED_LIBADD) AC_DEFINE( HAVE_FOOLIB, 1, [Whether Foo bar support is present and requested]) dnl Finally, tell the build system about the extension and what files are needed PHP_SUBST(FOO_SHARED_LIBADD) PHP_NEW_EXTENSION( foo, foo.c, $ext_shared) fi 

    This file will run nicely with phpize (I tested with PHP5). This particular example will of course end with a configuration error but should run ok for a properly existing and installed library.

     ./configure --with-foo=DIR ... ... checking whether to enable Foo bar support... yes, shared checking for libfoo.a in /Users/bar and /usr/local /usr ... not found configure: error: Please reinstall the libfoo.a distribution - includes should be in /include and libfoo.a should be in /lib 

    Of course the template could be extended to include other config.m4 stuff (debugging, check the validity of the library etc.) but it works nicely for me.

  • Search