Compile and Run C++ Application in Alpine Docker Container - c++

I've been provided the source code from an older project in c++ and need to compile and run it in a Docker container running Alpine Linux. I unfortunately don't have experience compiling in Linux or much with c++, with much google, I have tried to deploy a Docker container running Alpine Linux, install compile libraries/tools and try and compile using the make file, but I'm met with compile errors. I can't provide too much details about the source code as its company property, but I can provide the makefile and the steps I've been taking:
install compile tools in Alpine Linux:
apk update && apk add --no-cache autoconf build-base binutils cmake curl file gcc g++ git libgcc libtool linux-headers make musl-dev ninja tar unzip wget
makefile:
BIN := testExec
# relative paths to all project source files
SRCDIR := src
INCDIR := include
IDEDIR := ide_files
# files included in the tarball generated by 'make dist'
DISTFILES := $(BIN).a $(BIN).dll $(SRCDIR) $(INCDIR) $(IDEDIR)
# filename of the tar archive generated by 'make dist'
#DISTOUTPUT := $(BIN).tar.gz
# cross-compiler name prefix
CROSS := i686-pc-mingw32-
#CROSS := /usr/local/gnat-x86_64-darwin/bin/
# toolchain executables
CC := $(CROSS)gcc
CXX := $(CROSS)g++
LD := $(CROSS)gcc
AR := $(CROSS)ar
RM := rm
TAR := tar
PERL := perl
# path to script that generates ctags files
CTAGS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))ctags.pl
# output files generated by ctags
CTAGS_FILES := .tags .tags_sorted_by_file
# add other macro defines by calling make with a USER_DEFINES argument
# e.g. [ make USER_DEFINES='BLAH FOO="bar"' ] will add the string "-DBLAH -DFOO=bar" to your CFLAGS
UFLAGS := $(addprefix -D,$(USER_DEFINES))
DFLAGS := -DBUILD_DLL
# debugging + major profiling
#DBGFLAGS := -ggdb3 -feliminate-unused-debug-symbols -gno-strict-dwarf -pg -Q -ftime-report -fmem-report
# debugging + minor profiling
#DBGFLAGS := -ggdb3 -feliminate-unused-debug-symbols -gno-strict-dwarf -pg -Q
# debugging
DBGFLAGS := -ggdb3 -feliminate-unused-debug-symbols -gno-strict-dwarf
# ensure the profiling flags get included for the compiler -and- the linker
CFLAGS := $(DBGFLAGS) -Wall $(DFLAGS) $(UFLAGS) $(addprefix -I,$(INCDIR))
LDFLAGS := $(DBGFLAGS) -shared -lm
# flags required for dependency generation; passed to compilers
DEPFLAGS = -MT $# -MD -MP -MF $(DEPDIR)/$*.Td
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $#
LINK.o = $(LD) $(LDFLAGS) $(LDLIBS) -o $#.dll -Wl,--out-implib,$#.a
PRECOMPILE =
POSTCOMPILE = mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
# source files
SRCS := $(wildcard $(SRCDIR)/*.c)
# intermediate directory for generated object files
OBJDIR := .o
# intermediate directory for generated dependency files
DEPDIR := .d
# object files, auto generated from source files
OBJS := $(patsubst %,$(OBJDIR)/%.o,$(basename $(SRCS)))
# dependency files, auto generated from source files
DEPS := $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRCS)))
# compilers (at least gcc and clang) don't create the subdirectories automatically
$(shell mkdir -p $(dir $(OBJS)) >/dev/null)
$(shell mkdir -p $(dir $(DEPS)) >/dev/null)
.PHONY: all
all: $(BIN)
.PHONY: tar
tar: $(DISTFILES)
$(TAR) -czpvf $(DISTOUTPUT) --transform='s|^|$(BIN)/|' $^
.PHONY: dist
dist: $(BIN) tar
.PHONY: clean
clean:
$(RM) -rf $(OBJDIR) $(DEPDIR) $(BIN).a $(BIN).dll
.PHONY: distclean
distclean: clean
$(RM) -f $(DISTOUTPUT)
.PHONY: ctags
ctags:
$(PERL) $(GEN_CTAGS) $(dir $(SRCDIR))
.PHONY: check
check:
#echo no tests configured
.PHONY: help
help:
#echo ""
#echo " available make targets:"
#echo " all - [DEFAULT TARGET] compiles and links all files"
#echo " dist - creates a tarball containing sources and executables"
#echo " clean - removes executables and object file directories"
#echo " distclean - same as clean and also removes dist tarball"
#echo " ctags - creates ctags index files for entire project"
#echo " check - performs verification on project (not implemented)"
#echo ""
#echo " NOTE:"
#echo " you can pass additional flags to the compiler using the USER_DEFINES"
#echo " argument given to make, e.g. [ make USER_DEFINES='BLAH FOO=\"bar\"' ]"
#echo " will add the string \"-DBLAH -DFOO=bar\" to your CFLAGS"
.PHONY: $(BIN)
$(BIN): $(OBJS)
$(LINK.o) $^
$(OBJDIR)/%.o: %.c
$(OBJDIR)/%.o: %.c $(DEPDIR)/%.d
$(PRECOMPILE)
$(COMPILE.c) $<
$(POSTCOMPILE)
.PRECIOUS = $(DEPDIR)/%.d
$(DEPDIR)/%.d: ;
-include $(DEPS)
The makefile references i686-pc-mingw32-gcc so I've attempted to install the package that matches: https://pkgs.alpinelinux.org/contents?branch=edge&name=mingw-w64-gcc-base&arch=x86&repo=community For some reason I'm not getting the right files with the install though:
apk add mingw-w64-gcc-base --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/
bash-5.1# apk info -L apk add mingw-w64-gcc-base | grep usr/bin
usr/bin/x86_64-w64-mingw32-cpp
usr/bin/x86_64-w64-mingw32-gcc
usr/bin/x86_64-w64-mingw32-gcc-11.2.0
usr/bin/x86_64-w64-mingw32-gcc-ar
usr/bin/x86_64-w64-mingw32-gcc-nm
usr/bin/x86_64-w64-mingw32-gcc-ranlib
usr/bin/x86_64-w64-mingw32-gcov
usr/bin/x86_64-w64-mingw32-gcov-dump
usr/bin/x86_64-w64-mingw32-gcov-tool
usr/bin/x86_64-w64-mingw32-lto-dump
So instead, I tried to install with the x86_64-w64-mingw32-gcc, modified the makefile:
#CROSS := i686-pc-mingw32-
CROSS := x86_64-w64-mingw32-
The source code folder structure is:
/SRC/ide_files
/SRC/include
/SRC/src
the make file is:
/SRC/ide_files/testApp/makefile
I then try:
make -f ide_files/testApp/makefile
It errors with:
x86_64-w64-mingw32-gcc -ggdb3 -feliminate-unused-debug-symbols -gno-strict-dwarf -shared -lm -o testExec.dll -Wl,--out-implib,testExec.a <A lot of .o files>
Error relocating /usr/lib/gcc/x86_64-w64-mingw32/11.2.0/../../../../x86_64-w64-mingw32/bin/ld: qsort_r: symbol not found
collect2: error: ld returned 127 exit status
make: *** [ide_files/testApp/makefile:127: testExec] Error 1

Related

Problems building a generic Makefile with dependency tracking

I have the following project structure: a folder called "src" with all the .cpp, a folder called "include" with the .h, a folder called "build" for the .o (object files), another folder called "dep" for the .d files (dependencies) and finally another folder called "bin" for the executables.
Given that, I have done this makefile to carry out the build process
OPTIONS := -O2 -Wall
EXE_NAME = example.exe
BIN_PATH = bin/
BUILD_PATH = build/
DEP_PATH = dep/
INCLUDE_PATH = include/
SRC_PATH = src/
################################################################################
# get project files
ALL_CPP := $(shell find $(SRC_PATH) -type f -name "*.cpp")
ALL_H := $(shell find $(INCLUDE_PATH) -type f -name "*.h")
ALL_O := $(subst $(SRC_PATH),$(BUILD_PATH),$(subst .cpp,.o,$(ALL_CPP)))
ALL_D := $(subst $(SRC_PATH),$(DEP_PATH),$(subst .cpp,.d,$(ALL_CPP)))
all: $(BIN_PATH)$(EXE_NAME)
#linking
$(BIN_PATH)$(EXE_NAME): $(ALL_O)
g++ $(OPTIONS) -o $# $(ALL_O)
# generic build rule
$(BUILD_PATH)%.o: $(SRC_PATH)%.cpp
g++ $(OPTIONS) -c $< -o $(BUILD_PATH)$# -I$(INCLUDE_PATH) -MMD -MF $(DEP_PATH)$(#:.o=.d)
.PHONY: clean
clean:
rm $(ALL_D) $(ALL_O) $(BIN_PATH)$(EXE_NAME)
-include $(ALL_D)
But whenever I try to execute it, this error pops out:
make: *** No rule to make target 'build/file.o', needed by 'bin/example.exe'. Stop.
Which does not make sense, as there is a rule for building targets that end in ".o".
What might be going on here?
First, you should avoid using subst since it substitutes every instance of one set of text with another. Safer is to use patsubst:
ALL_O := $(patsubst $(SRC_PATH)%.cpp,$(BUILD_PATH)%.o,$(ALL_CPP))
ALL_D := $(patsubst $(SRC_PATH)%.cpp,$(DEP_PATH)%.d,$(ALL_CPP))
Second, your command line is wrong: $# already contains the path so you don't want to use $(BUILD_DIR)$#. You want just $# by itself. You'll have a similar problem for .d.
But I don't see any way to get the error you show (no rule to make target 'build/file.o') given the makefile you provide. Either there's something different about your makefile than what you have here or something mysterious is happening.
You can add the -d option to make to get some debug info.
Thanks to all who replied. As many of you suggested, the error I provided does not appear in the makefile version I posted, and that is because I did some code cleaning before posting which ironically end up solving the error (I had no idea the changes I made would be important). More precisely, I am sure the problem was with the line
$(BUILD_PATH)%.o: $(SRC_PATH)%.cpp
Which in the original makefile was
$(BUILD_PATH)%.o: %.cpp
I have also tried without the $(BUILD_PATH), which gave no luck, that's why I thought that line was not important.
I did some of the changes you suggested and the final working version of the Makefile is as follows
OPTIONS := -O2 -Wall -Wno-sign-compare -Wno-unused-parameter
EXE_NAME = example
BIN_PATH = bin/
BUILD_PATH = build/
DEP_PATH = dep/
INCLUDE_PATH = include/
SRC_PATH = src/
################################################################################
# get project files
ALL_CPP := $(shell find $(SRC_PATH) -type f -name "*.cpp")
ALL_H := $(shell find $(INCLUDE_PATH) -type f -name "*.h")
ALL_O := $(subst $(SRC_PATH),$(BUILD_PATH),$(subst .cpp,.o,$(ALL_CPP)))
ALL_D := $(subst $(SRC_PATH),$(DEP_PATH),$(subst .cpp,.d,$(ALL_CPP)))
all: $(BIN_PATH)$(EXE_NAME)
#linking
$(BIN_PATH)$(EXE_NAME): $(ALL_O)
#echo ' -> linking'
#g++ $(OPTIONS) -o $# $(ALL_O)
#echo Finished!
# generic build rule
$(BUILD_PATH)%.o: $(SRC_PATH)%.cpp
#echo ' -> building:' $<
#g++ $(OPTIONS) -c $< -o $# -I$(INCLUDE_PATH) -MMD -MF $(subst $(BUILD_PATH),$(DEP_PATH),$(#:.o=.d))
.PHONY: clean
clean:
#echo Removed files: $(ALL_D) $(ALL_O) $(BIN_PATH)$(EXE_NAME)
#rm $(ALL_D) $(ALL_O) $(BIN_PATH)$(EXE_NAME)
-include $(ALL_D)

GNU Makefile running on Mac OS?

I've written a simple generic makefile for c++. It works great on Debian/Ubuntu.
I would like to know if it could be running on MAC OS. I ask the question because i have no Mac OS at home and i can't test. Furthermore, if you have some advice, let's go.
I'm not sure about these lines
CXXFLAGS += -D OSX
LDFLAGS := -stdlib=libstdc++
I precise that it for build only STL C++ programm for the moment. No dynamic libraries.
# sources (*.cpp)
SRC := sources
# headers (*.cpp)
INC := include
MAIN := kwic_personnel.cpp
EXEC := prog
# main file is in current directory or in sources file
ifneq ("$(wildcard $(MAIN))","")
sources := $(MAIN) $(wildcard $(SRC)/*.cpp)
else
sources := $(wildcard $(SRC)/*.cpp)
endif
# obj files
objects := $(sources:.cpp=.o)
# dep files
deps := $(objects:.o=.d)
CXX := g++
CXXFLAGS := -I $(INC) -MMD -MP -g -std=c++17 -Wall -pedantic -Weffc++ -Werror
# OS name
UNAME := $(shell uname -s)
# if linux
ifeq ($(UNAME), Linux)
# my own choices on compilator with Debian
LDFLAGS := -L /usr/lib/x86_64-linux-gnu
LDLIBS := -lcurl
# else but mac OS
else
# NOT SURE ABOUT THAT
CXXFLAGS += -D OSX
LDFLAGS := -stdlib=libstdc++
endif
# linking
$(EXEC) : $(objects)
#echo "Generation du fichier executable..."
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(RM) $(objects) $(deps)
#echo "Compilation réussie !"
# compilation of cpp files
$(SRC)/%.o: $(SRC)/%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $^ -o $#
# remove exec
.PHONY: clean
clean:
$(RM) $(EXEC)
# dependencies
-include $(deps)
I use gnu make on both Mac and Ubuntu. I do a bunch of things I'll explain.
First my Ubuntu is via a local Docker container, and it NFS mounts my local Mac work directory. So I can edit locally on my Mac but compile for Linux. This means if I build on both, I could have a mix of different targets being built. Clearly, this is problematic, so here are some tricks.
UNAME := $(shell uname)
MACAPPEND=
LIB_ARGS= -cvrU
ifeq ($(UNAME), Darwin)
MACAPPEND=-mac
CXX=clang++
LIB_ARGS= -cvr
endif
SRCDIR := src
OBJDIR := obj${MACAPPEND}
DEPDIR := .d
BINDIR := bin${MACAPPEND}
Then it's the same ${SRCDIR} for everything, but $OBJDIR} might resolve to obj or obj-mac. Same with bin and bin-mac. I compile with this:
${OBJDIR}/%.o : %.cpp
$(COMPILE.cc) ${CXXFLAGS} $(OUTPUT_OPTION) $<
Note that I do this stuff so much, that I produce a Makefile-Base and stuff it into /usr/local/etc and then can do this:
include /usr/local/etc/Makefile-Base
If I'm making a library:
LIBNAME=show${MACAPPEND}
LIB=lib/libshow${MACAPPEND}.a
lib: ${LIB}
${LIB}: ${LIB_OBJ}
#mkdir -p lib
ar ${LIB_ARGS} ${LIB} ${LIB_OBJ}
ranlib ${LIB}
See LIB_ARGS used here and defined near the top of this post.
Note that a LOT of what I'm doing is because of the way I share my directory across both environments and might be bulding in both. There are very few things that you need to do special on Mac.

Change build rules in Makefile to build multiple file types

I want to change my Makefile to build cpp and mm files, since I have just begun adding Objective-C code into my project. This is the Makefile I have currently:
# Name of the project being built
PROJECT := modpe
# Name of process the tweak is loaded into
PROCESS := minecraftpe
# Local IP Address of device to SSH into
DEVICE := ryans-ipod-touch.local
# Path of the SDK on MacOS
SDKPATH := /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
# Build using libc++ and C++11 support
override CXXFLAGS += -stdlib=libc++ -std=c++11 -miphoneos-version-min=7.0 -isysroot $(SDKPATH)
override LDFLAGS += -stdlib=libc++ -std=c++11 -miphoneos-version-min=7.0 -isysroot $(SDKPATH)
# Names of the tweak library, substrate filter, and debian package
TWEAK := $(PROJECT).dylib
FILTER := $(PROJECT).plist
DEB := $(PROJECT).deb
# Directory for build products like executables, object files, and dependency files
BUILD := build
# Directory for debian package filesystem layout
LAYOUT := layout
# Directory for temporary staging of the debian package structure while creating the package
STAGING := $(BUILD)/deb
SUBSTRATE := $(STAGING)/Library/MobileSubstrate/DynamicLibraries
# Name of the unstripped version
UNSTRIPPED := $(BUILD)/$(TWEAK:.dylib=_unstripped.dylib)
# List of source files and their corresponding object file paths
SRCS := $(shell find . -type f -name '*.cpp')
OBJS := $(addprefix $(BUILD)/,$(SRCS:.cpp=.o))
# Supported architectures
ARCHS := arm64
ARCHFLAGS := $(addprefix -arch ,$(ARCHS))
# Frameworks for linking
FRAMEWORKS := CydiaSubstrate Forklift
override LDFLAGS += $(addprefix -framework ,$(FRAMEWORKS))
# Compiler and linker
CLANG := clang $(ARCHFLAGS)
CLANGXX := clang++ $(ARCHFLAGS)
CC := $(CLANG)
CXX := $(CLANGXX)
LD := $(CLANGXX)
STRIP := strip
# When invoked as "make VERBOSE=1", every command gets printed as it runs
ifdef VERBOSE
_v :=
else
_v := #
endif
### Build rules ###
all: $(TWEAK)
.PHONY: all
$(TWEAK): $(UNSTRIPPED)
#echo 'Strip $#'
$(_v)$(STRIP) -x -o $# $<
%.dylib: $(OBJS)
#echo 'Link $#'
$(_v)$(LD) $(LDFLAGS) -dynamiclib -o $# $^
%.cpp: $(BUILD)/%.d
$(BUILD)/%.o: %.cpp | $(BUILD)/.dir
#echo 'Compile $#'
$(_v)$(CXX) $(CXXFLAGS) -MD -MF $(BUILD)/$*.d -c -o $# $<
.SECONDARY: $(BUILD)/.dir
-include $(BUILD)/*.d
### Package rules ###
package: $(DEB)
.PHONY: package
$(DEB): $(TWEAK) $(FILTER)
#echo 'Stage $#'
$(_v)rm -rf $(STAGING)
$(_v)mkdir -p $(STAGING)
$(_v)cp -R $(LAYOUT)/* $(STAGING)
$(_v)mkdir -p $(SUBSTRATE)
$(_v)cp $^ $(SUBSTRATE)
$(_v)chown -R root:wheel $(STAGING)
$(_v)chmod 0755 $(SUBSTRATE)/$(TWEAK)
$(_v)chmod 0644 $(SUBSTRATE)/$(FILTER)
#echo 'Package $#'
$(_v)dpkg-deb -Zgzip -b $(STAGING) $#
$(_v)rm -rf $(STAGING)
### Install rules ###
install: $(DEB)
#echo 'Install $(DEB)'
scp $(DEB) root#$(DEVICE):/var/tmp/
#echo ‘Run dpkg -i /var/tmp/$(DEB) to install.’
ssh root#$(DEVICE)
.PHONY: install
### Offline install rules ###
offline:
#echo 'No WiFi to SSH: Using afc instead'
$(_v)sudo afc/afcclient put ./modpe.deb /modpe.deb
.PHONY: offline
### Clean rules ###
clean:
#echo 'Remove $(BUILD)'
$(_v)rm -rf $(BUILD)
.PHONY: clean
### General rules ###
%/.dir:
#echo 'Create directory $*/'
$(_v)mkdir -p $* && touch $#
# deploys test script
deploy: $(OFFLINE)
scp scripts/script.js root#$(DEVICE):/var/mobile/modpe/
.PHONY: deploy
So far the only thing that I know to change is the SRCS line:
SRCS := $(shell find -E . -regex “.*\.\(cpp\|mm\)”)
My question is, what else can I do to make this build .mm source files with .cpp source files, rather than ignoring them?
You haven't given us enough information to reproduce the problem or test solutions, so you may have to do some fine-tuning, but here's an approach that should work.
You have already modified the SRCS := ... line. Once you have verified that that works, modify the OBJS := ... line:
OBJS := $(patsubst %, $(BUILD)/%.o,$(basename $(SRCS)))
Then once you have verified that that works, add a new rule:
$(BUILD)/%.o: %.mm | $(BUILD)/.dir
#echo 'Compile $#'
$(_v)$(CXX) $(CXXFLAGS) -MD -MF $(BUILD)/$*.d -c -o $# $<

How to write a Makefile with separate source and header directories?

Following this tutorial...
I have 2 source files and 1 header file. I want to have them in separate directories like in the tutorial.
So I set this project up:
.
├── include
│   └── hellomake.h
├── Makefile
└── src
├── hellofunc.c
└── hellomake.c
Makefile:
IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)
ODIR=obj
LDIR =../lib
_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = hellomake.o hellofunc.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
The error I generate says:
gcc -o hellomake -I../include
gcc: fatal error: no input files
compilation terminated.
make: *** [hellomake] Error 4
What's happening?
Your tutorial promotes old and bad practices, you should avoid it IMHO.
In your rule here:
$(ODIR)/%.o: %.c $(DEPS)
You're telling make to look for sources in the current directory while they actually reside in the src directory, thus this pattern is never used and you have no suitable one.
Make sure you organize your project directory like this :
root
├── include/
│ └── all .h files here
├── lib/
│ └── all third-party library files (.a/.so files) here
├── src/
│ └── all .c files here
└── Makefile
Then let's take the process step by step, using good practices.
Firstly, don't define anything if you don't need to. Make has a lot of predefined variables and functions that you should use before trying to do it manually. In fact, he has so many that you can compile a simple file without even having a Makefile in the directory at all!
List your source and build output directories:
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin # or . if you want it in the current directory
Name your final target, that is, your executable:
EXE := $(BIN_DIR)/hellomake
List your source files:
SRC := $(wildcard $(SRC_DIR)/*.c)
From the source files, list the object files:
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
# You can also do it like that
OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC))
Now let's handle the flags
CPPFLAGS := -Iinclude -MMD -MP # -I is a preprocessor flag, not a compiler flag
CFLAGS := -Wall # some warnings about bad code
LDFLAGS := -Llib # -L is a linker flag
LDLIBS := -lm # Left empty if no libs are needed
(CPP stands for C PreProcessor here, not CPlusPlus! Use CXXFLAGS for C++ flags and CXX for C++ compiler.)
The -MMD -MP flags are used to generate the header dependencies automatically. We will use this later on to trigger a compilation when only a header changes.
Ok, time to roll some recipes now that our variables are correctly filled.
It is widely spread that the default target should be called all and that it should be the first target in your Makefile. Its prerequisites shall be the target you want to build when writing only make on the command line:
all: $(EXE)
One problem though is Make will think we want to actually create a file or folder named all, so let's tell him this is not a real target:
.PHONY: all
Now list the prerequisites for building your executable, and fill its recipe to tell make what to do with these:
$(EXE): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
(CC stands for C Compiler.)
Note that your $(BIN_DIR) might not exist yet so the call to the compiler might fail. Let's tell make that you want it to check for that first:
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(BIN_DIR):
mkdir -p $#
Some quick additional notes:
$(CC) is a built-in variable already containing what you need when compiling and linking in C
To avoid linker errors, it is strongly recommended to put $(LDFLAGS) before your object files and $(LDLIBS) after
$(CPPFLAGS) and $(CFLAGS) are useless here, the compilation phase is already over, it is the linking phase here
Next step, since your source and object files don't share the same prefix, you need to tell make exactly what to do since its built-in rules don't cover your specific case:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
Same problem as before, your $(OBJ_DIR) might not exist yet so the call to the compiler might fail. Let's update the rules:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
Ok, now the executable should build nicely. We want a simple rule to clean the build artifacts though:
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR) # The # disables the echoing of the command
(Again, clean is not a target that needs to be created, so add it to the .PHONY special target!)
Last thing. Remember about the automatic dependency generation? GCC and Clang will create .d files corresponding to your .o files, which contains Makefile rules for us to use, so let's include that in here:
-include $(OBJ:.o=.d) # The dash silences errors when files don't exist (yet)
Final result:
SRC_DIR := src
OBJ_DIR := obj
BIN_DIR := bin
EXE := $(BIN_DIR)/hellomake
SRC := $(wildcard $(SRC_DIR)/*.c)
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
CPPFLAGS := -Iinclude -MMD -MP
CFLAGS := -Wall
LDFLAGS := -Llib
LDLIBS := -lm
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ) | $(BIN_DIR)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
$(BIN_DIR) $(OBJ_DIR):
mkdir -p $#
clean:
#$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
-include $(OBJ:.o=.d)
the make utility, with no specific 'target' will make the first target in the file.
The first target is usually named 'all'
For the posted file, will make the object files and will not continue to make the executable when the target is not given in the command line
Suggest the following:
SHELL := /bin/sh
# following so could define executable name on command line
# using the '-D' parameter
#ifndef $(NAME)
NAME := hellomake
#endif
# use ':=' so macros only evaluated once
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CFLAGS := -g -Wall -Wextra -pedantic
LFLAGS :=
ODIR := obj
IDIR := ../include
LIBS :=
LIBPATH := ../lib
DEPS := $(wildcard $(IDIR)/*.h)
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
.PHONY: all
all: $(NAME) $(OBJS)
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $# $< -I$(DEPS)
$(NAME): $(OBJS)
$(CC) $(LFLAGS) -o $# $^ -L$(LIBPATH) -l$(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o
rm -f $(NAME)
however, in your proposed project,
not every source file needs every header file
so should use either gcc or sed to generate the dependency files
then use makefile rules similar to the following,
which may need a little 'tweaking' for your project
because the include files are not in the same directory
as the source files:
DEP := $(SRCS:.c=.d)
#
#create dependency files
#
%.d: %.c
#
# ========= START $< TO $# =========
$(CC) -M $(CPPFLAGS) $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
# ========= END $< TO $# =========
#
# compile the .c files into .o files using the compiler flags
#
%.o: %.c %.d
#
# ========= START $< TO $# =========
$(CC) $(CCFLAGS) -c $< -o $# -I$(IDIR)
# ========= END $< TO $# =========
#
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependencies for that .c file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif
The simple Makefile definitions seem OK to me as they appear in your question. Try specifying the compiler options before the file names:
$(ODIR)/%.o: %.c $(DEPS)
$(CC) $(CFLAGS) -c -o $# $<
hellomake: $(OBJ)
gcc $(CFLAGS) -o $# $^
You need to run make from the source directory.
When you got this error"
*gcc: fatal error: no input files
compilation terminated.*
", that means you do not have object files,
just check out that line "${OBJS} := " in Makefile.
Hi, bro!
If your project "helloFunc" 's architecture are just liking this:
helloFunc
|
|__Makefile
|__build
|__include
| |__hellomake.h
|__src
|__hellofunc.cpp
|__hellomake.cpp
your Makefile should be just like this:
# This is a Makefile for separated multiple sources to build with VSCode on mac
# Thanks, Job Vranish.
# (https://spin.atomicobject.com/2016/08/26/makefile-c-projects/)
# Reference: Makefile Tutorial
# (https://makefiletutorial.com/)
# Reference: #yagiyuki from Qiita
# (https://qiita.com/yagiyuki/items/ff343d381d9477e89f3b)
# Reference: simonsso from Github
# (https://github.com/simonsso/empty-cpp-project/blob/master/Makefile)
# Reference: Chinese Website blogger CDNS
# (https://blog.csdn.net/qq_22073849/article/details/88893201)
# (1)Compiler
# clang++
CXX = clang++
# (2)Compile options
# -Wall -Wextra -std=c++11 -g
CXX_FLAGS = -Wall -Wextra -std=c++11 -g
# (3)Build task directory path
# I do care about out-of-source builds
# ./build
BUILD_DIR ?= ./build
# (4)Source files directory path
# ./src
SRC_DIRS ?= ./src
# (5)Library files directory path
LIBDIR :=
# (6)Add library files
LIBS :=
# (7)Target file, excutable file.
# main
TARGET ?= main
# (8)Source files(code), to be compiled
# Find source files we want to compile
# *expression must around by single quotos
# ./src/bank.cpp ./src/main.cpp
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
# (9)Object files
# String substituion for every C/C++ file
# e.g: ./src/bank.cpp turns into ./build/bank.cpp.o
# ./build/bank.cpp.o ./build/main.cpp.o
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.cpp.o, $(notdir $(SRCS)))
# (10)Dependency files
# which will generate a .d file next to the .o file. Then to use the .d files,
# you just need to find them all:
#
DEPS := $(OBJS:.o=.d)
# (11)Include files directory path
# Every folder in ./src find include files to be passed via clang
# ./include
INC_DIRS := ./include
# (12)Include files add together a prefix, clang make sense that -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# (13)Make Makefiles output Dependency files
# That -MMD and -MP flags together to generate Makefiles
# That generated Makefiles will take .o as .d to the output
# That "-MMD" and "-MP" To generate the dependency files, all you have to do is
# add some flags to the compile command (supported by both Clang and GCC):
CPP_FLAGS ?= $(INC_FLAGS) -MMD -MP
# (14)Link: Generate executable file from object file
# make your target depend on the objects files:
${BUILD_DIR}/${TARGET} : $(OBJS)
$(CXX) $(OBJS) -o $#
# (15)Compile: Generate object files from source files
# $# := {TARGET}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(BUILD_DIR)/%.cpp.o: $(SRC_DIRS)/%.cpp
$(MKDIR_P) $(dir $#)
$(CXX) $(CPP_FLAGS) $(CXX_FLAGS) -c $< -o $#
#(16)Delete dependence files, object files, and the target file
.PHONY: all clean
all: ${BUILD_DIR}/${TARGET}
clean:
$(RM) $(DEPS) $(OBJS) ${BUILD_DIR}/${TARGET}
-include $(DEPS)
MKDIR_P ?= mkdir -p
Changing that Makefile to your needed Linux version:
# (1)Compiler
# g++
CXX = g++
# (2)Compile options
# -Wall -Wextra -std=c++11 -g
CXX_FLAGS = -Wall -Wextra -std=c++11 -g
# (3)Build task directory path
# I do care about out-of-source builds
# ./build
BUILD_DIR ?= ./build
# (4)Source files directory path
# ./src
SRC_DIRS ?= ./src
# (5)Library files directory path
LIBDIR :=
# (6)Add library files
LIBS :=
# (7)Target file, excutable file.
# main
TARGET ?= main
# (8)Source files(code), to be compiled
# Find source files we want to compile
# *expression must around by single quotos
# ./src/bank.cpp ./src/main.cpp
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
# (9)Object files
# String substituion for every C/C++ file
# e.g: ./src/bank.cpp turns into ./build/bank.cpp.o
# ./build/bank.cpp.o ./build/main.cpp.o
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.cpp.o, $(notdir $(SRCS)))
# (10)Dependency files
# which will generate a .d file next to the .o file. Then to use the .d files,
# you just need to find them all:
#
DEPS := $(OBJS:.o=.d)
# (11)Include files directory path
# Every folder in ./src find include files to be passed via clang
# ./include
INC_DIRS := ./include
# (12)Include files add together a prefix, gcc make sense that -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# (13)Make Makefiles output Dependency files
# That -MMD and -MP flags together to generate Makefiles
# That generated Makefiles will take .o as .d to the output
# That "-MMD" and "-MP" To generate the dependency files, all you have to do is
# add some flags to the compile command (supported by both Clang and GCC):
CPP_FLAGS ?= $(INC_FLAGS) -MMD -MP
# (14)Link: Generate executable file from object file
# make your target depend on the objects files:
${BUILD_DIR}/${TARGET} : $(OBJS)
$(CXX) $(OBJS) -o $#
# (15)Compile: Generate object files from source files
# $# := {TARGET}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(BUILD_DIR)/%.cpp.o: $(SRC_DIRS)/%.cpp
$(MKDIR_P) $(dir $#)
$(CXX) $(CPP_FLAGS) $(CXX_FLAGS) -c $< -o $#
#(16)Delete dependency files, object files and the target file
.PHONY: all clean
all: ${BUILD_DIR}/${TARGET}
clean:
$(RM) $(DEPS) $(OBJS) ${BUILD_DIR}/${TARGET}
-include $(DEPS)
MKDIR_P ?= mkdir -p
What you need to notice is that your "Makefile" file is the same directory of the include files and sources files,
so you need to change your "IDIR:=../include" to "IDIR:=./include" in your "Makefile".
END!
Here's what i'm using in my windows setup:
CC = g++
CFLAGS = -Wall -std=c++20
SRCDIR = src
HEADDIR = include
OBJDIR = build
BINDIR = bin
# where the executable will be stored
EXECUTABLE := $(BINDIR)/main
# list of all source files
SOURCES := $(wildcard $(SRCDIR)/*.cpp)
# list of all header files
INCLUDES := $(wildcard $(HEADDIR)/*.h)
# from the list of all source files, create a list of all object files
OBJECTS := $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
# all: clean $(EXECUTABLE)
all: $(EXECUTABLE)
# Link: Generate executable file from object file
$(EXECUTABLE): $(OBJECTS)
#echo LINKING..... $(CC) -o $# $(OBJECTS)
#$(CC) -o $# $(OBJECTS)
#echo RUNNING: $(EXECUTABLE)
#$(EXECUTABLE)
# Compile: Generate object files from source files
# $# := {EXECUTABLE}
# $< := THE first file
# $^ all the dependency
# C++ Sources
$(OBJDIR)/%.o : $(SRCDIR)/%.cpp | makedirs
#echo COMPILING... $(CC) $(CFLAGS) -c "$<" -o "$#"
#$(CC) $(CFLAGS) -c $< -o $#
# `|` is order-only-prerequisites
# https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html
makedirs:
# check if the file exists; if not, create it
# mkdir -p $(OBJDIR) in linux
#if not exist "$(OBJDIR)" mkdir $(OBJDIR)
#if not exist "$(BINDIR)" mkdir $(BINDIR)
#Delete dependence files, object files, and the EXECUTABLE file
clean:
#echo CLEANING UP
# check if the directories exist; if so, delete them
#if exist "$(OBJDIR)" rmdir /s /q $(OBJDIR)
#if exist "$(BINDIR)" rmdir /s /q $(BINDIR)

Makefile template for large C++ project?

I need a Makefile that will compile everything in the current directory and recursively down the tree, and preferably use the compiler's dependency thing (-M, etc.) so that whenever I type "make", as little as possible is recompiled.
Also, why is this not page 1 of the Makefile documentation?
While I would suggest using tools like cmake or alike, I understand that sometimes it is easier or better to use a plain old Makefile.
Here's a Makefile I've used on some projects, it creates dependency-files using gcc:
# Project Name (executable)
PROJECT = demoproject
# Compiler
CC = g++
# Run Options
COMMANDLINE_OPTIONS = /dev/ttyS0
# Compiler options during compilation
COMPILE_OPTIONS = -ansi -pedantic -Wall
#Header include directories
HEADERS =
#Libraries for linking
LIBS =
# Dependency options
DEPENDENCY_OPTIONS = -MM
#-- Do not edit below this line --
# Subdirs to search for additional source files
SUBDIRS := $(shell ls -F | grep "\/" )
DIRS := ./ $(SUBDIRS)
SOURCE_FILES := $(foreach d, $(DIRS), $(wildcard $(d)*.cpp) )
# Create an object file of every cpp file
OBJECTS = $(patsubst %.cpp, %.o, $(SOURCE_FILES))
# Dependencies
DEPENDENCIES = $(patsubst %.cpp, %.d, $(SOURCE_FILES))
# Create .d files
%.d: %.cpp
$(CC) $(DEPENDENCY_OPTIONS) $< -MT "$*.o $*.d" -MF $*.d
# Make $(PROJECT) the default target
all: $(DEPENDENCIES) $(PROJECT)
$(PROJECT): $(OBJECTS)
$(CC) -o $(PROJECT) $(OBJECTS) $(LIBS)
# Include dependencies (if there are any)
ifneq "$(strip $(DEPENDENCIES))" ""
include $(DEPENDENCIES)
endif
# Compile every cpp file to an object
%.o: %.cpp
$(CC) -c $(COMPILE_OPTIONS) -o $# $< $(HEADERS)
# Build & Run Project
run: $(PROJECT)
./$(PROJECT) $(COMMANDLINE_OPTIONS)
# Clean & Debug
.PHONY: makefile-debug
makefile-debug:
.PHONY: clean
clean:
rm -f $(PROJECT) $(OBJECTS)
.PHONY: depclean
depclean:
rm -f $(DEPENDENCIES)
clean-all: clean depclean