Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(GNU make) installcheck a library

How do I implement the make installcheck target when building a library? That is, I have a check target that creates a test program that links to the created library, called by some script in order to check if the library code functions properly, and I wish to perform the same checks but linking to the library after it has been installed; how can I implement this?

like image 342
Zorawar Avatar asked Jun 21 '13 19:06

Zorawar


People also ask

What is $$ in makefile?

Double dollar sign If you want a string to have a dollar sign, you can use $$ . This is how to use a shell variable in bash or sh . Note the differences between Makefile variables and Shell variables in this next example.

How do I make my own makefile?

A Makefile.am has the same syntax as an ordinary Makefile . When automake processes a Makefile.am it copies the entire file into the output Makefile.in (that will be later turned into Makefile by configure ) but will react to certain variable definitions by generating some build rules and other variables.

What is the default make target?

By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe.


1 Answers

Since I can't find any guides on this, I will present the method that I have come up with, which has been patched together from reading the GNU automake manual and some general trial and error. It might be ugly, but it works...

If the check routines for building a library involve linking the library to a test program and seeing if that program works correctly, then to installcheck the library we need only do the same thing but link the test program to the installed library rather than the locally built library.

Let's call the library alpha (so we'll be creating libalpha.so and/or libalpha.a). Supposing alpha's source code is in the file alpha.cpp in the src directory, we'll create src/Makefile.am as usual:

# src/Makefile.am

lib_LTLIBRARIES = libalpha.la
libalpha_la_SOURCES = alpha.cpp
include_HEADERS = alpha.h

The check routine involves creating a binary beta that links to alpha. The source code for beta is in the file beta.cpp in the directory tests. The automake file tests/Makefile.am looks like this:

# tests/Makefile.am

check_PROGRAMS = beta
beta_SOURCES = beta.cpp
beta_CPPFLAGS = -I$(top_srcdir)/src
beta_LDADD = $(top_builddir)/src/libalpha.la

We will create our check and installcheck routines by creating "local" targets in tests/Makefile.am like so:

# tests/Makefile.am

# ...

check-local:
    # recipe to run when 'make check' is called.

installcheck-local:
    # recipe to run when 'make installcheck' is called.

The check and installcheck targets conflict because use of either target prevents the other target from executing properly (one target "taints" the build tree for the other target); in order for the other target to execute properly we need to remove beta and its object files and have the target recompile and re-link as it sees fit according to its nature (installcheck to installed files, check to local files).

We can solve this issue of tainted build trees by simply running make clean in the recipe of both targets. This will clearly remove the tainted builds, but is overzealous because we don't need to rebuild whenever we run the same target again. The build tree is only tainted whenever we run the other target.

We can only solve this complication by remembering which of the two targets had been called previously, which we can do via the creation/destruction of an intermediary file (let's call it taint). The check target is tainted whenever the file taint exists, which it resolves by cleaning, rebuilding and removing taint; and the installcheck target is tainted when the file taint does not exist, which it resolves by cleaning, rebuilding and creating taint.

Our targets will take the form:

# tests/Makefile.am

# ...

check-local:
      # First, check to see if the build tree is tainted and rebuild if so
    test ! -f taint || $(MAKE) $(AM_MAKEFLAGS) check_rebuild
      # Then, run our check tests. Substitute with your shell scripts or testsuite files as appropriate
    ./beta

installcheck-local:
      # First, check to see if the build tree is tainted and rebuild if so
    test -f taint || $(MAKE) $(AM_MAKEFLAGS) installcheck_rebuild
      # Then, run our installcheck tests. Substitute with your shell scripts or testsuite files as appropriate
    ./beta

The target check_rebuild needs to rebuild according to how check will run, and will look like this:

# tests/Makefile.am

# ...

.PHONY: check_rebuild
check_rebuild:
    $(MAKE) $(AM_MAKEFLAGS) clean          # remove tainted build tree
    $(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT)  # rebuild beta
    rm -f taint                            # mark build tree as untainted

The target installcheck_rebuild likewise looks like this:

# tests/Makefile.am

# ...

.PHONY: installcheck_rebuild
installcheck_rebuild:
    $(MAKE) $(AM_MAKEFLAGS) clean          # remove tainted build tree
    $(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT) \
      beta_CPPFLAGS="-I$(DESTDIR)$(includedir)" \
      beta_LDADD="$(DESTDIR)$(libdir)/libalpha.la" \
      beta_DEPENDENCIES="$(DESTDIR)$(libdir)/libalpha.la"
    echo 1 > taint                         # mark build tree as untainted

Note that rebuilding beta in installcheck_rebuild now requires the overriding of the automake variables so that they point to the installed library.

Finally, we need to add the taint file to DISTCLEANFILES so that running distcheck does not fail with "files left in build directory after distclean" errors.

And that should be it. The final tests/Makefile.am should look like this:

# tests/Makefile.am

check_PROGRAMS = beta
beta_SOURCES = beta.cpp
beta_CPPFLAGS = -I$(top_srcdir)/src
beta_LDADD = $(top_builddir)/src/libalpha.la

taint_file = .taint

check-local:
    test ! -f $(taint_file) || $(MAKE) $(AM_MAKEFLAGS) check_rebuild
    ./beta # substitute with your actual test routines

installcheck-local:
    test -f $(taint_file) || $(MAKE) $(AM_MAKEFLAGS) installcheck_rebuild
    ./beta # substitute with your actual test routines

.PHONY: check_rebuild
check_rebuild:
    $(MAKE) $(AM_MAKEFLAGS) clean
    $(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT)
    rm -f $(taint_file)

.PHONY: installcheck_rebuild
installcheck_rebuild:
    $(MAKE) $(AM_MAKEFLAGS) clean
    $(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT) \
      beta_CPPFLAGS="-I$(DESTDIR)$(includedir)" \
      beta_LDADD="$(DESTDIR)$(libdir)/libalpha.la" \
      beta_DEPENDENCIES="$(DESTDIR)$(libdir)/libalpha.la"
    echo 1 > $(taint_file)

DISTCLEANFILES = $(taint_file)

Disclaimer

This has been checked on Linux for a "standard" build, but it may not work on other build environments or if you are trying to do something "exotic". Hopefully it should, but it is not something that I have bothered to check. If there are errors, the problem will likely be a missing or misused variable in one of my targets above.

For example, the check_rebuild target has the line:

check_rebuild:
    # ...
    $(MAKE) $(AM_MAKEFLAGS) beta$(EXEEXT)
    # ...

The variables $(AM_MAKEFLAGS) and $(EXEEXT) are a part of how automake itself creates routines to populate in the Makefiles it creates, and had I neglected them in my targets above, then it might have caused the build to fail (or at least not function as expected).

I have tried to make sure the targets that I have suggested above are likewise canonically constructed, but I may have missed something out. Your best bet in case of build errors is to open the Makefiles generated by automake itself during the build, seeing how it is creating objects and mimicking those constructs in the corresponding Makefile.am files, as I have tried to do.

The other likely issue may be in the "hackish" way I've built the beta binary in the installcheck_rebuild target. Again, your best bet in diagnosing problems will be to see how automake is doing things in the Makefiles it generates, and trying to mimic that. Otherwise, a read of the automake manual will be in order, and failing that, you will likely need the help of people more knowledgeable than me. Good luck.

like image 183
Zorawar Avatar answered Sep 20 '22 02:09

Zorawar