Passing shell commands to gcc compiler flags - Page 1
Forum section: electronics / general

author: krish2487
author_rank: Frequent Contributor
timestamp: January 25, 2019, 08:39:10 am
content: Hello,I have a question regarding passing shell commands to gcc compiler.Specifically, this is the problem.I am working on a program in C where I need to version control the main.c . For various reasons, it was agreed on that the last git commit hash would make for a good way to track the version and can be automated during the build.The idea is that invoking gcc like so gcc (other flags) -DVERSION=(shell command with regexp to get the last commit hash) *would* work.The VERSION variable is used elsewhere in the program to keep track of the firmware and print it for diagnostic purposes.Is it possible to pass a shell command to the compiler flags and if so how??Thanks in advance. :-)

author: helius
author_rank: Super Contributor
timestamp: January 25, 2019, 08:49:16 am
content: The traditional way, which works on all Unix shells, is to use backquote command substitution.Code:gcc other flags... -DVERSION="`git...`"The more modern way, which is syntactically cleaner, uses the$()syntax.Code:gcc other flags... -DVERSION="$(git...)"The double quotes are important so that the shell doesn't try to expand or split the command output as if it were a file name.

author: krish2487
author_rank: Frequent Contributor
timestamp: January 25, 2019, 09:02:11 am
content: @heliusThat was fast!! :-)Thank you for the answer. Let me try and get back with my findings.Again, thank you very much for the help!! :-)

author: krish2487
author_rank: Frequent Contributor
timestamp: January 25, 2019, 09:32:28 am
content: Hmm...The $() does not seem to work..It evaluates to a NULL value. and prints nothingThe "` `" seems to evaluate correctly and print the hash.However, there is another snag.Code:makeCC -o ex1.out ex1.cpp -Wall -Werror -DVERSION="`git rev-parse --short HEAD`"ex1.cpp:37:28: error: invalid digit 'c' in decimal constantversion_array = (char*)VERSION;^<command line>:1:20: note: expanded from here#define VERSION 814cdb3^ex1.cpp:38:19: error: invalid digit 'c' in decimal constantprintf((char*)VERSION);^<command line>:1:20: note: expanded from here#define VERSION 814cdb3^2 errors generated.make: ***  Error 1And this is the test C file.Code:#include <stdio.h>int main(){printf("Hello World\n");printf("Version Number: ");char version_array;version_array = (char*)VERSION;printf((char*)VERSION);printf("\n");}casting the hash does not seem to work. Not casting it returns the same errorAny pointers (pun intended ) :-D

author: helius
author_rank: Super Contributor
timestamp: January 25, 2019, 09:59:33 am
content: The git output is bare hexadecimal without the prefix that C requires (0x).Code:gxx other options... -DVERSION="0x`git ...`"But you have another problem because you're assuming it's a string...You need to add quotes to the #define if you want it to be a string. The shell has special treatment for quotes, so this is often challenging.Code:gxx other options... -DVERSION="\"`git ...`\""csh will throw a fit at this because it doesn't allow double quotes to be escaped. bash or zsh will probably work.By the way, the cast(char *)is unnecessary because the macro just gets pasted in. If you define the macro as a string, the preprocessor will put a string literal in its place. You obviously can't cast a random integer to a string and expect it to work.

author: westfw
author_rank: Super Contributor
timestamp: January 25, 2019, 10:05:13 am
content: Make also has a mechanism for getting shell command output assigned to a Make variable:https://www.gnu.org/software/make/manual/html_node/Shell-Function.html(Quoting might drive you batty, though.)

author: krish2487
author_rank: Frequent Contributor
timestamp: January 25, 2019, 11:00:04 am
content: Quote from: westfw onTodayat 09:05:13 pmThank you for the link. It looks interesting. I ll try with this as well. It might make it more verbose and understandable.Thank you very much helius. Your suggestion worked!! :-)Specifically this workedQuoteQuote from: helius onTodayat 08:59:33 pmgxx other options... -DVERSION="0x`git ...`"But you have another problem because you're assuming it's a string...You need to add quotes to the #define if you want it to be a string. The shell has special treatment for quotes, so this is often challenging.Code:gxx other options... -DVERSION="\"`git ...`\""csh will throw a fit at this because it doesn't allow double quotes to be escaped. bash or zsh will probably work.By the way, the cast(char *)is unnecessary because the macro just gets pasted in. If you define the macro as a string, the preprocessor will put a string literal in its place. You obviously can't cast a random integer to a string and expect it to work.

