Makefile change output hex file name from header file #define - Page 1
Forum section: electronics / general

author: hal9001
author_rank: Regular Contributor
timestamp: May 15, 2024, 09:02:16 am
content: I use a #define inside a header file for firmware version. Can the makefile append the version number from the #define to the name of the hex file? Im using Atmel Studio.

author: mfro
author_rank: Regular Contributor
timestamp: May 15, 2024, 09:39:05 am
content: Quote from: hal9001 on May 15, 2024, 09:02:16 amThe Makefile itself can't - but with a little help from the C preprocessor cpp, it's possible. Let's assume you have your version number in a file called version.h like so:Code:#define VERSION "1.0.0-beta"you can assign that to a Makefile variable using the C preprocessor:Code:VERSION = $(shell echo "#include \"version.h\"\nVERSION" | $(CPP) - -P -o -)The version string can than be appended to any name in the Makefile.(assuming you are using Linux or another *X-based OS like Mac OS. If you are using Windows, I'm afraid you need to ask somebody else)

author: hal9001
author_rank: Regular Contributor
timestamp: May 15, 2024, 09:53:48 am
content: Quote from: mfro on May 15, 2024, 09:39:05 amCheers! Im using Windows. Maybe power shell in windows can work the same way?

author: mfro
author_rank: Regular Contributor
timestamp: May 15, 2024, 10:01:38 am
content: Quote from: hal9001 on May 15, 2024, 09:53:48 amThat's entirely possible (maybe even CMD might do it), but I can't assist with that as I didn't touch any Windows for years.

author: tellurium
author_rank: Frequent Contributor
timestamp: May 15, 2024, 01:13:17 pm
content: You might try thisversion.h:Code:#define VERSION "1.2.3"Code:VERSION = $(shell echo VERSION | gcc -P -E -imacros version.h -)However the generated variable will be double-quoted , which is probably not what you want. But hey, there are at least two solutions for that:1. Find how to dequote a string - a quick google search suggests it is doable, or2. Stop using Windows, and start using something more developer-friendly

author: retiredfeline
author_rank: Frequent Contributor
timestamp: May 15, 2024, 02:14:36 pm
content: Can you do it the other way, assign the version in the Makefile and then pass it down to the #define? That's relatively easy.

author: Siwastaja
author_rank: Super Contributor
timestamp: May 15, 2024, 04:43:51 pm
content: Quote from: retiredfeline on May 15, 2024, 02:14:36 pmTaking this further:Code:GIT_VERSION := $(shell git describe --abbrev=16 --always --exclude '*')CFLAGS += -DGIT_VERSION=0x$(GIT_VERSION)ULLBasically a uint64_t automagically derived from the 64 first bits of the git commit hash, has served me well. And when printed in hex, it's human readable enough.

author: John Coloccia
author_rank: Super Contributor
timestamp: May 17, 2024, 05:11:07 am
content: We routinely pass defines to from our makefile to our compiler for just such purposes.That said, if you want to grab the hash, you should use the "plumbing" command rev-parse as opposed to describe, which is a porcelain command and can change at any time.git rev-parse HEAD|your_tag|or_whatever_else_you_wantor if you want to limit the lengthgit rev-parse --short=TheLengthYouWant HEAD|your_tag|or_whatever_else_you_wantso to get a 10 character hash of branch "Whatever"git rev-parse --short=10 Whatever

author: hal9001
author_rank: Regular Contributor
timestamp: May 17, 2024, 06:21:01 am
content: Quote from: John Coloccia on May 17, 2024, 05:11:07 amCheers. Passing the #define is preferred to git hash. Regretfully harder than I thought

author: bson
author_rank: Unknown
timestamp: May 18, 2024, 07:43:18 pm
content: I'd suggest learning to use git. Then use tags to identify versions, and have the Makefile extract either the tag and fill in version.h for you, or if the current head is not on a tag (named commit) use the commit ID.

author: John Coloccia
author_rank: Super Contributor
timestamp: May 19, 2024, 12:35:11 am
content: Quote from: hal9001 on May 17, 2024, 06:21:01 amI was explaining how to get a hash to pass it as a #define. I don't think I've ever run into a compiler that can't define on the command line. Is the syntax for defining a symbol on the command line the stumbling block?The make file can't do anything to your code, at least not easily, but if your compiler supports it you can pass something in that you can then use. Absolute worst case, it can emit a file you can include. What's your stumbling block?

author: tellurium
author_rank: Frequent Contributor
timestamp: May 19, 2024, 09:41:10 am
content: I am not an OP, but I had, and still have use cases when a header file is the source of truth, not git or whatever.Long story short - you have a project which holds a library, and it's header defines a version.Thus tricks with git, or taking versions from the Makefile, won't work. The version is set in the header, full stop.

author: mikerj
author_rank: Super Contributor
timestamp: May 19, 2024, 10:04:35 am
content: Not exactly what you asked for, but it's possible to extract defines from a header using a batch file which can then be appended to the hex file. This would be called as a post-build process, and on a Windows machine doesn't require any other dependencies. The example below shows how this could be done.Code:REM Path and file name of the header file holding the firmware revision data.set VERFILE=..\version.hREM Set the defines holding version details.set MAJOR_NAME=FIRMWARE_REV_MAJORset MINOR_NAME=FIRMWARE_REV_MINORset PATCH_NAME=FIRMWARE_REV_PATCHREM Extract major version number from header file.for /F "usebackq tokens=3" %%i in (`findstr /L /C:"#define %MAJOR_NAME% " %VERFILE%`) do set MAJOR_VER=%%~iREM Extract minor version number from header file.for /F "usebackq tokens=3" %%i in (`findstr /L /C:"#define %MINOR_NAME% " %VERFILE%`) do set MINOR_VER=%%~iREM Extract patch version number from header file.for /F "usebackq tokens=3" %%i in (`findstr /L /C:"#define %PATCH_NAME% " %VERFILE%`) do set PATCH_VER=%%~iREM Combine major, minor , patch levels AND model to form the revision string.set STRREV=%MAJOR_VER%_%MINOR_VER%_%PATCH_VER%_%1REM Display revision string.@echo Firmware revision is %STRREV%

author: ejeffrey
author_rank: Super Contributor
timestamp: May 19, 2024, 04:53:58 pm
content: Quote from: tellurium on May 19, 2024, 09:41:10 amThe use of git is incidental. It's just a convenient way to generate a unique and traceable tag. It's not even a proper version in the sense that it doesn't have ordering without referring to the source repo.However, I am deeply suspicious of any claim that "the source of truth must be the header file." If that is by administrative fiat with no technical reason, that's fine, although not very interesting. But if it's for a technical reason, that reason matters. In particular, if the header file is not actually part of the source code but generated by a step in the build process, then all of the make tricks using $(shell ...) may fail. That's because $(shell) expansion is done while parsing rules before building the dependency graph or building any targets. If version.h doesn't exist when you run make those rules will fail. In this case, the "real" source of truth is something else that is used to generate the headers, and you should get the version from that. If you have a pre-build step that runs before make (like gnu autoconf) that generates the header files, that's solves this problem, but then why couldn't autoconf generate the correct makefile in the first place?I'm also skeptical of the wisdom of extracting the define from a header file. You can do it with grep / awk and that works fine especially if you have a version.h with nothing but the version. Really the only thing that is is in a position to parse an arbitrary header file is the compiler / pre-compiler. You can use the compiler to extract the #define, which is more correct, but also more complicated. Storing the verison in the source code is fine in simple cases where it isn't needed anywhere else, but as soon as you need it for another part of your build process I think it's better to remove it to more neutral territory, which could just be a text file called version.txt, and inject it into the compiler with -D (or /D).A huge advantage of using the build process to inject the version is that you can have an automated way to distinguish developer builds from release builds by having your makefile choose what to inject. Builds produced by developers running "make" are automatically tagged as a development version (which may contain local changes), and only by the CI system running "make RELEASE=1" or something will it be targeted as a release candidate and guaranteed to be a clean, traceable build. Whenever possible, I tend to just have the version #ifndef / #defined to "unknown" in the source, and developer builds use that, only release builds get a real version tag at all.

author: ejeffrey
author_rank: Super Contributor
timestamp: May 19, 2024, 05:01:17 pm
content: However, with all that said, the following should work:Code:BASENAME = myprogVERSION = $(shell grep VERSION foo.h | cut -f3 -d " " )$(BASENAME)_$(VERSION).hex: $(BASENAME).elf:objcopy -O ihex $< $@

