Last Updated: 2021-01-28 Thu 14:03

CSCI 4061: HW01 Compiling C Programs, Makefiles

CODE DISTRIBUTION: hw01-code.zip

  • Download the code distribution every HW
  • See further setup instructions below

CHANGELOG: Empty

1 Rationale

Accessing a computer's capabilities via the command line is an essential tool in a computer scientist's toolkit. When logging into a remote system, non-graphical tools are the faster and often only option to interact. These exercises will gain you basic familiarity with editing short shell scripts on a Unix system to automate basic tasks. It will also review how to edit basic C programs, compile them, and run them.

Completing this HW will familiarize students with the following.

  • cd, ls, rm
  • gcc to compile C programs
  • make and Makefiles

1.1 Associated Reading

Basic knowledge of C programming and the Unix command line is assumed already. If this is your first time working with such things, follow the instructions closely and ask a LOT of questions. This HW is meant to be self-describing so it is likely you won't need too much extra reading but use your best judgment.

Unix Environment

Most coursework will be done in a Unix environment. It's a good idea to find an efficient way to access such environments to support your workflow. The following tutorial offers some options such as remote access and local installations to suite several situations.

Tutorial: Accessing a Unix Environment

Command Line Work

Our textbooks deal with shell commands only tangentially so are not a great source for this set of exercises. Information on shell programming is distributed throughout the texts associated with various topics so they are not good for a beginning text

Instead, try Ryan's Tutorials: Linux Tutorial for a reasonably concentrated introduction of command line tricks and shell programming.

C Programming

If this is your first time working with C, find a tutorial online to work through. If you are coming form another programming language, you may want to search for a tailored tutorial such as

c programming for java programmers

Makefiles

This HW will teach you the basics of make and Makefiles on its own but it is a deep and powerful to solve a certain class of problems. The manual for GNU make is an extensive source of information on it. You may wish to peruse it for additional details.

1.2 Grading Policy

Credit for this HW is earned by taking the associate Quiz which is linked under Gradescope. The quiz will ask similar questions as those that are present in the QUESTIONS.txt file and those that complete all answers in QUESTIONS.txt should have no trouble with the quiz.

See the full policy in the syllabus.

2 Download Code and Setup

Most HWs come with some code that you need to use, which is linked at the top of the HW specification in a ZIP file (compressed archive). After downloading the HW code, unzip it and get to work. The files in this HW's zip are

hw01-code
|-- collatz                              Directory with code for Problem 1
|   |-- collatz_funcs.c
|   |-- collatz.h
|   |-- collatz_main.c
|   |-- test_collatz.c
|   |-- test_collatz.org
|   |-- testy
|   |-- Makefile
|   `-- Makefile-commented
|-- mat-product                          Directory with code for Problem 2
|   |-- free_matrix.c       
|   |-- mat.h               
|   |-- op_main.c           
|   |-- outer_product.c     
|   |-- test_outer_product.org
|   |-- testy
|   `-- Makefile                         CREATE this file
`-- QUESTIONS.txt                        Fill with answers, paste into Gradescope

Here is an example terminal session illustrating the use of the unzip utility to unpack the provided zip.

# Print the working directory
>> pwd
/home/kauffman
# Most terminals/shells start in your home directory

# I downloaded hw01-code.zip to the Downloads folder so I'll change
# there. You may need to change to a different directory
>> cd Downloads

# There is the zip along with a few other things
>> ls
entr-3.8.tar.gz  eradman-entr-592856d50559  hw01-code.zip

# The unzip command will upack the contents of hw01-code.zip into a
# usable folder
>> unzip hw01-code.zip 

Archive:  hw01-code.zip
   creating: hw01-code/
   creating: hw01-code/collatz/
  inflating: hw01-code/collatz/Makefile  
  inflating: hw01-code/collatz/Makefile-commented  
  inflating: hw01-code/collatz/collatz_main.c  
  inflating: hw01-code/collatz/collatz_test.c  
  inflating: hw01-code/collatz/coll  
  inflating: hw01-code/collatz/collatz.h  
  inflating: hw01-code/collatz/collatz.c  
  inflating: hw01-code/QUESTIONS.txt  
   creating: hw01-code/mat-product/
  inflating: hw01-code/mat-product/op_main.c  
  inflating: hw01-code/mat-product/test_outer_product.c  
  inflating: hw01-code/mat-product/mat.h  
  inflating: hw01-code/mat-product/free_matrix.c  
  inflating: hw01-code/mat-product/outer_product.c  

# The folder hw01-code was created, change there
>> ls
entr-3.8.tar.gz  eradman-entr-592856d50559  hw01-code	hw01-code.zip
>> cd hw01-code
>> pwd
/home/kauffman/Downloads/hw01-code

# These are files and directories relevant to hw01
>> ls
collatz  mat-product  QUESTIONS.txt

3 What to Understand

Ensure that you understand

  • The basic structure of a Makefile, the notion of target, dependency, and commands.
  • The idea that a Makefile can detect changes and only rebuild targets that depend on changed files.
  • Common conventions such as make clean and make test

4 Questions

The file QUESTIONS.txt contains a series of questions about basic Unix shell navigation and C compilation. You should open QUESTIONS.txt in your favorite text editor and fill in answers to each Problem and part. Specific instructions for each problem are in subsequent sections.

                            ________________

                             HW01 QUESTIONS
                            ________________


- Name: (FILL THIS in)
- NetID: (THE kauf0095 IN kauf0095@umn.edu)

Write your answers to the questions below directly in this text file.
HW quiz questions will be related to the questions in this file.


PROBLEM 1: Compile and Run via make
===================================

  Unzip the provided zip file and change into the `hw01-code/collatz'
  directory.


