Showing posts with label make. Show all posts
Showing posts with label make. Show all posts

Wednesday, April 12, 2017

Quick diff tip, make, et al

I'm using make for a simple shell project, to run tests before committing. The check was trivial:

SHELL = bash

test:
	@./run-tests t | grep 'Summary: 16 PASSED, 0 FAILED, 0 ERRORED' >/dev/null

This has the nice quality of Silence is Golden: say nothing when all is good. However, it loses the quality of Complain on Failure: it simply fails without saying why.

A better solution, preserving both qualities:

SHELL = bash

test:
	@diff --color=auto \
	    <(./run-tests t | grep 'Summary: .* PASSED, .* FAILED, .* ERRORED') \
	    <(echo 'Summary: 16 PASSED, 0 FAILED, 0 ERRORED')

It still says nothing when all is good, but now shows on failure how many tests went awry. Bonus: color for programmers who like that sort of thing.

Why set SHELL to bash? I'm taking advantage of Process Substitution. Essentially the command outputs inside the subshells are turned into special kinds of files, and diff likes to compare files. Ksh and Zsh also support process substitution, so I'm going with the most widely available option.

UPDATE:

Why are my arguments to diff ordered like that? In usual testing language, I'm comparing "actual" vs "expected", and more commonly you'll see programmers list "expected" first.

diff by default colors the left-hand input in RED, and the right-hand input in GREEN. On failure, it makes more sense to color "actual" in red and "expected" in green. Example output on failure:

$ make
1c1
< Summary: 17 PASSED, 1 FAILED, 0 ERRORED
---
> Summary: 19 PASSED, 0 FAILED, 0 ERRORED
make: *** [Makefile:4: test] Error 1

Friday, September 16, 2016

Google Test, generated source, and GNU Make

I had trouble with this arrangement:

  • Using Pro*C for a client to generate "C" files from .pc sources
  • Google Test for unit testing
  • GNU Make

What was the problem?

Ideally I could write a pattern rule like this:

%-test.o: %.c %-test.cc

This means when I want to compile my C++ test source, it required make first run the Pro*C preprocessor to generate a "C" source used in the test. Why? Google tests follow this template:

#include "source-to-test.c"
#include <gtest/gtest.h>
// Tests follow

