31

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?

Chnossos
  • 9,971
  • 4
  • 28
  • 40
iBeyondPower
  • 357
  • 1
  • 5
  • 8
  • **Post your actual code, and format it so it is readable** – Eregrith Jun 01 '15 at 12:39
  • i dont think i should provide gcc with a file, all is contained in the makefile and all i call is make in the directory, because all 3 files are allready included in the makefile – iBeyondPower Jun 01 '15 at 12:43
  • 4
    One trick I find useful when debugging a make file is to set up a "show" target (or whatever name) with no dependencies that only prints your variables, i.e.: `@echo "OBJ=$(OBJ)"` (one line for each variable). Then with `make show` you will clearly see if they hold the correct content. – kebs Jun 01 '15 at 12:49
  • The purpose of a Makefile is to call commands. One of those is `gcc` for example. If you do not supply `gcc` with the files to link together, it's not gonna invent it. – Eregrith Jun 01 '15 at 12:52
  • well i have no intention to learning how to script makefiles but it seems it will be the only way to get me moving to doing what i actually want to do. i was hoping the tutorial would provide the basic structure on which i could expand the code as the first few steps all worked fine, i therfore assume the linking part is also correct, but thanks anyway. – iBeyondPower Jun 01 '15 at 12:58
  • There's something about your makefile or your environment you're not telling us, because that makefile works fine as written above. The only explanation for the behavior you see is if the `OBJ` variable were empty. I don't see any way that can happen given the above makefile. Are you sure you're using GNU make? What version? (run `make --version`) Is this really exactly the makefile you're using? – MadScientist Jun 02 '15 at 04:35
  • @kebs That is the purpose of the `$(info )` command. – Chnossos Jun 02 '15 at 16:43
  • @Chnossos Yeah, I know `$info`, just a personal habit. But besides the fact that `echo` is a shell command and `$info` a `make` command, are you aware of any practical differences ? (curious) – kebs Jun 02 '15 at 20:09
  • 1
    `$info` can be placed almost everywhere in your makefile, whereas `echo` can only be placed in variable declaration or rule body. – Chnossos Jun 02 '15 at 21:19
  • Are you on MacOS or another BSD system? `make` does not default to Gnu make there. You may have to install and use gmake. – chqrlie Jun 03 '15 at 05:20

5 Answers5

141

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!

  1. 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
  1. Name your final target, that is, your executable:
EXE := $(BIN_DIR)/hellomake
  1. List your source files:
SRC := $(wildcard $(SRC_DIR)/*.c)
  1. 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))
  1. 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)
MestreLion
  • 12,698
  • 8
  • 66
  • 57
Chnossos
  • 9,971
  • 4
  • 28
  • 40
  • 6
    makefiles need tabs, not spaces. Note that when copying from here. – Benedikt S. Vogler Aug 19 '20 at 18:23
  • in linux it is possible to check the spaces and tabs using the syntax cat -e -t -v Makefile – giammi56 Apr 27 '21 at 21:40
  • 1
    I want to extend your Makefile with the ability to create release and debug versions. – Homunculus Reticulli Oct 06 '21 at 15:35
  • @HomunculusReticulli Thank you! I've played with a lot of ways to add release/debug builds, but never liked the result. There were always weird quirks and corner cases, and the code was way more complex. I'd highly suggest reading about Modern CMake instead ([here](https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/) and [here](https://cliutils.gitlab.io/modern-cmake/)). – Chnossos Oct 06 '21 at 15:43
  • @BenediktS.Vogler: I've edited the answer so now it uses tabs instead, but unfortunately it seems SO displays them as spaces anyway (tabs are preserved in source). Well, at least now we have syntax highlight... – MestreLion Jan 20 '23 at 09:08
1

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
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • 2
    Your comment is not correct. Pattern rules and suffix rules (implicit rules) cannot count as default targets. The first explicit rule in the makefile above is `helloworld` and that's the target that will be created if no other target is given on the command line. Although adding an `all` target is not a bad idea, it's not needed. Also, your comment about using `-D` is wrong: make doesn't use `-D` to define variables. And you can't write preprocessor statements like `#ifdef` in a makefile (that's just a comment, to make). There are other problematic things here too. – MadScientist Jun 02 '15 at 04:39
0

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.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Switching the order of the flags won't help... the problem is that the object files are actually missing from the command line. – MadScientist Jun 02 '15 at 04:40
  • @MadScientist: switching the options is not the solution, but it is advisable. As for why `$(OBJ)` is empty, either the OP is not using Gnu make or there is something missing from the question. – chqrlie Jun 03 '15 at 05:19
0

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!

Vittore Marcas
  • 1,007
  • 8
  • 11
0

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)
Sharukh Rahman
  • 327
  • 3
  • 12