How to detect the system locale at compile time using gcc? - c

I'm using gcc to compile a C program (that is intended to be as portable as possible), and I have a separate module in which I define all the string constants I want to use. These are then defined as external constants in an include file used by the rest of the code.
I would like to use a different set of string constants based upon the user's locale, and my current thinking is to simply define a symbol with the desired language settings and use the pre-processor to conditionally compile the appropriate values. (Yes this does assume the user will be compiling the program themselves).
If I go down this route I can get the current language settings from the command line using
$ echo $LANG | cut -f 1 -d '.'
en_GB
but I have not managed to use this to define the symbol 'en_GB' (using -D) in a make file.
There may of course be a better ways to solve the problem!
It might be better to select the correct string constants at runtime but I don't want to have to update any of the source code other then the module the string constants are defined in.
Thanks

How to detect the system locale at compile time using gcc?
It is not possible, in the way you want to do. gcc compiles code, it does not detect system locale.
Pass the locale with some prefix.
-DLANG_$(echo $LANG | cut -f 1 -d '.')=1
Then just check with preprocessor if the macro is defined
#if LANG_en_US
stuff
#endif
To use shell script inside Makefile, you have to use shell.
CFLAGS += -DLANG_$(shell echo $$LANG | cut -f 1 -d '.')=1

Related

Reading a set of variables into a Makefile from a C source file?

I have an AVR8 GCC application that can be built with a standard makefile. Because some folks who want to build the application don't want to set up make and such (or have trouble doing so), I also have figured out how to set the project up so it can be compiled from the Arduino IDE as well.
All is working.
But, I normally set some items in the makefile, like the version number and such, but creating the VERSION string in the makefile and passing it as a define into each source file compilation. But, when run from the Arduino IDE, that step is obviously not occurring. So, I have to create a second #define in the Arduino sketch stub to recreate the define.
This means when I update the version, I need to do so in 2 places, in the makefile and in the source file.
The easy option is to simply move the VERSION creation to the source file, where both can use it. And, I'm OK doing that, but
The makefile actually needs the version information, both to create the right filename (think app_v1.2.3.4.bin) and embed the version number into the bin file since it is used by the boot-loader (if requested) to ensure the version the boot-loader flashes is newer than the one already in FLASH. So, if I move the VERSION, RELEASE, MODIFICATION, etc. defines into the C code, I need to find a way to pull them back into the makefile.
I tried using the file read operations in the makefile, but they seem to ignore:
#define VERSION 0
with the prefaced '#' char.
I see there's some options to run sed/awk/etc, in bash, but I don't want to make too many assumptions on the environment, and the makefile currently runs on Windows as well as Unix/Linux without any differences.
I tried a few stack overflow examples, but nothing seems to yield those 4 numbers from any file, .h or otherwise.
I'm OK with creating version.h with just:
#define VERSION 0
#define RELEASE 1
#define MODIFICATION 2
#define FIX 4
If I can read it into the makefile and create the variables I need.
Jim
You may take a look at gmtt which was designed exactly with you use case in mind. In gmtt the following should read and analyze your header file:
include gmtt.mk
# create a 3-column table from the header file. The first column is just the "#define"
VNR_TABLE := 3 $(file < version.h)
# Extract the values from the table: select column 3 from VNR_TABLE where column 2 equals a string constant.
# Be careful not to introduce spaces in the compare!
VER := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,VERSION))
REL := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,RELEASE))
MODF := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,MODIFICATION))
FIX := $(call select,3,$(VNR_TABLE),$$(call str-eq,$$2,FIX))
I couldn't test it but I think you get the idea.
PS: using a GNUmake library just means placing the included file alongside the makefile.
I think in this case you can use the ‘file’ function of makefiles.
It allows you to write (with > specifier) or read (with < specifier) to/from files. Then you can trim (with filter-out) your variables inside your makefile.
Source: https://www.gnu.org/software/make/manual/html_node/File-Function.html#File-Function
You can use GNU make's $(shell ...) function to extract the macro expansions. Assuming VERSION is defined in src.c and tokens are delimited by spaces (not tabs):
VERSION := $(shell sed -n -e "s/^\#define VERSION *\(.*\)/\1/p" src.c)
.PHONY: all
all:
#echo VERSION=$(VERSION)

Auto-include version of program based on date of git push