Google test includes your source file (not header) so the test code has access to static variables and functions (think "private" if you're from Java or C#).

So my problem is make is very clever with rules like:

%.foo: %.bar
%.qux: %.foo

And knows that if you want a "bob.qux", which needs a "bob.foo", and there's no "bob.foo" but there is a file named "foo.bar", make follows the recipe for turning a "bar" into a "foo", and this satisfies the rule for "bob.qux".

However the simple rule I guessed at:

%-test.o: %.c %-test.cc

Doesn't work! GNU Make has a corner case when there are multiple prerequisites (dependencies), and won't make missing files even where there's another rule saying how to do so.

There is another way to state what I want:

%-test.o: %-test.cc: %.c

This is called a static rule. It looks promising, but again doesn't work. GNU make does not support patterns (the "%") in static rules. I would need to write each case out explicitly, e.g.:

a-test.o: a-test.cc: a.c

While this does work, it's also a problem.


What's wrong with being explicit?

Nothing is wrong with "explicit" per se. Usually it's a good thing. In this case, it clashes with the rule of "say it once". For each test module a programmer writes, he would need to edit the Makefile with a new, duplicative rule. So when new tests break, instead of thinking "my code is wrong", he needs to ask "is it my code, or my build?" Extra cognitive burden.


What does work?

There is a way to get the benefit of a static rule without the duplication, but it's hackery—good hackery, to be sure, but violating the "rule of least surprise". Use make's powers to rewrite the Makefile at run time:

define BUILD_test
$(1:%=%.o): $(1:%-test=%.c) $(1:%=%.cc)
        $$(COMPILE.cc) $$(OUTPUT_OPTION) $(1:%=%.cc)
$(1): $(1:%=%.o)
endef
$(foreach t,$(wildcard *-test.cc),$(eval $(call BUILD_test,$(t:%.cc=%))))

What a mouthful! If I have a "foo-test.cc" file, make inserts these rules into the build:

foo-test.o: foo.c foo-test.cc
        $(COMPILE.cc) $(OUTPUT_OPTION) foo-test.cc
foo-test: foo-test.o

I'd like something simpler, less inscrutable. Suggestions welcome!

Wednesday, June 13, 2012

Emacs, your makefile friend

After long hiatus I find myself against writing (editing) makefiles. Using Emacs compile command makes all the difference. Emacs runs make in another process, sending output to an independent buffer (window, tab, panel) default named *compilation*.

In the *compilation* buffer, Emacs colorizes make's output. This greatly aids comprehension. As icing, Emacs recognizes output patterns from dozens of popular tools, and highlights them as information, warning or error as appropriate.

Emacs simplifies navigation with hyperlinking. The TAB key in *compilation* steps through warnings and errors. Magic.

As a bonus, I looked up common patterns stored in the Emacs variable, compilation-error-regexp-alist. The info page for this variable includes a link to "compilation.txt", a complete list of sample output for each supported tool. It was trivial for me to find right at the top:

* GNU style

symbol: gnu

foo.c:8: message
../foo.c:8: W: message
/tmp/foo.c:8: warning: message

That last line was my ticket. I arrange to echo similar text when something needs attention in my shell command, but without failing the entire build. I take care to break the output into separate shell echo commands so Emacs does not match prematurely when make displays executed commands. I could have prefixed my shell line with "@" (ampersand) to suppress make from printing it, but coding standards here discourage that.

a-file:
        if $(some_bad_condition_set_in_makefile) ; then \
        echo -n $@ ; \
        echo :1: warning: $@ is foobar. >&2 ; fi

I emulate the output of one of the tools Emacs groks, and I get the same magical colorizing and hyperlinking with my output:

making in /some/path
if true ; then \
 echo -n a-file ; \
 echo :1: Warning: a-file is foobar. >&2 ; fi
a-file:1: warning: a-file is foobar.

In *compilation* the TAB key jumps me to the start of a-file when it exists.

Thursday, July 14, 2011

A reminder why make is a beast

At work today was a vivid reminder why make is a beast.
I'm integrating protobuf into a custom build system based on GNU make, call it yamake ("yet another make") and a proprietary IDL-like language, call it SDL ("sort-of definition language"). SDL is firmly rooted in the culture and systems, so it is non-negotiable. Over the course of the past few days, learned the steps:
  1. Write a backend for the Python compiler script which translates SDL to your target language, in my case protobuf.
  2. Hand off protobuf backend to guru colleague who munges it to better match the overall systems, and patches it into yamake.
  3. Work on makefile to actually build usable jar of compiled protobuf for Java.
  4. Watch colleague redo munging to something more permanent, less hacktastic.
  5. Rework on makefile to actually build usable jar of compiled protobuf for Java.
  6. Roll fake maven local repo holding new jar, of course yamake knows nothing about deploying jars.
  7. Build new CORBA IDL jar for Java including new interfaces to access protobuf in other systems.
All this, and finally I can actually do useful work. Tomorrow.
But this is not the point of my post.
Yamake is a wrapper around venerable GNU make, so I am editing makefiles. Ultimately I have this dependency chain: SDL to protobuf to Java to classes to jar. But watch:
$ sdlc -t protobuf foobar.sdl -o proto/foobar.proto
$ protoc --java_out=java proto/foobar.proto
$ javac -sourcepath java java/my/package/FoobarProtos.java
$ jar cf foobar.jar -C java my/package/FoobarProtos*.class
It's actually more Rube-Goldbergesque than this, as the C++ versions of the protobuf play games with namespaces requiring various flags at various stages along with directory changes. So I made Java keep up. You come up with dependency rules that cope with option java_package = "something.random" and option java_outer_classname="SomethingProtos" and mismatched output directories!
I opted for using helper shell scripts for parsing protobuf for proper Java packages with class names and generating dependency makefiles to include in my actual makefile.
Maven, remind me to quit poking on you for your ugly XML and ridiculously noisy output. I forgot all the love you give me.