author: cv007
author_rank: Frequent Contributor
timestamp: May 19, 2024, 06:10:55 pm
content: QuoteI doubt you will be touching any makefile if using Studio. The IDE deals with the makefiles(s) andgenerallythey do not take kindly to a user making their own edits to them (edits could simply disappear as the IDE overwrites it, or may get the IDE confused, and so on).The IDE will most likely have the ability to specify/run a post build command/script (MPLABX does), and this could be something simple like grep'ing a header to get your version, renaming/cp/mv the hex file using its new name (can do as a single line command, or make a script and simply execute the script). Although running Windows, you may still have the ability to run things like grep/sed/mv/cp/etc, as they may have included these things (they will have 'make', so sometimes include a set of unix-y utilities also so the various flavors of OS all have access to the same tools, so for example they can all run the same post build scripts).I don't have Studio, but if you look around the build options there may be an option to do a post build step. If you simply enter something like 'ls' (no quote), and you get a file listing in the build output when compiling, you then know you have access to some unix tools.

author: tellurium
author_rank: Frequent Contributor
timestamp: May 19, 2024, 07:24:33 pm
content: Quote from: ejeffrey on May 19, 2024, 05:01:17 pmIf you read the thread carefully, you'll notice that OP uses Windows, hence all those quirks with the preprocessor (cross-platform way to do things), since grep/sed are not available on Windows.

author: John Coloccia
author_rank: Super Contributor
timestamp: May 19, 2024, 07:34:14 pm
content: As ejeffrey said, git was just an example.Our common pattern for handling this is to create a file called "version" or similar. In our case, it's release_hash.txt, and sometimes a version.txt as well, and is usually autogenerated from a script that does a bunch of other stuff too (changelogs, etc). But create the file(s) however you want. In the file, stick something like:Code:HASH=123ABCorVERSION=v1.2.3or whatever other information you want.In the makefile:Code:include version.txtor whatever you called it.You will end up with a variable called VERSION just as if you'd defined it in the makefile.You can then tell the compiler to define a symbol and set it to whatever you want, just like a #define. Or generate a header file if you really want to.The point of this is that it's EASY to get arbitrary information into the makefile, and to somehow pass that along to the compiler. It's way clunkier to get information from your source code and pass it to the makefile.One more thing I'll warn you about. You can just append the version name to the hex file, BUT it will eventually cause problems in the long run. When your version changes, make will have no idea that there USED to be a FILE_VERSION1.hex, and it will just get leftover. You should create a rule that does something like (using ejeffrey's example for reference):Code:$(BASENAME)_$(VERSION):make cleantouch $(BASENAME)_$(VERSION):This is just using an empty file to track the idea that the thing you're building now is different than the thing you build last time. There are other ways to handle this, but if you're project's not big and/or you're not changing versions all the time, this sledgehammer approach is really simple.Depending on what else your doing, there may be other cleanup to do as well. Just giving you a heads up if you want your makefile to be squeaky clean.One last thing I'll recommend. If you're going to work in Windows, and I generally work in Windows, use MSYS2. We've standardized on doing all of our builds/scripts/etc, as much as humanly possible, with tools like bash and all the other included tools. It's hard to imagine going back to using things like cmd and powershell.

author: tellurium
author_rank: Frequent Contributor
timestamp: May 19, 2024, 07:59:49 pm
content: Gents, I fully get what you're talking about, and it makes a perfect sense.The use case that I mentioned, is the other way around.Imagine you're creating a single-header library.You're going to ship it to other users. Everything is one single header file, including version definition. That line, "#define VERSION ...", is inside the header file. Not in the makefile. Not in the version.txt or in some other place.Now a Makefile may have some actions / rules which may use that version - for example, upload a CI artefact, or build a test binary that includes version in the file name.

author: John Coloccia
author_rank: Super Contributor
timestamp: May 19, 2024, 08:15:47 pm
content: Quote from: tellurium on May 19, 2024, 07:59:49 pmHow do you like this for abuse....version.hCode:#define VERSION=v1.2.3#ifndef VERSIONVERSION=v1.2.3#endifInclude in your .c and your makefile...I'll admit, it makes me throwup a bit in my mouth, but I'd do it if it kept me from parsing the header.

author: westfw
author_rank: Super Contributor
timestamp: May 19, 2024, 09:28:09 pm
content: I've used (for Optiboot) CPP to output a shell-script based on command-line options, and then execute the shell script during the makefile.Code:baudcheck: FORCE- @$(CC) --version- @$(CC) $(CFLAGS) -E baudcheck.c -o baudcheck.tmp.sh- @$(SH) baudcheck.tmp.shYou could use a similar technique to create a build-system-native executable that includes your headers and outputs your desired name, for later use in the Makefile.