0

My goal is to use a single Makefile for compiling a C app across various platforms.

I've been busily relearning C while working on a project, so as a result have not yet had the time to delve into make; instead I have a single, long gcc command in some .bat / .sh files. Needless to say that each time I change one such file, I have to make those same changes in the others. Hence, make. Conditions that need to be handled:

  • Builds for multiple OS & architecture, i.e. for now (at least) 32 & 64-bit for Windows & Linux, thus I need access to that info in the Makefile, and executables should go into distinct folders under /build: i.e. /win32, /win64, /lin32, /lin64 (et cetera, in future).
  • Required DLLs should be copied in alongside the built executable.
  • Separate compile & link phases (thanks @EtanReisner)

Question: Can someone show me how one would go about constructing a Makefile using the info below? And explain how each part works / why it is needed for my particular case? I have an understanding of the prerequisites approach by which Makefiles are built. Thanks in advance.

Here is my project directory tree:

+---bin
+---include
|   +---enet
|   +---GL
|   +---libxml
|   \---ncursesw
+---lib
+---src
+---fonts
\---shaders    

And mingw gcc for Windows 32-bit compile:

gcc -Iinclude ./src/main.c ./src/MainCtrl.c ./src/MainView.c ./src/PerspectiveView.c ./src/LogView.c ./src/LobbyView.c ./src/TerminalView.c ./src/World.c ./src/Chunk.c ./src/CellPattern.c ./src/CellPlan.c ./src/GridPoint.c ./src/Entity.c ./src/InfluenceRadial.c ./src/AoIList.c ./src/AoI.c ./src/ChunkRenderable.c ./src/PrimitiveRenderable.c ./src/Geometry.c ./src/glew/glew.c ./src/stb/stb_image_aug.c ../curt/list_generic.c ../curt/map_generic.c ../curt/intMap.c ../curt/floatMap.c ../hedgehog/hedgehog.c ./src/Inputs.c ./src/InputResponse.c ./src/Network.c ../Disjunction/c/disjunction.c ./src/my/math/Range.c ./src/my/math/Point.c -Llib -lglfw3-32 -lopengl32 -lgdi32 -lenet-32 -lws2_32 -lwinmm -llibxml2-32 -L. -ldll32/libncursesw6 -std=c11 -m32 -w -Wall -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wstrict-prototypes -Wlogical-op -Wcast-align -Wconversion -Wpedantic -Wfloat-equal -fopenmp -O0 -o ./build/win32/wa

And the gcc for Linux 64-bit compile:

gcc -Iinclude -I/usr/include/libxml2 ./src/main.c ./src/MainCtrl.c ./src/MainView.c ./src/PerspectiveView.c ./src/LogView.c ./src/LobbyView.c ./src/TerminalView.c ./src/World.c ./src/Chunk.c ./src/CellPattern.c ./src/CellPlan.c ./src/GridPoint.c ./src/Entity.c ./src/InfluenceRadial.c ./src/AoIList.c ./src/AoI.c ./src/ChunkRenderable.c ./src/PrimitiveRenderable.c ./src/Geometry.c ./src/glew/glew.c ./src/stb/stb_image_aug.c ../curt/list_generic.c ../curt/map_generic.c ../curt/intMap.c ../curt/floatMap.c ../hedgehog/hedgehog.c ./src/Inputs.c ./src/InputResponse.c ./src/Network.c ../disjunction/c/disjunction.c ./src/my/math/Range.c ./src/my/math/Point.c -Llib -L/usr/lib -l:libglfw.so.3 -lncurses -lGL -lxml2 -lenet -lm -std=c11 -m64 -w -Wall -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wstrict-prototypes -Wlogical-op -Wcast-align -Wconversion -Wpedantic -Wfloat-equal -fopenmp -g -o ./wa-64.elf

(worth diffing these.)

