Program Management: Header Files and make

Goals

This lab provides practice dividing a program into pieces, compiling those pieces separately, linking components together to form an executable, and automating this process using the GNU utility make.

Activities for this laboratory exercise are organized into two parts:

  1. Header and Implementation Files
  2. Makefiles

Part A: Header and Implementation Files

The work that follows utilizes the Scheme-like lists program scheme-lists.c from the recent lab.

  1. For safety, make a new directory for multi-file scheme-lists and copy your scheme-lists.c program to it. For example:
    mkdir make-scheme-lists
    cp scheme-lists.c make-scheme-lists/
  2. Separate the code in your scheme-lists.c program file into three files as follows:
    • scheme-lists.h — containing the definition of STRMAX, the struct node and node_t types, and all of the function prototypes.
    • scheme-lists.c — containing the implementation of all of your scheme-lists functions, except main.
    • scheme-lists-test.c — containing your main function.

    Use cut and paste carefully!! You will need to include standard libraries in both .c files, but you should consider which ones you will need.

    In order to get these source files to compile, both scheme-lists.c and scheme-lists-test.c should contain the following line at the top of the file:

    #include "scheme-lists.h"

    You should not #include your source file scheme-lists.c anywhere. (It is good practice to only include header files, and not implementation files, in other source files.)

    As discussed in the reading, your header file should contain the following lines, and they should surround all type definitions and function declarations in the file.

    #ifndef __SCHEME_LISTS_H__
    #define __SCHEME_LISTS_H__
    
    ...
    
    #endif
  3. Defining a program made up of several pieces requires a more careful treatment, as we explore below.
    1. Try building your program with the following command, typed at the shell prompt. Explain why it gives the error it does.
      clang scheme-lists-test.c
    2. Compile each of your source files (to create object files, but still not an executable program) with the following commands, typed at the shell prompt:
      clang -Wall -c scheme-lists.c
      clang -Wall -c scheme-lists-test.c
      Use the command ls to check that object files were produced.
    3. Now create an executable file with the following command:
      clang -o scheme-lists-test scheme-lists-test.o scheme-lists.o
      Check that the file scheme-lists-test exists with ls and then run the resulting program.
    4. Finally, compile both source files and link the resulting object files together with the following single command.
      clang -o scheme-lists-test scheme-lists-test.c scheme-lists.c
      Note, however, that waiting for large programs to build this way quickly becomes tedious since every source file must be recompiled from scratch.

As you finish this part of the lab, note that you have converted your scheme-lists code into an abstract data type (ADT). By listing the function prototypes, the header file tells other programmers what operations are supported by the data type. It does not specify how they are implemented; that is done in the implementation file. This makes portions of your code reusable in different programs so that commonly used code can be written once and then reused over and over.

Your data type may now be used in client programs, such as scheme-lists-test.c. Doing so requires only the following simple steps:

Part B: make and Makefiles

If the reading for this lab was overwhelming, you can take a detour and read .... and do the exercises .... in this short overview of makefiles from the basic file possible through more advanced levels.

The reading for this lab describes a detailed Makefile with directions for compiling, linking, and cleaning files related to a namelist.c program. This part considers a simplified Makefile.

  1. Copy a simplified Makefile to your make-scheme-lists directory and review it. Explain what is accomplished by each line in the file.

  2. Edit the Makefile so that it can be used to build your scheme-lists-test program. You will need to change the names of the .h and .c files in the Makefile to match the names of the files in your make-scheme-lists directory. (When you do this you will of course, want to change the "Author" comment at the top of the file, but give credit to the original.)

  3. In the terminal window, type these commands:

    • ls — to see a listing of your files
    • make clean — to prepare for a fresh build
    • ls — to view the result
    • make — to compile and link all source files
    • ls — to view the result

  4. Make a change in the source file scheme-lists.c. For example, you could add or modify a printf statement in some function. Which rules in the makefile do you expect will be run, the next time you invoke make, as a result of this change?

    Run make again, to verify your prediction experimentally.

  5. Make a change in the header file scheme-lists.h. For example, you could add a comment to the top of the file. Which rules in the makefile do you expect will be run, the next time you invoke make, as a result of this change?

    Run make again, to verify your prediction experimentally.

If You Have Time

  1. Take a look at GNU's documentation regarding make and makefiles.

    Note that this documentation is very lengthy, and it is not necessary for you to read or understand all of it in order to begin using makefiles. Reading through Section 3.2 or so provides a good introduction to the topic.