{"id":18,"date":"2023-01-21T00:48:56","date_gmt":"2023-01-21T05:48:56","guid":{"rendered":"https:\/\/www.5amsoftware.com\/blog\/?p=18"},"modified":"2023-01-21T00:48:56","modified_gmt":"2023-01-21T05:48:56","slug":"a-recursive-cross-compiling-make-system","status":"publish","type":"post","link":"https:\/\/www.5amsoftware.com\/blog\/2023\/01\/21\/a-recursive-cross-compiling-make-system\/","title":{"rendered":"A Recursive, Cross-Compiling Make System"},"content":{"rendered":"\n<p>There comes a point in any project that when it reaches a certain size, a flat directory hierarchy no longer works. There are too many files, some perhaps similarly named, and additional organization becomes needed. One approach is to use recursive Makefiles, with a top-level set of Makefiles controlling the build which call into multiple subdirectories that contain the actual source code.<\/p>\n\n\n\n<p>This article describes one such approach, with the additional requirements that it support cross-compiling for another CPU target and tracks file dependencies. Additional goodies include packaging the generated executables into a filesystem image and digitally signing it with GnuPG.<\/p>\n\n\n\n<p>Much of this work is inspired from various manuals, blog posts, and Stackoverflow questions found on the Internet. Unfortunately this framework is several years old and I did not record information sources at the time it was initially constructed. Many thanks are owed to anonymous coders on the Internet.<\/p>\n\n\n\n<p>This framework uses GNU Make and has not been tested with other implementations.<\/p>\n\n\n\n<p>A complete copy of this example can be found on my GitHub page <a href=\"https:\/\/github.com\/erenright\/makesystem\">here<\/a>.<\/p>\n\n\n\n<p><strong>Table of Contents<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#Structure-Layout\">Structure Layout<\/a><\/li>\n\n\n\n<li><a href=\"#Makefile\">Makefile<\/a><\/li>\n\n\n\n<li><a href=\"#common.mk\">common.mk<\/a><\/li>\n\n\n\n<li><a href=\"#version.mk\">version.mk<\/a><\/li>\n\n\n\n<li><a href=\"#exeA-Makefile\">exeA\/Makefile<\/a><\/li>\n\n\n\n<li><a href=\"#libA-Makefile\">libA\/Makefile<\/a><\/li>\n\n\n\n<li><a href=\"#exeB-Makefile\">exeB\/Makefile<\/a><\/li>\n\n\n\n<li><a href=\"#Adding-New-Components\">Adding New Components<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Structure-Layout\">Structure Layout<\/h2>\n\n\n\n<p>At the root level, three Makefiles are utilized.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>common.mk<\/code> &#8211; common targets and definitions that are global to the project<\/li>\n\n\n\n<li><code>version.mk<\/code> &#8211; specifies version control information<\/li>\n\n\n\n<li><code>Makefile<\/code> &#8211; top-level Makefile which ties it all together<\/li>\n<\/ol>\n\n\n\n<p>Adjacent to these Makefiles, one or more subdirectories are created to hold appropriately grouped code. These are nominally components that will each be compiled into an executable binary or a static <code>.a<\/code> library. Shared <code>.so<\/code> libraries was outside the scope of my needs, but likely simple to implement.<\/p>\n\n\n\n<p>The example framework consists of three components.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>exeA<\/code> &#8211; executable process &#8220;A&#8221;, which also depends on <code>libA<\/code><\/li>\n\n\n\n<li><code>exeB<\/code> &#8211; executable process &#8220;B&#8221;<\/li>\n\n\n\n<li><code>libA<\/code> &#8211; static <code>.a<\/code> library pulled in by <code>exeA<\/code><\/li>\n<\/ol>\n\n\n\n<p>The full directory tree thus looks like:<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>$ ls -l\ntotal 24\n-rw-rw-r-- 1 eric eric 1450 Jan 20 22:22 common.mk\ndrwxrwxr-x 2 eric eric 4096 Jan 20 22:59 exeA\ndrwxrwxr-x 2 eric eric 4096 Jan 20 22:59 exeB\ndrwxrwxr-x 2 eric eric 4096 Jan 20 22:59 libA\n-rw-rw-r-- 1 eric eric 1067 Jan 20 21:41 Makefile\n-rw-rw-r-- 1 eric eric  892 Jan 20 22:05 version.mk<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Makefile\">Makefile<\/h2>\n\n\n\n<p>The top-level Makefile is used to define components and major build targets.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>include version.mk\n\nlibA\t\t:= libA\nlibraries\t:= $(libA)\nexeA\t\t:= exeA\nexeB\t\t:= exeB\n\nimagedir\t:= image\nimagenamebase\t:= $(partNumber)_$(appName)_v$(versionMajor).$(versionMinor).$(versionRevision)\nimagefile\t:= $(imagenamebase).squashfs\n\n.PHONY: all $(exeA) $(exeB) $(libraries)\nall: $(exeA) $(exeB) $(libraries)\n\ninstall: $(exeA) $(exeB)\n\t$(RM) -rf $(imagedir) $(imagefile)\n\tinstall -D -m 755 -t $(imagedir)\/\t$(exeA)\/exeA\n\tinstall -D -m 755 -t $(imagedir)\/\t$(exeB)\/exeB\n\tmksquashfs $(imagedir) $(imagefile)\n\nsign: install\n\tgpg --detach-sign $(imagefile)\n\tmkdir $(imagenamebase)\n\tmv $(imagefile) $(imagefile).sig $(imagenamebase)\n\tzip -r $(imagenamebase).zip $(imagenamebase)\n\trm -rf $(imagenamebase)\n\t$(eval versionAutoNum=$(shell echo $$(($(versionAutoNum)+1))))\n\t@echo $(versionAutoNum) > $(autoNumFile)\n\nclean:\n\t$(MAKE) -C . TARGET=clean\n\trm -rf image *.squashfs *.zip\n\n$(exeA) $(exeB) $(libraries):\n\t$(MAKE) --directory=$@ $(TARGET)\n\n# Configure the various module dependencies\n$(exeA): $(libA)<\/code><\/pre>\n\n\n\n<p>Next we will break down the major pieces of this Makefile.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>include version.mk<\/code><\/pre>\n\n\n\n<p>Include <code>version.mk<\/code>, as we will need to know versioning information for constructing the final firmware filename, etc.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>libA\t\t:= libA\nlibraries\t:= $(libA)\nexeA\t\t:= exeA\nexeB\t\t:= exeB<\/code><\/pre>\n\n\n\n<p>Define various variables that can be reused later, rather than hard-coding specific component names. The example looks rather redundant, but becomes helpful once scaled out a bit. Additionally, all libraries are grouped into the <code>libraries<\/code> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>imagedir\t:= image\nimagenamebase\t:= $(partNumber)_$(appName)_v$(versionMajor).$(versionMinor).$(versionRevision)\nimagefile\t:= $(imagenamebase).squashfs<\/code><\/pre>\n\n\n\n<p>Various file and directory names that will be used later for firmware image construction. <code>imagedir<\/code> is the temporary directory to use to stage the firmware filesystem, <code>imagenamebase<\/code> is the base filename for the image which takes into account some metadata including software part number and version, and <code>imagefile<\/code> is the final target filesystem image. As the name implies, this image will be a squashfs image.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>.PHONY: all $(exeA) $(exeB) $(libraries)\nall: $(exeA) $(exeB) $(libraries)<\/code><\/pre>\n\n\n\n<p>Set up the <code>all<\/code> build target. This will compile each executable and library.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>install: $(exeA) $(exeB)\n\t$(RM) -rf $(imagedir) $(imagefile)\n\tinstall -D -m 755 -t $(imagedir)\/\t$(exeA)\/exeA\n\tinstall -D -m 755 -t $(imagedir)\/\t$(exeB)\/exeB\n\tmksquashfs $(imagedir) $(imagefile)<\/code><\/pre>\n\n\n\n<p>Set up the <code>install<\/code> build target. This will copy various ad-hoc files into <code>$(imagedir)<\/code>, which is them constructed into the filesystem image.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>sign: install\n\tgpg --detach-sign $(imagefile)\n\tmkdir $(imagenamebase)\n\tmv $(imagefile) $(imagefile).sig $(imagenamebase)\n\tzip -r $(imagenamebase).zip $(imagenamebase)\n\trm -rf $(imagenamebase)\n\t$(eval versionAutoNum=$(shell echo $$(($(versionAutoNum)+1))))\n\t@echo $(versionAutoNum) > $(autoNumFile)<\/code><\/pre>\n\n\n\n<p>Sign and archive the filesystem image. There is a lot going on here, so we will break down each step.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a detached signature for the image. This will use whatever default private key GnuPG selects.<\/li>\n\n\n\n<li>Create a staging directory to archive both the filesystem image and the signature.<\/li>\n\n\n\n<li>Move the filesystem image and the signature into the staging directory.<\/li>\n\n\n\n<li>Archive the staging directory into the firmware image package.<\/li>\n\n\n\n<li>Remove staging directory.<\/li>\n\n\n\n<li>The final two lines track the build &#8220;auto-number&#8221;. This build number is appended to the firmware image in order to differentiate subsequent builds during the development process, and incremented here.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>clean:\n\t$(MAKE) -C . TARGET=clean\n\trm -rf image *.squashfs *.zip<\/code><\/pre>\n\n\n\n<p>Clean the project, with a slightly convoluted approach. Call this Makefile again, but with the  <code>TARGET=clean<\/code> variable set. This will be picked up by the following build target.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>$(exeA) $(exeB) $(libraries):\n\t$(MAKE) --directory=$@ $(TARGET)<\/code><\/pre>\n\n\n\n<p>Target for the various executables and libraries. By default this will recurse into those component subdirectories and call <code>make<\/code>, which will then build the <code>all<\/code> target (the code). Should <code>make clean<\/code> have been called above, the recursed target will instead be <code>clean<\/code>, as defined in <code>common.mk<\/code>. This will clean up the various executables, libraries, object files, etc.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Configure the various module dependencies\n$(exeA): $(libA)<\/code><\/pre>\n\n\n\n<p>Defines component dependencies. In this case, <code>exeA<\/code> depends on <code>libA<\/code>, such that <code>libA<\/code> will be checked for being up-to-date and rebuilt automatically if not.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"common.mk\">common.mk<\/h2>\n\n\n\n<p>This file defines global data and recipes, including how to compile C files, dependency information, libraries, and link executables.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Set to 1 to enable debugging\n# Be sure to clean and rebuild after modifying\nDEBUG=0\n\nMV\t:= mv -f\nRM\t:= rm -f\nSED\t:= sed\n\n# If building for docker\/host system, add extra options\n# Otherwise, specify the cross compiler\nifeq ($(DOCKER),1)\n\t# Add any extra options here...\nelse\n\tCROSS\t:= arm-linux-\nendif\n\nCC\t\t:= $(CROSS)gcc\nLD\t\t:= $(CROSS)ld\nAR\t\t:= $(CROSS)ar\n\nobjects\t\t:= $(subst .c,.o,$(sources))\ndependencies\t:= $(subst .c,.d,$(sources))\n\n# Add any custom global cpp flags you might need here...\nCPPFLAGS\t+= -DSOMETHING=1\n\n# General flags and includes\ninclude_dirs\t:= ..\/libA\nCFLAGS\t\t+= -Wall -Werror\n\nifeq ($(DEBUG),1)\n\tCFLAGS\t+= -ggdb\nelse\n\tCFLAGS\t+= -O3\nendif\n\n# Combine include dirs for the pre-processor\nCPPFLAGS\t+= $(addprefix -I,$(include_dirs))\n\nvpath %.h $(include_dirs)\n\n.PHONY: library\nlibrary: $(library)\n\n$(library): $(objects)\n\t$(AR) $(ARFLAGS) $@ $^\n\n.PHONY: program\nprogram: $(program)\n\n$(program): $(objects) $(libraries)\n\t$(CC) -o $(program) $(LDFLAGS) $(objects) $(libraries) $(LDLIBS)\n\n.PHONY: clean\nclean:\n\t$(RM) $(objects) $(program) $(library) $(dependencies)\n\nifneq \"$(MAKECMDGOALS)\" \"clean\"\n  -include $(dependencies)\nendif\n\n%.c %.h: %.y\n\t$(YACC.y) --defines $&lt;\n\t$(MV) y.tab.c $*.c\n\t$(MV) y.tab.h $*.h\n\n%.d: %.c\n\t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M $&lt; |      \\\n\t$(SED) 's,\\($*\\.o\\) *:,\\1 $@: ,' > $@.tmp\n\t$(MV) $@.tmp $@<\/code><\/pre>\n\n\n\n<p>Next we will break down the major pieces of this Makefile.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Set to 1 to enable debugging\n# Be sure to clean and rebuild after modifying\nDEBUG=0<\/code><\/pre>\n\n\n\n<p>Flag enabling debug builds. It can be set to <code>1<\/code> here, or invoked via <code>make DEBUG=1<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>MV\t:= mv -f\nRM\t:= rm -f\nSED\t:= sed<\/code><\/pre>\n\n\n\n<p>Various standard utilities.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># If building for docker\/host system, add extra options\n# Otherwise, specify the cross compiler\nifeq ($(DOCKER),1)\n\t# Add any extra options here...\nelse\n\tCROSS\t:= arm-linux-\nendif<\/code><\/pre>\n\n\n\n<p>If <code>DOCKER=1<\/code> is defined, run a Docker build. This is also synonymous with a host build. The intent of it is for compiling the application which can be run within a Docker container for localize execution and debugging.<\/p>\n\n\n\n<p>If not defined, specify the prefix of the cross-compiler to use.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>CC\t:= $(CROSS)gcc\nLD\t:= $(CROSS)ld\nAR\t:= $(CROSS)ar<\/code><\/pre>\n\n\n\n<p>Define our compiler, linker, and archiver. If <code>$(CROSS)<\/code> is defined, it will expand to our cross-compiler. Otherwise, just use the host versions.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>objects\t\t:= $(subst .c,.o,$(sources))\ndependencies\t:= $(subst .c,.d,$(sources))<\/code><\/pre>\n\n\n\n<p>Macro expansion of the <code>sources<\/code> variable, defined in the component subdirectory Makefiles, generating lists of corresponding <code>.o<\/code> object files and <code>.d<\/code> dependency files.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Add any custom global cpp flags you might need here...\nCPPFLAGS\t+= -DSOMETHING=1<\/code><\/pre>\n\n\n\n<p>Somewhere to set any global defines.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># General flags and includes\ninclude_dirs\t:= ..\/libA\nCFLAGS\t\t+= -Wall -Werror<\/code><\/pre>\n\n\n\n<p>Configure global project include directories and base C flags. In this case, we warn on everything and consider warnings to be errors.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>ifeq ($(DEBUG),1)\n\tCFLAGS\t+= -ggdb\nelse\n\tCFLAGS\t+= -O3\nendif<\/code><\/pre>\n\n\n\n<p>If this was called as <code>make DEBUG=1<\/code>, then enable GDB debugging data with no optimizations. Otherwise for a standard build, turn on optimizations.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Combine include dirs for the pre-processor\nCPPFLAGS\t+= $(addprefix -I,$(include_dirs))<\/code><\/pre>\n\n\n\n<p>Macro expansion to turn all <code>include_dirs<\/code> into <code>-I<\/code> arguments for the compiler.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>vpath %.h $(include_dirs)<\/code><\/pre>\n\n\n\n<p>Tell the dependency checker where to find project-global include directories for header files.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>.PHONY: library\nlibrary: $(library)\n\n$(library): $(objects)\n\t$(AR) $(ARFLAGS) $@ $^<\/code><\/pre>\n\n\n\n<p>How we build a library. This depends on the specific objects, so all this needs to do is archive them into a <code>.a<\/code> file.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>.PHONY: program\nprogram: $(program)\n\n$(program): $(objects) $(libraries)\n\t$(CC) -o $(program) $(LDFLAGS) $(objects) $(libraries) $(LDLIBS)<\/code><\/pre>\n\n\n\n<p>How we build a program executable. This depends on the specific objects, so all this needs to do is link them into the executable.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>.PHONY: clean\nclean:\n\t$(RM) $(objects) $(program) $(library) $(dependencies)<\/code><\/pre>\n\n\n\n<p>How to clean a component directory. This will remove any objects, the executable, library, and dependency (<code>.d<\/code>) files.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>ifneq \"$(MAKECMDGOALS)\" \"clean\"\n  -include $(dependencies)\nendif<\/code><\/pre>\n\n\n\n<p>If cleaning, do not generate dependencies before cleaning as it would be pointless and wasteful.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>%.c %.h: %.y\n\t$(YACC.y) --defines $&lt;\n\t$(MV) y.tab.c $*.c\n\t$(MV) y.tab.h $*.h<\/code><\/pre>\n\n\n\n<p>Recipe for compiling yacc files. Not sure exactly why this is here, or if it is really necessary.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>%.d: %.c\n\t$(CC) $(CFLAGS) $(CPPFLAGS) -M $&lt; |      \\\n\t$(SED) 's,\\($*\\.o\\) *:,\\1 $@: ,' > $@.tmp\n\t$(MV) $@.tmp $@<\/code><\/pre>\n\n\n\n<p>How to generate dependency files. This is done by invoking the compiler and converting the output into a <code>.d<\/code> file corresponding to the <code>.c<\/code> file. This generates a fair bit of noise at compile-time, which can be suppressed by prefixing <code>$(CC)<\/code> and <code>$(MV)<\/code> with <code>@<\/code> if desired.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"version.mk\">version.mk<\/h2>\n\n\n\n<p>This file defines various version control information, exposing it for the build process and also the compiled software. An &#8220;auto number file&#8221; is utilized to append <code>-devX<\/code> to the build, for developer builds, or in the case of GitLab CI\/CD pipelines, the commit hash.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Basic application data\npartNumber\t:= 12345\nappName\t\t:= APPNAME\n\n# Version number\nversionMajor\t:= 1\nversionMinor\t:= 0\n# versionMinor with leading zeroes stripped\nversionMinorEx  := $(shell echo $(versionMinor) | sed -E 's\/^0+(&#91;0-9]+)\/\\1\/')\nversionRevision\t:= dev\nversionAutoNum\t:= 0\nautoNumFile\t:= .autonum\n\nifeq ($(origin CI_COMMIT_SHORT_SHA), undefined)\n# If not building under GitLab, auto-increment the build number\n\tnum := $(shell cat ${autoNumFile})\n\tifeq ($(num),)\n\telse\n\t\tversionAutoNum := $(num)\n\tendif\n\n\tversionRevision := $(versionRevision)$(versionAutoNum)\nelse\n# Otherwise, override version revision\n\tversionRevision := $(CI_COMMIT_SHORT_SHA)\nendif\n\n# If debug build, append \"-debug\" to the string\nifeq ($(DEBUG), 1)\n\tversionRevision := $(versionRevision)-debug\nendif\n\nCPPFLAGS\t\t+= -DVERSIONMAJOR=$(versionMajor) -DVERSIONMINOR=$(versionMinorEx) -D$(appName)\nexport CPPFLAGS<\/code><\/pre>\n\n\n\n<p>Next we will break down the major pieces of this Makefile.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Basic application data\npartNumber\t:= 12345\nappName\t\t:= APPNAME<\/code><\/pre>\n\n\n\n<p>As the comment says, basic application data. The part number and application name are used for firmware image filename construction, and the application name is passed to the compiler as <code>-D$(appName)<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># Version number\nversionMajor\t:= 1\nversionMinor\t:= 0\n# versionMinor with leading zeroes stripped\nversionMinorEx  := $(shell echo $(versionMinor) | sed -E 's\/^0+(&#91;0-9]+)\/\\1\/')\nversionRevision\t:= dev\nversionAutoNum\t:= 0\nautoNumFile\t:= .autonum<\/code><\/pre>\n\n\n\n<p>Version number information. The compiler is given <code>-DVERSIONMAJOR=$(versionMajor)<\/code> and <code>-DVERSIONMINOR=$(versionMinorEx)<\/code>. <code>versionMinorEx<\/code> is a copy of <code>versionMinor<\/code> with leading zeroes removed, as once you get to <code>08<\/code> this becomes an invalid octal number.<\/p>\n\n\n\n<p><code>versionRevision<\/code> defaults to &#8220;dev&#8221;, which will have <code>versionAutoNum<\/code> appended. This will be overridden with the git commit hash in the case of GitLab pipelines.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>ifeq ($(origin CI_COMMIT_SHORT_SHA), undefined)\n# If not building under GitLab, auto-increment the build number\n\tnum := $(shell cat ${autoNumFile})\n\tifeq ($(num),)\n\telse\n\t\tversionAutoNum := $(num)\n\tendif\n\n\tversionRevision := $(versionRevision)$(versionAutoNum)\nelse\n# Otherwise, override version revision\n\tversionRevision := $(CI_COMMIT_SHORT_SHA)\nendif<\/code><\/pre>\n\n\n\n<p>Configure <code>versionRevision<\/code> based on developer build or GitLab pipeline.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code># If debug build, append \"-debug\" to the string\nifeq ($(DEBUG), 1)\n\tversionRevision := $(versionRevision)-debug\nendif<\/code><\/pre>\n\n\n\n<p>If this is a debug build, include that in the firmware filename as well.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>CPPFLAGS\t\t+= -DVERSIONMAJOR=$(versionMajor) -DVERSIONMINOR=$(versionMinorEx) -D$(appName)\nexport CPPFLAGS<\/code><\/pre>\n\n\n\n<p>Expose version information via C pre-processor flags as described above.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Component-Makefiles\">Component Makefiles<\/h2>\n\n\n\n<p>Finally, the hard part is over. Next we configure the component-specific Makefiles. For the most part, these simply define the source files, target output name, and perhaps some additional compiler options. All of the heavy lifting is done by including <code>common.mk<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"exeA-Makefile\">exeA\/Makefile<\/h3>\n\n\n\n<p>Executable A has a single <code>.c<\/code> file, and also depends on <code>libA<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>sources := \\\n\texeA.c\n\nlibraries\t:= ..\/libA\/liblibA.a\nprogram\t\t:= exeA\n\n# Add any other customizations here, such as CFLAGS\n\nall: $(program)\n\ninclude ..\/common.mk<\/code><\/pre>\n\n\n\n<p>First, <code>sources<\/code> is simply a list of all constituent <code>.c<\/code> files.<\/p>\n\n\n\n<p><code>libraries<\/code> is an optional variable that contains a list of libraries, either as relative <code>.a<\/code> files or <code>-l<\/code> options for <code>ld<\/code>. In this case we need <code>libA<\/code>.<\/p>\n\n\n\n<p><code>program<\/code> is the target executable name that the <code>.c<\/code> files will link to.<\/p>\n\n\n\n<p><code>all: $(program)<\/code> is the recipe to compile the target executable. Probably this could be generalized in <code>common.mk<\/code> somehow.<\/p>\n\n\n\n<p>Finally, include <code>common.mk<\/code> for the rest of the build process.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"exeB-Makefile\">exeB\/Makefile<\/h3>\n\n\n\n<p>Executable B has a single <code>.c<\/code> file, with no library dependencies.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>sources := \\\n\texeB.c\n\nlibraries\t:= \nprogram\t\t:= exeB\n\nall: $(program)\n\ninclude ..\/common.mk<\/code><\/pre>\n\n\n\n<p>As described for <code>exeA\/Makefile<\/code>, except no library dependencies.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"libA-Makefile\">libA\/Makefile<\/h3>\n\n\n\n<p>This library consists of a single <code>.c<\/code> file.<\/p>\n\n\n\n<pre class=\"wp-block-code has-black-color has-text-color\"><code>library := liblibA.a\nsources := \\\n    libA.c\n\n# Add any other customizations here, such as CFLAGS\n\ninclude ..\/common.mk<\/code><\/pre>\n\n\n\n<p>As described for <code>exeA\/Makefile<\/code>, except here we define <code>library<\/code> as the target <code>.a<\/code> file instead of <code>program<\/code>. No <code>all<\/code> recipe is required for libraries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"Adding-New-Components\">Adding New Components<\/h2>\n\n\n\n<p>Adding new components is relatively straightforward using this framework.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create a new component directory.<\/li>\n\n\n\n<li>Add source files as needed.<\/li>\n\n\n\n<li>Create a <code>Makefile<\/code> in the component directory by copying an existing executable or library Makefile as appropriate.<\/li>\n\n\n\n<li>Update the new Makefile to define the <code>program<\/code>, <code>library<\/code>, and <code>sources<\/code> variables as needed.<\/li>\n\n\n\n<li>Update the root\/top-level <code>Makefile<\/code> to make it aware of the new component.<\/li>\n<\/ol>\n\n\n\n<p>This final step is the most complicated, as there is a fair bit of copy\/pasting going on as can likely be improved in some way. Start by adding the component to the variable lists at the top of the file. Next, update the <code>.PHONY: all<\/code> and <code>all<\/code> targets in the same way the other components are listed. At the bottom of the file, list the components to enable Make recursion and dependency tracking as appropriate. Finally, update the <code>install<\/code> target to copy any output files into the firmware image.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There comes a point in any project that when it reaches a certain size, a flat directory hierarchy no longer works. There are too many files,&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[8,9,10],"class_list":["post-18","post","type-post","status-publish","format-standard","hentry","category-programming","tag-cross-compiling","tag-gitlab","tag-make"],"_links":{"self":[{"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/posts\/18","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/comments?post=18"}],"version-history":[{"count":0,"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/posts\/18\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/media?parent=18"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/categories?post=18"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.5amsoftware.com\/blog\/wp-json\/wp\/v2\/tags?post=18"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}