Engineer
  • 8,529
  • 7
  • 65
  • 105
  • This article might be of some help: http://make.mad-scientist.net/papers/multi-architecture-builds/ – Etan Reisner Jul 02 '15 at 14:34
  • Basic approach. Split out common source files, common compiler flags, common linker flags, etc. into variables. Do the same with per-os/per-arch sets of arguments (can use their own variables but that isn't necessary since the conditionals can just add them to the main variables if you want). – Etan Reisner Jul 02 '15 at 14:36
  • 1
    The next thing to do would be to start using compiling and linking as independent steps to avoid needing to recompile every file when one file changes. – Etan Reisner Jul 02 '15 at 14:36
  • Thanks @EtanReisner, at least this gives me something to go on for now. – Engineer Jul 02 '15 at 21:48
  • Any reason you can't use autotools? – Kookerus Jul 02 '15 at 22:32

1 Answers1

0

Trial and error led tothe following... work in progress, but the basics are there. Features:

  • No need to manually list source / object files (all relevant sources expected to be in src)
  • Separate compile & link phases using shared logic for .c/.o file lists
  • OS & architecture autodetection (WIP), with manual override for arch ( e.g. make BITS=32)
  • Clean that works for both Win & *nixes
  • Copies in .DLLs on compile (WIP to supply a list of DLLs to copy in)

Thanks to Yanick Rochon, Samuel and Trevor Robinson for their answers, and to those who wrote the GNU Makefile examples.

CC = gcc

#Directories
HDRDIR   = include
SRCDIR   = ./src
OBJDIR   = ./obj
BINDIR   = ./bin
LIBDIR   = lib

#File lists
HDRS := $(wildcard $(HDRDIR)/*.h)
SRCS := $(wildcard $(SRCDIR)/*.c)
OBJS := $(SRCS:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

#Autodetect OS / architecture
OS =
BITS = 32

#ifeq ($(shell echo "check_quotes"),"check_quotes")
ifeq ($(OS),Windows_NT)
    OS   := win
    EXT  := .exe
    rm   := del /q
    cp   := copy /y
    ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
        CFLAGS += -D AMD64
    endif
    ifeq ($(PROCESSOR_ARCHITECTURE),x86)
        CFLAGS += -D IA32
    endif
else
    UNAME_S := $(shell uname -s)
    ifeq ($(UNAME_S),Linux)
        OS  := lin
        EXT := .elf
        rm  := rm -f
        cp  := cp
        CCFLAGS += -D LINUX
        BITS = $(shell getconf LONG_BIT)
    endif
    ifeq ($(UNAME_S),Darwin)
        OS := osx
        EXT :=
        rm  := rm -f
        cp  := cp
        CCFLAGS += -D OSX
        BITS = $(shell getconf LONG_BIT)
    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

#Define flags for compile & link
CFLAGS = -I$(HDRDIR) -std=c11 -m64 -fopenmp -Wall -Werror -Wmissing-prototypes -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wlogical-op -Wcast-align -Wconversion -Wpedantic -Wfloat-equal -w -O0 -DGLEW_STATIC
LFLAGS = -L$(LIBDIR)

#Do we want verbose linker messages?
LVERBOSE := false 
LLIBS =
ifeq ($(OS),win)
    LLIBS += -lglfw3-$(BITS) -lglew32s -lglew32 -lopengl32 -lgdi32 -lwinmm
endif
ifeq ($(OS),lin)
    LLIBS += -lglew32s -lglew32 -lGL -lm
endif
ifeq ($(LVERBOSE),true)
    LFLAGS += -Wl,--verbose
endif

TARGET = program$(EXT)

#Rules
$(BINDIR)/$(TARGET): $(OBJS)
    @$(CC) -o ./$@ $(OBJS) $(LFLAGS) $(LLIBS) 
    @echo Linking complete.
    @$(cp) $(LIBDIR)\glfw3.dll $(BINDIR)
    @echo DLLs accompanying executable.

$(OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o ./$@
    @echo Compiled $<.

.PHONEY: clean
clean:
ifeq ($(OS),win)
    @$(rm) $(subst /,\\,$(OBJS))
    @$(rm) $(subst /,\\,$(BINDIR)/*.*)
else
    @$(rm) $(OBJS)
    @$(rm) $(BINDIR)/*
endif
    @echo Cleanup complete.
#$(subst /,\\, $(SOMETHING)) replaces all (make-native) forward slashes with backslashes, for Windows.
Community
  • 1
  • 1
Engineer
  • 8,529
  • 7
  • 65
  • 105