Posts for 2011 August
A Makefile for code coverage report with C++
Post by Nico Brailovsky @ 2011-08-30 | Permalink | Leave a comment
So far you should know how to use makefiles and you should have a nice testable project. Then you have everything ready to get a coverage report. Yeah, using makefiles, you guessed!
This time we'll depend on two tools, gcov and gtest. These are in Ubuntu's repositories, so you should have no problem getting them. I won't even bother to explain this makefile (not because it's obvious but because I don't really remember how it works. I wrote this over a year ago).
.PHONY: clean coverage_report
coverage_report:
# Reset code coverage counters and clean up previous reports
rm -rf coverage_report
lcov --zerocounters --directory .
$(MAKE) COMPILE_TYPE=code_coverage &&
$(MAKE) COMPILE_TYPE=code_coverage test
lcov --capture --directory $(BIN_DIR)/$(OBJ_DIR)/code_coverage --base-directory . -o salida.out &&
lcov --remove salida.out "usr/include" -o salida.out &&
genhtml -o coverage_report salida.out
rm salida.out
Bonus makefile target: make your code pretty:
.PHONY: pretty
pretty:
find -L|egrep '.(cpp|h|hh)$$'|egrep -v 'svn|_Test.cpp$$' | xargs astyle --options=none
Remember to change your astyle options as needed.
Bonus II: Example project using gcov and gtest: gcov_gtest_sample.tar. The irony? It doesn't use my common makefile, it predates it.
Link: ASCII graphs, 2.0 style
Post by Nico Brailovsky @ 2011-08-25 | Permalink | Leave a comment
Every once in a while you need to draw a graph to quickly convey some information, and you don't want all the hassle of opening paint, drawing whatever you want, exporting it as png, and all that stuff. Sometimes it's just easier to do it as ASCII art, only you don't want to spend hours carefully aligning pipes and dashes. For these times Asciiflow exists.
Give it a try, it's a great way to quickly generate a diagram. Just remember to use monospace fonts.
A Makefile for TDD with C++
Post by Nico Brailovsky @ 2011-08-22 | Permalink | 1 comments | Leave a comment
So, after reading my post about makefiles you decided that you like them but would like to add some TDD to be buzzword compliant? No problem, that's easy to do.
Assuming you use a naming convention such as this one:
path/to/src/Object.h
path/to/src/Object.cpp
path/to/src/Object_Test.cpp
then it's easy to auto detect which tests should be built:
TEST_SRCS := $(patsubst ./%, %, $(shell find -L|grep -v svn|egrep "_Test.cpp$$" ) )
TEST_BINS := $(addprefix ./$(BIN_DIR)/, $(patsubst %.cpp, %, $(TEST_SRCS)) )
Then we have to define a special rule with pattern matching to compile the tests:
$(BIN_DIR)/%_Test: $(patsubst $(BIN_DIR)/%, %, %_Test.cpp ) %.cpp %.h
@echo "Making $@"
@mkdir -p $(shell dirname $@)
g++ $(CXXFLAGS) -g3 -O0 $< -o $@ -lpthread -lgtest_main -lgmock $(OBJECTS) $(LDFLAGS)
and some magic to auto execute every test when we "make test":
test: $(TEST_SRCS)
@for TEST in $(TEST_BINS); do
make "$$TEST";
echo "Execute $(TEST)";
./$$TEST;
done
Everything nice and tidy for a copy & paste session:
TEST_SRCS := $(patsubst ./%, %, $(shell find -L|grep -v svn|egrep "_Test.cpp$$" ) )
TEST_BINS := $(addprefix ./$(BIN_DIR)/, $(patsubst %.cpp, %, $(TEST_SRCS)) )
$(BIN_DIR)/%_Test: $(patsubst $(BIN_DIR)/%, %, %_Test.cpp ) %.cpp %.h
@echo "Making $@"
@mkdir -p $(shell dirname $@)
g++ $(CXXFLAGS) -g3 -O0 $< -o $@ -lpthread -lgtest_main -lgmock $(OBJECTS) $(LDFLAGS)
.PHONY: test
test: $(TEST_SRCS)
@for TEST in $(TEST_BINS); do
make "$$TEST";
echo "Execute $(TEST)";
./$$TEST;
done
Now you just need to run make test. Remember to add the proper Vim's mapping.
Makefiles
Post by Nico Brailovsky @ 2011-08-18 | Permalink | 2 comments | Leave a comment
For open source projects, makefiles are a must. All C++ projects need them, even though cmake is strong nowadays, and even though Java has its own version (actually, several of them, but that's not important now) a makefile could be used.
Even if it is an ubiquitous build system, it is pretty much outdated nowadays, and although using its basic features is easy, mastering it is a complex task. Worst still, mastering makefiles means you'll probably produce write-only-code, and as makefiles are code themselves, and must therefore be maintained, this can be a nuisance to a newcomer to your project.
There's an upside to makefiles being code: they can be reused. Once you find a configuration that suits your development process, you don't need to write it again. I'll post here some of the main targets I ussually include in a common.mk. As I mentioned, it's mostly write-only-code, yet you may find it useful:
# Dependency directoy
df=$(BUILD_DIR)/$(D)/$(F)
$(OBJECTS): $(BUILD_DIR)/%.o: %.cpp
@mkdir -p $(BUILD_DIR)/$(D)
$(COMPILE.cpp) -MD -o $@ $<
@cp $(df).d $(df).P;
sed -e 's/#.//' -e 's/^[^:]: //' -e 's/ *\$$//'
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P;
rm -f $(df).d
$(MAIN_OBJ): $(MAIN_SRC)
$(COMPILE.cpp) -MD -o $@ $<
# Binary name depends on BIN_DIR/BIN_NAME, so the call to create BIN can
# be forwarded to BIN_DIR/BIN_NAME
$(BINARY): $(BIN_DIR)/$(BINARY)
$(BIN_DIR)/$(BINARY): $(OBJECTS) $(DEPS_OBJECTS) $(MAIN_OBJ)
@mkdir -p $(BIN_DIR)
@# Workaround for a linker bug: if the libs are not
@# at the end it won't link (something to do with how the linker
@# lists the dependencies... too long for a comment, rtfm
g++ $(CXXFLAGS) $^ -o $(BIN_DIR)/$@ $(LDFLAGS)
@#$(LINK.cpp) $^ -o $@
-include $(DEPENDS)
How is this used? Well, don't even try to understand the dependency autogeneration, it'll make your head explode.
$(OBJECTS): $(BUILD_DIR)/%.o: %.cpp
This defines a rule for building .o objects; a variable named OBJECTS should be present when including this file.
$(MAIN_OBJ): $(MAIN_SRC)
A special rule is defined for a main object (actually this is needed to compile the tests, which we'll do next time, since you may have a different main function).
$(BINARY): $(BIN_DIR)/$(BINARY)
$(BIN_DIR)/$(BINARY): $(OBJECTS) $(DEPS_OBJECTS) $(MAIN_OBJ)
And finally, a rule for to create the real binary. Next time I'll add some cool features for TDD to this makefile.
Living on a null object
Post by Nico Brailovsky @ 2011-08-16 | Permalink | Leave a comment
Check this out:
struct S {
int f(){ return 42; }
};
int main() {
S x = (S) NULL;
return x->f();
}
What does this do? Does it compile? Does it crash? I'll give you a second.
Ready? It does compile, OK
But it doesn't crash.
Why, you may ask
Think about it, you must.
The compiler will mangle S::f and translate this into something like:
struct S {};
int mangled_S_f(struct S this){
return 42;
}
int main() {
S x = (S*) NULL;
mangled_S_f(x);
}
Now, in this new "translated" code, what do you think? Will it crash? It won't, since no one is going to dereference "this". Crazy, huh? This crazy idiom also allows even crazier things, like C++ objects committing sepuku
Vacations are over
Post by Nico Brailovsky @ 2011-08-15 | Permalink | Leave a comment
Long time without updates. I guess I needed vacations from the blog. It was not the first time and it probably won't be the last one I take, but I'm back now with another truckload of C++ ramblings and misc stuff. Like this one: