It is far better to grasp the Universe as it really is than to
persist in delusion, however satisfying and reassuring.
—Carl Sagan
TrMake (for transparent make) is a modularized makefile system designed for multi-platform C++ development in Unix environment with GNU make (v3.80 or higher). Its main features are:
Fast deployment — copy the mk directory where
the sources directory is, and possibly adjust the sample
makefile variables.
Transparent handling of dependencies and generated sources of multiple projects.
Operating system, compiler and architecture configuration, with debugging and profiling levels. The builds are maintained in independent directories for different configurations.
Straightforward handling of shared libraries (including versioning).
Support for unit testing frameworks such as
boost::test — linking with tested project's
files.
Easy customization of each project: include and library directories search paths, linked libraries, static linking of executables and libraries.
Out-of-the-box support for GCC, Intel, MIPSPro, and SunPRO compilers under Linux, IRIX, and Solaris, on Intel/AMD 32-bit and 64-bit platforms, MIPS and Sparc architectures.
Also supports Qt moc/uic preprocessors,
boost libraries, generating documentation with
Doxygen, Flex/Bison
parsers and Lazy C++ tool.
Easy customization and extension with some makefiles knowledge. Supporting other compilers and architectures or modifying supported suites' behavior is very simple.
Download the latest version of TrMake with
sample makefile settings and several example projects.
You can also download from the
SourceForge
repository.
The directory structure for a workspace is as follows:
makefile: top-level makefile. Declares
compiler/architecture used, debugging/profiling levels,
per-project libraries, inter-project dependencies etc.
mk/: make system modules directory (pulled from
makefile).
src/xxx: root directory for sources of project
xxx.
src/libxxx: root directory for sources of library
project libxxx.
src/xxx.test: root directory for test cases of
project xxx. The resulting executable has
xxx project's objects linked in as well. Remember to
specialize the EXCLUDE_FILES variable in order to
avoid linking with another main() function, and to
add appropriate inter-project dependencies.
src/libxxx.test: root directory for test cases of
library project libxxx. The resulting executable is
linked with libxxx in addition to other
libraries. Remember to add appropriate inter-project dependencies.
All the other directories are created upon need:
bin/os-suite-arch-debug-profile: directory (e.g.,
bin/linux-intel-x86amd-d1-p0), holding the resulting
binaries, such as xxx, xxx.st (static
executable), libxxx.so (shared library),
libxxx.a (static library). This directory is added to
the run-time library search path when compiling in non-zero debug
level.
obj/os-suite-arch-debug-profile/xxx: directory holding
objects, dependencies, and possibly other intermediate files used
during compilation process for project xxx.
doc/api/xxx: automatically generated documentation
for project xxx.
doc/cov/xxx: coverage tool-generated files for
project xxx.
prof/os-suite-arch/xxx: directory holding the files
used during profile-driven compilation. Note that profiling is
cumbersome with GCC, since there is currently no way to specify a
directory for *.gcda files, which need to be copied
manually between object directories.
The make commands are executed at the top-level directory
containing the makefile. When invoked with some target,
TrMake invokes make PROJECT=xxx for each project on the
same target. Thus, one can specify the PROJECT variable
to make in order to work on just the given project.
The following targets are supported:
make or make compile: compile executables
and libraries.
make all: equivalent to compile +
doc.
make clean: remove executables, objects and code
coverage results.
make clean.all: equivalent to clean +
clean.auto, and also removes generated documentation
and profiling results.
make doc: generate documentation with
Doxygen.
make cov: generate code coverage documentation (GCC
gcov or Intel codecov).
make asm: compile to assembly.
make auto: generate auto-generated sources (this is, of
course, a dependency for compile and asm
targets).
make clean.auto: remove auto-generated sources.
make test: run testing projects. The current switches
are defined for the boost::test framework.
make info: show important variables and file lists.
Note that CXXFLAGS, CFLAGS and
LDFLAGS are appended to rather than defined, so it is
possible to customize them via environment variables or the
makefile.
The assumed extensions for source files are:
.cc, .cpp, .cxx,
.C, .c++: files with any of these
extensions will be recognized as C++ sources.
.c: C sources.
.h: C++ and C headers.
.qh: Qt headers to be processed with moc
(also directly included in C++ sources).
.lzz: sources to be processed with Lazy
C++.
.l: lexer files to be processed with
Flex++.
.y: parser files to be processed with
Bison.
.ui: Qt user interfaces to be processed with
uic.
.pro: Qt qmake project descriptions to be
processed for embedded images generation (with uic).
.map: linker version scripts (typically there will be
at most one for a single shared library project).
The implied extensions for auto-generated sources:
.lzz.h, lzz.cc: headers and sources
generated by Lazy C++.
.l.cc: lexer sources generated by Flex++.
.y.h, .y.cc: parser headers and sources
generated by Bison.
.moc.cc: Qt sources generated by moc from
.qh headers.
.uic.qh, .uic.cc: Qt headers and sources
generated by uic from .ui interfaces. Note
that .uic.moc.cc sources are further generated from
.uic.qh headers.
.emb.cc: Qt embedded images sources generated by
uic from images listed in .pro files.
The distributed package includes some example projects:
libprinter: a library which can print "Hello!"...
user: an application using the library above to print
"Hello!"...
compr: an old Qt application for image compression :)
In the sample makefile, we see that the Universal
settings section configures the build for linux /
gcc / x86amd platform. You can change
these to suit your development environment. In addition, you can add
compr to EXCLUDE_PROJECTS if you don't
have Qt installed, or if the QTDIR environment variable
is not set.
As well, full debugging level is enabled (DEBUG = 2), and
profiling is disabled. Note that unless DEBUG is set to
0, the user project will automatically
locate the libprinter shared object when invoked from the
top-level directory (where makefile resides).
Next comes the Inter-project dependencies section, where
dependencies between final targets of the projects are defined. As
written in the comments, these dependencies influence both projects
build order (e.g., in case of parallel builds), and target relinking
due to other projects' results updates. Therefore, real target file
names should be used, with .so / .a suffixes
where necessary.
The last section is Per-project settings, where a lot of
variables can be defined in per-project subsections. For example, if
you want to build the libprinter project as a static
library, add STATIC = 1 to
customize.libprinter (and update the library filename in
the Inter-project dependencies section).
You can now build the projects, by just invoking make or,
e.g., make -j 3. I suggest that you familiarize yourself
with other targets, described in the Make commands section
above. Try, e.g., make doc if you have
Doxygen installed. (Remember that GNU make
version 3.80 or newer is required.)
Intel Compiler allows for good support of profiling and code
coverage. If it is installed on your workstation, set SUITE =
intel, and try (probably after disabling the compr
project):
make PROFILE=2 to build projects which generate
profiling information;
Invoke the user executable couple of times;
make PROFILE=1 to build projects using the generated
profiling information;
make cov to generate code coverage documentation.
To see the actual commands executed by make, use the
TRACE variable, e.g., make TRACE=1 ...
TrMake has the following modules directory structure:
makefile: defines projects settings and loads mk/top.mk.
mk/top.mk: defines phony targets and loads either
mk/proxy.mk or
mk/{files,targets,compiler,rules,engines,doxygen}.mk
+ object dependencies. Also applies per-project variables
customizations.
mk/proxy.mk: dispatches per-projects recursive makes
(with PROJECT variable set to a specific project),
with inter-project dependencies applied to the per-project makes.
mk/files.mk: file and directory names and extensions,
including actual file names extracted with the find
utility, and auto-generated file names.
mk/targets.mk: rules for final targets —
compilation, documentation, code coverage, testing, and
cleaning. Also uses inter-project dependencies to enable target
relinking in case of another project (i.e., a library) update.
mk/compiler.mk: defines compiler and linker switches;
loads appropriate compiler suite and linker definitions from
mk/suites and mk/linkers directories,
respectively.
mk/suites/{gcc,intel,mipspro,sunpro}.mk:
compiler-specific switches.
mk/linkers/{linux,irix,solaris}.mk: linker-specific
switches.
mk/rules.mk: rules for intermediate targets (objects
and generated sources).
mk/engines.mk: preprocessors and various utilities
executables and switches.
mk/doxygen.mk: configuration template for
Doxygen.
mk/doc/: this documentation, changelog and license.
Suppose that the sources directory has a new file type
.tr, which is transformed to C++ header and source files
using the special transmogrify preprocessor.
The following settings should then be added to the modules to
facilitate automatic handling of .tr files:
mk/files.mk: specify input and output files for
transmogrifying, e.g.,:
Add TRMG_SOURCES = $(filter %.tr,$(FILES)) to the
dynamic file lists section.
Add $(TRMG_SOURCES:%=%.$(CXX_EXT)) to the
AUTO_SOURCES variable.
Add $(TRMG_SOURCES:%=%.h) to the
AUTO_HEADERS variable.
mk/engines.mk: define variables for invoking the
utility, e.g.,
TRMG = transmogrify
TRMGFLAGS = -hext h -cppext $(CXX_EXT)
mk/rules.mk: define an implicit rule for source and
header generation from a .tr file:
%.tr.$(CXX_EXT) %.tr.h: %.tr
$(E) "Transmogrifying: $*.tr.{h,cc}"
$(Q)$(TRMG) -o $@ $(TRMGFLAGS) $<
To support a new compiler, add a file to mk/suites/,
e.g., mk/suites/watcom.mk. The
mk/compiler.mk loader lists the requirements from such a
file, which can be modeled after mk/suites/gcc.mk. Users
can then specify SUITE = watcom in the
makefile.
Similarly, to support a new linker, add a file to
mk/linkers/, e.g., mk/suites/aix.mk. The
mk/compiler.mk loader lists the requirements from such a
file, which can be modeled after
mk/linkers/linux.mk. Users can then specify OS =
aix in the makefile.
How to Write Shared Libraries, by Ulrich Drepper
C++ compiler manuals:
Linker manuals:
Other tools:
Other GNU make-based systems:
This is TrMake version 1.0, a transparent make system.
Copyright © 2000–2006 Michael Orlov.
TrMake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.