author: legacy
author_rank: Super Contributor
timestamp: January 25, 2019, 11:05:55 am
content: I prefer having abash scriptthat extracts information from { git, mercurial, subversion, doors, ... } such as { commit date, revision, branch, version, author, ... } and fills a C-module created just for the purpose of propagating this informationthe bash script is invoked by make, so it has a label ("touch" on my choice) in the Makefileso make touch ---> creates info.c, it does the job, and you it's a neat job

author: legacy
author_rank: Super Contributor
timestamp: January 25, 2019, 11:14:47 am
content: Quote from: westfw on January 25, 2019, 10:05:13 amCode:e --desel $(grep perl /var/profile/world)This is what I usually do to pass a list of items that need to go deselected (and deleted).Long story on the why? and what?, but it works on the bash shell, so it's OK even on Unix.

author: krish2487
author_rank: Frequent Contributor
timestamp: January 25, 2019, 01:08:25 pm
content: Interesting idea. Just out of curiosity does it not create another step that you have to keep track of, even in the makefile?Unless, of course the "touch" target is a dependancy for the release build target..Quote from: legacy onYesterdayat 10:05:55 pmbash scriptthat extracts information from { git, mercurial, subversion, doors, ... } such as { commit date, revision, branch, version, author, ... } and fills a C-module created just for the purpose of propagating this informationthe bash script is invoked by make, so it has a label ("touch" on my choice) in the Makefileso make touch ---> creates info.c, it does the job, and you it's a neat job

author: legacy
author_rank: Super Contributor
timestamp: January 25, 2019, 01:14:13 pm
content: as a prerequisite in the Makefile, you can assign "touch" as rule assigned to all:all: touch...or something similar so it will run touch and auto-generate info.c before compiling stuff

author: krish2487
author_rank: Frequent Contributor
timestamp: January 25, 2019, 01:16:32 pm
content: I assumed as much.Very elegant and modular! :-)Thanks.Lots of wonderful ideas here!! :-)

author: cv007
author_rank: Frequent Contributor
timestamp: January 25, 2019, 04:56:44 pm
content: let whatever you do with git make a file- your git process is in charge of creating the hash (and only creates when needed)echo "#define GITHASH \"$(git log -1 --format="%H")\"" > githash.h//and just include the file where needed//main.c#include githash.hthe git process creates the info, the c files use as needed- they are separate jobs and no need to add any more options to the compiler

author: andyturk
author_rank: Frequent Contributor
timestamp: January 25, 2019, 09:41:48 pm
content: There's a better way to do this with GNU make. Basically, you generate new version information whenever the git repo itself changes.Code:$(OBJ)/build_info.o : $(ROOT)/.git/HEAD $(ROOT)/.git/index | $(BUILD)@echo Creating $(BUILD)/build_info.cc@rm -f $(BUILD)/build_info.cc@echo "const char *GIT_VERSION = \"$(shell git describe --long --always --match or-* --dirty)\";" >> $(BUILD)/build_info.cc@echo "const char *GIT_COMMIT = \"$(shell git rev-parse HEAD)\";" >> $(BUILD)/build_info.cc@echo "const char *BUILD_TIME = \"$(shell date)\";" >> $(BUILD)/build_info.cc@echo "const char *APP_AUTHOR = \"$(shell whoami)\";" >> $(BUILD)/build_info.cc@$(CXX) $(CFLAGS) $(CXXFLAGS) -c $(BUILD)/build_info.cc -o $(OBJ)/build_info.oThis rule produces a build_info.cc and corresponding .o file whenever the HEAD or index of the repo is updated. The build_info is not under revision control itself, so it's generated as necessary for each build. The GIT_VERSION variable lets you tag releases in the repo and also makes it clear when some of the files have been modified but not committed (i.e., the "dirty" suffix).

author: lucazader
author_rank: Regular Contributor
timestamp: January 26, 2019, 12:58:55 am
content: We do something very similar to legacy seems to do.We do it in python, and also have ti extract a user readable version number from git tags using git describe.This python script then gets run at the start of every build. and it generates a version.c file.Really useful for tracking down code versions etc.another important thing it can do is tell you if your build was dirty or not (eg not all files/changes committed into git yet) to make sure that if you try and re-build version xxxx to reproduce a bug, you can reproduce the bin file exaclty