I am developing a program in C that needs to include the date and the time of the last push performed in a given branch in the git repository in order to version it.
I know how I can get the data from git and use it to generate a version number in the format I want.
(https://stackoverflow.com/a/51403241/7114946)
The problem is I don't know how to include this within the code of the program because it ultimately needs to "printf" this info during the starting of the app.
I have found the preprocessor macros __TIME__ and __DATE__. I wonder if there are other similar macros to include some given string/data.
Thanks
edit: I need to include this info in compilation time, not when the code is uploaded into git.
You can define macros on the command line:
gcc -DFOO=BAR -c prog.c
acts like #define FOO BAR in prog.c.
You can combine this with shell command interpolation to get the output of an external program in there:
gcc -DGIT_TIMESTAMP="\"$(git whateveryourcommandis)\"" prog.c
(The escaped quotes are there to make the macro expand to a string literal.)
And then prog.c can do e.g.:
printf("my git timestamp is %s\n", GIT_TIMESTAMP);

How to invoke the C preprocessor?

How can we invoke the C preprocessor in a C program, like if we want to prepend some of our files to the list of standard library files while using #include<file_name> using -I, what we should do?
Adding, if we want to add comments to the output of our program, I have heard that we can use -C invocation commands. How to do it? Somebody please correct me and explain further if I am wrong.
The mechanism you linked to is explicitly not to be supposed from within a program, but it is to use the preprocessor for other things, like text libraries etc.
So you can transform a text using the means of the C preprocessor and convert it to its "expanded" form, without compiling it (perhaps it is a configuration file or whatever).
For example, you can have a
commonpart.h:
[General]
foo=1
bar=2
#define VALUE 3
and a
cfg.tmpl
#include "commonpart.h"
// This is a commet which won't show up in the end
baz=VALUE
you can do cpp -P cfg.tmpl and thus compile all this to
[General]
foo=1
bar=2
baz=3

Getting the GCC include path with GNU Autotools

I'm writing an implementation of the C preprocessor that, when running on Linux, needs to know the path on which to find header files. This can be obtained by running gcc -v. I want to compile the results into the binary of my preprocessor rather than having to invoke gcc -v on every run, so I'm currently thinking of writing a Python script to be run at compile time, that would obtain the path and write it into a small C source file to be included in the build.
On the other hand, I get the impression GNU Autotools is basically the specialist in obtaining system-specific information to be used at build time. Does Autotools have the ability to obtain the #include path in such a way that it can be incorporated as a string into the program being built (as opposed to being used for the build process)? If so, how?
If you want to get the internal include/ directory used by GCC, run the gcc -print-file-name=include command, e.g. in shell syntax
the_gcc_include_dir=$(gcc -print-file-name=include)
This $the_gcc_include_dirdirectory contains files like <stdarg.h> and <stddef.h> and many others.
You also want the include-fixed/ directory, so
the_gcc_include_fixed_dir=$(gcc -print-file-name=include-fixed)
This $the_gcc_include_fixed_dir contains files like <limits.h> and also a useful README
You probably don't need autotools in your case.
I ended up parsing gcc's include path with a Python script:
print 'string gcc_include_path[] = {'
for s in sys.stdin:
if s[0] == ' ':
s = s.strip()
print '\t"'+s+'",'
print '};'
and calling it from Makefile:
echo | cpp -Wp,-v 2>&1 >/dev/null | python include_path.py >include_path

Environment variabile in Macro path

I need to define some path to files with macros. How can I use the $HOME environment variable?
I can't find it on the GNU manual, and this doesn't work:
#define LOGMMBOXMAN "$HOME/mmbox/LOGmmboxman"
No it shouldn't and you probably don't want constant-defined settings like that in any case. If you did that and it worked as you're intending to use it, your home directory would be built in as whatever $HOME is for whoever's doing the building. The executable then depends on that specific home directory existing. If that's OK, just #define your own home. I suspect it isn't though, so you need to deduce it at runtime.
For run-time deduction what you want is this, such that:
const char* home_dir = getenv("HOME");
If there is no $HOME defined, you get NULL returned so be sure to test for this.
You can then build your string based on that. You'll need #include <stdlib.h>.
Sounds like you are really asking "how can I set some cpp macro from my environment?"
If sothen you should just be able to add it to CPPFLAGS.
export CPPFLAGS="$CPPFLAGS -D LOGMMBOXMAN=$HOME/mmbox/LOGmmboxman"
Then in your code
#ifndef LOGBOXMAN
#error LOGBOXMAN not defined
#endif
Then make sure your source is built using the CPPFLAGS in the command line to gcc:
$ gcc -c file.c $CPPFLAGS
You can't. You need to use your build system to define a macro with the $HOME value (or equivalent on a non-unix system), i.e. something like this:
gcc -DHOME="/home/username" file.c
Or "/Users/username" for Mac OS X, or "C:\Users\username" (or something) for Windows. Basically, GCC provides the -D flag to define a macro on the command line. You can set up a script (or your build system) to take care of this macro definition for you, or perhaps make a system-dependent include file to define the HOME macro properly.
Then, in your C header, you can do:
#define LOGMMBOXMAN HOME "/mmbox/LOGmmboxman"
Note that, in C, consecutive string literals are concatenated. So this macro expands to:
"/home/username" "/mmbox/LOGmmboxman"
Which C interprets as
"/home/username/mmbox/LOGmmboxman"
EDIT: All that thinking, and I didn't think! D'oh!
As others have pointed out, you probably don't want to do this. This will hard-code your program to work for one specific user's home directory. This will likely cause problems if you want each user to use your program, but for each to keep his (or her) own separate files.
Ninefingers' answer is what you're most likely looking for. In the event that you ever find yourself in need of the above technique (i.e. storing application files in a system-specific place) I will leave my answer unchanged, but I expect it won't help you here.

Resources