Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tell automake to run a few commands on a program just before installing or just after linking

Tags:

automake

I would like use automake to process/modify the binaries before they are installed. For example, I would like to extract the symbols from the binary into a separate file and location (like this). Another example is to collect the md5sums of critical assets for publishing a report.

This is a simplified (but representative) Makefile.am that I have:

abc_PROGRAMS = foo
foo_SOURCES = foo.cpp

pqr_PROGRAMS = bar
bar_SOURCES = bar.cpp

ALL_SYMS := $(PROGRAMS:%=%.sym)

sym_DATA = $(ALL_SYMS)

# A convenient target such as "make foo.sym" will trigger this rule:
$(ALL_SYMS): %.sym : %
   ...
   objcopy --preserve-dates --only-keep-debug $< $@
   objcopy --preserve-dates --strip-all --add-gnu-debuglink=$@ $<
   ...

I have tried a few ways to hook in my script in during install, but with limited success.

  1. One potential way is using install-exec-local:

    install-exec-local: $(ALL_SYMS)
    

    The problem with install-exec-local is that when I use: "make install -j", the install-exec-local target runs in parallel with the install, so binaries foo and bar get installed into at their respective destination dirs, even while they are being were being modified by the %.sym rule, thereby potentiall messing up the installed files. However this solution works if -j is not used. I would like if I can leverage the advantages of using -j.

  2. Another potential way is using install-exec-hook:

    install-exec-hook: $(ALL_SYMS)
    

    The problem with install-exec-hook is that the programs foo and bar are already installed before I get a chance to modify them using the %.sym rule. Besides foo and bar get installed at different locations, I don't have a generic way to get their destination locations while writing a suitable install-exec-hook rule.

  3. Another potential way is to define STRIP_PROGRAM or something equivalent, but that does not work really well, because i think it won't work with the "make install" target, but only with "make install-strip -j". I have not gone this path, because our build scripts use "make install -j".

  4. Another way is to do it is by creating build rules that run during build time rather than install time. But this one is really cumbersome, that it creates many files in the build directory and I would like to avoid it. Also new people in the project while adding new programs can easily bypass these finely tuned elaborate arrangements.

    abc_PROGRAMS = foo
    foo : foo.tmp
    foo.sym : foo
    noinst_PROGRAMS = foo.tmp
    foo_tmp_SOURCES = foo.cpp
    
    
    pqr_PROGRAMS = bar
    bar : bar.tmp
    bar.sym : bar
    noinst_PROGRAMS += bar.tmp
    bar_tmp_SOURCES = bar.cpp
    
    sym_DATA = foo.sym bar.sym
    
    foo bar: % : %.tmp
    ...
    cp $@ $<
    objcopy --only-keep-debug $@ [email protected]
    objcopy --strip-all [email protected] $@
    

I would have loved if automake provided a hook which ran before a program is installed, or just after a program is built.

The stackoverflow.com community is really making a difference to the quality of solutions that are available on the internet. I am sure someone must have faced a problem similar to mine. I am awaiting good solutions or industry best practices on this issue. Lastly, I apologize for not being able to make the question much more brief or easier to understand.

like image 549
Shriram V Avatar asked Oct 21 '12 05:10

Shriram V


2 Answers

Tell automake to run a few commands on a program just before installing or just after linking

The "just after linking" part can be accomplished using libtool's postlink_cmds step. postlink_cmds is documented:

Variable: postlink_cmds

Commands necessary for finishing linking programs. postlink_cmds are executed immediately after the program is linked. Any occurrence of the string @OUTPUT@ in postlink_cmds is replaced by the name of the created executable (i.e. not the wrapper, if a wrapper is generated) prior to execution. Similarly, @TOOL_OUTPUT@ is replaced by the toolchain format of @OUTPUT@. Normally disabled (i.e. postlink_cmds empty).


Below is part of a configure.ac that uses sed to patch libtool and postlink_cmds after libtool is created. We needed it to apply software capabilities on a Solaris shared object because our link options to set them through a mapfile -M <mapfile> was being stripped (apparently libtool reserves nearly all -M options).

Though libtool has a postlink_cmds we don't know how to set it more traditionally. Maybe there is a AC_XXX somewhere to do it.

#############################################################################
## Hack ahead. GNU's libtool does not honor our '-M cryptopp.mapfile' in AM_LDFLAGS
## for the libcryptopp.so recipe. Many thanks to John Martin of Oracle for help wielding
## elfedit. The hw_cap = 0x1800 is SSE2 SSE and its needed for old hardware when new
## instructions are present, like SSE4.1, SSE4.2, CLMUL, AES, RDRAND, RDSEED and SHA.
## TODO: determine if we are allowed to hijack libtool's postlink_cmds function. It
## works well but we may be violating a policy somewhere along the line.

if test "$IS_SUN_OS" -ne "0" && test "$IS_SUN_COMPILER" -ne "0"; then
   if test "$IS_IA32" -ne "0"; then

      echo "Adding postlink script"
      rm -f postlink.sh 2>/dev/null
      echo "echo \"Fixing shared object capabilities\"" >> postlink.sh
      echo "elfedit -e 'cap:hw1 0x1800' .libs/cryptest" >> postlink.sh
      echo "elfedit -e 'cap:hw1 0x1800' .libs/cryptestcwd" >> postlink.sh
      echo "elfedit -e 'cap:hw1 0x1800' .libs/libcryptopp.so.6.0.0" >> postlink.sh
      echo "" >> postlink.sh
      chmod +x postlink.sh

      echo "Adding postlink script to libtool postlink_cmds"
      sed 's|postlink_cmds=""|postlink_cmds="./postlink.sh"|g' libtool > libtool.fixed
      mv libtool.fixed libtool
      chmod +x libtool
   fi
fi

Before hacking with sed, I tried setting postlink_cmds more traditionally in configure.ac. Unfortunately, it did not work and it resulted in postlink_cmds="" in the generated libtool. Here is something that does not work:

if test x"$postlink_cmds" = "x"; then
    postlink_cmds="./postlink.sh"
else
    postlink_cmds="~ ./postlink.sh"
fi

The ~ is due to the libtool manual stating:

... The convention used for naming variables that hold shell commands for delayed evaluation, is to use the suffix _cmd where a single line of valid shell script is needed, and the suffix _cmds where multiple lines of shell script may be delayed for later evaluation. By convention, _cmds variables delimit the evaluation units with the ~ character where necessary.

like image 197
jww Avatar answered Nov 15 '22 08:11

jww


Along the lines of option #3, you might in this case consider setting up one of the INSTALL variables (INSTALL, INSTALL_DATA, INSTALL_PROGRAM, INSTALL_SCRIPT) and point it at a script that does what you want, and calls the real install as the last step.

like image 21
ldav1s Avatar answered Nov 15 '22 07:11

ldav1s