1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
|
* SYNOPSIS
This project is meant to provide some GNU Makefiles to take the boilerplate away
from defining a project build. For instance:
#+BEGIN_SRC makefile
include /usr/include/mrbuild/Makefile.common.header
PROJECT_NAME := foo
ABI_VERSION := 0
TAIL_VERSION := 1
# Build all .c sources into a library: libfoo. If all library sources are in
# src/, you can put $(wildcard src/*.c) here. Raw wildcards are not allowed in
# LIB_SOURCES
LIB_SOURCES := $(wildcard *.c)
# build executable foo_tst from foo_tst.cc; it will be linked with libfoo
# build executable foo_run from foo_run.cc; it will be linked with libfoo
#
# These should be separate from LIB_SOURCES. If all non-library sources are in
# bin/, you can put $(wildcard bin/*.c) here. Raw wildcards are not allowed in
# BIN_SOURCES
BIN_SOURCES := foo_tst.c foo_run.c
# Any extra flags. The build system always builds with debugging information and
# (if no other -O flag is given) with optimization
CCXXFLAGS += -Wno-unused-parameter
# Extra libraries to link with. The library is linked with this. The executables
# are linked with this AND with the library
LDLIBS += -lbar
# distribute (in a 'make install' and thus into the package) all headers except
# those that match *tst*
DIST_INCLUDE := *.h
DIST_INCLUDE_EXCEPT := *tst*
# distribute (in a 'make install' and thus into the package) all executables
# built from BIN_SOURCES (foo_tst, foo_run) except *_tst, so only foo_run is
# distributed
DIST_BIN_EXCEPT := *_tst
include /usr/include/mrbuild/Makefile.common.footer
#+END_SRC
* DESCRIPTION
When defining the build rules for a project, there's often much boilerplate
involved, the provenance of which is often lost to time. And there's usually
much cargo-culting, and every project effectively forks the build scripts. The
results aren't good, and mrbuild attempts to organize this. mrbuild provides a
consistent set of =Makefiles= and project tree layout conventions. These make it
easy to get friendly and consistent builds.
A project that uses mrbuild is defined by a single =Makefile=. This is done by
defining some variables and =include=-ing =Makefile.common.header=,
=Makefile.common.footer= from =mrbuild=. Those files produce the rules that
=make= uses to do its job. Note that unlike the CMake-based system, this is a
one-step process: we don't create =Makefiles= and then use them to build stuff;
we just build the stuff.
Each project builds a library (a /shared/ object) and optionally, some
executables. mrbuild knows how to build commonly-used things: QT interfaces,
python extensions, etc. Intentionally, there's no mechanism to "find" dependency
libraries: you should know where your dependencies live, and you can tell Make
yourself the normal way: with =CFLAGS= and =LDFLAGS=.
mrbuild will set up the =RPATH= tags to make the current directory findable.
Thus executables can be run without installing, and they will link with the
locally-built library. At install-time these =RPATH= tags will be stripped-away,
so no extra steps are required when building packages.
The project build definition file is a plain =Makefile=, so no special syntax
and rules need to be mentioned here. And because it's a plain =Makefile=, we can
define whatever arbitrary rules we want for anything not specifically covered by
mrbuild.
Furthermore, again since this is a plain =Makefile=, we can pass it external
options in the environment. For instance, if we want to build without
optimizations, simply invoke
#+BEGIN_EXAMPLE
CCXXFLAGS=-O0 make
#+END_EXAMPLE
The build system will pick up the new build option, and is smart enough to
replace the default of =-O3= with the given =-O0=.
Note that this is subtly different from
#+BEGIN_EXAMPLE
make CCXXFLAGS=-O0
#+END_EXAMPLE
This latter invocation is /not/ supported, and we throw an error if this is
encountered.
** API
A complete list of the variables =mrbuild= knows about:
*** Variables that MUST be defined
- =PROJECT_NAME=
The name of the project. Ends up in the SONAME. Libraries may or may not be
called =lib...=, but the =lib= will be prepended to the SONAME if it's missing.
- =ABI_VERSION=
Required for libraries. An integer indicating the version of the ABI. Usually
the same as the major version number of the project. Together with
=TAIL_VERSION=, these define the default
#+BEGIN_EXAMPLE
VERSION ?= $(ABI_VERSION).$(TAIL_VERSION)
#+END_EXAMPLE
which can be used in the per-project =Makefile=
- =TAIL_VERSION=
Required for libraries. An arbitrary string. Together with =ABI_VERSION=, these
define the default
#+BEGIN_EXAMPLE
VERSION ?= $(ABI_VERSION).$(TAIL_VERSION)
#+END_EXAMPLE
which can be used in the per-project =Makefile=
- =VERSION=
Required for non-libraries, optional for libraries. The version string. The
project =Makefile= can set this to whatever. If omitted, it defaults to
#+BEGIN_EXAMPLE
VERSION ?= $(ABI_VERSION).$(TAIL_VERSION)
#+END_EXAMPLE
If =VERSION= and =ABI_VERSION= and =TAIL_VERSION= are all defined, the library
SONAME comes from the ABI and TAIL versions, but the documentation will have the
VERSION string.
The compiler gets a preprocessor =#define= that can be used in the sources; this
is called =MRBUILD_VERSION=. It isn't just =VERSION= to avoid conflicts with
other symbols the code may be using.
*** Build variables that MAY be defined
- =LIB_SOURCES=
Sources to build into a library. If omitted, no library will be built. Wildcards
are /not/ allowed
- =BIN_SOURCES=
Sources to build into executables. By default each executable will be built from
the library and each source in =BIN_SOURCES=. So if we have =a.c= and =b.c= in
=BIN_SOURCES=, then two executables will be built: =a= and =b=, each linking in
the library. Wildcards are /not/ allowed
- =CFLAGS=, =CXXFLAGS=, =CCXXFLAGS=, =CPPFLAGS=
Flags for C, C++, both and the preprocessor respectively. By default we pass
=-O3= and (for C++) =-std=c++0x=. If we specify any other optimization level or
standard, the defaults will be omitted. This is commonly used to build without
optimizations:
#+BEGIN_EXAMPLE
CCXXFLAGS=-O0 make
#+END_EXAMPLE
In the =Makefile= these should be touched with =+== to not override any values
passed in the environment.
- =LDFLAGS=
Similar to the above. Contains the linker flags.
- =LDLIBS=
Similar to the above. Contains the libraries we link with. By default this
applies to all objects, libraries and executables we build. This is often
overkill; if we want to apply some linker flag just to a particular object, use
a per-target variable:
#+BEGIN_SRC makefile
BIN_SOURCES = a.c
a: LDLIBS += -lbleh
#+END_SRC
*** Installation variables that MAY be defined
The =DIST_...= variables are only looked-at if we =make install=, which in our
world happens only when we're building a package. Generally =make install=
copies the files indicated by the =DIST_...= variables to =DESTDIR=.
- =DIST_BIN=
Executables that we distribute. May include wildcards. If omitted, defaults to
all the executables that =$(BIN_SOURCES)= produce
- =DIST_INCLUDE=
Headers that we distribute. May include wildcards. If omitted, no headers are
distributed.
- =DIST_BIN_EXCEPT=, =DIST_INCLUDE_EXCEPT=
Simple distribution blacklists. May include wildcards. Anything that is matched
by =DIST_XXX_EXCEPT= is /not/ distributed, even if it appears in =DIST_XXX=. For
fancier logic, use the =..._FINDSPEC= variables described below
- =DIST_DOC=
Documentation we ship. May include wildcards.
- =DIST_MAN=
Man-pages we ship. May include wildcards.
- =DIST_DATA=
Arbitrary data we ship. May include wildcards.
- =DIST_PERL_MODULES=
Perl modules
- =DIST_PY2_MODULES=
Python2 modules
- =DIST_PY3_MODULES=
Python3 modules
- =DIST_BIN_EXCEPT_FINDSPEC=, =DIST_INCLUDE_EXCEPT_FINDSPEC=, =DIST_DOC_EXCEPT_FINDSPEC=, =DIST_MAN_EXCEPT_FINDSPEC=, =DIST_DATA_EXCEPT_FINDSPEC=
After we install a set of files to the =DESTDIR=, we may want to delete some
subset of them. This is similar to the =..._EXCEPT= blacklists above, but
accomplished with the =find= utility, so we have more flexibility. For instance,
to install all the manpages except onces for tests, do this:
#+BEGIN_SRC makefile
DIST_MAN := doxygen-doc/man/man3
DIST_MAN_EXCEPT_FINDSPEC := -type f -name '*_test.3'
#+END_SRC
To install only the manpage for the =frobnicator= utility (delete all others) we
do this:
#+BEGIN_SRC makefile
DIST_MAN := doxygen-doc/man/man3
DIST_MAN_EXCEPT_FINDSPEC := -type f \! \( -name 'frobnicator.3' \)
#+END_SRC
- =EXTRA_CLEAN=
Additional targets to clean out during a =make clean=
- =INSTALL_ROOT_XXX=
By default (without a user-defined =INSTALL_ROOT_XXX=) mrbuild installs to
standard locations, but there are situations where we may want to override this.
We can do that by defining =INSTALL_ROOT_XXX= for each specific kind of product.
The definition in mrbuild:
#+begin_src makefile
INSTALL_ROOT_BIN ?= /usr/bin
INSTALL_ROOT_INCLUDE ?= /usr/include/$(PROJECT_NAME)
INSTALL_ROOT_DOC ?= /usr/share/doc/$(PROJECT_NAME)
INSTALL_ROOT_MAN ?= /$(MANDIR)
INSTALL_ROOT_DATA ?= /usr/share/$(PROJECT_NAME)
INSTALL_ROOT_PERL_MODULES ?= /usr/share/perl5
INSTALL_ROOT_PY2_MODULES ?= /$(PY2_MODULE_PATH)
INSTALL_ROOT_PY3_MODULES ?= /$(PY3_MODULE_PATH)
#+end_src
Each of these may be overridden by the user, and the default value is given
above. Most of the time, the defaults should be used, and these should be
omitted.
*** QT GUIs
mrbuild has rules to handle QT moc and =.ui= stuff. An executable that uses QT
can be defined like this:
#+BEGIN_SRC makefile
BIN_SOURCES := gui.cc
MOC_OBJECTS := $(patsubst %.hh,moc_%.o,$(shell grep -l Q_OBJECT *.hh))
gui: $(MOC_OBJECTS) # gui.o will be linked in automatically
#+END_SRC
*** Manpages
mrbuild knows how to install manpages, but not how to build them (this is
usually project-specific). In my usage I do [[http://notes.secretsauce.net/notes/2018/10/23_manpages-and-readmes.html][this]], which I find quite useful.
*** Python extensions
mrbuild knows how to build python extension modules directly: without
=distutils= or any such silliness. The result is that all the building is
handled by =make=, and everything works the way it's supposed to. This is
described in detail [[http://notes.secretsauce.net/notes/2017/11/14_python-extension-modules-without-setuptools-or-distutils.html][here]].
** More complex example
An annotated example showing some more complex usage appears in
[[file:build_examples/GNU_Make/Makefile]], and is copied here
#+BEGIN_SRC makefile
# -*- Makefile -*-
PYTHON_VERSION_FOR_EXTENSIONS := 3
include /usr/include/mrbuild/Makefile.common.header
# This is a sample Makefile using the Makefile.common.header,
# Makefile.common.footer infrastructure. A quick way to bootstrap a new project
# is to copy this file to the root directory of the project and then to modify
# each variable to fit that particular project.
# The name of the project. By convention, libraries should be called lib... but
# this isn't required
PROJECT_NAME := libfrobnicator
# The version of the library. We treat the major version as the version of the
# ABI/API. So every time we change the ABI or an API in a backwards-incompatible
# way, we bump the ABI_VERSION. If we make non-breaking changes, bumping the
# TAIL_VERSION is sufficient. In this example, the full version is 0.1
ABI_VERSION := 0
TAIL_VERSION := 1
# Build all C and C++ sources in src/ into the library
LIB_SOURCES := $(wildcard src/*.c*)
# Build all C and C++ sources in bin/ into separate executables
BIN_SOURCES := $(wildcard bin/*.c*)
# If bin/run_foo.c exists, it is picked up in BIN_SOURCES, and the bin/run_foo
# executable will be built from the library and bin/run_foo.o (built from
# bin/run_foo.c). This is the default behavior and nothing needs to be specified
# I specify that bin/run_foo2 consists of the library and bin/run_foo2.o (as
# usual) AND links with bin/run_foo2_extra.o. The latter will be built from
# bin/run_foo2_extra.c (or .cc or .cpp and so on, whichever exists)
bin/run_foo2: bin/run_foo2_extra.o
# Suppose I have bin/run_foo3.c to build bin/run_foo3. And suppose bin/run_foo3
# needs to additionally build with sources generated from run_foo3.in: the .o
# links with bin/run_foo3_generated.o (built from bin/run_foo3_generated.c) and
# the .c #includes run_foo3_generated.h, and that both of these are generated
# from run_foo3.in. We specify this in the usual way, with a tiny bit of
# mrbuild-specific stuff:
run_foo3: run_foo3_generated.o
run_foo3.o: run_foo3_generated.h
%3_generated.h %3_generated.c: %3.in
make_generated_files $<
EXTRA_CLEAN += run_foo3_generated.h run_foo3_generated.c
# If we're using gengetopt to generate the sources, the build rule and the
# EXTRA_CLEAN list above are provided in mrbuild, and can be omitted.
# Any extra flags to pass to the C and C++ compilers. The build system always
# builds with debugging information and (if no other -O flag is given) with
# optimization. Use += to not override any settings from the commandline
CCXXFLAGS += -Wno-unused-parameter
# Extra flags to pass to the C compiler when building src/bleh.o from src/bleh.c
src/bleh.o: CFLAGS += -DFOO
# Link bin/run_foo with -lbar. Do NOT link the library with -lbar.
bin/run_foo: LDLIBS += -lbar
# Link all the executables AND the library with -lzap
LDLIBS += -lzap
# If we have doxygen docs, we can state the rule to build them. Everything will
# be built into doxygen-doc/, the DIST_DOC and DIST_MAN distribution lists below
# install the man-pages and the html docs
doc: doxygen-doc/
doxygen-doc/: frobnicator.dox
SRCDIR='.' PROJECT='frobnicator' DOCDIR=$@ VERSION='$(VERSION)' PERL_PATH='/bin/perl' HAVE_DOT='YES' DOT_PATH='/bin' GENERATE_MAN='YES' GENERATE_RTF='NO' GENERATE_XML='NO' GENERATE_HTMLHELP='NO' GENERATE_CHI='NO' GENERATE_HTML='YES' GENERATE_LATEX='NO' doxygen $<
doxygen-doc/%: doxygen-doc/ ;
.PHONY: doc
EXTRA_CLEAN += doxygen-doc
# distribute (in a 'make install' and thus into the package) all headers in src/
# except those that match src/*tst*
DIST_INCLUDE := src/*.h
DIST_INCLUDE_EXCEPT := src/*tst*
# distribute (in a 'make install' and thus into the package) all executables
# built from BIN_SOURCES except bin/*_tst. And ship the python application
DIST_BIN := $(filter-out bin/%_tst,$(wildcard $(BIN_TARGETS))) python-tool
# distribute all generated manpages in section 3 EXCEPT those for the test
# program
DIST_MAN := doxygen-doc/man/man3
DIST_MAN_EXCEPT_FINDSPEC := -type f -name '*_tst.3'
# distribute the html documentation
DIST_DOC := doxygen-doc/html
# This is the manpage-generating technique from
# http://notes.secretsauce.net/notes/2018/10/23_manpages-and-readmes.html
#
# generate manpages from distributed binaries, and ship them.
DIST_MAN += $(addsuffix .1,$(DIST_BIN))
$(DIST_MAN): %.1: %.pod
pod2man --center="title: does something" --name=THING --release="thing 0.1" --section=1 $< $@
%.pod: %
make-pod-from-help $< > $@
cat footer.pod >> $@
EXTRA_CLEAN += $(DIST_MAN) $(patsubst %.1,%.pod,$(DIST_MAN))
# This is the python-extension-generating technique from
# http://notes.secretsauce.net/notes/2017/11/14_python-extension-modules-without-setuptools-or-distutils.html
frobnicator_pywrap.o: CFLAGS += $(PY_MRBUILD_CFLAGS)
frobnicator_pywrap.o: $(addsuffix .h,$(wildcard *.docstring))
frobnicator/_frobnicator$(PY_EXT_SUFFIX): frobnicator_pywrap.o libfrobnicator.so
$(PY_MRBUILD_LINKER) $(PY_MRBUILD_LDFLAGS) $< -lfrobnicator -o $@
# The python libraries (compiled ones and ones written in python) all live in
# frobnicator/
DIST_PY3_MODULES := frobnicator
all: frobnicator/_frobnicator$(PY_EXT_SUFFIX)
EXTRA_CLEAN += frobnicator/*.so
include /usr/include/mrbuild/Makefile.common.footer
#+END_SRC
* MAINTAINER
This is maintained by Dima Kogan <dima@secretsauce.net>
* LICENSE AND COPYRIGHT
Released under an MIT-style license. Modify and distribute as you like
Copyright 2016-2019 California Institute of Technology
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
|