After completing this lab, students should be able to:
gcc
This first lab is primarily focused on logistics and supporting software that we'll make use of in future labs this semester. If you're not sure how something works, or how to check whether you've completed some task correctly, be sure to ask!
fourier.cs.iit.edu
At this point you should've received an e-mail containing login credentials for
fourier.cs.iit.edu
, a CS department Linux server on which you work on machine
problems for this class. Using Fourier will ensure a consistent working
environment, and we will be using a comparable system to evaluate your
submissions.
To log in to Fourier, you need an SSH client. At a terminal on most computers running Linux, macOS, or Windows 10, you can do this with the command:
ssh username@fourier.cs.iit.edu
On Windows you may need to first install the Windows Subsystem for Linux (WSL). You can find instructions here.
If you are off-campus, you must first connect to the IIT VPN before trying to SSH into Fourier. You can find instructions on how to do this at https://ots.iit.edu/network-infrastructure/vpn-remote-access. If you haven't yet had a VPN account provisioned for you, email me to request one.
Git is a distributed version control system, and GitHub is a Git repository hosting service. You'll use the first to manage all your code and the second to share it with me. There are a few steps you need to follow to obtain the starting codebase for each assignment; some of them you only need to do once (i.e., for this first lab). We'll walk you through the steps below.
Start by claiming your repository for this lab on GitHub via the invitation on the class website (found in the "Repo invite" column of the machine problem list). You'll be prompted to create an account on GitHub (or sign in, if you already have one), and then you'll be asked to pick your username from a list --- please locate and select your IIT Hawk ID and accept the assignment. When the process is complete, you should be taken to a URL that looks something like this (where USER is your own GitHub username):
https://github.com/cs351/mp-prelim-USER
This is your repository's homepage on GitHub. You can always come back here to see the status of your work as reflected on GitHub. When we ask you to submit work you will push it here, and we will pull work from this GitHub repository for grading. Remember, if your work isn't here we can't see it!
Note that while you can technically edit/upload files via the web interface on GitHub, we strongly recommend you not do so. Instead, you should use the command line tools we'll be discussing shortly to clone the repository on a separate server and do all your work there.
In order to access and clone (i.e., make a copy of) your repository on Fourier, you need to create a key-pair and register your public key with GitHub.
When logged in to Fourier, run the following command:
ssh-keygen -t rsa -b 4096
This will start the process of creating a key-pair. You will be prompted for a bunch of values -- just keep hitting Enter to accept the default values until the program completes and you see output that looks like this:
Your identification has been saved in /home/jdoe/.ssh/id_rsa.
Your public key has been saved in /home/jdoe/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:nZpJhBSQ5s6g5g+EmMmMQUBoTvgnOhFKHgv1peAN5WI jdoe@fourier.cs.iit.edu
The key's randomart image is:
+---[RSA 4096]----+
|*++.oo+. |
|** *o+ . |
|XoEo= . . |
|OO+.o . . . |
|*B * S o |
|=. o . + |
|oo + |
| .. |
| .. |
+----[SHA256]-----+
Now type the following command to view the newly generated public key:
cat ~/.ssh/id_rsa.pub
You should see output that looks something like this (but longer):
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCz8psHIjcPSjOoJeMtpBnhplPUkJAbHkXvZMdAWJNmvWz4rQFpJUfa2HwJXWxSw== jdoe@fourier.cs.iit.edu
Copy all of the output (starting with the "ssh-rsa" and ending with the address) to your clipboard for use in the next step.
When logged into GitHub, click on your avatar (in the top right corner) and click "Settings." From here, click on "SSH and GPG keys" in the left panel. Then click "New SSH key", and paste the output you copied in the previous step into the "Key" text area. Type "Fourier" into the "Title" field for identification purposes. Finally, click "Add SSH key" to complete this step. If all goes correctly, you should see something like this in your SSH key list:
Now that you've added your key to GitHub, you can authenticate with GitHub and clone your repository from Fourier. Run the following command, replacing the "REPOS-NAME" with your repository's name (which is the last part of your repository's homepage URL from the earlier step -- it should look something like "mp-prelim-USER"):
git clone git@github.com:cs351/REPOS-NAME.git
If successful, this will produce output like the following:
remote: Enumerating objects: 151, done.
remote: Counting objects: 100% (151/151), done.
remote: Compressing objects: 100% (103/103), done.
remote: Total 151 (delta 6), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (151/151), 3.59 MiB | 3.10 MiB/s, done.
Resolving deltas: 100% (6/6), done.
Checking connectivity... done.
The command will also create a folder named "mp-prelim-USER", in which you will find starter files for this lab.
For each future lab, you will need to repeat some of the steps above to obtain the starter code. You will not, however, need to generate a new key-pair. You will also not need to register your public key with GitHub again.
Now that you have a copy of your repository on Fourier, you can navigate to and within it using the cd
command (which stands for "change directory"). For example, if your repository is named "mp-prelim-USER", you can navigate to it by running the following command:
cd mp-prelim-USER
If you then type ls
(which stands for "list"), you should see a list of files in your repository. For this lab, you should see:
Makefile README.md hello.c hello.h main.c
You can type pwd
(which stands for "print working directory") to see the full path to the current working directory. You should see something like:
/home/class/spring-23/cs351/USERNAME/mp-prelim-USER
Everything under the directory /home/class/spring-23/cs351/USERNAME/
is owned by you. We call this your "home directory". Notice that you're currently sitting in a subdirectory of your home directory called "mp-prelim-USER". You can navigate back to your home directory by running the following command:
cd ..
The ".." means "the parent directory of the current directory".
Go back into the "mp-prelim-USER" directory by running the following command:
cd mp-prelim-USER
To display the contents of a file, you can use the cat
command. For example, to display the contents of the "main.c" file in your repository, you would run the following command:
cat main.c
You can also use the less
command to scroll through the contents of a file that won't fit on a single screen. For example, to display the contents of the "README.md" file in your repository, you would run the following command:
less README.md
You can use the arrow keys to scroll around, and press q
to quit.
For more information on navigating the filesystem via the command line, you can read this tutorial.
To edit a file, we suggest using the vi
text editor. Before proceeding, know that it will take some time to learn and get used to vi
. We think it's well worth investing the time to learn it well, as you will likely find yourself in a situation in the future where you need to edit a file on a remote server, and vi
is both ubiquitous (in the UNIX world) and powerful.
To edit the main.c
file with vi
, you would run the following commands (assuming you are already in the mp-prelim-USER
directory):
vi main.c
If you've never used vi
before, don't hit any keys just yet!. You can move up and down in the file with the k
and j
keys, and you can move left and right with the h
and l
keys. To exit vi
, press the ESC
key, then type :q!
and press ENTER
.
When you have the time to dedicate an hour or so to learning the basics of vi
, type the following command:
vimtutor
This will open a tutorial that will teach you the basics of vi
(technically, of vim
, which is a more powerful but fully compatible vi
clone).
In the main.c
source file, you'll find a header comment at the top, which you should edit and replace with your own name, IIT email address, AID, and today's date. Save your work.
To submit your work, you must first commit your changes to the repository. Do this using the following command:
git commit -am "Signing honor pledge"
What this command does is save a record of your changes to the repository, annotated with the message in quotes. Every time you make a substantive set of changes to one or more files in your repository you should commit your work with a short, descriptive commit message.
One great thing about commits is that you can easily compare the differences between them (or between a commit and the current state of your files, known as the "working tree"). This can be a real lifesaver if you accidentally introduce a bug into your code and want to see what you changed since the last commit, or even just roll back all your changes entirely.
Commits are not automatically sent to GitHub, however. To do that, you need to run this next command:
git push
On success, the command should produce output like the following:
Counting objects: 5, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 318 bytes | 0 bytes/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To https://github.com/cs351/mp-prelim-USER.git
b0bf469..9b59620 master -> master
This tells you that all your commits have now been synchronized with your repository on GitHub, and we can pull them for grading. In the future, you'll need to push your commits whenever you're done working on a given machine problem and want to make your work available to us.
You'll write code in the next machine problem. Promise. This time we'll skip that part and jump to the fun parts: building and running.
Let's try to compile "main.c". Run the command:
gcc main.c
Didn't work. (Why not?)
Try compiling "hello.c" with the command:
gcc hello.c
Shouldn't work, either. What gives?
Now try:
gcc hello.c main.c
Good. No errors. The compiler created an executable for you named "a.out", by default. Run it with:
./a.out
Try:
./a.out Overlord
We can of course rename the executable, but let's have the compiler put it in the right place for us. Run the commands:
rm a.out
gcc -o hello hello.c main.c
./hello Overlord
But what about the multi-stage compilation and linking process discussed in
class? This is actually going on behind the scenes already (try invoking gcc
with the -v
flag to see what it's doing). To build the intermediate object
files and link them together in separate steps, do:
gcc -c hello.c
gcc -c main.c
gcc -o hello hello.o main.o
See how we're referring to the ".o" files in the third step? Those are the
object files that were generated when we invoked gcc with the -c
flag, which
tells it to stop before the linking step.
If the project being built is complex enough, it may be necessary to separate the final linking step from the creation of a multitude of intermediate object files. Manually invoking the compiler in such situations is a real pain, and definitely not something we'd want to keep doing!
Enter the standard C build tool: make
make
is used to automate builds. Try it out (first, we delete the files we
previously built):
rm -f *.o hello
make
This should produce output like the following:
gcc -g -Wall -O2 -c -o hello.o hello.c
gcc -g -Wall -O2 -c -o main.o main.c
gcc -g -Wall -O2 -o hello hello.o main.o
Woohoo! There's automation for you! Try it again:
make
You should get:
make: Nothing to be done for `all'.
make
knows that no files have changed since we last build the executable,
see. We can update the timestamp of one of the files (using touch
) to force a
rebuild:
touch hello.c
make
See how it only rebuilds one of the intermediate object files? Pretty nifty.
We can also ask it to run a test for us:
make test
Which produces output:
Running test...
./hello tester
Hello tester!
And finally, to clean up the files we've generated:
make clean
The last command makes use of the rm
command to remove/delete files.
make
is not magical, of course --- it makes use of the provide "Makefile" to
determine what action(s) to take, depending on the specified target. For
reference, here are the contents of said Makefile. Note the four targets (all
,
hello
, test
, and clean
), two of which we invoked explicitly before --- the
first target (all
), it turns out, is used by default when we ran the make
command with no argument.
CC = gcc
CFLAGS = -g -Wall -O2
SRCS = hello.c main.c
OBJS = $(SRCS:.c=.o)
all: hello
hello: $(OBJS)
$(CC) $(CFLAGS) -o hello $(OBJS)
test: hello
@echo "Running test..."
./hello tester
clean:
rm -f $(OBJS) hello
You should be able to make some educated guesses as to what's going on in this file, but you should also skim through the Introduction to the GNU make
manual for details.
We will provide you with a Makefile for each machine problem, but it will be helpful to understand what's going on when you use make
commands in the future!
To earn points for this machine problem, you must have signed and committed your "main.c" file and pushed your changes to the GitHub repository. The procedure for doing this is similar in most future labs, so be sure you know how to do this!
Procedural takeaways:
git commit -am "Commit message"
), and push to submit (git push
)make
to build, and often times some variation on make test
to run a
hardcoded test.