Commit 884d93a0 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner

Import the build system

parents
RESTRICT := build
RELATIVE := ../
include $(RELATIVE)/Makefile
# Generate lists of targets and other things
$(eval $(foreach BINARY,$(BINARIES),$(call REGISTER_BIN,$(BINARY))))
$(eval $(foreach LIB,$(LIBRARIES),$(call REGISTER_LIB,$(LIB))))
ifndef NO_DOC
$(eval $(foreach DOC,$(DOCS),$(call REGISTER_DOC,$(DOC))))
endif
$(eval $(foreach SCRIPT,$(SCRIPTS),$(call REGISTER_SCRIPT,$(SCRIPT))))
# Top-level targets to be called by user
all: $(TARGETS)
clean:
rm -rf $(O)/bin $(O)/lib $(O)/docs $(O)/.objs $(O)/.deps $(TARGETS)
.PHONY: all clean
DOCS += build/buildsystem
all:
# To be filled in by included makefiles
TARGETS =
BINARIES =
DOCS =
# Build compiler flags
ifdef RELEASE
CFLAGS += -O2
else
CFLAGS += -ggdb3 -O0 -DDEBUG
endif
ifeq ($(TARGET),mips)
CC = mips-softfloat-linux-uclibc-gcc
endif
ifdef VERBOSE
Q=
M=@\#
else
Q=@
M=@echo " "
endif
ifndef PLUGIN_PATH
PLUGIN_PATH := lib
endif
CFLAGS_ALL += $(CFLAGS) --std=gnu99 -pedantic -Wall -Wextra -DOUTPUT_DIRECTORY=\"$(abspath $(O))\" -DSOURCE_DIRECTORY=\"$(abspath $(S))\" -DPAGE_SIZE=$(PAGE_SIZE) -DPREFIX=$(abspath $(O)) -DMAX_LOG_LEVEL=$(MAX_LOG_LEVEL) -DPLUGIN_PATH=\"$(PLUGIN_PATH)\" $(addprefix,-D,$(EXTRA_DEFINES))
LDFLAGS_ALL += $(LDFLAGS)
ifdef STATIC
CFLAGS_ALL += -static -DSTATIC
LDFLAGS_ALL += -static
endif
ifdef LOGIN_PASSWD_HALF
CFLAGS_ALL += -DPASSWD_HALF="$$(cat $(LOGIN_PASSWD_HALF))"
else
CFLAGS_ALL += -DPASSWD_HALF='{0}'
endif
ifeq ($(ENABLE_PYTHON), 1)
CFLAGS_ALL += $(shell python2.7-config --includes)
endif
# Magic to track dependencies on header files. They are stored in the .d files
# by the compiler, then we load them on the next run and use them.
DEP_FILES := $(shell if test -d $(O)/.deps ; then find $(O)/.deps -name '*.d' ; fi | sort)
include $(DEP_FILES)
# Stuff to generate dependencies and link commands from the target_SOMETHING variables.
ifdef STATIC
LIB_SUFFIX :=.a
else
LIB_SUFFIX :=.so
endif
define COMMON_REGISTER
$(1): $$(patsubst %,$(O)/.objs/$(3)%.o,$$($(2)_MODULES))
$(1): CFLAGS_ALL += $$($(2)_CFLAGS)
$(1): LDFLAGS_ALL += $$($(2)_LDFLAGS)
ifdef STATIC
TODO: Static linking
else
$(1): LINK_LIBRARIES += -L$(O)/lib -Wl,-R$(abspath $(O)/lib) $$(addprefix -l,$$($(2)_LOCAL_LIBS) $$($(2)_SYSTEM_LIBS))
$(1): LINK_SO_LIBRARIES += $$(addprefix -l,$$($(2)_SO_LIBS))
$(1): $$(patsubst %,$(O)/lib/lib%.so,$$($(2)_LOCAL_LIBS))
endif
endef
# Function to register a binary to TARGETS and generate the rules to build it.
# It shall be called as:
#
# binary_name_MODULES := main other_file third_file
# $(eval $(call REGISTER_BIN,binary_name,src/path/to/directory))
define REGISTER_BIN
ifneq (,$(findstring ^$(RESTRICT),^$(1)))
TARGETS += $(O)/$(1)
endif
$(call COMMON_REGISTER,$(O)/bin/$$(notdir $(1)),$$(notdir $(1)),$$(dir $(1)))
$(O)/$(1): $(O)/bin/$$(notdir $(1))
$(M) LN $$@
$(Q)mkdir -p $$(dir $(O)/$(1))
$(Q)ln -sf $$(abspath $$<) $$@
endef
# Similar, for libraries
define REGISTER_LIB
ifneq (,$(findstring ^$(RESTRICT),^$(1)))
TARGETS += $(O)/$(1)$(LIB_SUFFIX)
endif
$(call COMMON_REGISTER,$(O)/lib/$$(notdir $(1)).a,$$(notdir $(1)),$$(dir $(1)))
$(call COMMON_REGISTER,$(O)/lib/$$(notdir $(1)).so,$$(notdir $(1)),$$(dir $(1)))
$(O)/$(1).a: $(O)/lib/$$(notdir $(1)).a
$(M) LN $$@
$(Q)mkdir -p $$(dir $(O)/$(1))
$(Q)ln -sf $$(abspath $$<) $$@
$(O)/$(1).so: $(O)/lib/$$(notdir $(1)).so
$(M) LN $$@
$(Q)mkdir -p $$(dir $(O)/$(1))
$(Q)ln -sf $$(abspath $$<) $$@
endef
# Similar, for documentation
define REGISTER_DOC
ifneq (,$(findstring ^$(RESTRICT),^$(1)))
TARGETS += $(O)/$(1).html
endif
$(O)/docs/$$(notdir $(1)).html: $(S)/$$(addsuffix .txt,$(1))
$(O)/$(1).html: $(O)/docs/$$(notdir $(1)).html
$(M) LN $$@
$(Q)mkdir -p $$(dir $(O)/$(1))
$(Q)ln -sf $$(abspath $$<) $$@
endef
define REGISTER_SCRIPT
ifneq (,$(findstring ^$(RESTRICT),^$(1)))
TARGETS += $(O)/$(1)
endif
$(O)/$(1): $(O)/bin/$$(notdir $(1))
$(M) LN $$@
$(Q)mkdir -p $$(dir $(O)/$(1))
$(Q)ln -sf $$(abspath $$<) $$@
$(O)/bin/$$(notdir $(1)): $(S)/$$(dir $(1))/$$($$(notdir $(1))_SOURCE)
$(M) LN $$@
$(Q)mkdir -p $(O)/bin/
$(Q)ln -sf $$(abspath $$<) $$@
endef
# Generic rules to compile and link
$(O)/.objs/%.o: $(S)/%.c
$(M) CC $@
$(Q)mkdir -p $(dir $@ $(subst .objs/,.deps/,$@))
$(Q)$(CC) $(CFLAGS_ALL) -c $< -o $@ -MD -MF $(patsubst %.o,%.pre,$(subst .objs/,.deps/,$@))
$(Q)$(S)/build/normalize_dep_file.pl $(O) $(patsubst %.o,%.pre,$(subst .objs/,.deps/,$@)) >$(patsubst %.o,%.d,$(subst .objs/,.deps/,$@))
$(O)/bin/%:
$(M) LD $@
$(Q)mkdir -p $(dir $@)
$(Q)$(CC) $(LDFLAGS_ALL) $(LINK_LIBRARIES) $(filter %.o,$^) -o $@
$(O)/lib/%.a:
$(M) AR $@
$(Q)mkdir -p $(dir $@)
$(Q)$(AR) -rc $@ $(filter %.o,$^)
$(O)/lib/%.so: CFLAGS_ALL += -fPIC
$(O)/lib/%.so:
$(M) LD $@
$(Q)mkdir -p $(dir $@)
$(Q)$(CC) $(LDFLAGS_ALL) $(LINK_SO_LIBRARIES) -shared -o $@ $(filter %.o,$^)
$(O)/docs/%:
$(M) DOC-HTML $@
$(Q)mkdir -p $(dir $@)
$(Q)$(ASCIIDOC) -o $@ $<
The build system
================
The build system is not based on automake or CMake or any other
makefile generator. They all try to solve the problems of make by
producing very complex and long inputs for make. That, obviously
doesn't work, so you get both problems of make and huge complexity. If
we are to have the problems of make anyway, we can at least have it
simple.
So, the system leverages some nice features of GNU make and uses just
plain makefiles. It includes sub-makefiles instead of running make
recursively, since it allows tracking dependencies across directory
boundaries and uses parallel compilation across directories.
The makefiles
-------------
There are several kinds of makefiles. The first type is the ones under
the `build` directory. They contain the actual rules and definitions
of the build system itself. It's the magic that makes it work and they
require a good knowledge of GNU make syntax to understand and modify.
But it shouldn't be needed to modify these under usual circumstances.
Then there are the `Makefile.dir` ones. There should be one in each
directory with code and they contain the definitions of what should be
compiled. This is the kind of makefile modified when source files or
subdirectories are added.
The third kind is the `Makefile` in the top-level directory of the
project. This one sets default options for compilation on the given
computer. Currently, it's a static one, so if something doesn't work,
proper values need to be either entered manually or passed on the make
command line. But it is expected to be generated by some kind of
configure script in future (and it would be the only generated file).
The final type is the plain `Makefile` in each directory. These are
just thin wrappers to call the whole build system correctly, so one
can simply call make from whatever directory. No rules or definitions
should come in here.
Defining project parts
----------------------
To compile part of the project (either a binary or library), you do
two things:
* Append the path of the result to given type of target variable.
* Define variables defining which files it is built from and other
options.
Imagine you want to build a binary called `hello_world` and the
sources for it are located in `src/hello_world`. You'd use the
following code:
# Be sure to append by `+=`, not override by `=`. The basename
# must be unique across the whole project (you can't have another
# hello_world in other directory).
BINARIES += src/hello_world/hello_world
# List sources. Without the .c at the end or path. The input files
# don't need to be unique, so there can be another main.c in other
# directory. You don't list the headers.
hello_world_SOURCES := \
main \
hello \
world
# Compilation flags, appended to the common ones.
hello_world_CFLAGS := -fpermisive
# Linker flags, similar. Don't use to link against other libraries.
# But this can be used to provide additional paths, for example
# (though these should come from the top-level `Makefile`).
hello_world_LDFLAGS := -static
# Libraries linked from the system. This one links the math library
# (-lm) and pcap (-llibpcap)
hello_world_SYSTEM_LIBS := m libpcap
# The same for libraries from the project. They are separated so
# the dependencies against the local libraries can be tracked.
hello_world_LOCAL_LIBS := libworld
Only the `_SOURCES` variable is mandatory. You can create a shared
library the same way, it is only appended to `LIBRARIES` instead to
`BINARIES`.
You can also generate documentation (`html` from the `txt` by
asciidoc). It has no variables to influence it and you append them to
the `DOCS` variable.
Both `LIBRARIES` and `DOCS` are listed without the suffix.
Note that the results are created in `bin/`, `lib` or `docs`
respectively. They are only symlinked to the directory where they come
from. That means `src/hello_world/hello_world` will be symlink to
`bin/hello_world`.
Dependency tracking
-------------------
The system needs to be told about dependencies on libraries (by the
`_LOCAL_LIBS` variable). But dependencies on header files are tracked
automatically. It uses the `gcc`'s feature of storing the dependencies
into a separate file and then using it on successive runs.
This works reasonably well with one exception -- removing a header
file from repository. If you remove all references to the header and
remove the header itself, the dependency on it is still recorded and
make will fail. You need to run `make clean` in that case and compile
from scratch.
The correct way to remove a header is to remove the `#include` lines,
recompile and remove the file afterwards.
Configuring the build
---------------------
There are several variables that influence how the project is built.
They can be specified either in the top-level `Makefile` or on the
`make`'s command line (eg. by calling `make RELEASE=1` to create a
release build). The values on command line take precedence (as usual
with make).
If you provide different options than before, you should start with a
clean build. Otherwise, there could be some confusion if only some
parts would be rebuilt with the new flags and some were left from
previous builds with previous options.
Variables that are expected to be set are:
ASCIIDOC::
The asciidoc tool used to compile.
CC::
The C compiler used. It is tested with `gcc`, but others might
work as well. You can set it to a compiler for other architecture
for cross-compiling. This overrides whatever is set by the `TARGET`
variable.
CFLAGS::
Flags passed to the compiler. This is expected to contain things
like optimisation flags. There will be other options appended to
this by the build system.
+
It overrides whatever is set by the `TARGET` variable and `RELEASE`
variable.
EXTRA_DEFINES::
Additional defines that will be passed to the compiler.
EXTRA_INCLUDES::
Additional paths to search for inclusion of header files.
EXTRA_LIBS::
Additional libraries to link into the result.
LDFLAGS::
Flags passed to the linker. Other flags will be appended by the
build system.
+
This overrides whatever is set by the `TARGET` and `RELEASE`
variables.
LOGIN_PASSWD_HALF::
A path to a file containing the static half of password used for the
challenge-response login.
MAX_LOG_LEVEL::
Maximum level of log messages that are output. Default is LOG_DEBUG.
NO_DOC::
Disables compilation of documentation.
O::
The top-level output compilation directory. See following chapter.
It is not expected to be changed on command line.
PAGE_SIZE::
The page size to be passed as a define to the compiled code. It is
attempted to be auto-detected, but can be redefined to something
else.
PLUGIN_PATH::
Path where the plugin libraries should be found in the target
system. May be absolute or relative. Defaults to ``lib''.
RELEASE::
By default, it compiles with debug flags (eg. `-O0 -ggdb3`). With
this set, it uses optimisation flags (`-O2`).
S::
The top-level source directory. See following chapter. It is not
expected to be changed on command line.
STATIC::
If set, link statically instead of dynamically.
TARGET::
This can be used for cross-compiling for other architectures. It'll
change the compiler used and flags. Currently, the value of `mips`
is supported. It also sets the `STATIC` variable, so the generated
output is easier to just copy to the target machine.
VERBOSE::
By default, the system prints a short summary (`CC file.o`). If this
is set to something, it outputs the whole compilation command.
Separate build directory
------------------------
The system allows for building in a separate directory from the
sources. It can be used when the source is on read-only filesystem or
when there should be multiple builds with different options.
The `O` variable specifies the directory where results will be
created. The `s` where the sources reside.
The easiest way to create a new build directory is to copy the
top-level `Makefile` to the place where the build should happen. Then,
the `S` variable there should be adjusted to point to the sources
directory. Optionally, other options can be tweaked there. The `O`
variable can be left intact.
Defines passed to the compiler
------------------------------
We use compiler defines (`-D`) to provide some configuration to the
compiled source code, instead of generating `config.h`. These are the
currently passed ones.
DEBUG::
Defined if the RELEASE is not turned on.
MAX_LOG_LEVEL::
Maximum level of log messages to be output. It has the value of
MAX_LOG_LEVEL variable in make.
OUTPUT_DIRECTORY::
Value of the `O` variable.
PAGE_SIZE::
The size of the memory page. It has the value set in PAGE_SIZE
variable in make.
PASSWD_HALF::
The static half of password used for the challenge-response login.
PLUGIN_PATH::
Where the plugin libraries should be found at runtime.
PREFIX::
Currently, value of the `O` variable. It is expected to contain
whatever prefix the project will be installed into, but since
installation is not yet supported, we opt for this.
SOURCE_DIRECTORY::
Value of the `S` variable.
STATIC::
Defined if STATIC is turned on in build system.
Missing bits
------------
* Installation. The current system compiles the project and it must be
run from the directory where it was built. The idea is to provide
another target (`install`) which would re-link all the libraries and
binaries (so correct paths are embedded in them) with output files
being set to `PREFIX/{bin/lib}`. The `PREFIX` would be new variable.
But the plug-ins might want to go somewhere else.
* Linking statically.
#!/usr/bin/perl
use strict;
use warnings;
# This is a script used by the build system, to normalize the dependencies that get out of GCC.
# It allows calling the make from subdirectories, since the paths to the files are different then.
# So we replace the variable parts of paths with the $(O) make variable.
die "Not enough arguments. And you're not supposed to call it directly anyway.\n" unless @ARGV == 2;
my ($O, $in) = @ARGV;
$O .= '/';
$O =~ s/^\.\///; # GCC is so nice it removes the first dot. Or is it make? Whatever, but we don't have it there.
$O =~ s/\./\\./g;
open my $input, '<', $in or die "Could not read $in ($!)\n";
while (<$input>) {
s#^$O(\S)#\$(O)/$1#;
s# $O([^/\\ ])# \$(O)/$1#g;
print;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment