I am writing a Makefile which compiles a number of C files, some in the same directory as the Makefile and others in parent directories (must be there for historic reasons). I want to put all *.o files in a single directory (objdir in my case). This seems to work, but for some reason, this Makefile always builds everything, even if nothing has changed. Where am I going wrong in my rules/dependecies?
UPDATE: I've distilled my Makefile and sources down to the bare minimum (see below for source file contents. To be clear, this does compile and link. The problem is that it does the compilation and linking every time I run make, even if nothing has changed. This is not typical make behavior.
Makefile contents:
CC = gcc
CFLAGS = -g -Wall -Werror -I..
SRCS = main.c ../a.c
OBJS = $(patsubst %.c, objdir/%.o, $(SRCS))
LN_OBJS = $(patsubst %.c, objdir/%.o, $(notdir $(SRCS)))
MAIN = foo
.PHONY: clean
default: objdir $(MAIN)
#echo Done
$(MAIN): $(OBJS)
$(CC) $(CFLAGS) -o $(MAIN) $(LN_OBJS )
objdir/%.o: %.c
$(CC) $(CFLAGS) -c $< -o objdir/$(notdir $#)
objdir:
#mkdir -p objdir
clean:
#$(RM) -rf objdir $(FDP) $(DK)
main.c contents
#include "stdlib.h"
#include "stdio.h"
#include "a.h"
int main(int argc, char**argv)
{
printf("Hello\n");
a();
}
../a.c contents
#include <stdio.h>
void a()
{
printf("Hi from a\n");
}
../a.h contents
#ifndef __a__
#define __a__
void a();
#endif
Your rule for objdir/%.o: is expanded to objdir/../a.o: ../a.c, but you then go and create objdir/$(notdir $#), violating rule 2 of the Rules of Makefiles.
One way to fix this is with vpath (also note that objdir should be a (order-only) dependency of the objects):
SRCS = main.c a.c
vpath %.c ../
# [...]
objdir/%.o: %.c | objdir
$(CC) $(CFLAGS) -c $< -o $#
Related
I have three files, posl.h, state.c and main.c. What I plan on doing is adding functions that are used throughout the project in posl.h, and then I call it to test the function with main.c, and then make the function in state.c. The problem I am having is that I keep getting an error of undefined reference to init_poslState() even though I have it defined in posl.h
main.c
#include <posl.h>
int main(int argc, char * argv[]) {
pState poslState = init_poslState();
return 0;
}
posl.h
#ifndef POSL_LANGUAGE_H
#define POSL_LANGUAGE_H
#define POSL_MAJOR_VERSION 1
#define POSL_MINOR_VERSION 0
#define POSL_RELEASE_VERSION 0
// State
typedef struct POSL_STATE {
// ...
} pState;
pState init_poslState();
void free_poslState(pState poslState);
#endif
state.c
#include "state.h"
#include <posl.h>
pState init_poslState() {
pState newState;
return newState;
}
Makefile
CFLAGS=-g -Wall -Wextra -I./include
CC=gcc $(CFLAGS)
CORE_O_FILES=./src/Core/lexer.o ./src/Core/parser.o ./src/Core/state.o
CLI_O_FILES=
O_FILES=$(CORE_O_FILES)
# Making CLI Tool
posl: $(CLI_O_FILES) libposl.a ./src/CLI/main.c
$(CC) -o posl -L./ -lposl ./src/CLI/main.c $(CLI_O_FILES)
# Making Library
libposl.a: $(O_FILES) ./include/posl.h
ar rcs libposl.a $^
# Core Files
./src/Core/lexer.o: ./src/Core/lexer.c ./src/Core/lexer.h
$(CC) -o $# -c ./src/Core/lexer.c
./src/Core/parser.o: ./src/Core/parser.c ./src/Core/parser.h
$(CC) -o $# -c ./src/Core/parser.c
./src/Core/state.o: ./src/Core/state.c ./src/Core/state.h
$(CC) -o $# -c ./src/Core/state.c
# PHONY List
.PHONY: all
all:
make update-libs
make libposl.a
make posl
make pcc
# Post-Compile Clean
.PHONY: pcc
pcc:
rm -rf ./src/Core/*.o
rm -rf ./src/CLI/*.o
.PHONY: clean
clean:
make pcc
rm -rf ./libposl.a ./posl*
The order of compiler and (especially) linker options is significant. With this command ...
$(CC) -o posl -L./ -lposl ./src/CLI/main.c $(CLI_O_FILES)
... the linker will not attempt to resolve any function references from main.c against functions in libposl.a. It will look only to objects and libraries appearing after main.c on the command line.
Therefore, rewrite that recipe to
$(CC) -o posl -L. ./src/CLI/main.c $(CLI_O_FILES) -lposl
Welp, #user17732522 answered my question. I had the -l flag messed up, and it wasn't after my source files. ~Thank you guys!~
This simple program (two .c files and one .h) compiles and links properly from the command line. However, my Makefile is throwing an error during the linking stage. Proper tab usage is in effect.
CC = gcc
BINDIR = bin/
OBJDIR = obj/
SRCDIR = src/
MKDIR = mkdir -p
RM = rm -rf
SRC = $(wildcard $(SRCDIR)*.c)
_OBJS = $(patsubst $(SRCDIR)%.c, %.o, $(SRC))
OBJS = $(addprefix $(OBJDIR), $(_OBJS))
CFLAGS = -Wall -g -Iinclude
.PHONY: all
all: $(BIN)
_BIN = a.out
BIN = $(addprefix $(BINDIR), $(_BIN))
$(BIN): $(OBJS) $(BINDIR)
$(CC) -o $# $(CFLAGS) $<
$(BINDIR):
$(MKDIR) $(BINDIR)
$(OBJS): $(SRC) $(OBJDIR)
$(CC) $(CFLAGS) -c $< -o $#
$(OBJDIR):
$(MKDIR) $(OBJDIR)
.PHONY: clean
clean:
#echo "Cleaning things up..."
$(RM) $(OBJDIR) $(BINDIR)
The program's files:
main.c
#include <stdio.h>
#include <stdlib.h>
#include "hellomake.h"
int main(void) {
myPrintHelloMake();
return EXIT_SUCCESS;
}
hellomake.c
#include <stdio.h>
#include "hellomake.h"
void myPrintHelloMake(void) {
puts("Hello makefiles!");
}
hellomake.h
#ifndef __HELLO_H__
#define __HELLO_H__
void myPrintHelloMake(void);
#endif
The .c files are in the src/ directory and the .h file is in the include/ directory. Makefile compiles the code, creates the obj/ directory, and places and main.o and hellomake.o therein. However, that is where things break down. Here is gcc's complaint:
$ make
mkdir -p obj/
gcc -Wall -g -Iinclude -c src/hellomake.c -o obj/hellomake.o gcc -Wall -g -Iinclude -c src/hellomake.c -o obj/main.o mkdir -p bin/ gcc -o bin/a.out -Wall -g -Iinclude
obj/hellomake.o /usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu/Scrt1.o: in function `_start': (.text+0x20): undefined reference to `main' collect2: error: ld returned 1 exit status make: *** [Makefile:34: bin/a.out] Error 1
There were a number of issues.
I'd leave off / from (e.g.) BINDIR et. al.
The patsubst was incorrect. It needed $(OBJDIR) in the the TO
Using _OBJS was unnecessary/harmful.
The rule for $(OBJS) was incorrect. It created the two .o files using the first prereq source, so [the function] main was defined twice. It needs a pattern rule instead.
Using $(BINDIR) as a prereq for $(BIN) added bin/ at the end of the gcc line [and it complained]
As M.Oehm mentioned, $< only gets the first prereq.
The all: was indented so it was not recognized properly
Anyway, here's the corrected Makefile:
CC = gcc
BINDIR = bin
OBJDIR = obj
SRCDIR = src
MKDIR = mkdir -p
RM = rm -rf
SRC = $(wildcard $(SRCDIR)/*.c)
###_OBJS = $(patsubst $(SRCDIR)%.c, %.o, $(SRC))
###OBJS = $(addprefix $(OBJDIR), $(_OBJS))
OBJS = $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SRC))
CFLAGS = -Wall -g -Iinclude
_BIN = a.out
BIN = $(addprefix $(BINDIR)/, $(_BIN))
.PHONY: all
all: $(BINDIR) $(OBJDIR) $(BIN)
####$(BIN): $(OBJS) $(BINDIR)
$(BIN): $(OBJS) $(BINDIR)
###$(CC) -o $# $(CFLAGS) $<
$(CC) -o $# $(CFLAGS) $(OBJS)
$(BINDIR):
$(MKDIR) $(BINDIR)
###$(OBJS): $(SRC) $(OBJDIR)
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $#
$(OBJDIR):
$(MKDIR) $(OBJDIR)
.PHONY: clean
clean:
#echo "Cleaning things up..."
$(RM) $(OBJDIR) $(BINDIR)
So here is my Makefile:
# My First Makefile
HEADERS = stdio.h
all: main.o input.o output.o
cc -o all main.o input.o output.o
main.o: main.c $(HEADERS)
cc -c main.c -o main.o
input.o: input.c $(HEADERS)
cc -c input.c -o input.o
output.o: output.c $(HEADERS)
cc -c output.c -o output.o
clean:
-rm -f *.o
-rm -f all
And below is input.c:
#ifndef __STDIO_H__
#define __STDIO_H__
#include <stdio.h>
void getChar()
{
getchar();
}
#endif
Below is output.c:
#ifndef __STDIO_H__
#define __STDIO_H__
#include <stdio.h>
void putChar(char c)
{
putchar(c);
}
#endif
Below is main.c:
#ifndef __STDIO_H__
#define __STDIO_H__
#include <stdio.h>
int main()
{
char c;
while ((c = etChar()) != '\n')
{
putChar(c);
}
}
#endif
Yet whenever I press "make" command in Terminal,
I get:
make: *** No rule to make target `stdio.h', needed by `main.o'. Stop.
cc refers to clang
What is it that I am doing wrong here?
I was trying to get used to $(ARGS) by using $(HEADERS)
but it seems that the clang compiler does not accept this.
Do you think I should use gcc instead?
It is unnecessary to write stdio.h in your Makefile. GCC will automatically search it in DEFAULT PATH (/usr/include, ....). Write header files as targes in Makefile only if you use your own header files.
# Makefile
# define CC
CC := gcc
# define final targets
TARGETS := main
# just trigger $(TARGETS)
all: $(TARGETS)
# build main from main.o, input.o, output.o
main: main.o input.o output.o
$(CC) -o $# $^
# build main.o/input.o/output.c from main.c/input.c/output.c
%.o: %.c
$(CC) -c -o $# $<
# trigger $(TARGETS), then run ./main
run: $(TARGETS)
./main
clean:
-rm $(TARGETS)
-rm *.o
# define targets which are not bound to file
.PHONY: all clean run
In your case:
"make" will look for stdio.h in the current directory, and is complaining that the header is not found.
Just to fix this, you should mention the full path where stdio.h is located, for example:
HEADERS = /usr/include/stdio.h
Ideally, if you want to check for the presence of standard headers like stdio.h, you should use tools like automake and autoconf, which generate the Makefile automatically. Here's a link which points to the autoconf documentation:
https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html
I want to have Makefile for my static library where I can conditionally build it as program to perform simple self-test. My current Makefille looks like follows:
OBJECTS = sm3.o ./core/sm3_internal.o
INCLUDES = -I. -I./core
CFLAGS = -g -Wall -O3
CC = c99
OUT = libsm3.a
%.o: %.c
$(CC) -c -o $# $< $(CFLAGS) $(INCLUDES)
$(OUT): $(OBJECTS)
ar rcs $(OUT) $(OBJECTS)
At my main library file I have:
#ifdef TEST
main(int argc, int* argv[])
{
//my self-test logic here
}
#endif
Where should I add -DTEST flag? If I add it to CFLAGS and then add line:
test: $(OBJECTS)
library also would be build with main, which obviously is not something I want.
I would suggest that you compile main.c conditionally instead:
$(TESTOUT): $(OBJECTS) $(TESTOBJ)
$(CC) -o $# $<
I am trying to write a Makefile with the following directory structure -
Example - Contains Makefile, main.c, xyz.c, xyz.h and sub-directories Hal and Interrupt_Config
Hal - Contains test2.c and test2.h
Interrupt_Config - Contains try.h
Below is the Makefile I am using-
EXE := practice
CC := gcc
CPPFLAGS := -IHal -IInterrupt_Config
VPATH := Hal:Interrupt_Config
MAIN_OBS := $(patsubst %.c,%.o,$(wildcard *.c))
INT_OBS := $(patsubst %.c,%.o,$(wildcard Interrupt_Config/*.c))
HAL_OBS := $(patsubst %.c,%.o,$(wildcard Hal/*.c))
ALL_DEPS := $(patsubst %.o,%.d,$(MAIN_OBS), $(HAL_OBS), $(INT_OBS))
all: $(EXE)
$(EXE): $(MAIN_OBS) $(HAL_OBS) $(INT_OBS)
$(CC) -o $# $(LDFLAGS) $^ $(LDLIBS)
%.o: %.c
$(CC) -o $# -MD -MP $(CPPFLAGS) $(CFLAGS) -c $<
-include $(ALL_DEPS)
clean:
rm -f $(EXE) $(MAIN_OBS) $(INT_OBS) $(HAL_OBS) $(ALL_DEPS)
.PHONY: all clean
Whenever I am trying to do make without including Interrupt_Config/try.h it works absolutely fine . My main.c includes are like this -
..
#include "test2.h"
#include "xyz.h"
#include "try.h" // Problem is here
..
and try.h is very simple like this -
#ifndef TRY_H
#define TRY_H
#define TRUE 1
#define FALSE 0
#endif
But the moment I use values of TRUE or FALSE anywhere in main.c and try to do make again. It throws me error - 'TRUE' undefined (first use in this function) and similar for 'FALSE'.
I am not able to understand what exactly is the problem, since I am new to Makefiles.
EDIT
main.c
#include <stdio.h>
#include <stdlib.h>
#include "test2.h"
#include "try.h"
#include "xyz.h"
int main()
{
bugs(); //test2.h
bunny(); //test2.h
t_print(); // xyz.h
printf("Yes! your age is %d\n",TRUE); // try.h
}
I just rewrote every file including Makefile it worked this time, Not sure what was the issue since everything seemed okay.