(A) Compiling and Running a C Program
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  The directory `collatz' contains a small C program that computes
  sequences of numbers according to the following convention:
  ,----
  | For an input number N
  | - If N is even, set N to N / 2
  | - If N is odd,  set N to 3*N + 1
  `----
  A variety of folks have conjectured that this sequence always reaches
  1 (see <https://en.wikipedia.org/wiki/Collatz_conjecture>) but and the
  code provide in the `collatz' directory terminates when this happens.

  We will simply be using the code here to refresh on compiling and
  running code.

  Issue the following compilation command to compile the program then
  run it starting the sequence at 17. Show your results.

  ,----
  | > gcc -o coll collatz_funcs.c collatz_main.c 
  `----


(B) Use of Makefiles
~~~~~~~~~~~~~~~~~~~~

  Compiling more than a few C files together is tedious if one must do
  so with individual `gcc' invocations. Instead, compiling and linking
  the files in a larger project is the purvey of a build system.

  On Unix, `make' is the classic tool used to automate the build
  process.  Instructions on what needs compiling and how to do so are
  written into a file, traditionally called a `Makefile'. `make' itself
  is a program which will read through the `Makefile' and execute the
  commands contained within. Several features are present in `make':
  - Users specify what needs to be done but `make' will figure out the
    order to do them in to avoid redundancy.
  - `make' will only recompiled programs if the source code has
    changed. If no changes have been made, it may report `Nothing to do'
    or `So-and-so is already up to date'.
  - `make' has many shortcuts built into it for common C compilation
    tasks so that files can be short. However, it can be used to
    automate many other types of task such as posting HTML pages
    associated with HWs.

  Below is a sample `Makefile' that is present in the `collatz'
  directory. It can be used to build the collatz program and has
  extensive comments to explain some of what is happening in the
  file. Study this carefully and ask questions of the lab staff if
  needed.

  ,----
  | # Makefile to create the collatz programs. To use, type
  | #   > make
  | # Which will create the collatz_main program. Alternatively
  | #   > make collatz_main
  | # will also create the collatz_main program.
  | #
  | # To create and run the test program use
  | #   > make test
  | #
  | # Makefiles are divided into "rules" comprised of target,
  | # dependencies, and commands to execute to finish the target.
  | # 
  | # target : dependency1 dependency2
  | #       command1
  | #       command2
  | #       @command3 which is not printed
  | #       command4
  | #
  | # Some targets are files to create like the "collatz_main" program or
  | # "collatz_main.o" file. Other targets are "virtual" which specify
  | # commands to run such as 'clean'. Some targets like 'test' are a
  | # combination of creating files and running commands: 'make test' will
  | # ensure several programs are created before running the automated
  | # tests.
  | 
  | CFLAGS = -Wall -g					       # variable holding options to the c compiler
  | CC     = gcc $(CFLAGS)					       # variable holding the compilation command
  | 
  | collatz_main : collatz_funcs.o collatz_main.o collatz.h	       # collatz_main is a program, depends on two files, is default target
  | 	$(CC) -o collatz_main collatz_funcs.o collatz_main.o   # when the other files are ready, compile collatz_main
  | 	@echo collatz_main is ready			       # issue a report that the program is ready
  | 
  | collatz_main.o : collatz_main.c collatz.h		       # collatz_main.o depends on two source files
  | 	$(CC) -c collatz_main.c				       # compile only, don't link yet
  | 
  | collatz_funcs.o : collatz_funcs.c collatz.h		       # collatz.o depends on collatz.c and collatz.h
  | 	$(CC) -c $<					       # compile, use shortcut $< which becomes "collatz.c"
  | 
  | test_collatz : test_collatz.c collatz_funcs.o collatz.h	       # builds a test program for collatz functions
  | 	$(CC) -o $@ $^
  | 
  | test : test_collatz collatz_main			       # runs automated tests, builds required programs first
  | 	@chmod u+x testy				       # ensures that the 'testy' script is executable
  | 	./testy test_collatz.org $(testnum)		       # runs automated tests
  | 
  | clean:							       # No dependencies: clean is the de facto cleanup target
  | 	@echo Cleaning up object files			       # Report what's happening
  | 	rm -f *.o collatz_main				       # Remove all the .o files and programs
  | 
  | clean-tests:						       # target to remove temporary files associated with testing
  | 	@echo Removing temporary test files
  | 	rm -f *.o test_collatz test-results
  `----

  Type the command `make' and paste the results of this command below.
  Run the program that results from this compilation and show it below.


(C) Makefile Targets
~~~~~~~~~~~~~~~~~~~~

  The default target in a Makefile is the first one to appear but other
  targets can also be reference explicitly.  Two common targets we will
  make use of are
  - `make clean' : remove all compiled files (and programs)
  - `make test' : run some automated tests associated with the project

  Run these two commands and explain how they relate to the commands
  that appear in the `Makefile'. Note that many assignments in this
  class will feature some sort of testing target which will run
  automated tests of the correctness of a program.


(D) Testing Code
~~~~~~~~~~~~~~~~

  Throughout the course we will utilize a small testing framework called
  'testy' which is provided with the HW code. This script is what
  actually runs tests and reports results when one types `make test' as
  evidenced by the output of make:
  ,----
  | > make test                     # build and run tests
  | gcc -Wall -g -c collatz_funcs.c # compiling code
  | ...
  | ./testy test_collatz.org        # run the 'testy' script on the tests in 'test_collatz.org'
  | ============================================================
  | == testy test_collatz.org : collatz_func.c and collatz_main.c tests 
  | ...
  `----

  By default results, testy reports testing failures and puts verbose
  output in files that are noted while it runs. for example Test 1 fails
  and the results for the failure are showing the file indicated:
  ,----
  | > make test
  | ...
  | 1)  collatz_next(1)              : FAIL -> results in file 'test-results/collatz-01-result.tmp'
  |                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  `----

  These results can be examined in any text editor. One quick way to
  examine them in a terminal is with the `less' text viewer.  Try typing
  ,----
  | > less test-results/collatz-01-result.tmp
  `----
  Useful keybindings in `less' are

  ------------------------------------------------
   Key       Effect in less                       
  ------------------------------------------------
   q         quit less and return to the terminal 
   spacebar  scroll down a page                   
   u         scroll up a page                     
   j         scroll down one line                 
   k         scroll up one line                   
  ------------------------------------------------

  There are a few tricks associated with 'testy' that are worth
  knowing. To run individual tests for debugging purposes one can set
  the `testnum' environment variable.
  ,----
  | > make test testnum=5
  | ...
  | > make test testnum=12
  | ...
  `----

  If several tests are to be run, quote the test numbers as in the
  following. By default, only summary output is shown on the screen.  If
  the `SHOW' environment variable is set, then any failures are printed
  immediately.
  ,----
  | > make test testnum='3 5 12'
  | ...
  | > make test testnum='3 5 12' SHOW=1
  | ...
  `----

  In space below, paste the results of running the above `make' commands
  in a terminal.


(E) Compile based on Changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  Build systems are useful because they usually detect changes to files
  in a project and then recompile only those files that are dependent on
  the changes. This saves time on large projects with many files as not
  everything needs to be recompiled.  One can simulate a editing a file
  with the `touch' command which changes the 'timestamp' a file to look
  like it has been modified recently though its contents will not
  actually change.

  Issue the following sequence of commands and explain why each `make'
  compiles a different set of files.

  ,----
  | > make clean
  | 
  | > make
  | 
  | > make                          # intentional repeat
  | 
  | > touch collatz_funcs.c         # simulate edit of collatz_funcs.c
  | 
  | > make
  | 
  | > rm collatz_main
  | 
  | > make
  | 
  | > touch collatz_main.c          # simulate edit of collatz_main.c
  | 
  | > make                          
  | 
  | > make                          # intentional repeat
  `----


PROBLEM 2: Create a `Makefile'
==============================

  Change into the `mat-product' directory. This directory contains
  several C files.
  ----------------------------------------------------------------------------------
   File                    Notes                                                    
  ----------------------------------------------------------------------------------
   mat.h                   header file listing project functions                    
   outer_product.c         function that creates an outer product matrix            
   free_matrix.c           function that frees memory associated with a heap matric 
   op_main.c               main function which uses two C files above               
   testy                   test running script                                      
   test_outer_product.org  automated tests for outer_product.c                      
  ----------------------------------------------------------------------------------

  For this problem, create a `Makefile' that will build the project
  executables and run tests. The following make targets should be
  available.
  - `make': build the `op_main' executable which is dependent on
    functions in `op_main.c, outer_product.c, free_matrix.c'
  - `make test': builds the `op_main' program AND runs the automated
    tests.
  - `make clean': removes all `.o' files and all programs associated
    with the project.
  Ensure that Object files are built from each C file to enable
  efficient separate compilation and linking as is the case in the
  `collatz/Makefile'.

  A demonstration of the Makefile behavior is below.

  ,----
  | > make
  | gcc -Wall -g -c op_main.c
  | gcc -Wall -g -c outer_product.c
  | gcc -Wall -g -c free_matrix.c
  | gcc -Wall -g -o op_main op_main.o outer_product.o free_matrix.o
  | 
  | > ./op_main 3 1.1 2.2 3.3  2 1.1 2.2
  |     1.21     2.42 
  |     2.42     4.84 
  |     3.63     7.26 
  | 
  | > make clean
  | rm -f *.o op_main
  | 
  | > ./op_main 3 1.1 2.2 3.3  2 1.1 2.2
  | bash: ./op_main: No such file or directory
  | 
  | > make test
  | gcc -Wall -g -c op_main.c
  | gcc -Wall -g -c outer_product.c
  | gcc -Wall -g -c free_matrix.c
  | gcc -Wall -g -o op_main op_main.o outer_product.o free_matrix.o
  | ./testy test_outer_product.org
  | ============================================================
  | == testy test_outer_product.org : Tests for outer_product.c and op_main.c
  | == Running 5 / 5 tests
  | 1)  Singleton vectors    : ok
  | 2)  Small vectors 1      : ok
  | 3)  Small vectors 2      : ok
  | 4)  Size 1 for x, for y  : ok
  | 5)  Medium vectors       : ok
  | ============================================================
  | RESULTS: 5 / 5 tests passed
  | 
  | > make test
  | ./testy test_outer_product.org
  | ============================================================
  | == testy test_outer_product.org : Tests for outer_product.c and op_main.c
  | == Running 5 / 5 tests
  | 1)  Singleton vectors    : ok
  | 2)  Small vectors 1      : ok
  | 3)  Small vectors 2      : ok
  | 4)  Size 1 for x, for y  : ok
  | 5)  Medium vectors       : ok
  | ============================================================
  | RESULTS: 5 / 5 tests passed
  | 
  | > touch free_matrix.c
  | > make
  | gcc -g -c free_matrix.c
  | gcc -g -o op_main op_main.o outer_product.o free_matrix.o
  | 
  | > ./op_main 3 1.1 2.2 3.3  2 1.1 2.2
  |     1.21     2.42 
  |     2.42     4.84 
  |     3.63     7.26 
  | 
  | > rm op_main
  | 
  | > make test
  | gcc -Wall -g -o op_main op_main.o outer_product.o free_matrix.o
  | ./testy test_outer_product.org
  | ============================================================
  | == testy test_outer_product.org : Tests for outer_product.c and op_main.c
  | == Running 5 / 5 tests
  | 1)  Singleton vectors    : ok
  | 2)  Small vectors 1      : ok
  | 3)  Small vectors 2      : ok
  | 4)  Size 1 for x, for y  : ok
  | 5)  Medium vectors       : ok
  | ============================================================
  | RESULTS: 5 / 5 tests passed
  `----


  Use the `Makefile' provided in the `collatz' directory as a model and
  paste your resulting `Makefile' below. (Note: there is no C testing
  file for this project but the tests depend on the `op_main' program as
  shown in the last few demo lines).


Understanding the Code
======================

  The focus of this HW is to acquaint you with the utility of Makefiles
  in efficiently compiling projects, rebuilding based on source changes,
  and running provided tests. You should gather some of common
  conventions associated with it such as `make clean'.

  While it is not essential that you spend significant time
  understanding all the code in `collatz' and `mat-product', it is
  fairly elementary C and students wishing to refresh their knowledge of
  the language and techniques may want to spend some time examining how
  this code works. Labs may review some of the code in these projects.

Author: Chris Kauffman (kauffman@cs.gmu.edu)
Date: 2021-01-28 Thu 14:03