2

I have a makefile (gnu make) working under linux, but when porting it to windows it does not work. The goal of the makefile is to make all the *.cpp file that reside in different subdirectories and compile them into *.obj files in a single BUILD_DIR.

Between linux and windows I only adjusted the SOURCES variable, the linux line is shown but commented. When I check all the names and directories they show the same (relative paths in their perspective notation) and what I expect.

The "error message" I get on windows is: make: *** No rule to make target 'DEM.cpp', needed by 'DEM.obj'. Stop.

In debug mode it says: File 'DEM.cpp' does not exist. (which it obviously does)

On linux it finds the file via the VPATH, in debug mode it says: No need to remake target 'DEM.cpp'; using VPATH name './Code/DEM.cpp'.

The subdirectory structure in both linux and windows is the same and the makefile is run from the same location.

Question: What is going wrong here, I have the feeling it has something to do with VPATH being handled differently on windows but I am not certain.

My makefile is:

# MAKEFILE    
# Some makefile settings 
SHELL = /bin/sh     #Make sure the shell is the standard shell
.SUFFIXES:      #Clear al implicit suffixes
.SUFFIXES: .cpp .obj    #Set used suffixes

# Variable Declaration
CXX := g++
BUILD_DIR = .\bin\Release
PROGRAM_NAME := DEM.exe

#SOURCES := $(shell find . -name '*.cpp')                   #All *.cpp files with directory (LINUX style)
SOURCES := $(shell FORFILES /S /M *.cpp /C "CMD /C ECHO @relpath")  #All *.cpp files with directory
NAMES := $(notdir $(basename $(SOURCES)))                   #Get all files names of the *.cpp files without extensions
OBJECTS := $(addsuffix .obj, $(NAMES))                      #Get the to be generate *.o files without directory
SRC_DIRS := $(dir $(SOURCES))                               #All the directory in which the sources are found
VPATH := $(SRC_DIRS) $(BUILD_DIR)

.PHONY: all
all: build                                  #Standard entry point, run release
    @echo "BUILD DONE"

.PHONY: build
    build: $(PROGRAM_NAME)
    @echo "BUILD DONE"

$(PROGRAM_NAME): $(OBJECTS)
    @echo "Link executable"
    $(CXX) -o $(BUILD_DIR)/$(PROGRAM_NAME) $(addprefix $(BUILD_DIR)/,$(OBJECTS))

$(OBJECTS): %.obj: %.cpp
    @echo "Compile into object code: $<"
    ${CXX} $(CXXFLAGS)  -c $< -o $(BUILD_DIR)/$@    #Compiles object code and places it in the $(BUILD_DIR)

Update 1: Based on the comment of some programmer dude, I ran it with -p, and got the following interesting result:

# Not a target:
DEM.cpp:
#  Implicit rule search has been done.
#  File does not exist.
#  File has not been updated.

# Not a target:
World.cpp:
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.

# Lot more targets below

It seems only to not find DEM.cpp but it finds everything else. DEM.cpp resides in C:\Users\dklomp\Documents\Programming\C++ Source\DEM\DEM\Code, but should resolve to .\Code\ Most other files reside in subdirectories in Code. But stdafx.cpp also resides in .\Code\ and there is no problem finding it.

Can it be a name clash with the directory name DEM being similar to DEM.cpp

Update 2: For information and closure. I had already check the VPATH by reading the variable by printing it this seemed to give the correct information. However if i read it with @(info $(VPATH)) it seemet empty:

@echo "VPATH print variable: "
@echo "$(VPATH)"
@echo "VPATH print info: "
$(info VPATH)

gave:

"VPATH print variable: "
"./Code/InputOutput/ ./Code/InputOutput/ ./Code/InputOutput/ 
./Code/InputOutput/ ./Code/ ./Code/ ./Code/Models/ ./Code/Models/ 
./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Forces/ 
./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Forces/ ./Code/Tools/ 
./Code/Tools/ ./Code/Tools/ ./Code/Tools/ ./Code/Solvers/ ./Code/Solvers/ 
./Code/Solvers/ ./Code/World/ ./Code/World/ ./Code/World/ ./Code/World/ 
./Code/World/ ./Code/Interactions/ ./Code/Interactions/ ./Code/Interactions/ 
./Code/Interactions/ ./Code/Interactions/ ./Code/Interactions/                                                               
bin\Release"
"VPATH print info: "

Indeed with a lot of repetitions (will use the sort suggestion of hardcoreHenry) but it seemed oke. However the VPATH info is empty for some reason.

However implementing the proposed solution by code_fodder and removing all the inline code comments works. The inline code comments on the variable declaration do not seem to matter, those in the rules section do for windows, linux seems to handle inline code comments fine everywhere.

As usually thanks for the help and suggestions.

