Machine problem: Preliminaries

Objectives

After completing this lab, students should be able to:

  1. Log in to Fourier (the course server) using SSH
  2. Accept a GitHub Classroom invitation and clone the associated repository
  3. Navigate the filesystem and edit files on Fourier via the command line
  4. Commit and push changes to a Git repository
  5. Compile C code with gcc
  6. Use make to run tasks automatically

Overview

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 / GitHub

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.

Accept the lab invitation

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.

Generate your key-pair

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.

Register your public key with GitHub

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:

SSH key list

Clone your repository

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.

Git / GitHub summary

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.

Navigating the filesystem

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.

Editing files

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).

Signing your honor pledge

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.

Building

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

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.

Makefiles

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!

Finishing up

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:

  1. Commit often (git commit -am "Commit message"), and push to submit (git push)
  2. make to build, and often times some variation on make test to run a hardcoded test.