Makefile optimization for quickly compiling a library as quickly as possible? - c

Are there any methods I can use to shave seconds off my library's compilation?
My old Makefile took 1.86s to run, up to 5.147 total.
After I changed
%.o: %.c
$(CC) $(FLAGS) -c -o $# $<
Into:
#$(CC) $(FLAGS) -c $(OBJ:.o=.c)
And added # to silence all the output, my new Makefile took 1.40s to run, up to 3.481 s total.
I'm wondering if there's any ways I can optimize my makefile to simply make it run faster or make it more "useful"? Where do I go to learn about micro-adjustments to get a slightly faster compiled program?
https://github.com/Hourai42/libft/blob/master/Makefile
NAME = libft.a
FLAGS = -Wall -Wextra -Werror
CC = gcc
OBJ = ft_strrchr.o ft_putstr.o ft_strcmp.o ft_strlcat.o ft_memcpy.o \
ft_strequm.o ft_memmove.o ft_strsplit.o ft_strncpy.o ft_lstmap.o \
ft_lstadd.o ft_striter.o ft_strstr.o ft_isdigit.o ft_putnbr.o \
ft_memccpy.o ft_strdel.o ft_nmbwrd.o ft_memchr.o ft_bzero.o \
ft_isalnum.o ft_putstr_fd.o ft_lstiter.o ft_lstdelone.o ft_toupper.o
\
ft_strcpy.o ft_lstnew.o ft_strdup.o ft_putnbr_fd.o ft_striteri.o \
ft_strmap.o ft_putendl_fd.o ft_memdel.o ft_strnstr.o ft_putchar.o \
ft_isascii.o ft_strlen.o ft_strsub.o ft_strnew.o ft_memalloc.o
ft_putendl.o \
ft_strncat.o ft_itoa.o ft_strncmp.o ft_memcmp.o ft_strtrim.o
ft_strequ.o
\
ft_putchar_fd.o ft_memset.o ft_isalpha.o ft_strcat.o ft_tolower.o \
ft_strnequ.o ft_strjoin.o ft_atoi.o ft_strclr.o ft_strmapi.o \
ft_isprint.o ft_lstdel.o ft_strchr.o ft_mallocwrd.o ft_countplace.o \
ft_isprime.o ft_hassqrt.o ft_fibonacci.o ft_recursive_power.o \
ft_recursive_factorial.o
all: $(NAME)
$(NAME):
#$(CC) $(FLAGS) -c $(OBJ:.o=.c)
#ar rc $(NAME) $(OBJ)
#ranlib $(NAME)
clean:
#/bin/rm -f $(OBJ)
fclean: clean
#/bin/rm -f $(NAME)
re: fclean all
.PHONY: clean fclean re

Redo only what needs to (huge improvement).
Use parallel make (significant improvement if run on a multicore CPU).
Invoke only one single shell per recipe (moderate to minor improvement).
Example:
NAME = libft.a
FLAGS = -Wall -Wextra -Werror
CC = gcc
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
.PHONY: all clean ultraclean
all: $(NAME)
%.o: %.c
#$(CC) $(FLAGS) -c -o $# $<
$(NAME): $(OBJS)
#ar rc $(NAME) $^ && \
ranlib $(NAME)
clean:
#/bin/rm -f $(OBJS)
ultraclean:
#/bin/rm -f $(OBJS) $(NAME)
And then, if you have, let's say, 6 cores and hyper-threading:
$ make -j12
Note: of course, with parallel make, do not parallelize cleaning targets (clean, ultraclean) and building targets (all), as you do with your re target.

Related

VScode fails to recognize include path from Makefile

