Tutorial :OS detecting makefile


I routinely work on several different computers and several different operating systems, which are Mac OS X, Linux, or Solaris. For the project I'm working on, I pull my code from a remote git repository.

I like to be able to work on my projects regardless of which terminal I'm at. So far, I've found ways to get around the OS changes by changing the makefile every time I switch computers. However, this is tedious and causes a bunch of headaches.

How can I modify my makefile so that it detects which OS I'm using and modifies syntax accordingly?

Here is the makefile:

cc = gcc -g  CC = g++ -g  yacc=$(YACC)  lex=$(FLEX)    all: assembler    assembler: y.tab.o lex.yy.o          $(CC) -o assembler y.tab.o lex.yy.o -ll -l y    assembler.o: assembler.c          $(cc) -o assembler.o assembler.c    y.tab.o: assem.y          $(yacc) -d assem.y          $(CC) -c y.tab.c    lex.yy.o: assem.l          $(lex) assem.l          $(cc) -c lex.yy.c    clean:          rm -f lex.yy.c y.tab.c y.tab.h assembler *.o *.tmp *.debug *.acts  


There are many good answers here already, but I wanted to share a more complete example that both:

  • doesn't assume uname exists on Windows
  • also detects the processor

The CCFLAGS defined here aren't necessarily recommended or ideal; they're just what the project to which I was adding OS/CPU auto-detection happened to be using.

ifeq ($(OS),Windows_NT)      CCFLAGS += -D WIN32      ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)          CCFLAGS += -D AMD64      else          ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)              CCFLAGS += -D AMD64          endif          ifeq ($(PROCESSOR_ARCHITECTURE),x86)              CCFLAGS += -D IA32          endif      endif  else      UNAME_S := $(shell uname -s)      ifeq ($(UNAME_S),Linux)          CCFLAGS += -D LINUX      endif      ifeq ($(UNAME_S),Darwin)          CCFLAGS += -D OSX      endif      UNAME_P := $(shell uname -p)      ifeq ($(UNAME_P),x86_64)          CCFLAGS += -D AMD64      endif      ifneq ($(filter %86,$(UNAME_P)),)          CCFLAGS += -D IA32      endif      ifneq ($(filter arm%,$(UNAME_P)),)          CCFLAGS += -D ARM      endif  endif  


The uname command (http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html) with no parameters should tell you the operating system name. I'd use that, then make conditionals based on the return value.


UNAME := $(shell uname)    ifeq ($(UNAME), Linux)  # do something Linux-y  endif  ifeq ($(UNAME), Solaris)  # do something Solaris-y  endif  


If you do not need sophisticated stuff as done by Git LFS, you can detect the Operating System just using two simple tricks:

  • environment variable OS
  • and then the command uname -s
ifeq ($(OS),Windows_NT)      detected_OS := Windows  else      detected_OS := $(shell uname -s)  endif  

Or a more safe way, if not Windows and command uname not available:

ifeq ($(OS),Windows_NT)      detected_OS := Windows  else      detected_OS := $(shell sh -c 'uname -s 2>/dev/null || echo not')  endif  

Then you can select the relevant stuff depending on detected_OS:

ifeq ($(detected_OS),Windows)      CFLAGS += -D WIN32  endif  ifeq ($(detected_OS),Darwin)  # Mac OS X      CFLAGS += -D OSX  endif  ifeq ($(detected_OS),Linux)      CFLAGS   +=   -D LINUX  endif  ifeq ($(detected_OS),GNU)           # Debian GNU Hurd      CFLAGS   +=   -D GNU_HURD  endif  ifeq ($(detected_OS),GNU/kFreeBSD)  # Debian kFreeBSD      CFLAGS   +=   -D GNU_kFreeBSD  endif  ifeq ($(detected_OS),FreeBSD)      CFLAGS   +=   -D FreeBSD  endif  ifeq ($(detected_OS),NetBSD)      CFLAGS   +=   -D NetBSD  endif  ifeq ($(detected_OS),DragonFly)      CFLAGS   +=   -D DragonFly  endif  ifeq ($(detected_OS),Haiku)      CFLAGS   +=   -D Haiku  endif  

Please see also this detailed answer about importance of uname -s better than uname -o.

The use of OS (instead of uname -s) simplifies the identification algorithm. You can still use solely uname -s but you have to deal with if/else blocks to check all MinGW/Cygwin/... variations.

Note: The environment variable OS is always set to "Windows_NT" on any Windows platform (see Windows Environment Variables on Wikipedia). An alternative of OS is the environment variable MSVC (it checks the presence of MS Visual Studio, see example using MSVC).

Below I provide a complete example using make and gcc to build a shared library: *.so or *.dll depending on the platform. The example is as simplest as possible to be more understandable :-)

To install make and gcc on Windows see Cygwin or MinGW.

My example is based on 5 files

 â"œâ"€â"€ lib   â"‚   â""â"€â"€ Makefile   â"‚   â""â"€â"€ hello.h   â"‚   â""â"€â"€ hello.c   â""â"€â"€ app       â""â"€â"€ Makefile       â""â"€â"€ main.c  

Do not forget: files Makefile are indented using tabulations.

The two files Makefile

1. lib/Makefile

ifeq ($(OS),Windows_NT)      uname_S := Windows  else      uname_S := $(shell uname -s)  endif    ifeq ($(uname_S), Windows)      target = hello.dll  endif  ifeq ($(uname_S), Linux)      target = libhello.so  endif  #ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111  #    target = .....  

Next Post »