D.J. Klomp
  • 2,429
  • 1
  • 15
  • 30
  • 1
    You could use the `-d` option when invoking `make` to see debugging information about what `make` does, files it's processing and more information. You could also use the `-p` option to print the "database" of rules and variables. See e..g [this options summary](https://www.gnu.org/software/make/manual/make.html#Options-Summary) for a list of options to the GNU Make program. – Some programmer dude Aug 13 '19 at 09:27
  • Can you also add into your makefile some further debug (maybe add them at the end: `$(info OBJECTS: $(OBJECTS))` and `$(info SOURCES: $(SOURCES))` - it would be interesting to see what they come out as. We would be looking for things like spaces and / or other suspicious characters... – code_fodder Aug 13 '19 at 20:28
  • `\C++ Source\ `has a space in it, which could be your issue, as `VPATH` typically expects a space delimited list of directories. Try an `$(info VPATH=$(VPATH) SOURCES=$(SOURCES))` and see what that gives you. As a side note, you might want to do `SRC_DIRS := $(sort $(dir $(SOURCES)))`, to avoid duplicate paths – HardcoreHenry Aug 13 '19 at 20:32
  • @HardcoreHenry yeah, but the OP suggests that this should resolve to `.\Code\DEM.cpp` - so I want to see the actual results to confirm this... I did a quick windows test and it seems to work (relative to the path that you call it). – code_fodder Aug 13 '19 at 20:37
  • That's why I was asking for the `$(info VPATH)` (you must have posted as I was typing, so it seems I parroted you). OP does suggest that, but adding a space in the directory name _would_ explain why the Makefile fails in windows not Linux. I'm not a Windows programmer, so I don't know exactly what his windows command does, so I thought I'd check. – HardcoreHenry Aug 13 '19 at 20:47
  • @HardcoreHenry - yep, thinking the same thing : ) – code_fodder Aug 13 '19 at 21:00
  • Another one... the windows/linux differences bother me a little bit, I am not sure if the output of FORFILES is exactly what you want (it seems to add quotes around each entry). You can create your own recursive wildcard function: `rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))` and use it like: `SOURCES := $(call rwildcard, ./, *.cpp)` then it will work in both linux and windows. (I got the function from https://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make) but I have used similar because its platform independent... – code_fodder Aug 13 '19 at 21:02
  • I made this change and then your makefile built a similar project for me (some source files in various sub-folders). But I had to create the output dir first `./bin/Release` did not exist - you could also add that as a rule - I'll post the whole thing below.... – code_fodder Aug 13 '19 at 21:06
  • For those things you need different for windows you can use variables specified on the command line (like the need to add `-WL-subsystem,windows`) and use an `ifeq` within your `Makefile`, e.g. `ifeq ($(ss),-DWIN) ..... endif` to set differences between how it behaves on Linux and windows. – David C. Rankin Aug 14 '19 at 00:08

1 Answers1

0

I tested this in a path that had spaces before the current location (e.g. like d:\path with spaces\myfolder\sub_folder1\sub_folder2\).

I think there is some issue with the FORFILES syntax/output - at least I could not get it to work. As soon as I used the more generic rwildcard from second answer from here it started to work quite well. Then I added rules to create/clean your output folder.

Also I removed any inline comments, since I am not 100% sure they are valid because they leave space characters everywhere and makefile is not very tolerant of that... I.E. make-sure you don't leave any trailing white space on any lines.

Also - this all assumes its gnu-make and not nmake

# MAKEFILE    
# Some makefile settings 
SHELL = /bin/sh
.SUFFIXES:
.SUFFIXES: .cpp .obj

# Variable Declaration
CXX := g++
BUILD_DIR = .\bin\Release
PROGRAM_NAME := DEM.exe

# Define a recursive wild card function that is portable.
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))

#SOURCES := $(shell FORFILES /S /M *.cpp /C "CMD /C ECHO @relpath")
SOURCES := $(call rwildcard, ./, *.cpp)
$(info SOURCES: $(SOURCES))
NAMES := $(notdir $(basename $(SOURCES)))
OBJECTS := $(addsuffix .obj, $(NAMES))
SRC_DIRS := $(dir $(SOURCES))
VPATH := $(SRC_DIRS) $(BUILD_DIR)

.PHONY: all
all: create_dirs build
    @echo "BUILD DONE"

# Has an order dependency on create_dirs to ensure that happens first, and so 
# it works with parallel build (e.g. `make -j4`)
.PHONY: build
build: $(PROGRAM_NAME) | create_dirs
    @echo "BUILD DONE"

$(PROGRAM_NAME): $(OBJECTS)
    @echo "Link executable"
    $(CXX) -o $(BUILD_DIR)/$(PROGRAM_NAME) $(addprefix $(BUILD_DIR)/,$(OBJECTS))

$(OBJECTS): %.obj: %.cpp
    @echo "Compile into object code: $<"
    ${CXX} $(CXXFLAGS)  -c $< -o $(BUILD_DIR)/$@

.PHONY: create_dirs
create_dirs:
    @echo "creating dirs"
    mkdir -p $(BUILD_DIR)

.PHONY: clean
clean:
    @echo "cleaning"
    rm -rf $(BUILD_DIR)
code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • Indeed under windows the inline code comments in the rule section yield an error. – D.J. Klomp Aug 15 '19 at 08:32
  • @D.J.Klomp I think this is the same for all gnu-like make files (not sure about nmake). I wish makefile had better error messages though, they can be quite cryptic.... – code_fodder Aug 15 '19 at 08:44