Bellow is my Makefile from which vscode should understand that the #include "philo.h" and #include "philo_bonus.h" are okay even though those .h files are not in the same folders (srcs and srcs_bonus) as my .c, thanks to the -I include/.
What is very weird is that it behaves in the expected way for my srcs/ files but no for my srcs_bonus/ files which give me the error : cannot open source file "philo_bonus.h"
It does compile well so i assume the issue is vscode's. I have tried reloading the window several times with no differences and this issue has come up accross different projects. Is this a glitch or am I missing something here ?
SRCS = philo.c \
thread.c \
util.c \
lib.c \
free.c \
init.c \
SRCS_BONUS = philo_bonus.c \
thread_bonus.c \
util_bonus.c \
lib_bonus.c \
free_bonus.c \
init_bonus.c \
OBJS = $(addprefix srcs/, $(SRCS:.c=.o))
OBJS_BONUS = $(addprefix srcs_bonus/, $(SRCS_BONUS:.c=.o))
INC = -I include/
CC = cc -pthread
AR = ar rcs
CFLAGS = -g3 -Wall -Wextra -Werror -fsanitize=address
RM = rm -f
NAME = philo
%.o: %.c
$(CC) $(CFLAGS) $(INC) -c $< -o $#
$(NAME): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(NAME)
all: $(NAME)
bonus: $(OBJS_BONUS)
$(CC) $(CFLAGS) $(OBJS_BONUS) -o $(NAME)
clean:
$(RM) $(OBJS) $(OBJS_BONUS)
fclean: clean
$(RM) $(NAME)
re: fclean all
.PHONY: all bonus clean fclean re
Here is my file tree :
include/
├─ philo.h
├─ philo_bonus.h
srcs/
├─ files.c
srcs_bonus/
├─ files_bonus.c
Makefile
I am on Ubuntu 22.04.1

