# Makefile -- Platform Dependency

My application has two parts -- a client and a server. Within this, there are different versions for different platforms. The user specifies this in the args, i.e. "make PLATFORM=x11".

However, I feel like I'm separating the client and server build operations in a very inefficient way, and that I have too many variables. It works, but I'm not sure how to improve it.

Any suggested improvements would be much appreciated!

CC=gcc
CFLAGS=-c -Wall
LDFLAGS=
BUILD_DIR = ../build/
OBJ_DIR = $(BUILD_DIR)obj/ LIBS= CLIENT_NAME_DIR = client/ CLIENT_SRC_FILES :=$(wildcard $(CLIENT_NAME_DIR)*.c) CLIENT_PLATFORM_DIR =$(PLATFORM)/
CLIENT_SRC_FILES += $(wildcard$(CLIENT_NAME_DIR)$(CLIENT_PLATFORM_DIR)*.c) CLIENT_OBJ_DIR =$(OBJ_DIR)$(CLIENT_NAME_DIR) CLIENT_OBJ_FILES :=$(addprefix $(CLIENT_OBJ_DIR),$(notdir $(CLIENT_SRC_FILES:.c=.o))) CLIENT_LIBS =$(LIBS) -lX11
CLIENT_EXECUTABLE_NAME = outcast-client
CLIENT_EXECUTABLE=$(BUILD_DIR)$(CLIENT_EXECUTABLE_NAME)

SERVER_NAME_DIR = server/
SERVER_SRC_FILES := $(wildcard$(SERVER_NAME_DIR)*.c)
SERVER_OBJ_DIR = $(OBJ_DIR)$(SERVER_NAME_DIR)
SERVER_OBJ_FILES := $(addprefix$(SERVER_OBJ_DIR),$(notdir$(SERVER_SRC_FILES:.c=.o)))
SERVER_LIBS = $(LIBS) SERVER_EXECUTABLE_NAME = outcast-server SERVER_EXECUTABLE=$(BUILD_DIR)$(SERVER_EXECUTABLE_NAME) all: clean client server client:$(CLIENT_SRC_FILES) $(CLIENT_EXECUTABLE) server:$(SERVER_SRC_FILES) $(SERVER_EXECUTABLE)$(CLIENT_EXECUTABLE): $(CLIENT_OBJ_FILES)$(CC) $(LDFLAGS)$(CLIENT_OBJ_FILES) -o $@$(CLIENT_LIBS)

$(CLIENT_OBJ_DIR)%.o:$(CLIENT_NAME_DIR)$(CLIENT_PLATFORM_DIR)%.c$(CC) $(CFLAGS) -c -o$@ $<$(CLIENT_OBJ_DIR)%.o: $(CLIENT_NAME_DIR)%.c$(CC) $(CFLAGS) -c -o$@ $<$(SERVER_EXECUTABLE): $(SERVER_OBJ_FILES)$(CC) $(LDFLAGS)$(SERVER_OBJ_FILES) -o $@$(SERVER_LIBS)

$(SERVER_OBJ_DIR)%.o:$(SERVER_NAME_DIR)%.c
$(CC)$(CFLAGS) -c -o $@$<

clean:
rm -rf $(BUILD_DIR) mkdir -p$(CLIENT_OBJ_DIR)
mkdir -p $(SERVER_OBJ_DIR)  ## 3 Answers Conner, I have a few minor points. • Firstly, you have no includes (ie, no -Idir). Do you really have no headers? • You should make the non-targets 'PHONY' so make knows they will never exist: .PHONY clean clean: rm -f ...  Same with all, server and client • It is not normal to clean everything on each build - usually you let make decide what is out of date and needs to be rebuilt. For that you need to have all the dependencies declared to make (eg headers) • client and server depend upon the build directories and the executables, but not directly upon the sources. • the clean target remakes the build directories. To me, these directories should be made when you build the targets: $(CLIENT_OBJ_DIR) $(SERVER_OBJ_DIR): mkdir -p$@

client: $(CLIENT_OBJ_DIR)$(CLIENT_EXECUTABLE)

server: $(SERVER_OBJ_DIR)$(SERVER_EXECUTABLE)

• -Wall does not really give you 'all' warnings. There are many more that are worth enabling.

• Very sorry that it took me a year and a half to accept your answer... better late than never? :) Thanks! May 21 '14 at 12:52

Not exactly what you are asking for but I would suggest using cmake instead. Some benefits:

• less code
• support for a separate build tree
• avoiding the white-space (tab) bugs that occur easily in a Makefile

First create a file called CMakeLists.txt with this content:

cmake_minimum_required(VERSION 2.8)
project(outcast)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") file(GLOB client_sources "client/${PLATFORM}/*.c")
add_executable(outcast-client ${client_sources}) target_link_libraries(outcast-client x11) install(TARGETS outcast-client RUNTIME DESTINATION bin) file(GLOB server_sources "server/*.c") add_executable(outcast-server${server_sources})
install(TARGETS outcast-server RUNTIME DESTINATION bin)


then build and install your programs outcast-client and outcast-server like this

erik@linux:~$ls ~/outcast_source CMakeLists.txt client/ server/ erik@linux:~$ mkdir /tmp/build
erik@linux:~$mkdir /tmp/install erik@linux:~$ cd /tmp/build/
erik@linux:/tmp/build$CC=gcc cmake -DPLATFORM=X11 -DCMAKE_INSTALL_PREFIX=/tmp/install ~/outcast_source  Instead of finding the source files with a GLOB expression it is probably better to list them one by one. See https://stackoverflow.com/questions/1027247/best-way-to-specify-sourcefiles-in-cmake CMake has great support for detecting properties of the build system. You could for instance write something like this in your CMakeLists.txt find_package(X11) if(${X11_FOUND})
file(GLOB client_sources "client/X11/*.c")
add_executable(outcast-client ${client_sources}) target_link_libraries(outcast-client${X11_LIBRARIES})
install(TARGETS outcast-client RUNTIME DESTINATION bin)
endif()

• For what it's worth, I did end up switching to CMake (as well as using it for most later projects). Thanks! May 21 '14 at 12:53

You can definitely simplify that:

TARGET          ?= CLIENT

SRC_FILES       := $(wildcard$($(TARGET)_NAME_DIR)*.c) ^^^^^^^^^^^  Here$(TARGET) will expand first (to CLIENT or what is set on the command line) and then \$(CLIENT_NAME_DIR) will be expanded.

So you end up with only a few variables specific to the Client/Server and all the other variables are generalized based on that. The same then applies to all your build rules which can be collapsed down into one set of commands (rather than two).