How can you test ‘dynamic’ conditions in a gnu Makefile?
The net is full of GNU-Makefile advise explaining how to define variables and then later use them at compile time to run different commands or different targets. This is all very useful but often time you want to run a command and test it’s result for certain (dynamic) conditions.
What do I mean by checking ‘dynamic’ conditions in a Makefile?
A “dynamic” condition is one whose outcome depends on the result of building one of your targets (or running some other command within the body of one of your build rules).
Example (integrating unittests into your build process)
In the example which I will give we will want to integrate testing into our project’s build procedures and have the output make reflect whether the tests failed or passed. So we will want to build and run our test apps and then echo to standard output whether the test passed or failed.
Let’ suppose that all of our test apps are designed such that when they run ok they don’t produce any output and when some of the tests fail there will be error messages generated on stderr or stdout. Then, inside our makefile, we can test for this dynamic condition (whether the unit-test passed of failed) and print different messages or take some other action.
Here’s the contents of our sample Makefile:
define report_results_func
if [ -s $(1) ]; \
then \
echo FAILURE; \
else \
echo OK; \
fi
endef
unittest :
@echo "Compiling and running UNITTEST"
# your compilation command and your flags go below...
@gcc $(YOUR_C_FLAGS) unittest.c -o unittest
# run the test, redirect output and errors to a file
@./unittest &> unittest.out
@$(call report_results_func,unittest.out)
The most interesting parts of this Makefile (highlighted in blue) are of course the user defined function checking the test results:
define report_results_func
…and how we call this function from within the ‘unittest’ build rule:
@$(call report_results_func,unittest.out)
The function report_results_func, (which is really a Makefile variable, but since its contents will be dynamically evaluated whenever it is used it is in effect a “function” and so we will call it a function below) – this function will test whether a file (represented by the first param – $1, which must evaluate to a known existing filename) is of size 0.
The if statement and everything else within the body of the function is written in a bash shell script syntax, so this example assumes that your shell is bash. The [ -s
is a standard bash construct for testing if a size of a file is 0 (see the section on CONDITIONAL EXPRESSIONS in bash man page).
So the function will echo “OK” when the size of the file (whose name is passed in as param $1) is 0 and “FAILURE” when this file size is not 0. Since we expect correct tests to produce no output this is exactly what we need.
The best part? You can reuse the above code in your projects – just call the function with a different output filename as a parameter, e.g.
# run another test, redirect output and errors to a file
@./test-1 &> test-1.out
@$(call report_results_func, test-1.out)
…The above will run another test, called test-1, redirect its output to a file named test-1.out and then call our report_results_func function to report results of the test (assuming test-1 has been successfully compiled already, of course…).
One Response to Checking ‘dynamic’ conditions in Makefile’s
Leave a Reply Cancel reply
-
Categories
- Android Development
- Bash
- C programming
- dpkg/apt-get
- drupal
- Emacs
- Git
- Java
- Linux administration
- Linux device drivers
- Linux Gaming
- Linux kernel
- Linux Networking
- Linux on Windows
- Linux printing
- Linux sound and ALSA
- Package Managers
- Programming
- RPM
- Shell and environment
- Tips and tricks
- Uncategorized
- VirtualBox
- Virtualization
- web development
- wine
- WMaker
- Wordpress Tips
- X Window System
- XFCE
-
Articles
- August 2020
- August 2019
- May 2019
- July 2017
- February 2017
- January 2017
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- April 2016
- March 2016
- December 2015
- November 2015
- September 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- October 2014
- February 2014
- January 2014
- November 2013
- October 2013
- June 2013
- April 2013
- March 2013
- February 2013
- January 2013
- December 2012
- October 2012
- August 2012
- July 2012
- June 2012
- May 2012
- April 2012
- March 2012
- February 2012
- September 2011
- August 2011
- June 2011
- May 2011
- April 2011
- March 2011
- February 2011
-
Meta
Cloud
audio bash boot compile C programming cups drupal emacs etc Fedora git grep how to httpd init kernel libc linux linux partition localtime login make mount mp3 mysql networking oracle package managers password phpMyAdmin programming rpm shell sql vbox version control system virtual box vm web server wordpress www xargs xfce xwin yum
Another type of ‘dynamic’ condition that you might need to handle in your Makefile is setting a variable on a per-target basis (target-specific variables).
Suppose the following setup:
1. In your project Makfiles you need to be able to build your main project app(s) as well as some unittest suites/apps. So you need a rule for building the main app (main_app:) and for building unittests (unittests:).
2. Based on an environment variable DEBUG you either build your app and unittests with debug symbols and asserts enabled (-g) or you build for release, i.e. no debug info and no asserts (-DNDEBUG). One exception to the above is that when building the unittests you never disable the assert macros, even DEBUG=0 because your unittests are based on asserts.
3. So you need 2 targets main_app: and unittest: and you need to handle the DEBUG=1 or DEBUG=0 condition differently depending on whether you’re building main_app or unittest…
You do this by using target-specific variable assignment. Here’s one way to do it:
ifneq “$(DEBUG)” “”
ifneq “$(DEBUG)” “0”
all: CFLAGS += -g
else
all: CFLAGS += -DNDEBUG
endif
else
all: CFLAGS += -DNDEBUG
endif
all:
$(CC) $(CFLAGS) $(other_flags) main_app.c -o main_app
ifneq “$(DEBUG)” “”
ifneq “$(DEBUG)” “0”
all: CFLAGS += -g
endif
endif
unittests:
$(CC) $(CFLAGS) $(other_flags) unittest.c -o unittest