toolchainX
toolchainX

Reputation: 2057

convert multiple makefiles into one makefile

I just have writed some pure c programs for practice, each little program have a XXX.h file XXX.c file and XXX_test.c file with the main function in it. both of the XXX.c file and the XXX_test.c may have include some other YYY.h files. I use the the gcc -MM to find the dependency of XXX_test.c files automatically. finally, I come up with the makefile(using GNU makefile) XXX.mk:

# change your targets to your real targets
MINGW := MINGW32_NT-6.1
CC = gcc
OFLAG = -o 
CFLAGS = -Wall -g -DDEBUG
# get the dependent header files and change their name to source files
TARGET := XXX_test
FILES := $(filter %.c %.h, $(shell gcc -MM $(addsuffix .c, $(TARGET))))
SRCS := $(FILES:.h=.c)
DEPS := $(SRCS:.c=.d)
OBJECTS := $(SRCS:.c=.o)

#determine the os platform
ifdef SystemRoot
   ifeq ($(shell uname), ${MINGW})
      RM = rm -f
      FixPath = $1
   else
      RM = del /Q
      FixPath = $(subst /,\,$1)
   endif
else
   ifeq ($(shell uname), Linux)
      RM = rm -f
      FixPath = $1
   endif
endif


all: ${TARGET}
# include .d files generated by gcc -MMD opt
-include $(DEPS)
# to generate the .o files as well as the .d files
%.o:%.c
    $(CC) $(CFLAGS) -c -MMD $< -o $@

# generate the executable program
${TARGET}: ${OBJECTS}
    ${CC} $^ ${OFLAG} $@

.PHONY:clean
# the "$" is not needed before "FixPath"
clean:
    $(RM) $(call FixPath, ${OBJECTS} ${DEPS})

each little program have a XXX.mk file. they are almost the same except that the content in the variable TARGET is different(see the makefile above). so I have XXX.mk, YYY.mk etc ... The question I want to ask is how can I convert all the makefiles, i.e. XXX.mk, YYY.mk ... into one makefile? for example generic.mk with a varible TARGETS(note the S) assigned to XXX_test YYY_test .... the thing that I find difficult to handle is the variable dependencies:

TARGET --> OBJECTS --> SRCS --> FILES --> TARGET(the FILES is generate by gcc using -MM opt)
                         ↓
                        DEPS(used to include the .d files)

each target(such as XXX_test) should depend on their on bunch of files. I want to see the power of GNU makefile, how could I do it? I am learning to use the gmake.

Upvotes: 0

Views: 1164

Answers (2)

uml&#228;ute
uml&#228;ute

Reputation: 31374

as you found out, the main problem you are having is, that the expansion of OBJECTS from the DEPS doesn't work well with multiple targets.

one possibility to solve this is to use a two stage approach, where the second stage deals only with building a single binary (and does all the dependency tracking and binary object compilation), and the first stage calls the 2nd one for each given binary.

this solution involves two Makefiles (not exactly what you asked for, but still):

Makefile:

TARGETS = XXX_test YYY_test ZZZ_test XYZ_test
CLEANTARGETS=$(TARGETS:=_clean)

all: $(TARGETS)
clean: $(CLEANTARGETS)

$(TARGETS) $(CLEANTARGETS):
        make -f Makefile.sub $@

.PHONY: all clean $(CLEANTARGETS)

this makefile simply calls another make with the program to build as argument and the secondary Makefile.sub:

# change your targets to your real targets
MINGW := MINGW32_NT-6.1
CC = gcc
OFLAG = -o 
CFLAGS = -Wall -g -DDEBUG
# get the dependent header files and change their name to source files
TARGET=$(MAKECMDGOALS:_clean=)
FILES= $(filter %.c %.h, $(shell gcc -MM $(addsuffix .c, $(TARGET))))
SRCS= $(patsubst %.h,%.c,$(filter %.c %.h, $(shell gcc -MM $(addsuffix .c, $(TARGET)))))

DEPS= $(SRCS:.c=.d)
OBJECTS= $(SRCS:.c=.o)

#determine the os platform
ifdef SystemRoot
   ifeq ($(shell uname), $(MINGW))
      RM = rm -f
      FixPath = $1
   else
      RM = del /Q
      FixPath = $(subst /,\,$1)
   endif
else
   ifeq ($(shell uname), Linux)
      RM = rm -f
      FixPath = $1
   endif
endif


# include .d files generated by gcc -MMD opt
-include $(DEPS)
# to generate the .o files as well as the .d files
%.o:%.c
    $(CC) $(CFLAGS) -c -MMD $< -o $@

# generate the executable program
$(TARGET): $(OBJECTS)
    $(CC) $^ $(OFLAG) $@

.PHONY:clean
# the "$" is not needed before "FixPath"
clean:
    $(RM) $(call FixPath, $(OBJECTS) $(DEPS))

$(TARGET)_clean: clean

if you insist on using a single Makefile, then i'm afraid, that you have to do the dependency tracking manually. one of the more common build-systems is autotools where you have to provide the sources for each binary yourself (to run the following you need autotools installed; run autoreconf to generate configure and Makefile.in from the following code; then run ./configure to create a cross-platform Makefile):

Makefile.am:

 bin_PROGRAMS = XXX_test YYY_test
 XXX_test_SOURCES = XXX_test.c XXX.c
 YYY_test_SOURCES = YYY_test.c YYY.c

configure.ac:

AC_INIT([XYZ],[0.1])
AM_INIT_AUTOMAKE($PACKAGE_NAME,$PACKAGE_VERSION)
AC_CONFIG_FILES([Makefile])
AC_PROG_INSTALL
AC_LANG_C
AC_PROG_CC
AC_OUTPUT

Upvotes: 2

uml&#228;ute
uml&#228;ute

Reputation: 31374

target/dependencies can be multi-valued. if you specify

 TARGET := XXX_test XXY_test XYZ_test XZY_test

then running make all (which is the default, since all is the first target in Makefile; so you can as well simply run make), will build all those ???_test files for you.

Upvotes: -1

Related Questions