Makefile not giving the expected compilation results - c++

I am quite new to Make. I am attempting to write a Makefile to build a medium-sized Linux C/C++ application as below.
Making a simple Makefile by having all source files in one location and explicitly listing the source files works ok for me but I would like it to be more generic.
I have all my source files (C and C++) in the src folder in different subdirectories. I have header files inside an inc and inc/common folder, and then libs inside a lib folder.
The Makefile is run on the same level :
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin
CXX := /bin/arm-linux-gnueabi-g++
EXE := $(BIN_DIR)/runfile
SRC := $(shell find $(SRC_DIR) -name *.cpp -or -name *.c)
OBJ := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(addsuffix .o,$(basename $(SRC))))
CPPFLAGS := -Iinc -Iinc/common -MMD -MP
CXXFLAGS := -std=c++11 -Wall
LDFLAGS := -Llib
LDLIBS :=
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJ_DIR)/%.o: $(SRC) | $(OBJ_DIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
I get lots of errors such as below when I run it, including problems opening dependency files. I think i'm almost there, but can't see my error exactly :
compilation terminated.
/bin/arm-linux-gnueabi-g++ -Iinc -Iinc/common -MMD -MP -std=c++11 -Wall -c -o obj/main.d.o
cc -Llib obj/main.d.o -o obj/main.d
/usr/bin/ld: obj/main.d.o: relocations in generic ELF (EM: 40)
/usr/bin/ld: obj/main.d.o: relocations in generic ELF (EM: 40)
/usr/bin/ld: obj/main.d.o: error adding symbols: file in wrong format

I don't see how the output you show can be generated from the makefile you show here but anyway.
This is not right:
$(OBJ_DIR)/%.o: $(SRC) | $(OBJ_DIR)
A pattern rule is a template that tells make "if you want to build a target that matches this pattern, then you can build it from the prerequisites that match this pattern".
Here you are listing ALL your source files as a prerequisite for EVERY object file. Suppose SRC is set to foo.c bar.c biz.c baz.c, then this expands to:
obj/%.o : foo.c bar.c biz.c baz.c | obj
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
You're telling make that every single .o target depends on ALL the source files, not just the one for that object file. Further, the automatic variable $< always expands to the first prerequisite, which here will always be foo.c. So, you're compiling foo.c four times, creating each of the object files.
The very first important rule when debugging makefiles is to look carefully at the output (command lines) that make prints. If they are not right, then your makefile is not right. If you do that you'll see all the compile lines are compiling the same source, like:
g++ -c foo.c -o obj/foo.o
g++ -c foo.c -o obj/bar.o
g++ -c foo.c -o obj/biz.o
g++ -c foo.c -o obj/baz.o
That clearly cannot work and it's why you get link errors trying to link together all these object files: they all have the same content.
You need this:
$(OBJ_DIR)/%.o : $(SRC_DIR)/%.cpp
#mkdir -p $(#D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $#
which tells make how to build an object file from a single source file.
You also need to create the actual output directory that the object file will go into. Just creating $(OBJ_DIR) is not enough, if the object file appears in a subdirectory.

Related

why is GNU make not filling out the $^ variable here?

I can compile the project just fine if I run the project by hand with g++ source/* -lSDL2 -o bin/fly_fishing. When I do run make, I get
mkdir -p bin
g++ -lSDL2 -o bin/fly_fishing
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x17): undefined reference to `main'
collect2: error: ld returned 1 exit status
make: *** [Makefile:20: bin/fly_fishing] Error 1
Which tells me that it's not populating from $^ for linking. So what have I missed here? Here's the makefile for reference.
SRC_DIR := source
OBJ_DIR := objects
BIN_DIR := bin
EXE := $(BIN_DIR)/fly_fishing
SRC := $(wildcard $(SRC_DIR)/*.c)
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
CXXFLAGS := -Wall
#CFLAGS := -Wall
LDLIBS := -lSDL2a
LDFLAGS :=
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CXX) -c $(CXXFLAGS) $(CFLAGS) $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
Which tells me that it's not populating from $^ for linking.
That seems unlikely. Much more likely would be that $^ expands to nothing. Which would be the case if $(OBJ) expands to nothing. Which seems plausible because I don't see any corresponding objects being built (though perhaps you've omitted that, or they were built on a previous run). And $(OBJ) expanding to nothing implies that $(SRC) expands to nothing.
So what have I missed here?
That $(SRC) expands to nothing is not inconsistent with the data presented. I observe that the manual compilation command you present is
g++ source/* -lSDL2 -o bin/fly_fishing
That does seem to suggest that there are indeed source files in source/, but do they match the pattern source/*.c? Since you're compiling with g++, I bet not. It would be highly unconventional to name C++ source files to end with .c, and surely you would not attempt to compile C source files with a C++ compiler. I infer, then, that your source files are all named with .cpp, or maybe .cc or .C, all of which forms are conventions for C++ source names.
If all your source names follow one or another of those patterns then indeed, this ...
SRC := $(wildcard $(SRC_DIR)/*.c)
... will result in $(SRC) being empty.

Makefile: compile objects to different directory and build from there

My Makefile:
CXX = clang++
CXXFLAGS = -g -Wall -std=c++14
LDFLAGS = -lboost_system -lcrypto -lssl -lcpprest -lpthread
OBJDIR = obj
SRCDIR = .
SRC := $(shell find $(SRCDIR) -name "*.cpp")
OBJ := $(SRC:%.cpp=%.o)
APP = run
all: $(APP)
$(APP): $(OBJ)
#echo "== LINKING EXECUTABLE $(APP)"
#$(CXX) $^ $(LDFLAGS) -o $(APP)
%.o: %.cpp
#echo "COMPILING SOURCE $< INTO OBJECT $#"
#$(CXX) -c $(CXXFLAGS) $< -o $#
clean:
find . -name *.o -delete
rm -f $(APP)
Directory structure:
Makefile
sources/
directory1
...cpp
directory2
...cpp
...
main.cpp
obj/
I try to make make create *.o files in a directory obj/ and then compile the final executable from there. I tried various approaches and they fail because of the project structure that stores *.cpp files in sub-directories. Particularly, I've tried the following: https://stackoverflow.com/a/26032630/2042546
I've also tried to manipulate the command itself clang++ $< -o obj/$# but it breaks whole idea of make and it's dependency management.
If I modify OBJ via patsubstr and notdir, make becomes unable to deduce dependency of a *.o on a corresponding *.cpp by it's path, cause *.o's path loses it's directory part and becomes unable to find it's *.cpp file when executing %.o:%.cpp rule (I hope I managed to write down my thoughts correctly).
If you want objects to live in the same source directory structure but under obj, then simply change your pattern rule (and how you generate the object files). And you should create the directory first:
OBJ := $(SRC:%.cpp=$(OBJDIR)/%.o)
...
$(OBJDIR)/%.o: %.cpp
#echo "COMPILING SOURCE $< INTO OBJECT $#"
#mkdir -p '$(#D)'
#$(CXX) -c $(CXXFLAGS) $< -o $#

Makefile - .o: file not recognized: File truncated?

I'm just learning GNU make, but I'm having trouble linking when using .d (dependency) files. Can anyone point me in the right direction with this error:
...../part1.o: file not recognized: File truncated
recipe for target 'bin/target/prog' failed
It's a simple program containing: main.cpp, part1.cpp, part1.h, part2.cpp, part2.h
Where part1 and part2 have a method to print something.
This is from terminal when running make:
I don't get why I'm getting a warning for using #pragma once?
stud#GoldenImageASE:~/Desktop/ISU/L1/2$ make ARCH=target -f Makefile.th
Compiling...part2.cpp
arm-devkit-g++ -MTbuild/target/part2.o -MM -I. part2.cpp > build/target/part2.d
Compiling...part1.cpp
arm-devkit-g++ -MTbuild/target/part1.o -MM -I. part1.cpp > build/target/part1.d
Compiling...main.cpp
arm-devkit-g++ -MTbuild/target/main.o -MM -I. main.cpp > build/target/main.d
object file....main.o
arm-devkit-g++ -I. -c main.cpp part1.h part2.h > build/target/main.o
part1.h:1:9: warning: #pragma once in main file
#pragma once
^
part2.h:1:9: warning: #pragma once in main file
#pragma once
^
object file....part1.o
arm-devkit-g++ -I. -c part1.cpp > build/target/part1.o
object file....part2.o
arm-devkit-g++ -I. -c part2.cpp > build/target/part2.o
arm-devkit-g++ -I. -o build/target/main.o build/target/part1.o build/target/part2.o -o prog
build/target/part1.o: file not recognized: File truncated
collect2: error: ld returned 1 exit status
Makefile.th:27: recipe for target 'bin/target/prog' failed
make: *** [bin/target/prog] Error 1
My Makefile is found below:
# Variables
SOURCES=main.cpp part1.cpp part2.cpp
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)
EXE=prog
CXXFLAGS =-I.
# Making for host
# > make ARCH=host
ifeq (${ARCH},host)
CXX=g++
BUILD_DIR=build/host
EXE_DIR=bin/host
endif
# Making for target
# > make ARCH= target
ifeq (${ARCH},target)
CXX=arm-devkit-g++
BUILD_DIR=build/target
EXE_DIR=bin/target
endif
$(addprefix ${EXE_DIR}/,$(EXE)): $(addprefix ${BUILD_DIR}/,$(DEPS)) $(addprefix ${BUILD_DIR}/,$(OBJECTS))
# << Check the $(DEPS) new dependency
#mkdir -p $(dir $#)
$(CXX) $(CXXFLAGS) -o $(addprefix ${BUILD_DIR}/,$(OBJECTS))
$(addprefix $(BUILD_DIR)/, %.o): %.cpp
#echo "object file...."$*.o
$(CXX) $(CXXFLAGS) -c $^ > $#
# Rule that describes how a .d ( dependency ) file is created from a .cpp
# Similar to the assigment %. cpp -> %.o
${BUILD_DIR}/%.d: %.cpp
#mkdir -p $(dir $#)
#echo "Compiling..."$<
$(CXX) -MT$(#:.d=.o) -MM $(CXXFLAGS) $^ > $#
debug:
#echo "DEPS: "$(DEPS)"\n"
#echo "OBJ: " $(addprefix ${BUILD_DIR}/,$(OBJECTS))"\n"
#echo "EXE: " $(addprefix ${EXE_DIR}/,$(EXE))"\n"
.PHONY:clean
clean:
rm -f $(EXE) $(addprefix ${BUILD_DIR}/,$(DEPS)) $(addprefix ${BUILD_DIR}/,$(OBJECTS))
ifneq ($(MAKECMDGOALS),clean)
-include $(addprefix ${BUILD_DIR}/,$(DEPS))
endif
You have two unrelated problems. The first one is that you have two conflicting -o options when linking.
The actual problem you ask about is something different, but still related to the -o option: Namely that you don't have one when attempting to create the object files.
When creating the object files, the generated object file is not written to standard output, therefore your redirection will not cause the gcc frontend program to create an object file with the name you think.
For example:
arm-devkit-g++ -I. -c part1.cpp > build/target/part1.o
The above command will create an object file named part1.o in the current directory, and write the (empty) standard output to the file build/target/part1.o. That will leave build/target/part1.o empty, which is what the linker is complaining about (that's what it means when it says the file is truncated).
The command should instead look like
arm-devkit-g++ -I. -c part1.cpp -o build/target/part1.o
Note the use of the -o option to name the output file.
You need to modify the makefile to not use redirection when building the object files.
Also, you should not list the header files when building object files, only the source file you want to build, so the command
arm-devkit-g++ -I. -c main.cpp part1.h part2.h > build/target/main.o
should really be
arm-devkit-g++ -I. -c main.cpp -o build/target/main.o
You're not compiling your object files correctly. g++ doesn't output the files to stdout, it writes them to the local directly. If you want to put the object files in a specific directory, you need to use the -o option:
$(BUILD_DIR)/%.o: %.cpp | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -c $^ -o $#
^^^^^^
$(BUILD_DIR):
#mkdir -p $#
Also you're building your executable incorrectly. The dependencies are insufficient and you don't have your target listed. You'll want this:
$(EXE_DIR)/$(EXE) : $(addprefix $(BUILD_DIR),$(OBJECTS)) | $(EXE_DIR)
$(CXX) $(CXXFLAGS) -o $# $^
$(EXE_DIR):
#mkdir -p $#
This will create an order-only dependency from your binary on the directory, and correctly build the binary. Note that you shouldn't have a dependency from your target on your .d files. That makes little sense. Instead, your rule for building the .os should simply also build the .ds (that rule suffers the same problem currently as your .o rule):
# build the .o and the .d in one go
$(BUILD_DIR)/%.o : %.cpp | $(BUILD_DIR)
$(CXX) $(CXXFLAGS) -o $# -c $< -MP -MMD -MF $(#:.o=.d)
AS AN EDITORIAL ASIDE, THERE IS THIS COMMON TENDENCY TO WRITE ALL VARIABLES IN MAKEFILES IN SHOUTY_CAPS. THIS IS PROVABLY MUCH MORE DIFFICULT TO READ THAN USING JUST USING snake_case. Lower case letters work just fine.

Makefile does not find object file

I want my object files to be created in a subdirectory and not where Makefile lives. So, I saw this answer, which I couldn't apply to my case, so I tried this:
OBJS = main.o IO.o alloc.o communication.o
OBJSDIR = obj
SOURCE = main.cpp src/IO.cpp src/alloc.cpp src/communication.cpp
HEADER = headers/IO.h headers/alloc.h headers/communication.h
OUT = test
CXX = ../../mpich-install/bin/mpic++
CXXFLAGS = -I../../intel/mkl/include -Wl,--start-group -Wl,--end-group -lpthread -lm -ldl -Wall
LDFLAGS = ../../intel/mkl/lib/intel64/libmkl_scalapack_lp64.a -Wl,--start-group ../../intel/mkl/lib/intel64/libmkl_intel_lp64.a ../../intel/mkl/lib/intel64/libmkl_core.a ../../intel/mkl/lib/intel64/libmkl_sequential.a -Wl,--end-group ../../intel/mkl/lib/intel64/libmkl_blacs_intelmpi_lp64.a -lpthread -lm -ldl
all: $(OBJSDIR) $(OUT)
$(OBJSDIR):
mkdir $(OBJSDIR)
$(OUT): $(OBJS)
$(CXX) $(OBJS) -o $(OUT) $(CXXFLAGS) $(LDFLAGS)
# make -f Makefile clean
# create/compile the individual files >>separately<<
$(OBJSDIR)/main.o: main.cpp
$(CXX) -c main.cpp $(CXXFLAGS)
$(OBJSDIR)/IO.o: src/IO.cpp
$(CXX) -c src/IO.cpp $(CXXFLAGS)
$(OBJSDIR)/alloc.o: src/alloc.cpp
$(CXX) -c src/alloc.cpp $(CXXFLAGS)
$(OBJSDIR)/communication.o: src/communication.cpp
$(CXX) -c src/communication.cpp $(CXXFLAGS)
.PHONY: clean
clean:
rm -rf *.o
and I am getting:
gsamaras#pythagoras:~/konstantis/cholesky$ make
../../mpich-install/bin/mpic++ -I../../intel/mkl/include -Wl,--start-group -Wl,--end-group -lpthread -lm -ldl -Wall -c -o main.o main.cpp
make: *** No rule to make target 'IO.o', needed by 'test'. Stop.
I have a src folder, where all the .cpp files live (except from main.cpp, that lives in the same directory as the Makefile) and a headers directory, where all the header files live.
EDIT
I modified the first two lines, as such:
OBJSDIR = obj
OBJS = $(OBJSDIR)/main.o $(OBJSDIR)/IO.o $(OBJSDIR)/alloc.o $(OBJSDIR)/communication.o
and I am getting:
g++: error: obj/main.o: No such file or directory
...
The problem lies into the fact that the object files are still generated in the main directory!
You want to change the lines that invoke your compiler from:
$(OBJSDIR)/IO.o: src/IO.cpp
$(CXX) -c src/IO.cpp $(CXXFLAGS)
to:
$(OBJSDIR)/IO.o: src/IO.cpp
$(CXX) -c src/IO.cpp $(CXXFLAGS) -o $#
Note that $# is the automatic variable that corresponds to the target file being created. So in the above case it will be obj/IO.o. -o specifies the output filename.
Furthermore while it is unrelated to your question one of the nice things about placing all the build artifacts into a separate directory is cleaning is much easier:
clean:
rm -rf $(OBJSDIR) $(OUT)
Also, as a final note if you ever wish to do a parallel build you will have an issue as your object files rely on the build directory. There are a couple solutions to this including calling mkdir -p objs before every compiler invocation or setting up the directory as a dependency that is only built if it does not exist.

cygwin g++ Linker doesn't find shared library

I have been creating a library. When I compile it as a static library, it works fine. Now I want to turn it into a shared library. The library is created and in the proper place, but when I try to compile the client code, the linking phase says that it can't find the library.
I already tried to rename it to al or dylib but that doesn't help either. When I put the -v flag on the linking, I can see that my library path is there. I also tried different paths. I used a relative path, but even with a full path it doesn't find it.
The Makefile from the library:
.SUFFIXES:
.SUFFIXES: .o .cpp
.SUFFIXES: .o .d
CC := g++
LNK:= g++
CXXFLAGS_RELEASE = -fPIC -shared -O2 -Wall -fmessage-length=0
CXXFLAGS_DEBUG = -fPIC -shared -g -Wall -fmessage-length=0 -D _DEBUG
CXXFLAGS = $(CXXFLAGS_DEBUG)
OBJDIR:= obj
SRCDIR:= src
HDIR:= include
INCLUDE_PATHS:= -Iinclude -Iinclude/interfaces -Iinclude/support
CPP_FILES := propertyfile/propertyfile.cpp \
propertyfile/propertyitem.cpp \
propertyfile/propertyfactory.cpp \
helper/string_helper.cpp
OBJ := $(patsubst %.cpp,$(OBJDIR)/%.o, $(CPP_FILES))
SRC := $(patsubst %.cpp,$(SRCDIR)/%.o, $(CPP_FILES))
LIBS:=
TARGET:= libsupport.so
all: $(TARGET)
$(TARGET): $(OBJ)
$(LNK) -o $(TARGET) $(OBJ) -shared
#cp $(TARGET) ../lib
#cp -r include ..
clean:
rm -f $(OBJ) $(ASM) $(TARGET)
-include $(patsubst %.cpp,$(OBJDIR)/%.d, $(CPP_FILES))
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(OBJDIR)/%.d
#mkdir -p `dirname $#`
$(CC) $(CXXFLAGS) -c $< -o $# $(INCLUDE_PATHS)
$(OBJDIR)/%.d: $(SRCDIR)/%.cpp
#mkdir -p `dirname $#`
$(CC) $(CXXFLAGS) -MM -MT $# -MF $(OBJDIR)/$*.d -c $< $(INCLUDE_PATHS)
And here is the Makefile for the application:
.SUFFIXES:
.SUFFIXES: .o .cpp
CC := g++
LD := g++
CXXFLAGS_RELEASE = -O2 -Wall -fmessage-length=0
CXXFLAGS_DEBUG = -g -Wall -fmessage-length=0 -D _DEBUG
CXXFLAGS = $(CXXFLAGS_DEBUG)
OBJDIR:= obj
SRCDIR:= src
INCLUDE_PATHS:= -Iinclude -I../include
LIBS:= -L /cygdrive/d/src/c/lib -lsupport
CPP_FILES := nohupshd.cpp \
daemon.cpp \
task.cpp
OBJ := $(patsubst %.cpp,$(OBJDIR)/%.o, $(CPP_FILES))
SRC := $(patsubst %.cpp,$(SRCDIR)/%.o, $(CPP_FILES))
TARGET:= nohupshd
all: $(TARGET)
$(TARGET): $(OBJ)
$(LD) -o $(TARGET) $(OBJ) $(LIBS)
clean:
rm -f $(OBJ) $(ASM) $(TARGET)
-include $(patsubst %.cpp,$(OBJDIR)/%.d, $(CPP_FILES))
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(OBJDIR)/%.d
#mkdir -p `dirname $#`
$(CC) $(CXXFLAGS) -c $< -o $# $(INCLUDE_PATHS)
$(OBJDIR)/%.d: $(SRCDIR)/%.cpp
#mkdir -p `dirname $#`
$(CC) $(CXXFLAGS) -MM -MT $# -MF $(OBJDIR)/$*.d -c $< $(INCLUDE_PATHS)
After some experimenting I found a solution on how to compile a shared library under cygwin.
Apparently the compiler is looking for a DLL file even though it is inside cygwin. so the first step is to add your path, where the library is going to be to the PATH variable.
export PATH=$PATH:/cygdrive/d/src/c/lib
Apparently when linking against a shared library, the linker seems to look for a DLL file by default. I don't know why, because inside cygwin I would expect it to look for a .so file just like on other UNIX systems.
However, there are two solutions to this, which both work.
First, you can create a link to your .so library with the name .dll
ln -s /cygdrive/d/src/lib/libsupport.so libsupport.dll
In this case the makefile doesn't have to be changed and -lsupport will find the library while linking. I prefer this solution.
Second, you can specify the linker option with the full name.
LIBS:= -L /cygdrive/d/src/c/lib -l:libsupport.so
then you don't have to create a link.
So the crucial thing seems to be that the shared library must be in the PATH under cygwin. Using LD_LIBRARY_PATH doesn't help in that case as you can link the executable, but when trying to run it, it will not find it.
ldd nohupshd.exe
libsupport.so => not found
UPDATE: For some reason when I checked with ldd, my library was suddenly gone from the list. I found out that cygwin uses the name to differentiate between MS Windows and Unix shared libraries. So in order to make it work, the name of the library must be cyg.so to make it work, otherwise the exectuable seems to be some Windows build. In this case you don't need to create the link named x.dll as the shared library stays inside the Unix environment.
$(LNK) -o cyg$(TARGET).so $(OBJ) -shared
When using eclipse for debugging, the path to the shared library must also be in the windows path environment variable. Otherwise the debug session immediately terminates without an error.