Files to Find, Libraries to Compile

A guide to finding c source files with Bash and compiling them with gcc

Compilation process

I have a problem: there are all of these directories with all these “.c” files in them, and I have to find specific ones to compile into a static library. However, I’m lazy and I’d rather spend more time now to do less work later. I’m also allergic to tedium, so I’d rather do something that seems more complicated, so I don’t get bored with a repetitive task like manually finding files and manually copying them over. That being said, here’s how I figured out a way to use Bash commands to do that work for me.

First, I navigate to the root of my repository. cd ~/holbertonschool-low_level_programming

I create a directory called “allCfiles” with a child directory called “libToBuild” mkdir -p allCfiles/libToBuild. allCfiles is created with libToBuild nested inside of it.

I look for all files in my current directory and all sub-directories whose name ends in “.c” and execute a copy command from within find. I do this with the -exec cp flag. The -exec flag tells the find command to execute a command on each item found. The copy command copies each object found (denoted as '{}') into the allCfiles directory. *note: the {} and ; need to be escaped like this: '{}' and \; to escape interpretation by the shell.

find . -depth -type f -name "*.c" -exec cp '{}' allCfiles \;

The output of the command without -exec cp '{}' allCfiles \;looks like this:

-exec cp '{}' allCfiles \; operates on each file found in this manner and copies them into the directory allCfiles .

List of the allCfiles directory

I copy-paste a list of files to find into an empty text file and call it files_to_find

Because all of the lines in the file files_to_find end in .o, and all my files end in .c, I need to strip the .o off of each line. Why I do this is to use grep pattern matching. grep "9-strcpy.o" files_to_find will not match 9-strcpy.c, and I will not get any results.

I get around this with sed: sed -i 's/.o//g' files_to_find . Sed is a stream editor, and can manipulate text streams with regular expressions, as exemplified here 's/.o//g' This expression translates as “for all strings, if the pattern .o is found, replace it with nothing.”

The option -i for sed changes the output on the file itself, instead of printing it to the standard output. The last argument is the target file name.

The following example is the command sed 's/.o//g' files_to_find but it does not have the -i flag, so it will not edit the file directly.

This enables grep to find 0-isupper.c by using the pattern: 0-isupper

At this point, I am still in my repository root at ~/holbertonschool-low_level_programming . The files_to_find file is in here with me, and the new directory I am populating is in a couple directories below me. ~/holbertonschool-low_level_programming/allCfiles/libToBuild

I navigate into allCfiles with cd allCfiles and bring files_to_find with me: mv ../files_to_find . The double dot tells mv that my file is in the parent directory, and I specify that I want to move it in my current directory with the . at the end of the command.

I call the copy command with a shell expansion containing my grep comparison commands to copy all of the matching files into the target directory : cp $(ls -1 | grep -F -f files_to_find) libToBuild

The expansion ls -1 allCfiles | grep -F -f files_to_find returns the following result

When combined with the cp command in a shell expansion $(), the command moves all of the files into the directory allCfiles/libToBuild.

Lastly, I navigate to the directory that contains all of my files:

cd libToBuild

A summary of the commands:

cd ~/holbertonschool-low_level_programming

mkdir -p allCfiles/libToBuild

find . -depth -type f -name "*.c" -exec cp '{}' allCfiles \;

sed -i 's/.o//g' files_to_find

cd allCfiles && mv ../files_to_find .

cp $(ls -1 | grep -F -f files_to_find) libToBuild

cd libToBuild

Finding Prototypes

Our next step is to create the header file for source files that require prototypes from other files. In this case, an essential function to my library is _putchar.c and some other functions require it.

Lucky for me, I was provided with a list of prototypes.

I copy-pasted this list into a text file and named it holberton.h

Compiling a Static Library

Now that we have our required files in our directory, and have generated out header file, it’s time to compile them and turn them into a static library. To do that, we need gcc and ar

(if you want more clarification about these commands, run man gcc and man ar, though man gcc is very dense, as it contains many customization options for compilation.)

The commands are fairly simple, now that our files are in place. First, compile all c source code with gcc -Wall -Werror -Wextra -pedantic -c *.c Most of the flag options are for error checking, but the -c flag is important. It runs all c files in the current working directory through the preprocessor, then the assembler, and stops at the compiler. The files that are generated here end in .o and are binary files that contain machine code.

The final step is to link all of the machine code files together into a library, where all functions from all of the source files can be called from.

We do this with ar -rc liball.a *.o

Lets break this down. The command ar creates, modifies and extracts from archives. The flag -rc tells ar to create a new archive, and append any new files. liball.a is the name for our new library. *.o tells ar to include all machine code files in the current working directory.

The output is the static library liball.a and can be used to compile another program. All custom functions are compiled to object code in one file and are portable, meaning you can move them between systems. To use this library when compiling another source file, use the command,

gcc main.c -L. -lall

The library is specified as a flag -lall This is short for the actual filename, which begins with lib###.a and ends with .a . Hence, l for lib and all for the name of the library.

— Happy Hacking!