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

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


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;
}

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