Makefile compilation warning (libftprintf.a the table of contents is empty (no object file members in the libr ary define global symbols)

I am trying to compile my Makefile to make library from different directories. The library is successfully compiled but I am facing the following warning
/Applications/Xcode.app/Contents/Developer/usr/bin/make -C ./libft
make[1]: Nothing to be done for `all'.
gcc -Wall -Werror -Wextra -c srcs/ft*.c libft/ft*.c -I ./includes
ar rcs libftprintf.a srcs/ft*.c libft/ft*.c*****
warning: /Applications/Xcode.app/Contents/Developer/Toolchains/Xcod
eDefault.xctoolchain/usr/bin/ranlib: archive library: libftprintf.a
the table of contents is empty (no object file members in the libr
ary define global symbols)
Please kindly advise on how to solve this warning. The structure of my directory is as follow:
steh#u90z01s01 printf % ls
Makefile includes main.c libft srcs
My Makefile is as following
NAME := libftprintf.a
CC := gcc
AR := ar rcc
CFLAGS:= -Wall -Werror -Wextra -c
SRCS = srcs/ft*.c libft/ft*.c
INCLUDES = ./includes
OBJ_FILES = $(SRCS:%.c = %.o)
$(NAME): $(OBJ_FILES)#.fr
$(MAKE) -C ./libft
$(CC) $(CFLAGS) $(SRCS) -I $(INCLUDES)
$(AR) $(NAME) $(OBJ_FILES)
all: $(NAME)
clean:
#echo "Cleaning..."
rm -rf $(NAME) ft*.o
fclean:
rm -rf $(NAME)
re: fclean all
norm:
#norminette $(LIB_FILES)
.PHONY: clean fclean all re norm
This is my latest code
NAME := libftprintf.a
CC := gcc
AR := ar rcs
CFLAGS := -Wall -Werror -Wextra -c
SRCS = ./libft/ft*.c ./srcs/ft*.c
OBJS = ft*.o
LIBFT = ./libft
INC = ./includes
# Colors
GREEN= \033[1;32m
RED= \033[1;31m
all: $(NAME)
$(NAME):
#make re -C $(LIBFT)
#$(CC) $(CFLAGS) $(SRCS) -I $(INC)
#$(AR) $(NAME) $(OBJS)
#ranlib $(NAME)
#echo "$(GREEN)ft_printf compiled!"

Using make Static Pattern Rules for multiple files

I'm having trouble creating a pattern to apply to every file in a subdirectory
I have the following tree:
libft/
..makefile
..obj/
..bonus/
........bonusfile1.c
........bonusfile2.c
........bonusfile3.c
........[...etc...]
..mainfile1.c
..mainfile2.c
..mainfile3.c
..[...etc...]
I'm trying to compile the bonus/bonusfiles into the objects obj directory but no mater what I type I'm coming up short of my objective.
My makefile:
SRC_FILES= ft_atoi.c \
ft_bzero.c \
...
BONUS_FILES=ft_lstadd_back.c \
ft_lstadd_front.c \
...
NAME=libft.a
LIBSO=libft.so
CC=gcc
CFLAGS=-Wall -Wextra -Werror
BONUS_DIR=bonus/
OBJ_DIR=obj/
HDR_NAME=libft.h
BONUS_HDR=_bonus.h
SRC_PATH=$(addprefix $(SRC_DIR), $(SRC_FILES))
BONUS_PATH=$(addprefix $(BONUS_DIR), $(BONUS_FILES))
SRC_NAMES=${SRC_FILES:.c=.o}
BONUS_NAMES=${BONUS_FILES:.c=.o}
SRC_NAMES_O=$(addprefix $(OBJ_DIR), $(SRC_NAMES))
BONUS_NAMES_O=$(addprefix $(OBJ_DIR), $(BONUS_NAMES))
HDR= $(addprefix $(HDR_DIR),$(HDR_NAME))
all: $(NAME)
$(NAME): $(OBJ_DIR) $(SRC_NAMES)
ar -rc $# $(SRC_NAMES_O)
ranlib $#
$(OBJ_DIR):
mkdir $#
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJ_DIR)$#
bonus: $(BONUS_PATH)
ar -r $(NAME) $(BONUS_NAMES_O)
ranlib $#
$(BONUS_PATH): %.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJ_DIR)$#
clean:
rm -rf $(OBJ_DIR)
rm -rf src/*.o
rm -rf *.o
sclean: clean
rm -rf $(NAME) $(LIBSO) a.out
fclean: clean
rm -f $(NAME)
re: fclean all
so:
$(CC) -nostartfiles -fPIC $(CFLAGS) $(SRC_FILES) $(BONUS_PATH)
gcc -nostartfiles -shared -o libft.so $(SRC_FILES) $(BONUS_PATH)
.PHONY: all clean fclean re so bonus
What I wanted to do was something similar to the %.o: %.c target but I can't. What am I doing wrong?

Makefile relink error

I am trying to get this makefile relink and not recompile unessecarily files that aren't modified. The "libft" is my library and doesnt have any errors. The error that I am having when doing
make
is :
make: *** No rule to make target `main.o', needed by `ft_printf'. Stop.
My makefile is:
NAME = ft_printf
SRC = main.c\
ft_printf.c\
parser_main.c\
utils.c\
debug_funcs.c
OBJ = $(SRC:.c=.o)
SRC_PATH = srcs/
SRC_POS = $(addprefix $(SRC_PATH),$(SRC))
INC = -I includes
LIBFT = libft/libft.a
CC = gcc
FLAGS = -Wall -Wextra -Werror
all: $(NAME)
$(NAME): $(OBJ)
$(CC) $(FLAGS) $(OBJ) -o $(NAME) $(LIBFT)
%.o: %.c
$(CC) -o $# -c $< $(FLAGS)
$(LIBFT):
make -C ./libft/
clean:
rm -f $(OBJ)
make clean -C ./libft/
fclean: clean
rm -f $(NAME)
make fclean -C ./libft/
re: fclean all
Any idea ? I can't figure it out and i think it's because %.o:%.c isn't called
Given the existence of these variables:
SRC_PATH = srcs/
SRC_POS = $(addprefix $(SRC_PATH),$(SRC))
I'm guessing that your source files actually live in srcs/ whereas you're building your object files in . So this pattern rule:
%.o: %.c
when trying to match main.o won't find a main.c since that file really is srcs/main.c. Since that pattern doesn't match, the rule itself isn't considered, and since no other rule is found, you get an error.
Instead, try:
%.o : $(SRC_PATH)/%.c
$(CC) -o $# -c $< $(FLAGS)

Makefile: How to create both static and shared libraries in C?

Ladies, gentlemen, hello.
I'm trying to create a Makefile in C which will create two libraries
One static,one shared.So far my Makefile works for the static part.
Projet file structure:
//root
//root/src
An other point to mention, this Makefile also creates *.o in my root project directory and the /src dir.
What to do so it only creates object files inside the /src directory ?
Makefile:
SNAME = libmy_printf_`uname -m`-`uname -s`.a
DNAME = libmy_printf_`uname -m`-`uname -s`.so
SRC = $(wildcard src/*.c)
OBJ = $(SRC:.c=.o)
CC = gcc
RM = rm -f
CFLAGS = -W -Wall -ansi -pedantic -Werror -g3 -fPIC
LDFLAGS = -L. -l$(NAME)
STATIC: $(OBJ)
$(CC) -c $(SRC)
ar r $(SNAME) $(OBJ)
ranlib $(SNAME)
DYNAMIC: $(OBJ)
$(CC) -c $(SRC)
$(CC) -shared -o $(DNAME) $(OBJ)
.PHONY: my_printf_static
my_printf_static: $(STATIC)
.PHONY: my_printf_dynamic
my_printf_dynamic: $(DYNAMIC)
.PHONY: all
all: my_printf_static my_printf_dynamic
.PHONY: clean
clean:
$(RM) $(OBJ)
.PHONY: fclean
fclean: clean
$(RM) $(SNAME) $(DNAME)
.PHONY: re
re: fclean all
Thanks!
Your makefile can be boiled down to this:
NAME := libmy_printf_$(shell uname -m)-$(shell uname -s)
SNAME := $(NAME).a
DNAME := $(NAME).so
SRC := $(wildcard src/*.c)
OBJ := $(SRC:.c=.o)
CFLAGS := -ansi -pedantic -Wall -W -Werror -g3 -fPIC
LDFLAGS := -L.
LDLIBS := -l$(...)
.PHONY: all clean fclean re
all: $(SNAME) $(DNAME)
$(SNAME): $(OBJ)
$(AR) $(ARFLAGS) $# $^
$(DNAME): LDFLAGS += -shared
$(DNAME): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
clean:
$(RM) $(OBJ)
fclean: clean
$(RM) $(SNAME) $(DNAME)
re: fclean all
There are multiple things that you should know:
Don't use back-quotes commands, use the $(shell) built-in function in conjunction with the := assignment operator to prevent commands being re-run multiple times (unless this is the desired behavior).
Use only one .PHONY special rule, placed above all rules, and list them there.
Redefining $(CC) or $(RM) variables like you did is pointless since they already contain what you wanted here.
You wrote -l$(NAME) but you didn't define a NAME variable. I change it to $(...) since I couldn't guess what you really wanted here, don't forget to handle this.
Use the name of the targets to be created as the name of the related rules. That way Make won't recreate the targets unless you really want it (by calling the clean, fclean or re rules explicitly).
-L flags and -l flags should not be mixed in the same variable, unless placed at the right place in the linking command. Actually you didn't even used them. I explicitly separated them in the LDFLAGS and LDLIBS built-in variables, as per Make implicit rules.
If you have any questions, go ahead.
As discussed in the comments, if you need to remove the -fPIC flag from the compilation flags for the static library, you should consider building object files in different directories:
EDIT: I added your my_printf_static and my_printf_dynamic rules:
NAME := libmy_printf_$(shell uname -m)-$(shell uname -s)
SNAME := $(NAME).a
DNAME := $(NAME).so
SRC := $(wildcard src/*.c)
SDIR := build-static
SOBJ := $(SRC:src/%.c=$(SDIR)/%.o)
DDIR := build-shared
DOBJ := $(SRC:src/%.c=$(DDIR)/%.o)
CFLAGS := -ansi -pedantic -Wall -Werror -W -g3
LDFLAGS := -L.
LDLIBS := -l$(...)
.PHONY: all clean fclean re my_printf_static my_printf_dynamic
all: my_printf_static my_printf_dynamic
my_printf_static: $(SNAME)
my_printf_dynamic: $(DNAME)
$(SNAME): $(SOBJ)
$(AR) $(ARFLAGS) $# $^
$(DNAME): CFLAGS += -fPIC
$(DNAME): LDFLAGS += -shared
$(DNAME): $(DOBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(SDIR)/%.o: src/%.c | $(SDIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
$(DDIR)/%.o: src/%.c | $(DDIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
$(SDIR) $(DDIR):
#mkdir $#
clean:
$(RM) -r $(SDIR) $(DDIR)
fclean: clean
$(RM) $(SNAME) $(DNAME)
re: fclean all
What to do so it only creates object files inside the /src directory ?
Don't run the compiler twice. Your STATIC and DYNAMIC rules both depend on $(OBJ), which will cause those files to be built by make's implicit rules. Then, immediately after that you run the compiler again within those rules. Just take those lines out. make normally prints the commands it's going to run, so you should see why it's happening in your build log.

Resources