We're trying to develop an as-portable-as-possible Makefile...
Neither uname nor uname -v is definitive in one suite of cases...
which ld is also unhelpful, as both linkers are present...
I imagine we could just parse output of gcc -v for '--with-ld=/usr/bin/ld', then test the features/version of that linker. But is the best way to do this?
What are 'Best Practices' here? Can gcc be queried more cleanly - from within a Makefile - for its linker options?
The first thing that comes to my mind (tested with GNU make and clearmake):
# redirect gcc -v to stdout && count the number of occurrences
GCC_WITH_LD := $(shell gcc -v 2>&1 | grep -c "\--with-ld")
ifeq ($(GCC_WITH_LD),0)
$(shell echo --with-ld NOT FOUND 1>&2) # print to stderr
# exit using error directive?
else
$(shell echo --with-ld FOUND 1>&2) # print to stderr
endif
mytarget:
#echo myjobs
The option you're looking for is -print-prog-name:
andy#Andrews-Mac-Pro ~ % gcc -print-prog-name=ld
/Library/Developer/CommandLineTools/usr/bin/ld
Then in the Makefile you can set LD with:
LD := $(shell gcc -print-prog-name=ld)
This also works on Solaris.
Related
I don't have a configure stage in my baseline (for various reasons), but I need to check whether my C compiler can support the -mavx2 flag or not.
If I have an empty file, call it test.cc then I can do something like:
$(CC) -mavx2 -c test.cc -o test.o
And check the return status code. Obviously I don't want to leave those test.* files lying around though, but can't think of a good way to generate/test/delete them outside of a recipe. Does anyone know of a good pattern to do this?
Or, you could just take input from stdin, and output to /dev/null:
SUPPORTS_MAVX2:=$(shell echo 'void main(){}' | \
gcc -x c -maxv3 -o /dev/null - 2>/dev/null; \
echo $$?)
Then there are no artifact files to be deleted. The -x c is necessary to tell gcc what language it is (as it can't determine that from the filename in this case), and you need the trailing - as well.
Turns out you can use "eval" to remove the temporary file and return the status code, so this works:
AVX2 = $(shell touch test.cc; $(CC) -mavx2 -c test.cc -o /dev/null >& /dev/null; eval "rm -f test.cc; echo $$?")
Edit and without the temporary file (assuming a sane system):
AVX2 = $(shell $(CC) -mavx2 -c /usr/include/stdlib.h -o /dev/null >& /dev/null; echo $?)
I have some GAS assembly code, and I'm compiling it directly through GCC to use preprocessor features like #include:
gcc main.S
I came across a reported Binutils bug for a given Binutils version, and I would like to have two versions of the code, decided at compile time:
a workaround version for the buggy Binutils version
the main code version otherwise
For the version of GCC itself, I can use __GNUC__ and related macros as explained at: How do I test at compile time the current version of GCC?
Is there something like that for the version of Binutils?
I could modify my build system to check the as --version myself and pass a gcc -D define, but I wonder if that can be avoided.
There is a special symbol called .gasversion. (with a leading and trailing dot). You can use it this way:
.data
.if .gasversion. >= 22900
.ascii "binutils 2.29 or newer"
.endif
.if .gasversion. >= 22800
.ascii "binutils 2.28 or newer"
.endif
Note that this is not a preprocessor feature (as GCC does not know the GAS/BFD version and does not pass it to the preprocessor). So you have to use GAS constructs like .if and .macro to implement what you need.
Often, an alternative approach is used where the actual presence of the bug is tested in some configure script and the workaround is activated only if necessary. This means that the workaround is only used when it is absolutely required—version numbers do not reflect distribution backports which could have fixed the bug. Obviously, this only makes sense if the workaround is costly (because it introduces additional run-time overhead).
How to check the version of Binutils on GNU GAS assembly code at compile time?
Crypto++ had a a similar problem. They needed to know AS and LD versions to ensure instructions part of ISAs like SSE4 (-msse4.1), AES (-maes) and SHA (-msha) were available during a build (using Intel as an example).
In a GNUmakefile Crypto++ used to perform:
GCC_COMPILER := $(shell $(CXX) --version 2>/dev/null | $(GREP) -v -E '(llvm|clang)' | $(GREP) -i -c -E '(gcc|g\+\+)')
...
ifneq ($(GCC_COMPILER),0)
IS_GCC_29 := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E gcc-9[0-9][0-9])
GCC42_OR_LATER := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E "gcc version (4\.[2-9]|[5-9]\.)")
GCC46_OR_LATER := $(shell $(CXX) -v 2>&1 | $(GREP) -i -c -E "gcc version (4\.[6-9]|[5-9]\.)")
endif
ifneq ($(HAVE_GAS),0)
GAS210_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.[1-9][0-9]|[3-9])")
GAS217_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.1[7-9]|2\.[2-9]|[3-9])")
GAS218_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.1[8-9]|2\.[2-9]|[3-9])")
GAS219_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.19|2\.[2-9]|[3-9])")
GAS224_OR_LATER := $(shell $(CXX) -xc -c /dev/null -Wa,-v -o/dev/null 2>&1 | $(GREP) -c -E "GNU assembler version (2\.2[4-9]|2\.[3-9]|[3-9])")
endif
And later Crypto++ would do stuff like:
ifeq ($(HAVE_GAS)$(GAS224_OR_LATER),10)
CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
endif
The 10 string is basically equivalent to the following. It is the GNU Makefile way of doing boolean expressions:
if HAVE_GAS==true && GAS224_OR_LATER==false
CXXFLAGS += -DCRYPTOPP_DISABLE_SHA
fi
By the way, the GAS version checking broke with Clang and the Integrated Assembler. Clang does not respond to -Wa,-v like AS does. LLVM Bug 24200 was filed because of it: Fail to fetch version string of assembler when using integrated assembler.
What Crypto++ found was, this did not scale well. It was OK 10 or 20 years ago (literally, when it was initially used). However it broke down when (1) new platforms use ancient toolchains, like modern BSD pinning to GPL2 toolchains (2) new compilers were installed on old platforms, like Clang 7.0 on a Power6 machine, and (3) Clang and its integrated assembler, which did not need AS to assemble higher ISAs.
ARM platforms was also very troublesome because the project could not reliably determine when to include <arm_neon.h> and <arm_acle.h> based on platforms and compiler versions. Sometimes the headers were available for a platform and compiler, sometimes they were not (even on the same platform with different versions of the same compiler). Preprocessor macros like __ARM_ACLE__ were completely missing (see ARM C Language Extensions (ACLE)). Android and iOS just does what the hell it wants breaking from what happens on armhf and friends or what is stated in the docs. And Microsoft found a new way to break it with their header <arm64_neon.h>.
Now Crypto++ performs a test compile through the GNU Makefile to see if a program can be compiled, assembled and linked. However, it is not braindead like Autotools or Cmake. Crypto++ looks for any diagnostic and fails the test for any diagnostic. This caught cases Autotools and Cmake were missing, like SunCC emitting like "illegal option: -xarch=sha". Autotools and Cmake would report success and later the build would fail. (Apparently Autotools and Cmake only check compiler return codes, and not diagnostic messages like "illegal option").
The Crypto++ tests now look like:
SUN_COMPILER := $(shell $(CXX) -V 2>&1 | $(GREP) -i -c -E 'CC: (Sun|Studio)')
...
ifeq ($(SUN_COMPILER),1)
SSE2_FLAG = -xarch=sse2
else
SSE2_FLAG = -msse2
endif
...
TPROG = TestPrograms/test_x86_sse2.cxx
TOPT = $(SSE2_FLAG)
HAVE_OPT = $(shell $(CXX) $(TCXXFLAGS) $(ZOPT) $(TOPT) $(TPROG) -o $(TOUT) 2>&1 | tr ' ' '\n' | wc -l)
ifeq ($(strip $(HAVE_OPT)),0)
CHACHA_FLAG = $(SSE2_FLAG)
SUN_LDFLAGS += $(SSE2_FLAG)
else
SSE2_FLAG =
endif
And test_x86_sse2.cxx, which is compiled at -O0 so the optimzer does not remove the code:
$ cat TestPrograms/test_x86_sse2.cxx
#include <emmintrin.h>
int main(int argc, char* argv[])
{
__m128i x = _mm_setzero_si128();
x=_mm_add_epi64(x,x);
return 0;
}
The command line limit for windows is ~7000 characters. The clang command for my project is ~130,000 due to a very large number of includes. Is there some way to tell clang about my project includes from a separate file?
I admit to being curious why you have all of the includes on the command line, however, you can use this syntax to put all of the commands in a file:
clang #cmds
where cmds contains something like:
echristo#dzur ~/tmp> cat cmds
-c
foo.c
-o
foo.o
so the full process would look a bit like:
echristo#dzur ~/tmp> ls
cmds foo.c
echristo#dzur ~/tmp> clang #cmds
echristo#dzur ~/tmp> ls
cmds foo.c foo.o
I have to compile some mysql c api code and tired of writing all this in command line:
gcc main.c -o main `mysql_config --cflags --libs`
I wrote a shell script in bash and pass positional parameter in bash:
gcc $1 -o ${"$1":0:2} 'mysql_config --cflags --libs' but this doesn't work. I get error message: ./compile: line 2: ${"$1":0:-2}: bad substitution. Can someone please tell me what I am doing wrong?
The only way I got this to work is by assigning a new variable:
filename=$1;
gcc $filename -o ${filename:0:-2} `mysql_config --cflags --libs`
Is this the only way to do it or is there a way to fix what I am doing wrong in the first case?
You almost had it:
${1:0:2}
You don't need another reference to $1 inside the brackets since everything in it will be interpreted as the name of the variable, as in the case of ${filename:0:-2}.
In response to the comments under the question, here's an example makefile for this situation:
MSQL_FLAGS := $(shell mysql_config --cflags)
MSQL_LIBS := $(shell mysql_config --libs)
main : main.c
gcc $(MSQL_FLAGS) -o $# $< $(MSQL_LIBS)
I would like to use some gcc warning switchs that aren't available in older gcc versions (eg. -Wtype-limits).
Is there an easy way to check the gcc version and only add those extra options if a recent gcc is used ?
I wouldn't say its easy, but you can use the shell function of GNU make to execute a shell command like gcc --version and then use the ifeq conditional expression to check the version number and set your CFLAGS variable appropriately.
Here's a quick example makefile:
CC = gcc
GCCVERSION = $(shell gcc --version | grep ^gcc | sed 's/^.* //g')
CFLAGS = -g
ifeq "$(GCCVERSION)" "4.4.3"
CFLAGS += -Wtype-limits
endif
all:
$(CC) $(CFLAGS) prog.c -o prog
Edit: There is no ifgt. However, you can use the shell expr command to do a greater than comparison. Here's an example
CC = gcc
GCCVERSIONGTEQ4 := $(shell expr `gcc -dumpversion | cut -f1 -d.` \>= 4)
CFLAGS = -g
ifeq "$(GCCVERSIONGTEQ4)" "1"
CFLAGS += -Wtype-limits
endif
all:
$(CC) $(CFLAGS) prog.c -o prog
To transform full 3-part gcc version (not only first digit) into numerical format, suitable for comparison (e.g. 40701) use
gcc -dumpfullversion -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$/&00/'
Which addresses the possibility of double-digit numbers in any of the version part, and possibility of missing 3-rd part of the version in output of gcc -dumpversion (which is the case in some earlier gcc versions).
So to test the version in makefile, use something like (note $$ inside last sed command)
GCC_GTEQ_472 := $(shell expr `gcc -dumpfullversion -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/'` \>= 40702)
ifeq "$(GCC_GTEQ_472)" "1"
...
endif
I just encountered this problem where I needed to test the first two digits of gcc and wanted a more readable option than the clever sed hackery above. I used bc to do the comparison since it supports floating point (expr treats non-integers as strings):
GCC_VER_GTE44 := $(shell echo `gcc -dumpversion | cut -f1-2 -d.` \>= 4.4 | bc )
ifeq ($(GCC_VER_GTE44),1)
...
endif
If they release gcc 4.10 after gcc 4.9, then a bit of sed hacking is necessary, but this is still pretty readable:
GCC_VER_GTE44 := $(shell echo `gcc -dumpversion | cut -f1-2 -d.` \>= 4.4 | sed -e 's/\./*100+/g' | bc )
ifeq ($(GCC_VER_GTE44),1)
...
endif
I found this and thought it was really clever. It implements >, >=, <, and <= with fewer shell calls:
GCC_VERSION := $(shell gcc -dumpversion)
VERSION := 7.4.0
ifeq ($(VERSION),$(firstword $(sort $(GCC_VERSION) $(VERSION))))
# stuff that requires GCC_VERSION >= VERSION
endif
This example shows >=. You can implement >, <=, or < using combinations of ifneq and $(lastword).
References:
https://lists.gnu.org/archive/html/help-make/2006-04/msg00065.html
Are you using something like autoconf?
It might be worth invoking a 'dummy' compile via gcc with the flag enabled and if that one fails because the compiler doesn't recognise the flag, you can fall back to the command line that doesn't use the newer warning options.
I've made a ready-to-use IF_GCC macro, based on the answers above:
MY_GCC_VERSION=$(if $(GCC_VERSION),$(GCC_VERSION),$(GCC_DEFAULT_VER))
MY_GCC_TOINT=$(shell echo $(1) | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$//')
MY_IF_GCC=$(if $(shell test $(call MY_GCC_TOINT, $(MY_GCC_VERSION)) -$(1) $(2) || echo 0),$(4),$(3))
GCC_DEFAULT_VER:=$(firstword $(shell cc -V 2>&1 | grep default | sed -r 's/( *)([0-9.]+),(.*)/\2/g'))
Usage: $(call MY_IF_GCC,ge,30305,-fan_option_for_gcc_ge_3.3.5)
As the second argument, you can use any operator of those supported by test(1): eq, gt, lt, ge, le etc.
If cc -V doesn't work for you, replace it with gcc -dumpversion or whatever suitable
Hope that helps.
Following Chris, but using awk
GCC_VER_GTE44 := $(shell expr $$(gcc -dumpversion | awk -F. '{print $$3+100*($$2+100*$$1)}') \>= 40400)
note $ needs to be escaped in Makefile with another $.
I think awk is a better tool for this purpose, as it can both split the the version string into $1 $2 and $3 and then do the comparison with >, <, >= etc. in one command. The whole line is omitted from awk output if the expression is false.
GCC_VERSION_GT_75 = $(shell gcc -dumpfullversion -dumpversion | awk -F. '$$1 > 7 && $$2 > 5')
ifneq ($(GCC_VERSION_GT_75),)
# stuff that requires gcc version > 7.5
endif