Makefile and linking .a libraries - c++

I've got a src folder which, after running make, creates 4 libraries in .a format.
There's also a folder tests which contains tests.cpp. This file depends on the libraries previously mentioned.
I'm trying to get a Makefile inside tests that generates the libraries inside src (this it does) and then uses these to compile tests.cpp (this it doesn't - I get many many undefined references, as if it weren't linking properly).
The folder tests contains this Makefile:
include ../src/makefile.inc
DIR = ../src
OBJLIBS = ../src/CapaFisica.a ../src/CapaLogica.a ../src/CapaInterfaz.a ../src/Utilitarios.a
TARGETS = tests.cpp
EXE = tests
all : $(EXE)
%.a :
-for d in $(DIR); do (cd $$d; $(MAKE)); done
tests: $(OBJLIBS)
$(CC) $(CFLAGS) $(OBJLIBS) -o $# $(TARGETS)
clean :
$(ECHO) Borrando archivos.
-$(RM) -f ./tests
cd $(DIR); make clean
find . -name "*.o" -type f -print | xargs /bin/rm -f
What am I doing wrong?

gcc linker is sensitive about the order of .o files and static libraries specified on the command line.
Replacing
$(CC) $(CFLAGS) $(OBJLIBS) -o $# $(TARGETS)
with
$(CC) $(CFLAGS) -o $# $(TARGETS) $(OBJLIBS)
might help. Also make sure that .a files in $(OBJLIBS) are in the correct order if they depend on each other. Depending library must be on the command line before the library which defines the symbols.
For more details see this question: Why does the order in which libraries are linked sometimes cause errors in GCC?

Related

Linking libssh statically for MinGW

I am trying to statically link libssh to my project which is built using a mingw/mysys makefile, however no matter what I try I get a whole bunch of undefined reference errors. I've spent hours researching this but I still can't fix it. Below is my makefile and some example output. I also compiled the libssh.a file using cmake with the WITH_STATIC_LIB option set to 1. I don't understand how I can continue to get these errors even though my linker can find the libssh.a file. Did I build it incorrectly?
Example output:
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: build/BMU.o: in function `ZN3ssh7SessionC1Ev':
./include/libssh/libsshpp.hpp:130: undefined reference to `_imp__ssh_new'
.
.
.
Makefile
CC := g++
TARGET := "dist/target"
BUILDDIR := build
SRCDIR := src
CFLAGS := -std=c++17 -g -mconsole
SRCEXT := cpp
SOURCES := $(wildcard $(SRCDIR)/*.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%, $(BUILDDIR)/%, $(SOURCES:.$(SRCEXT)=.o))
INCLUDE := -I./include
LIB := -L./lib -lws2_32 -lssh -lmodbus -static
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
#printf "\e[33m\e[1mBuilding...\e[0m\n";
#mkdir -p $(BUILDDIR)
#echo " $(notdir $#) from $(notdir $<)"
#$(CC) $(CFLAGS) $(INCLUDE) -c -o $# $<
$(TARGET): $(OBJECTS)
#printf "\e[35m\e[1mLinking...\e[0m\n";
#mkdir -p dist
#echo " $(notdir $(OBJECTS))"
#$(CC) $(CFLAGS) -o $# $^ $(LIB)
Building Libssh:
First I did
git clone https://git.libssh.org/projects/libssh.git libssh
then using cmake-gui built it for MySys makefile with 'WITH_STATIC_LIB" set to 'ON' and 'WITH_SHARED_LIB' to 'OFF'
Then I ran make from my terminal and it created a libssh.a file which I moved to the ./lib folder in my project.
The make file was able to find the libssh.a file but I still got these errors.
Any help is appreciated and yes I looked a tons of other stackoverflow posts before posting myself.
Other things I tried
#define LIBSSH_STATIC 1 and without it
using vcpkg to install libssh, but this generates a .lib file which has the same problems as the .a file
Edit:
Including LIBSSH_STATIC causes the output to look like:
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: ./lib\libssh.a(channels.c.obj):channels.c:(.text+0x1979): undefined reference to `_imp__ssh_buffer_add_data'
The correct CMake parameter for building static libraries (including for libssh) is -DBUILD_SHARED_LIBS:BOOL=OFF
As long as you're seeing _imp__ in the undefined reference to errors your linker is looking for the symbols exported from a shared library (DLL).
To make sure the project that uses libssh looks for the static library you must define LIBSSH_STATIC, for example by adding compiler flag -DLIBSSH_STATIC to CFLAGS in your Makefile.

Makefile in sub-directory

I am trying to write a makefile in a subdirectory of my eclipse makefile project.
MyProject \
test.cpp
Build\
Makefile
I am also trying to create a generic makefile. I am having trouble defining the targets since the build is not happening in the same directory as the source.
CC = g++
CC_FLAGS = -g3
EXEC = test
SOURCEDIR = ..
SOURCES = $(shell find $(SOURCEDIR) -name '*.c' -o -name '*.cpp')
OBJECTS = $(addsuffix .o,$(subst ../,,$(SOURCES)))
.DEFAULT_GOAL = all
$(EXEC): $(OBJECTS)
$(CC) $(OBJECTS) -o $(EXEC)
%.o: %.cpp
$(CC) -c $(CC_FLAGS) $< -o $#
.PHONY: clean
clean:
rm -f $(EXEC) $(OBJECTS)
.PHONY: all
all: $(EXEC)
Right now when I build I get the error...
make all
make: *** No rule to make target 'test.cpp.o', needed by 'test'. Stop.
Can anyone tell me why this is not working or recommend a better approach.
If you change your dependency from object to source as follows:
%.cpp.o: $(SOURCEDIR)/%.cpp
it seems to work.
In general I would prefer to NOT do anything in a build dir, because I personally expect a build dir is a temporary dir which can be removed completely for distribution which is not the case if the Makefile resists there. But this is a matter of taste.
Also I do not prefer to use all c/cpp you find as objects/sources in the make process. If you need to add files for different variants in your projects, you enter a nightmare to change all these things later.
And also as a hint: Typically objects are <basename>.o and not <basename>.cpp.o
And another one:
Users expect that the clean target also remove the executable. If not, you will never see a rebuild by simply do make clean; make, because the executable is in place and all dependencies are fulfilled.

How to "make" an SDL project on linux?

Makefiles are quite confusing to me. I am attempting to "make" my project that I have worked on in Windows. The confusing part is actually constructing the make file from scratch. I would am trying to also link to the SDL2 library, and that is in a '.a' format.
Here is my code for the make file so far, I have tried multiple versions, and this is the latest:
CXX = gcc
OUT = Engine
SRC =Software-Rendering/src/
SDL_INCLUDE_DIR =Software-Rendering/lib/SDL2/include/SDL/
LIB_DIR =Software-Rendering/lib/SDL2/x86/linuxLib/
SDL = -l${LIB_DIR}libSDL -l${LIB_DIR}/libSDL2main
CPP_FILES =Bitmap.cpp Main.cpp Vector3.cpp Window.cpp
H_FILES =Bitmap.h ErrorReport.h Vector3.h Window.h
O_FILES = Bitmap.o ErrorReport.o Main.o Vector3.o Window.o
all: $(OUT)
$(OUT): $(O_FILES)
$(CXX) -o $# $^ ${SDL}
#Making all of the object files down
$(O_FILES): $(H_FILES)
$(CXX) -c $(CPP_FILES)
#Make sure we can easily clean up the directory
clean:
rm -f Engine ${O_FILES}
clean_obj:
rm -f ${O_FILES}
I decided to put the ".a" files in a special directoy in my project so whenever someone clones my repository on github all of the files for compiling and linking are already there.
Why isn't this working and how can I make it work?
Your library linking directive are wrong -- -l prefixes lib to the name you specify, and then searches through the libdir path set by the -L options. So what you want is something like:
SDL = -L$(LIB_DIR) -lSDL -lSDL2main
You can make it clearer/more standard by using the standard varnames for libraries:
LDFLAGS = -L$(LIB_DIR)
LDLIBS = -lSDL -lSDL2main
$(OUT): $(O_FILES)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
Also, get rid of the explicit command to compile source files -- the default built in rule is fine and easier to use.

Unable to find headers with make

I have a (fairly simple) makefile adapted from here that I am attempting to use to build a project on Ubuntu. The project tree is fairly simple: Makefile is in the root project directory, and there are src/, include/, build/, and bin/, where source code, header files, object files, and executables are stored, respectively.
When I run make from the root directory of the project, I get the following error message:
Linking...
g++ src/Main.cpp src/Foo.cpp -o bin/runner
src/Main.cpp:1:19: fatal error: Foo.hpp: No such file or directory
#include "Foo.hpp"
^
compilation terminated.
src/Foo.cpp:1:19: fatal error: Foo.hpp: No such file or directory
#include "Foo.hpp"
^
compilation terminated.
make: *** [bin/runner] Error 1
All that's currently in the project is Main.cpp. which calls two test functions Foo() and Bar() from Foo.cpp, which references a header file Foo.hpp. Here is the makefile:
CC := g++ # This is the main compiler
SRCDIR := src # Directory for source code
BUILDDIR := build # Directory containing all object files, which are removed on "make clean"
TARGET := bin/runner # bin/runner contains the main executable for project
# bin/ contains all other executables in the project (such as tests)
SRCEXT := cpp # File extension of source code
# Look for all the source files in SRCDIR with the file extension specified above
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
# Name all object files the same root name as the source files from which they came, but add a .o extension to the end
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
# The -g flag specifies that debugging information should be produced in the native format of the OS
CFLAGS := -g -Wall
# Various flags for libraries that might need to be linked
INC := -I include # Ensures that all header files (in the include/ folder) are accessible for build
# Show the components that are currently being compiled/linked
# Also, this is the main procedure for make: The TARGET is built from the objects, and
# object files are built from source
$(TARGET): $(OBJECTS)
#echo " Linking..."
#echo " $(CC) $^ -o $(TARGET)"; $(CC) $^ -o $(TARGET)
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
#mkdir -p $(BUILDDIR)
#echo " $(CC) $(CFLAGS) $(INC) -c -o $# $<"; $(CC) $(CFLAGS) $(INC) -c -o $# $<
# Directives for "make clean" which cleans all object files out of the build/ folder
clean:
#echo " Cleaning...";
#echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET)
# Destroys everything in the build/ and bin/runner/ folders. Does not clean test executables.
.PHONY: clean
What am I missing here in order to get the header files to be properly linked?
EDIT: Here is the new makefile, and the current output:
# This is the main compiler
CC := g++
# Directory for source code
SRCDIR := src
# Directory containing all object files, which are removed on "make clean"
BUILDDIR := build
# bin/runner contains the main executable for project
# bin/ contains all other executables in the project (such as tests)
TARGET := bin/runner
# File extension of source code
SRCEXT := cpp
# Ensures that all header files (in the include/ folder) are accessible for build
INC := -I/include
# Look for all the source files in SRCDIR with the file extension specified above
# SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
SOURCES := $(wildcard $(SRCDIR)/*.$(SRCEXT))
# Name all object files the same root name as the source files from which they came, but add a .o extension to the end
# OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
OBJECTS := $(addprefix $(TARGET)/, $(notdir $(SOURCES:.cpp=.o)))
# The -g flag specifies that debugging information should be produced in the native format of the OS
CFLAGS := -g -Wall
# Various flags for libraries that might need to be linked
LIB := #-pthread -lmongoclient -L lib -lboost_thread-mt -lboost_filesystem-mt -lboost_system-mt
# Show the components that are currently being compiled/linked
# Also, this is the main procedure for make: The TARGET is built from the objects, and
# object files are built from source
$(TARGET): $(OBJECTS)
#echo " Linking..."
$(CC) $^ -o $(TARGET)
# #echo " $(CC) $^ -o $(TARGET) $(LIB)"; $(CC) $^ -o $(TARGET) $(LIB)
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
#mkdir -p $(BUILDDIR)
$(CC) $(CFLAGS) $(INC) -c -o $# $<
# Directives for "make clean" which cleans all object files out of the build/ folder
clean:
#echo " Cleaning...";
#echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET)
# Tests
# tester:
# $(CC) $(CFLAGS) test/tester.cpp $(INC) $(LIB) -o bin/tester
# Spikes
# ticket:
# $(CC) $(CFLAGS) spikes/ticket.cpp $(INC) $(LIB) -o bin/ticket
# Destroys everything in the build/ and bin/runner/ folders. Does not clean test executables.
.PHONY: clean
Output:
[scott]> make
make: *** No rule to make target `bin/runner/Foo.o', needed by `bin/runner'. Stop.
tl;dr
Don't put end-of-line comments on variable assignments in make. It doesn't work the way you might expect.
Explanation
Your makefile isn't running the steps you expect it is.
You shouldn't be seeing Linking... for the compilation step.
make shouldn't be attempting to create the target from the source .cpp files.
You should be seeing your INC and CFLAGS values on the compilation line (but you are getting linking output so obviously aren't seeing them).
That's why your header can't be found by the way, your linking line doesn't have -I on it anywhere.
The reason that's happening is because make is applying the wrong rule.
make is applying the wrong rule because your variables are being set incorrectly.
Your variables are being set incorrectly because your variables have values you don't expect.
The makefile you started from had errors the author wasn't aware of.
make is not always very smart.
When you write
FOO := some value # comment
you expect FOO to have the value some value but make sees things differently.
make gives it the value some value since it can't tell the difference between the space between some and value and the space after value and before the comment.
So when you run your shell command (with *.$(SRCEXT) unquoted) the shell just ignores the trailing spaces). (Try quoting *.'$(SRCEXT)' and see what you get.)
However when you then try to $(SOURCES:=.$(SRCEXT)=.o) make doesn't drop the spaces and you have actually written $(src/Main.cpp src/Foo.cpp:=cpp =.o) which, you may notice, is a pattern that doesn't actually match.
As a result $(OBJECTS) gets the unmodified value of $(SOURCES) and "confuses" the $(TARGET): $(OBJECTS) line later causing make to skip your compilation target.
(Oh, also, that's why your linking line has a million spaces between g++ and the first source file.)
Oh, also, you don't need to shell out for find there unless your src/ directory has sub-directories of its own (and even then not with some make magic) because $(wildcard $(SRCDIR)/*.$(SRCEXT)) will work just fine (and also would have failed earlier I believe given this problem).
Define an environment variable that has . (current working directory) first, then ./include (not just include subdirectory but as ,/include and rest of the INCLUDE dirs that you might already have because of the compiler or other software requirement)
set INCLUDE :=.:./include:$INCLUDE
Alternately, use:
INC := -I. -I./include
gcc -I option is as: -I dir
Adds the directory dir to the list of directories to be searched for header files. Directories named by '-I' are searched before the standard system include directories. If the directory dir is a standard system include directory, the option is ignored to ensure that the default search order for system directories and the special treatment of system headers are not defeated

Makefile for compiling different "modules" which can include each other

We have a project with a module structure like this:
- Project
- mod1
- mod1.cpp
- mod1.h
- main.cpp
- mod2
- mod2.cpp
- mod2.h
- main.cpp
- more modules
The main.cpp file in each module instantiates and tests that module. One module can include and use another module. So for example, module 1 can include module 2 and eventually other modules.
We want to create a makefile which compiles and includes the correct modules and main.cpp file. So if I write "make module2" the makefile would compile mod2.cpp, main.cpp (in module 2) and include mod2.h. If I write "make module1" the makefile would compile mod2.cpp, mod1.cpp main.cpp (in module 1) and include mod1.h and mod2.h.
My experience with makefiles is modest, and I've used several days on this without success.
Making it generic would be preferable so that adding a new module would not require major changes to the makefile.
The closest solution I got is this:
.PHONY: clean
FLAGS=-std=c++11 -g -I"$(SYSTEMC_PATH)/include" -I"$(SYSTEMC_PATH)" -L"$(SYSTEMC_PATH)/lib-linux64" -lsystemc $(addprefix -I, $(wildcard src/*))
SRCS_WITHOUT_MAIN= $(filter-out %main.cpp, $(shell find src -name '*.cpp'))
TARGET_OBJS=$(subst src, .build/obj, $(subst .cpp,.o,$(SRCS_WITHOUT_MAIN)))
all: $(filter-out unit_test, $(subst src/, ,$(wildcard src/*)))
.SECONDARY:
.build/obj/%.o: src/%.cpp
#mkdir -p $(shell dirname $#)
g++ $(FLAGS) $^ -c -o $#
clean:
#rm -r .build
%: $(TARGET_OBJS) .build/obj/%/main.o
#mkdir -p $(shell dirname .build/bin/$#)
g++ $(FLAGS) $^ -o .build/bin/$#
SRCS_WITHOUT_MAIN holds all the cpp files of all modules except the main files. TARGET_OBJS are the corresponding list of object files. The % target matches on, for example "mod1" which compiles all cpp files and main.cpp of mod1.
The problem is, occasionally we get segfaults while running after compiling, and we need to do a "make clean && make" for it to work again. One day I used 4 hours debugging my code just to find out the makefile is some kind of broken. Now everybody on the project uses "make clean && make" all the time in fear of going through the same as I did...
Does anyone know a clever way to do this? By the way: this is a SystemC project.
Here is a crude but effective makefile that will do the job:
CC=g++
vpath %.h mod1 mod2
module1: mod1.o mod2.o main1.o
$(CC) $^ -o $#
module2: mod2.o main2.o
$(CC) $^ -o $#
mod1.o: mod1/mod1.cpp mod1.h mod2.h
mod2.o: mod2/mod2.cpp mod1.h mod2.h
main1.o: mod1/main.cpp mod1.h mod2.h
main2.o: mod2/main.cpp mod1.h mod2.h
%.o:
$(CC) -c $< -o $#
Note:
It puts all object files (e.g. mod1.o) in the parent directory, which is where I presume the makefile is.
It names the two "main" object files "main1.o" and "main2.o". Having two files with the same name in the same codebase is just asking for trouble.
Further refinements are possible; this is designed for simplicity.
The strategy of using pattern rules to match names of modules which can depend upon each other may not be possible using GNU make. From the GNU make 3.82 manual (edition 0.71)
No single implicit rule can appear more than once in a chain. This
means that 'make' will not even consider such a ridiculous thing as
making 'foo' from 'foo.o.o' by running the linker twice. This
constraint has the added benefit of preventing any infinite loop in
the search for an implicit rule chain.
(I found some discussion of this here - things may be different in later versions.)
So if module3 depends on module2, and module2 depends on module1, if we run a pattern rule to build module3, we can't then use the same rule to build module2.
It is possible to get round this using static pattern rules, where the list of targets the rule is matched is specified. I've edited my answer to use these.
You could adapt the following to your problem. Each subdirectory has a number of "*.in" files, and when the subdirectory is built files with the corresponding "*.out" file is created. The modules themselves depend on each other: in this case, mod3 depends on mod2, which in turn depends on mod1. When e.g. the file "mod1/file.in" is updated, running "make mod3" causes mod1, mod2 and mod3 to be rebuilt.
MODULES=mod1 mod2 mod3
mod1_DEPS =
mod2_DEPS = mod1
mod3_DEPS = mod2
TARGET_OBJS=$(subst .in,.out,$(shell find $# -name '*.in'))
.PRECIOUS: %.out
%.out: %.in
touch $#
%.in:
.SECONDEXPANSION:
$(MODULES) : $$(TARGET_OBJS) $$($$#_DEPS)
#echo Building module $#
The .SECONDEXPANSION target and using $$ instead of $ allows access to the $# variable in the TARGET_OBJS macro.
To treat modules differently whether they are being built as programs or libraries, you could have two separate rules, which search for their sources differently:
MODULES=mod1 mod2 mod3
mod1_DEPS =
mod2_DEPS = mod1_AS_MODULE
mod3_DEPS = mod2_AS_MODULE
TARGET_OBJS=$(subst .in,.out,$(shell find $* -name '*.in'))
MODULE_TARGET_OBJS= $(filter-out main.out $(subst .in,.out,$(shell find $* -name '*.in')))
# (elided code as above example)
$(addsuffix _AS_MODULE, $(MODULES)) : %_AS_MODULE: $$(MODULE_TARGET_OBJS) $$(%_DEPS)
#echo Building module $#
$(addsuffix _AS_PROGRAM, $(MODULES)) : %_AS_PROGRAM: $$(TARGET_OBJS) $$(%_DEPS)
#echo Building program $#
This would be invoked as, say, "make mod3_AS_PROGRAM".