Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you write a makefile for both clang and gcc?

I would like to use std::experimental::filesystem in my code, this requires me to compile using -lstdc++fs with GCC and -lc++experimental with Clang. At the moment I have a makefile and makefile.clang reflecting the difference in compilation, alternatively I've thought about using a clang build target so I can run build clang.

Is there some canonical way to set compiler-specific flags in a makefile?

like image 302
shians Avatar asked Jun 20 '18 05:06

shians


People also ask

Are GCC and Clang compatible?

For example, even though Clang implements atomic intrinsics which correspond exactly with C11 atomics, it also implements GCC's __sync_* intrinsics for compatibility with GCC and libstdc++. Clang also maintains ABI compatibility with GCC-generated object code.

Is GCC and Clang the same?

Clang is designed as an API from its inception, allowing it to be reused by source analysis tools, refactoring, IDEs (etc) as well as for code generation. GCC is built as a monolithic static compiler, which makes it extremely difficult to use as an API and integrate into other tools.

Does Clang optimize better than GCC?

Clang reduces the single-thread compilation time by 5% to 10% compared with GCC. Therefore, Clang offers more advantages for the construction of large projects.


4 Answers

As the user "Some programmer dude" mentioned, there are conditionals in GNU make. You could easily check for the compiler version this way:

CXXFLAGS = -Og -Wall -Wextra

GCC_CXXFLAGS = -DMESSAGE='"Compiled with GCC"'
CLANG_CXXFLAGS = -DMESSAGE='"Compiled with Clang"'
UNKNOWN_CXXFLAGS = -DMESSAGE='"Compiled with an unknown compiler"'

ifeq ($(CXX),g++)
  CXXFLAGS += $(GCC_CXXFLAGS)
else ifeq ($(CXX),clang)
  CXXFLAGS += $(CLANG_CXXFLAGS)
else
  CXXFLAGS += $(UNKNOWN_CXXFLAGS)
endif

Given the following source file test.cpp you can compile it with make CXX=g++ test or make CXX=clang test and it should pass the appropriate flags to each compiler.

#include <iostream>

int main() {
  std::cout << "Hello World " << MESSAGE << std::endl;
  return 0;
}
like image 128
PaulR Avatar answered Oct 05 '22 05:10

PaulR


You can use CMake to achieve that. It is a better to use if you want to have portable code.

CMake allows to generate Makefile that is appropriate for your system(e.g. your system default compiler). CMake has a lot of features that can be very useful to check actual system configuration.

In this answer, you have example how do that: In cmake, how can I test if the compiler is Clang?

A reliable check is to use the CMAKE__COMPILER_ID variables. E.g., to check the C++ compiler:

if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
  # using Clang
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
  # using GCC
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
  # using Intel C++
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
  # using Visual Studio C++
endif()

If you still want to use Makefile you should check this answer: https://stackoverflow.com/a/2826178/7042963

like image 45
BartekPL Avatar answered Oct 05 '22 04:10

BartekPL


In order to handle versioned compilers, like you mentioned in the comment to the accepted answer, you need to use $(findstring find,in) as such:

# Detect if CXX is g++ or clang++, in this order.
ifeq '' '$(findstring clang++,$(CXX))'
  LDLIBS = -lstdc++fs
else
  LDLIBS = -lc++experimental
endif

The caveat here is that you cannot use $(findstring g++,$(CXX)) since it'll match clang++ unintentionally.

A more in-depth alternative to handle things more precisely would be:

# Detect if CXX is clang++ or g++, in this order.
ifneq '' '$(findstring clang++,$(CXX))'
  LDLIBS = -lc++experimental
else ifneq '' '$(findstring g++,$(CXX))'
  LDLIBS = -lstdc++fs
endif
like image 41
bit2shift Avatar answered Oct 05 '22 04:10

bit2shift


This approach parses the compiler's version string looking for clang. If it doesn't find clang then it looks for g++ in order to resolve an issue with macOS, where g++ is aliased to clang.

# Set compiler-specific flags
GCC_CXXFLAGS = -DMESSAGE='"Compiled with GCC"'
CLANG_CXXFLAGS = -DMESSAGE='"Compiled with Clang"'
UNKNOWN_CXXFLAGS = -DMESSAGE='"Compiled with an unknown compiler"'

# Detect if CXX is clang++ or g++, in this order.
COMPILER_VERSION := $(shell $(CXX) --version)
ifneq '' '$(findstring clang,$(COMPILER_VERSION))'
  CFLAGS += $(CLANG_CXXFLAGS)
else ifneq '' '$(findstring g++,$(COMPILER_VERSION))'
  CFLAGS += $(GCC_CXXFLAGS)
else
  $(warning Unknown compiler)
  CFLAGS += $(UNKNOWN_CXXFLAGS)
endif

Credit to @bit2shift's answer, which inspired this one.

like image 39
Kenn Sebesta Avatar answered Oct 05 '22 04:10

Kenn Sebesta