ct-cake --autoct-cake will try to determine the correct source files to generate executables from and also determine the tests to build and run. It works by spidering over the source code to determine what implementation files to build, what libraries to link against and what compiler flags to set. Only build what you need, and throw out your Makefiles.
Zomojo Pty, Ltd. - Geoff Ericksson (geoff@zomojo.com)
You can download this project from releases
You can also clone the project with Git by running:
$ git clone git://github.com/Zomojo/compiletools
Or pip install using
pip install compiletools
sudo dnf install python2-setuptools python2-configargparse python2-appdirs python2-psutilor if you prefer to use python3
sudo dnf install python3-setuptools python3-configargparse python3-appdirs python3-psutil
The ct-* tools can run from the cloned git repository. Simply edit your PATH variable to include the repo. For example, if you cloned into your home directory then
PATH=~/compiletools:$PATHNow, copy this source code into a file called example.cpp:
#include <stdio.h> #include <pthread.h> #include <iostream> //#LDFLAGS=-pthread -lrt static void pclock(const char *msg, clockid_t cid) { struct timespec ts; printf("%s", msg); if (clock_gettime(cid, &ts) == -1) perror("clock_gettime"); printf("%4ld.%03ld\n", ts.tv_sec, ts.tv_nsec / 1000000); } int main() { clockid_t cid; pthread_t pt = pthread_self(); if (pthread_getcpuclockid(pt, &cid)) perror("getcpuclockid failed"); pclock("the clock is", cid); return 0; }Compile it and run it using this command:
$ ct-cake --auto && ./bin/examplewhich will produce output similar to
make -s -j 3 -f /home/geoff/tmp/bin/gcc.debug/Makefile build ... /home/geoff/tmp/example.cpp ... /home/geoff/tmp/bin/gcc.debug/example bin/example the clock is 0.001
What occured was ct-cake searched all the files to find a source file containing the word "main". It then searched the source file to extract the link flags that are annotated in the source code, so it wasn't necessary to supply them at the command-line. compiletools is based on the principle that the source code and the build settings are kept together. This will increase the likelihood of keeping the compile flags in sync with the header includes. A Makefile was created that expressed the required build and then make was called. Finally the execuatable was executed.
The compiletools project provides a variety of tools for building C/C++ projects and for exploring code dependencies. ct-cake is the Swiss army knife of build tools that combines many of the compiletools into one uber-tool. For many C/C++ projects you can compile simply using
ct-cake --autoThe --auto flag tells ct-cake to search for files that will be the "main" file. From that set of files, ct-cake then uses the header includes to determine what implementation (cpp) files are also required to be built and linked into the final executable/s.
Cake works off the same principles as Ruby on Rails. It will make your life easy if you don't arbitrarily name things. The main rules are:
ct-cake works off a "pull" philosophy of building, unlike the "push" model of most build processes. Often, there is the monolithic build script that rebuilds everything. Users iterate over changing a file, relinking everything and then rerunning their binary. A hierarchy of libraries is built up and then linked in to the final executables. All of this takes a lot of time, particularly for C++.
With ct-cake, you only pull in what is strictly necessary to what you need to run right now. Say, you are testing a particular tool in a large project, with a large base of 2000 library files for string handling, sockets, etc. There is simply no make file. You might want to create a build.sh for regression testing, but it's not essential.
The basic workflow is to simply type:
ct-cake --autoIf there are multiple executables that --auto finds and you only want to build one specific one:
ct-cake path/to/src/app.cpp
Only the library cpp files that are needed, directly, or indictly to create ./bin/app are actually compiled. If you don't #include anything that refers to a library file, you don't pay for it. Also, only the link options that are strictly needed to generate the app are included. Its possible to do in make files, but such fine-level granularity is rarely set up in practice, because its too error-prone to do manually, or with recursive make goodness.
ct-cake generates the header dependencies for the "main.cpp" file you specify at the command line by either examining the "#include" lines in the source code or by executing "gcc -MM -MF". For each header file found in the source file, it looks for an underlying implementation (cpp) file with the same name, and adds that implementation file to the build. ct-cake also reads the first 8KB of each file for special comments that indicate needed link and compile flags. Then it recurses through the dependencies of the cpp file, and uses this spidering to generate complete dependency information for the application. A Makefile is generated and finally it calls make.
ct-ake works very differently to other build systems, which specify a hierarchy of link flags and compile options, because ct-cake ties the compiler flags directly to the source code. If you have compress.hpp that requires "-lzip" on the link line, add the following comment in the first 8KB of the header file:
//#LDFLAGS=-lzip
Whenever the header is included (either directly or indirectly), the -lzip will be automatically added to the link step. If you stop using the header, for a particular executable, cake will figure that out and not link against it.
If you want to compile a cpp file with a particular optimization enabled, add, say:
//#CXXFLAGS=-fstrict-aliasingBecause the code and build flags are defined so close to each other, its much easier to tweak the compilation locally.
Because ct-cake internally generates a makefile to build the C++ file, cake is about as fast as a handrolled Makefile that uses the same lazily generated dependencies. A typical project takes 0.04 seconds to build if nothing is out of date, versus 2 seconds for, say, Boost.Build.
ct-cake also eliminates the redundant generation of static archive files that a more hierarchical build process would generate as intermediaries, saving the cost of running 'ar'.
Note that ct-cake doesn't build all cpp files that you have checked out, only those strictly needed to build your particular binary, so you only pay for what you use. This difference alone should see a large improvement on most projects, especially for incremental rebuilds.
The compiletools programs require almost no configuration. However, it is still useful to have some shortcut build templates such as 'release', 'profile' etc.
Config files for the ct-* applications are programmatically located using python-appdirs, which on linux is a wrapper around the XDG specification. Thus default locations are /etc/xdb/ct/ and $HOME/.config/ct/. Configuration parsing is done using python-configargparse which automatically handles environment variables, command line arguments, system configs and user configs. Specifically, the config files are searched for in the following locations (from lowest to highest priority):
command line > environment variables > config file values > defaultsAn example /etc/xdg/ct/ file looks as follows:
ID=GNU CC=gcc CXX=g++ LD=g++ CFLAGS=-fPIC -g -Wall -O3 -DNDEBUG -finline-functions -Wno-inline CXXFLAGS=-std=c++11 -fPIC -g -Wall -fdiagnostics-color=auto -O3 -DNDEBUG -finline-functions -Wno-inline LDFLAGS=-fPIC -Wall -Werror -Xlinker --build-id TESTPREFIX=timeout 300 valgrind --quiet --error-exitcode=1
CXXFLAGS lists the flags appended to each compilation job. The value in /etc/xdg/ct/gcc.debug.conf is completely overridden by the environment variable, which is completely overridden by the command-line argument --CXXFLAGS=. Likewise, LDFLAGS sets the default options used for linking.
TESTPREFIX specifies a command prefix to place in front of unit test runs. This should ideally be a tool like valgrind, gdb or purify that can be configured to execute the app and return a non-zero exit code on any failure.
$ ct-cake --variant=release a.cpp && ./bin/a
ct-cake integrates with unit tests in a fairly simple (and perhaps simplistic) way.
ct-cake allows you to specify multiple build targets on each line, so the following is valid and useful:$ ct-cake utilities/*.cpp # builds all apps and places them under bin/Unit tests are executables that are generated, that create an additional build step. They must run and return an exit code of 0 as part of the build process. To specify that executables are unit tests, use the --tests flag.
$ ct-cake utilities/*.cpp --tests tests/*.cppIf the TESTPREFIX variable is set, you can automatically check all unit tests with a code purifying tool. For example:
export TESTPREFIX="valgrind --quiet --error-exitcode=1"will cause all unit tests to only pass if they run through valgrind with no memory errors.
For most simple projects, a build.sh script that looks like the following is quite useful. You can simply add more cpp to the apps directory to generate more tools from the project, or add test scripts to the regression directory to improve test coverage.
Code generation steps can be added at the beginning of the build.sh, before cake runs.
#!/bin/bash set -e ct-cake apps/*.cpp --tests tests/*.cpp "$@"
The special "$@" marker is the recommended way of forwarding arguments to an application. You can then run the build script like this:
$ ./build.sh --variant=releaseor:
$ ./build.sh --variant=release --append-CXXFLAGS=-DSPECIALMODE