You'll write plenty of code this semester, but before you get to it you first need to learn how to edit, test, and submit your work. We'll cover those steps here.
To keep this writeup short we'll do lots of interactive demonstrations, but the critical steps and commands will be documented for future reference.
Ideally, you should work on the CS department Linux course server --- fourier.cs.iit.edu --- to write, test, and submit all your work. This will ensure a consistent working environment. You might get away with working on machine problems on your own computer if you have Linux and a similar toolchain to that installed on fourier, but if you run into issues you'll be on your own.
Machine problem writeups will always assume you're working on fourier. So to start: let's get logged in.
After obtaining your login information (your username is typically the same as the one you use for MyIIT, and your password is set to your CWID), you'll log in to fourier with an SSH client. At the terminal, you can do this with the command:
On Windows you may need to start up a separate SSH client such as PuTTY if you don't have a command line client.
After logging in, resist the overwhelming urge to immediately start being productive! Repeat after me: TERMINAL MULTIPLEXERS ARE AWESOME. You'll learn about them next.
MPs require multiple coding, testing, and debugging sessions to complete, which require multiple corresponding login sessions on fourier. Each time you login, you'll have to navigate into the appropriate work directory, re-open source file(s) in editors, start up debugging sessions, run trace files to obtain output, etc. All before you can even think about getting work done.
All the above constitute annoying cognitive overhead, and we can all use less of that (especially given how difficult it is for most students to allocate time to sit down and work on coursework in the first place).
Wouldn't it be great if you could set things up --- your editing windows, debuggers, trace output position --- just the way you like it, one time, then have it remain that way each time you log back in? Heck yeah. That's where terminal multiplexers fit in.
Technically, a terminal multiplexer is a program that:
Less technically, a terminal multiplexer = bliss.
To start tmux, just use the command
tmux. I'll walk you through a few demo
sessions, but the important keys are as follows:
C-b: default prefix key (prefixes all following command keys)
c: create a new window
p: change to the next/previous window
": split the current window
<space>: arrange the panes in the current window according to some preset
o: change to the next pane in the current window
!: break the current pane out of the current window
?: list all key bindings
d: detach from tmux
After detaching from a session, the following command will re-attach you to it (given that you only have one tmux session running):
You should NOT create a new tmux session (with the command
tmux) each time you
log in. Instead, you need only do this once --- on subsequent logins you will
simply reattach to your existing session with
So... Logged in: check. tmux session attached: check. Moving on.
Here are the steps for setting things up --- we'll walk you through them interactively.
Clone the private repository (which is owned by the instructor) --- you must've accepted an e-mail invitation from the instructor and connected the repository to either a pre-exising or new account on Bitbucket for this to work:
git clone https://bitbucket.org/michaelee/cs351-semester-username.git
Set your "upstream" repository to the official public course repository (also owned by the instructor's account). This makes it so you can grab updates from the official repository in the future.
cd cs351-semester-username git remote add upstream https://bitbucket.org/michaelee/cs351.git
Fetch the initial contents of the public repository, merge them into your local branch, then push them to your private repository:
git fetch upstream git merge upstream/master git push origin master
Now you can make changes to files within your local repository and commit them. Add your information to the "README.md" file (you can scan ahead to the section on editors if you need some help choosing a suitable text editor), then run this command:
git commit -am "Adding information to README"
Now push your commit to the private repository on Bitbucket:
git push origin master
This previous step is effectively how you "submit" your work, as I will be
pulling from this private shared repository after the submission deadline to get
your changes. If you don't
push, I won't see (and can't grade) your work!
That said, you don't need to
push after each
commit. I recommend you
frequently while coding --- a
push will send all commits you've made to the
Finally: before starting each machine problem, you should make sure all your
local changes are commited, then fetch and merge any additions to the public
course repository with
git fetch upstream; git merge upstream/master. Note
that when you execute the
merge step, you may be dropped into a text editor to
edit a merge message --- quitting with the default message is just fine. We'll
remind you to do this in each machine problem writeup.
A good programmer has strong opinions about programming languages, coding conventions, and text editors. There are only two UNIX text editors worth learning, and learning well: Emacs and Vim. Both are installed on fourier. Pick one and learn a new feature every day.
If you can't be bothered investing time and energy into a tool that you'll use more than any other and will save you countless keystrokes and hours, then use the nano editor instead. It's also on fourier. I don't recommend it.
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.
For the rest of this machine problem you'll work in the "
of your repository. In there, you'll find these files:
Let's try to compile "main.c" (the
$ indicates the shell prompt, and is
followed by the command you should enter):
$ gcc main.c /tmp/ccbmZeXz.o: In function `main': main.c:(.text+0x24): undefined reference to `say_hello_to' main.c:(.text+0x30): undefined reference to `say_hello_to' collect2: ld returned 1 exit status
Didn't work. (Why not?)
Try compiling "hello.c":
$ gcc hello.c /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../lib64/crt1.o: In function `_start': (.text+0x20): undefined reference to `main' collect2: ld returned 1 exit status
$ gcc hello.c main.c
Good. No errors. The compiler created an executable for you named "a.out", by default. Run it:
$ ./a.out Hello world! $ ./a.out Master Hello Master!
We can of course rename the executable, but let's have the compiler put it in the right place for us:
$ rm a.out $ gcc -o hello hello.c main.c $ ./hello Overlord Hello Overlord!
But what about the multi-stage compilation and linking process we discussed in
class? Never fear: 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
$ rm -f *.o hello $ make 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 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
$ touch hello.c $ make gcc -g -Wall -O2 -c -o hello.o hello.c gcc -g -Wall -O2 -o hello hello.o main.o
See how it only rebuilds one of the intermediate object files? Pretty nifty.
We can also ask it to run a test for us, then clean up generated files:
$ make test Running test... ./hello tester Hello tester! $ make clean rm -f hello.o main.o hello
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 (
clean), two of which we invoked explicitly before --- the
first target (
all), it turns out, is used by default when we ran the
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
To earn the points for this machine problem, you must have pushed the contents of the public course repository to your private one on Bitbucket. This is also a prerequisite for working on all subsequent machine problems, so please make sure you know how to do this!
And the big procedural takeaways:
(git fetch upstream ; git merge upstream/master)(you may need to edit/save a merge message).
git push origin master
maketo build, and often times some variation on
make testto run a hardcoded test.