简体   繁体   中英

Makefile dealing with different source and object directories under Windows and Linux

I have a problem creating a Makefile for a project with source files scattered in different folders. As the source files might have the same names, I'd like to send the objects into different folders as well. With a single source and object file, I'm doing fine following rules like posted elsewhere ( Automatic makefile with source and object files in different directories ). The Makefile has to be adequate to be used with Windows and Linux, which I solve by the pathfix definition (see code below). This part is doing fine, but I might overlook some detail. Here is a shortened version of the Makefile showing my approach:

CXX = g++

CFLAGS = -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp

FILES = main.cpp
FILES_MINIME = minime_class.cpp fifo_class.cpp fit_fun.cpp funcs.cpp gabe_class.cpp minimizer_s.cpp random.cpp

# check the OS
ifdef windir
 binfix = .exe
 pathfix = $(subst /,\,$1)
 OSID = 1
else
 ifeq ($(shell uname), Linux)
  binfix =
  pathfix = $1
  OSID = 0
 else
  ifneq ($(shell uname), Linux)
   $(error OS not identified)
  endif
 endif
endif

# define the different paths of objects and sources
ODIR = obj
SDIR = .
MINIME_SDIR = $(call pathfix, ../minime_pp/src)
MINIME_ODIR = $(call pathfix, obj/minime_pp)

# create templates for the objects by replacing the cpp with an o
_OBJECTS = $(patsubst %.cpp, %.o, $(FILES))
_OBJECTS_MINIME = $(patsubst %.cpp, %.o, $(FILES_MINIME))

# combine the source- and the file names
SOURCE_MINIME = $(call pathfix, $(addprefix $(MINIME_SDIR)/, $(FILES_MINIME)))

# now combine the object- and the file names
OBJECTS = $(call pathfix, $(addprefix $(ODIR)/, $(_OBJECTS)))
OBJECTS_MINIME = $(call pathfix, $(addprefix $(MINIME_ODIR)/, $(_OBJECTS_MINIME)))

# add upp all the objects
OBJECTS_ALL = $(OBJECTS) $(OBJECTS_MINIME)

# define the rules
$(PROJECT): $(OBJECTS_ALL)
    $(CXX) -o $@$(binfix) $^

$(OBJECTS): $(FILES) Makefile
    $(CXX) $(CFLAGS) -c $< -o $@

$(OBJECTS_MINIME): $(SOURCE_MINIME)
    $(CXX) $(CFLAGS) -c $< -o $@

# create the folders (if necessary)
$(OBJECTS): | $(ODIR)

$(OBJECTS_MINIME): | $(MINIME_ODIR)

$(ODIR):
    mkdir $(ODIR)

$(MINIME_ODIR):
    mkdir $(MINIME_ODIR)

The problem is easiest illustrated on the output. On Windows, mingw32-make > output.txt reads

g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c main.cpp -o obj\main.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\minime_class.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\fifo_class.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\fit_fun.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\funcs.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\gabe_class.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\minimizer_s.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\random.o

The reason for this is the -c $< syntax which takes only the first entry of the dependencies (in this case ..\\minime_pp\\src\\minime_class.cpp ).

The solution for this might be to switch back to a definition like

$(MINIME_ODIR)/%.o: $(MINIME_SDIR)/%.cpp
    $(CXX) $(CFLAGS) -c $< -o $@

The problem now is the backslash - there is no requirement in $(OBJECTS_ALL) that will match this new definition and thus nothing happens. Using the \\ in the definition won't work. So I thought you get rid of the backslash - forward slash conversion, but then the automatic directory creation won't work on Windows. Something like -c $< but with $< picking the 'matching' one from the list without /%.cpp is basically what I'm looking for.

If someone knows an way around writing two Makefiles (Windows, Linux) thanks for a hint.

You should never use backslash separators in makefiles. Almost all Windows tools support normal UNIX slash as directory separators. For those few that don't it's easier to convert them for that specific utility inside the recipe: in other words, put the calls to $(pathfix ...) inside the recipes only where they're needed.

Note that almost without fail the commands that require backslashes will be Windows-specific (command.com) commands, which won't be portable to UNIX anyway.

I do something like this to have the same makefile for both Windows and Linux/MacOS:

ifeq ($(OS),Windows_NT)
    RM = del /Q /F
    RRM = rmdir /Q /S
    mkdirp = mkdir $(subst /,\,$1)
else
    RM = rm -f
    RRM = rm -rf
    mkdirp = mkdir -p $1
endif

$(BUILDDIR):
    $(call mkdirp, $@)

clean:
    -$(RRM) $(BUILDDIR)
    -$(RM) $(TARGET)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM