]> cloudbase.mooo.com Git - z180-stamp.git/blobdiff - mk/Readme.txt
Add non-recursive make system from Andrzej Ostruszka.
[z180-stamp.git] / mk / Readme.txt
diff --git a/mk/Readme.txt b/mk/Readme.txt
new file mode 100644 (file)
index 0000000..c341b48
--- /dev/null
@@ -0,0 +1,469 @@
+################################################################################
+#                  Non-recursive make build system                             #
+#                  -------------------------------                             #
+#     Copyright (C) 2012 Andrzej Ostruszka <andrzej.ostruszka@gmail.com>       #
+#                                                                              #
+#          URL: http://github.com/aostruszka/nonrec-make                       #
+#          (or older: http://nonrec-make.googlecode.com/)                      #
+#                                                                              #
+# 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.                          #
+#                                                                              #
+# Except as contained in this notice, the name(s) of the above copyright       #
+# holders shall not be used in advertising or otherwise to promote the sale,   #
+# use or other dealings in this Software without prior written authorization.  #
+#                                                                              #
+# 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.                                                             #
+################################################################################
+
+NOTE: This readme _might_ not be up to date.  For up to date information
+see the above URL (and accompanying wiki pages).
+
+This is my attempt to implement a non-recursive make build system.  For
+the motivation Google for the paper "Recursive make consider harmful" by
+Peter Miller.
+
+I've seen couple of other proposals and decided to have something that
+will be a blend of nice ideas I have seen plus some improvements.  If
+you actually use this I'd like to hear from you :) - is it useful, does
+it perform well, do you have any suggestions for the improvements ...
+and so on.  This implementation is based on GNU make and its new
+features introduced in 3.80.  But don't use that version - these
+features had bugs in that version.  Use version 3.81 where everything
+works OK.
+
+Before you keep on reading though, just take a look at the structure of
+the Rules.mk files (Rules.top has exactly the same structure as Rules.mk
+- it just has another name to ease location of the top level project
+directory from its subfolders).
+I've got a feeling that it is much easier to understand how the system
+looks from the user perspective just by looking at the example than
+reading its explanation :D.
+
+OK, now that you have a feeling how the Rules.mk look like let me walk
+you through an example (ex1 in the repository).  Consider the project
+that has some source files at the top directory and is depending on two
+libraries in Dir_1 and Dir_2 and another one in Dir_3.  The libraries
+themselves are partitioned between several subdirectories and Dir_2 has
+some examples in a separate subfolder (do not pay attention to all *.c
+files).
+
+ex1/
+  Makefile
+  Rules.top <- Just a symlink to Rules.mk to mark where the top level is
+  Rules.mk
+  main.c
+  top_a.c
+  top_b.c
+  cli.c
+  cli_dep.c
+  mk/* <- This is where the mk files from this build system are
+  Dir_1/
+    Makefile
+    Rules.mk
+    dir_1_file1.c
+    dir_1_file2.c
+    dir_1_file3.c
+    Dir_1a/
+      Makefile
+      Rules.mk
+      dir_1a_file1.c
+      dir_1a_file2.c
+      dir_1a_file3.c
+    Dir_1b/
+      Makefile
+      Rules.mk
+      src/
+        dir_1b_file1.c
+        dir_1b_file2.c
+  Dir_2/
+    Makefile
+    Rules.mk
+    dir_2_file1.c
+    dir_2_file2.c
+    Dir_2a/
+      Makefile
+      Rules.mk
+      dir_2a_file1.c
+      dir_2a_file2.c
+      dir_2a_file3.c
+    Dir_2b/
+      Makefile
+      Rules.mk
+      dir_2b_file1.c
+      dir_2b_file2.c
+      dir_2b_file3.c
+    Dir_ex/
+      Makefile
+      Rules.mk
+      ex.c
+  Dir_3/
+    Makefile
+    Rules.mk
+    dir_3_file1.c
+    dir_3_file2.c
+
+There's one top level make file (Rules.top) which eventually includes
+all the makefiles.  In addition in each directory there is Makefile
+(which can be a link to the one in the top level directory) which
+searches for the Rules.top includes it with the default goal
+changed to rebuild only targets for the current directory.  This allows
+you to run make from each subdirectory and update only part of the
+targets (in contrary to other implementation which usually require
+you to run it at the top level and make full build each time).
+
+This build system was designed to have very simple structure of the
+"user makefiles".  The user just has to set the Rules.mk files in each
+directory and some general configuration options.  All the "magic" is
+hidden in header.mk, footer.mk and skel.mk which don't have to be
+modified [1].
+
+The structure of the Rules.mk is following (this is from top level
+Rules.top which has the same format as Rules.mk - and in fact it is
+suggested that it should be a symlink to normal Rules.mk file since it
+will allow for this project to act as a subproject of some super project
+treating your whole project tree as a subdirectory[2]):
+
+-8<---Rules.top---------------
+1:  TARGETS = app.exe cli.exe
+2:  SUBDIRS = Dir_1 Dir_2
+3:
+4:  app.exe_DEPS = top_a.o top_b.o main.o $(SUBDIRS_TGTS)
+5:  app.exe_LIBS = -lm
+6:  # Let's use DEFAULT_MAKECMD for app.exe
+7:
+8:  cli.exe_DEPS = cli.o cli_dep.o
+9:  cli.exe_CMD = $(LINK.c) $^ $(LDLIBS) -o $@
+-8<---------------------------
+
+Line 1 - this directory has two targets that should be built.
+Line 2 - this directory has two subdirectories that should be scanned
+Line 4 - app.exe depends on ... (SUBDIRS_TGTS is a variable that
+        contains all the targets from the subdirectories mentioned at
+        line 4)
+Line 5 - app.exe should be linked with math library
+Line 6 - app.exe will be built with default "rule"
+Line 8 - cli.exe depends on ... and
+Line 9 - use the following command to build it
+
+You can specify the targets for current directory in two ways:
+1. Give them in TARGETS.  Each target can have it's own *_DEPS, *_LIBS
+  and *_CMD which give the dependencies, additional libs needed and
+  a command to run which will update the target.  They are explained
+  a bit below.
+2. The targets are simply objects - or in more general files that
+  match patterns in AUTO_TGTS, and have appropriate rules in 'skeleton'.
+  In that case you can list them in OBJS or SRCS like e.g. in Rules.mk
+  from Dir_1a
+
+-8<---Dir_2/Dir_2a/Rules.mk---
+1: SRCS := dir_2a_file1.c dir_2a_file2.c dir_2a_file3.c
+-8<---------------------------
+
+There are "reserved" variables that you should not modify.  Most notably:
+- $(d) is the directory of the current Rules.mk [see note 2]
+- $(TOP) is the top level directory of the project tree
+- $(MK) is the directory where the included *.mk makefiles are
+For the full list you have to take a look at the makefiles in mk
+directory (e.g. in the skel.mk there are macros 'include_subdir_rules',
+'save_vars', 'tgt_rule' and 'skeleton' which you should not change [1]).
+
+Going back to the Rules.mk.  Normally wildcards in variable assignments
+are not expanded in make but this make system detects wildcards in SRCS
+and expands them (both in directory of the Rules.mk and its SRCS_VPATH
+subdirectories - see below what SRCS_VPATH is used for).  Thus you can
+simply say in Rules.mk:
+
+SRCS := *.c
+
+If you have directory with large number of files where simple glob is
+what you want to use in SRCS but there are some files that you'd like to
+exclude just list them in SRCS_EXCLUDES :) - this is a list of makefile
+patterns e.g.
+
+SRCS_EXCLUDES := extra% test%
+
+Of course you can use the built in make wildcards but you should do that
+as follows:
+
+SRCS := $(notdir $(wildcard $(d)/*.c))
+
+Keep in mind that the directory where make is invoked is usually different
+from where given Rules.mk is located.
+
+When supplying the value for *_DEPS you can refer to the object from the
+same directory with no directory prefix.  To be specific all
+dependencies that are not absolute paths will be treated as ones from
+the $(OBJDIR) subdirectory of current directory (OBJDIR and OBJPATH are
+"discussed" below).  You can use SUBDIRS_TGTS variable which will list
+all targets in subdirectories.  You can also name them explicitly like
+$(TARGETS_$(d)/subdir) and so on - see e.g. the Rules.mk in Dir_2
+directory where Dir_ex is mentioned as a subdirectory but is excluded
+from the *_DEPS (this allows you to create folders that "inherit" all
+the setting from the project build system but are not meant to be a part
+of the project itself - like examples).  For instance:
+
+dir1_lib.a_DEPS = dir_1_file1.o dir_1_file2.o dir_1_file3.o $(SUBDIRS_TGTS)
+
+tells that dir1_lib.a (which will be created in Dir_1/$(OBJDIR)) depends
+on several object files from the same directory and the targets from all
+subdirectories.
+
+One last thing about the TARGETS/OBJS.  By default source files for the
+objects are searched in the directory where Rules.mk is, but if you want
+to have source files in a subdirectory (say 'src') you can do that via
+SRCS_VPATH variable (see skel.mk).  E.g.:
+
+SRCS_VPATH := src1 src2
+
+will cause make to first look at the directory where Rules.mk is present
+and then in its src1 and src2 subdirectories.
+
+*_LIBS are appended to the LDLIBS variable when updating the target.
+This variable is used by several make built in rules but if you create
+your own rule or MAKECMD.* (see next paragraph) you can refer to it (see
+the function 'save_vars').
+
+When *_CMD is not present and the target does not match any pattern in
+AUTO_TGTS then either MAKECMD.suff (where .suff is the suffix of the
+target) or DEFAULT_MAKECMD is used - take a look into skel.mk.
+
+If you want to setup special flags for compilation you can do that via
+"directory specific variables".  As an example here's what I did for
+compilation of C files.  There's a built in rule in make which uses
+COMPILE.c variable for making %.o out of %.c so I added $(DIR_CFLAGS) to
+its default value and DIR_CFLAGS is defined in skel.mk as:
+
+DIR_INCLUDES = $(addprefix -I,$(INCLUDES_$(<D)))
+DIR_CFLAGS = $(CFLAGS_$(<D)) $(DIR_INCLUDES)
+
+So it extracts the directory part of the first prerequisite in the rule
+(that is %.c file - check the section 'automatic variables' in make
+manual for the meaning of $(<) and $(<D)) and refer to variables named
+CFLAGS_the_directory_part and INCLUDES_the_directory_part.
+Thus if you wanted to add special includes for files in Dir_1/Dir_1b you
+could add:
+
+INCLUDES_$(d) := $(TOP)/some/special/include_dir
+
+into its Rules.mk and all files in this directory will be compiled with
+-I$(TOP)/some...  switch.  The same goes for CFLAGS and CXXFLAGS.
+
+The same goes for the linker flags - quoting from skel.mk:
+
+LDFLAGS = $(addprefix -L,$(LIBDIRS_$(subst /$(OBJDIR),,$(@D))))
+LDLIBS = $(LIBS_$(@))
+
+The above means that if targets in given directory need to be linked
+with special -L switches you can provide them via LIBDIRS_$(d)
+variables.  If there are some global -L switches just append them in
+skel.mk.  The second line above shows how *_LIBS variable that you can
+give for specific target gets added to the LDLIBS (there's 'save_vars'
+in between if you're curious :)).
+
+You can of course use target specific variables that GNU make supports
+so you have more control (if you don't know what target specific
+variables are take a look into manual).  Say you want to compile
+dir_1b_file2.c with an additional flag but all other files in
+Dir_1/Dir_1b directory should not have this flag turned on.  All you
+need to do is to add this line into Rules.mk in Dir_1b directory.
+
+$(OBJPATH)/dir_1b_file2.o : CFLAGS += -ansi
+
+OBJPATH is a variable that contains the full directory where the
+resulting object file will be placed.  While we are at this, by default
+all targets are compiled into OBJDIR (defined in skel.mk as 'obj')
+subdirectory of the directory where Rules.mk is present.  You can use
+this OBJDIR variable (and perhaps some conditional statements) to setup
+the output path according to your current compilation mode.  E.g.
+obj/debug for objects with debugging information or obj/ppc_7xx for
+cross compilation to the given Power PC family and so on.  There's
+predefined (in config* files) HOST_ARCH variable that you can use for
+this (e.g. set OBJDIR := obj/$(HOST_ARCH) in skel.mk).
+
+Finally let me explain what targets are defined and the way you can run
+them from command line.  By default (that is if you have not modified
+anything :)) there are several targets that are "global".  It is 'all',
+'clean_all' and 'dist_clean'.  If you specify them in the command line
+they will respectively rebuild whole tree, clean everything and clean
+everything together with the removal of OBJDIRs - no matter from which
+directory you started make.  BTW if there's something that you want to
+clean up and it's not in the OBJDIR - e.g. you've got file lexer.l out
+of which lexer.c and lexer.h is generated and you want them to be
+removed - you can specify this in the variable CLEAN (this is relative
+to the directory where Rules.mk is).
+In addition to those each dir has "it's own" targets.  These are:
+
+1) dir_<directory_path>
+   which builds all targets in given directory plus its dependencies
+2) tree_<directory_path>
+   which builds all targets in subtree starting at directory given
+3) clean_<directory_path>
+   which removes all "products" from the $(OBJDIR) subdirectory of
+   current directory
+4) clean_tree_<directory_path>
+   which does clean_<directory_path> and the same for each its
+   subdirectory
+
+For your convenience there are couple "aliases" defined (see Makefile).
+When no target is given on command line it defaults to dir_$(pwd).
+If you give 'clean' as a target that will result in execution of target
+clean_$(pwd) and the same for 'clean_tree'.  E.g. say you're in Dir_1.
+Then:
+
+* 'make' (same as 'make dir_$(pwd)')
+   builds all targets in the Dir_1 which in our example is
+   Dir_1/obj/dir1_lib.a - of course any of its dependencies that are not
+   up to date are updated also.  This rule has one exception - if your
+   Rules.mk has no targets and only SUBDIRS (e.g. you have grouped
+   several subdirectories in one directory) then simple 'make' in this
+   directory - instead of doing nothing - will build targets of all its
+   subdirectories.
+* 'make tree' (same as 'make tree_$(pwd)')
+   rebuilds everything in given subtree
+* 'make clean' (same as 'make clean_$(pwd)')
+   removes everything from Dir_1/obj/
+* 'make clean_tree (same as 'make clean_tree_$(pwd)')
+   cleans Dir_1/obj and Dir_1/Dir_1[abc]/obj
+
+You can obviously provide the path by yourself - it does not have to
+be $(pwd) - and as usual you can build particular object files too e.g.
+'make $(pwd)/obj/dir_1_file1.o'
+
+And that would be it.  Gee, so much writing for something that is rather
+simple to use - go ahead and take a look again at these Rules.mk in
+various directories.  Setting up you project should be simple by now :).
+
+Have fun!
+
+                                       Andrzej Ostruszka
+
+[1] Unless this build system does not do what you wanted :-P.  In that
+case you probably need to spiff it up.  So you'll need to
+digest it first and note [3] is my hand at it :).
+
+[2] There is one limitation that you should be aware.
+Prior to commit 070f681 you should not have in your project two "target
+specific" LIBS, LDFLAGS or CMD variables (that is those that are used
+during second phase of make execution) that have the same names!  For
+example in one part of your tree you're generating target abc which has
+it's abc_CMD and in the other part another target that has the same name
+and also it's own command.  In such case the last assignment to abc_CMD
+will hold and it will be used for both targets.  The same goes for LIBS
+and LDFLAGS.
+
+And this also applied to situation when you would like to use two
+subprojects in one larger project.  There should be no clash between
+variables from these subprojects.
+
+Starting with commit 070f681 you can have in your (sub)projects two
+LIBS, LDFLAGS or CMD variables.  However there is a catch here! Since
+these variables are not mandatory (you don't have to provide them for
+each target) in order to differentiate between case where abc_CMD was
+actually given for this instance of abc target from the situation where
+abc_CMD is still visible from previous instance of abc target those
+abc_(LIBS|LDFLAGS|CMD) variables are cleared once their value is
+used/remembered (see save_vars macro in skel.mk). That means referring
+to these variables outside Rules.mk where they were assigned will not
+work (and even in the same Rules.mk they will work only in case of
+simply expanded variables - not recursive). If you have such need I'd
+advice to introduce your own variable and use this variable in all
+places, e.g.:
+
+MATH_LIBS := -lgsl -lgslcblas -lm
+...
+TARGETS := abc
+
+abc_DEPS = ...
+abc_LIBS = $(MATH_LIBS)
+...
+
+[3] You should know that make works in two phases - first it scans the
+makefiles and then it begins their execution (see discussion of
+'immediate' and 'deferred' in section 'Reading Makefiles' of make
+manual).  This implies that the $(d) variable is not valid during
+execution of the commands used for target updates.  If you need to refer
+to the directory of the target or prerequisite you should rely on
+automatic variables (@, <, ...) and built in functions (dir, notdir,
+...).
+
+Every Rules.mk (or Rules.top) need to be included in cooperation with
+header.mk and footer.mk.  This is now done automatically but in older
+versions this was not so and user had to include them manually.
+The main purpose of header is to clear all variables that you can use
+inside Rules.mk so that values set in one rules file does not propagate
+to other.  In addition at the top level it sets the $(d) variable (which
+stands for the directory where the currently included Rules.mk is) and
+includes the skel.mk so the top level Rules.top can have exactly the
+same structure as other Rules.mk.
+
+The skel.mk is a skeleton which defines variables used by make.  In
+addition it includes:
+- config.mk - this is a file with the "configuration" for your project
+- def_rules.mk - where to put general rules (e.g. pattern rules).  E.g.
+  if you want to add a rule that builds %.o out of %.m4 (by running m4
+  preprocessor before passing the contents of file to CC) just put it in
+  here.
+
+skel.mk also defines some functions like save_vars and tgt_rule
+which are called in the footer.mk.  Take a look into make manual for the
+way the functions are defined and called if you're not familiar with it.
+
+include_subdir_rules: This is where I keep track of directory of
+       currently included makefile and include the Rules.mk from the
+       subdirectories.  This function is called in footer.mk in foreach
+       loop with a subdirectory name as an argument.
+
+save_vars: This one is very simple it just saves the target specific
+       variables under their "full path" variables.  E.g.
+       dir1_lib.a_DEPS will get saved as DEPS_$(TOP)/Dir_1/obj/dir1_lib.a
+       This has two purposes.  First it allows you to have targets with
+       the same names in different directories (not that I recommend
+       this :)) and allows for easier definition of tgt_rules :-P
+
+tgt_rule: This is where the rule for given target is created.  First
+       I convert all relative dependencies to the absolute ones then
+       I include all dependency files (by default they are created as
+       side effects during compilation - if your compiler does not
+       allow you to do that just make simple shell script that will do
+       this).  I also append appropriate libs and then issue the rule
+       for this target.  By using the short circuiting 'or' command
+       I give priority to the CMD over MAKECMD.suffix which itself has
+       priority over DEFAULT_MAKECMD.
+
+skeleton: This is just a skeleton for compilation of files in given
+       directory.  If you want to add some rule here then also add
+       appropriate pattern to the AUTO_TGTS that will filter out these
+       targets and prevent generation of the specific rules via
+       tgt_rule function.
+
+The footer.mk is where Rules.mk gets translated into the proper
+makefile.  There's not much to explain, just take a look in there.
+First I memorize the targets for given directory (either from OBJS/SRCS
+or from TARGETS) with appropriate directory prefix.  If there's need to
+save the target specific *_(CMD|DEP|LIB) I do it.  Then I include all
+the Rules.mk from the subdirectories.  Then I define the targets for
+given directory either explicitly (like clean_all or clean_$(d)) or by
+evaluation of the 'skeleton' mentioned above or by iterating through all
+targets which do not match AUTO_TGTS and evaluating what tgt_rule
+function for this target has returned.
+
+In case you want to play with these settings make sure you understand
+how make works, what are it's phases, when and how the variables are
+expanded and so on.  It will save you a lot of time :).
+
+Best regards
+Andrzej