paint-brush
How to Create a Library in C with a Makefileby@zhadan
3,626 reads
3,626 reads

How to Create a Library in C with a Makefile

by Anatolii ZhadanAugust 27th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

A Makefile is a file that lists all the filenames that must contain necessary functions. The first line of the Makefile creates a variable “ESES” which lists the files containing the functions. In the second line, a new variable ‘OFILES’ is created which is assigned the values from “CFILES,” but with their extensions changed.
featured image - How to Create a Library in C with a Makefile
Anatolii Zhadan HackerNoon profile picture

Hello everyone, I’d like to discuss creating a library using a Makefile in C with some practical examples.


Firstly, let’s understand how Makefile works in general:

targets: prerequisites
 command
 command
 command


The rules in a Makefile involve specifying filenames as targets, which are typically singular. The commands to implement the process must begin with a tab character instead of spaces. Prerequisites or dependencies are also mentioned in the form of filenames that must be available before executing the commands for the target.


For instance:


test: test.c
 cc test.c -o test


In this case, the Makefile checks if a file named “test” exists. If it doesn’t, it then checks if a file named “test.c” exists. If “test.c” does exist, the Makefile executes a series of commands, typically compiling “test.c” and outputting the result into a file named “test”. If “test” is successfully created, subsequent attempts to use “make” will ignore this rule as “test” already exists.


**About variables:

\ Variables can only be strings. You typically use:=, but = also works. To use variables we need to write them inside${}or $().

test := dude

all:
 echo $(test) // will output "dude"
 echo ${test} // will output "dude"

 # Bad practice, but works
 echo $test // will output "dude"


**Rules:

\ Rules allow you to modify the compilation settings in your Makefile:

  • CC: Program for compiling C programs; default cc
  • CXX: Program for compiling C++ programs; default g++
  • CFLAGS: Extra flags to give to the C compiler
  • CXXFLAGS: Extra flags to give to the C++ compiler
  • CPPFLAGS: Extra flags to give to the C preprocessor
  • LDFLAGS: Extra flags to give to compilers when they are supposed to invoke the linker


CC = gcc
CFLAGS = -Wall -Wextra -Werror
// Our Makefile will compile everything with the gcc compiler
// using the flags -Wall, -Wextra, and -Werror.


How to write a Makefile that can create a library

CFILES = test.c test2.c test3.c
  
OFILES = $(SRCS:.c=.o)

CFLAGS = -Wall -Wextra -Werror

NAME = libft.a

all: $(NAME) clean

$(NAME): $(OFILES)
     ar rcs $(NAME) $(OFILES) 

clean:
  rm -f $(OFILES)

fclean:  
  clean rm -f $(NAME)

re: fclean $(NAME)

.PHONY:  all clean fclean re


The first line of the Makefile creates a variable “CFILES”, which lists all the filenames that contain necessary functions for the library. It’s a good practice to use all uppercase letters for variables in Makefiles for easy readability. In this case, “CFILES” is assigned the values “test.c”, “test2.c”, and “test3.c”, which correspond to the files containing the required functions.


OFILES = $(SRCS:.c=.o)


In the second line, a new variable “OFILES” is created which lists the filenames from “CFILES”, but with their extensions changed from “.c” to “.o”, since the resultant files will be object files. Object files contain machine code specific to each source code file in the program and in our case are created by the “ar” command.


CC = gcc
CFLAGS = -Wall -Wextra -Werror


The next two lines “CC = gcc” and “CFLAGS = -Wall -Wextra -Werror” represent the rules that I described earlier in this article.


NAME = libft.a


The line “NAME = libft.a” creates a new variable “NAME”, which will contain the name of the output file we want to obtain at the end of the Makefile execution. Here, the expected output file is a library named “libft.a”.


all: $(NAME) clean


The target “all” defined in the next line is the default target used to build the main product of the compilation process. This target checks if the file specified in the “NAME” variable exists. If it doesn’t, the Makefile moves to the next line, “$(NAME): $(OFILES)”, to start creating the file. After the file is created, the “clean” target is used to delete all files with the “.o” extension.


The next Makefile rule checks if the file specified in the “NAME” variable exists. If it doesn’t, the Makefile moves to the next line, which checks for any “.o” files specified in the “OFILES” variable. If present, the Makefile uses the “ar” command to create an archive file, which becomes our static library. The “r”, “c”, and “s” flags are used with the “ar” command to replace or add files to the archive, create the archive if it doesn’t exist, and write an index into the archive for faster searches. It’s generally a good idea to include the “s” flag when creating a static library with “ar”, especially if the library contains many object files. Ultimately, the Makefile creates an archive named “libft.a” containing all “.o” files, thereby creating our static library.


clean:
  rm -f $(OFILES)


The next line defines a target “clean”, which allows us to remove all the object files generated during the compilation process. The “rm -f” command is used to remove files without prompting for confirmation, and the “$(OFILES)” variable specifies the list of object files to be removed. By running “make clean”, we can remove all “.o” files, ensuring a clean state for the next compilation.


fclean: clean 
  rm -f $(NAME)


The target “fclean” depends on the “clean” target. When we run “make fclean”, it first runs “make clean” to remove all object files, and then removes the library file specified in the $(NAME) variable using the “rm -f” command.


re: fclean $(NAME)


The “re” target in the Makefile stands for “rebuild”. When we rebuild, we first delete all “.o” files and our library file, and then start a fresh compilation of our library using the $(NAME) target.


.PHONY:  all clean fclean re


The “.PHONY” target is used to list targets that do not represent files but are actions that can be performed by make. Declaring a target as “.PHONY” informs make that it’s not associated with a file, so make won’t attempt to find a file with that name. This ensures that the target’s recipe is always executed, irrespective of the presence of a file with the same name as the target. It’s a good practice to declare targets that don’t represent files as “.PHONY” to avoid any unexpected behavior in the Makefile. Without it, implicit rules might attempt to build the executable.


More information about Makefiles:

  1. makefiletutorial.com


Finish

I hope you understood the instructions correctly. If you have any edits to this article, I will be waiting for your comments. If I really helped you, I kindly ask you to subscribe to my LinkedIn and provide feedback on this article.


Also published here.