.. _make-intro: Make - the programmer's best friend ################################### .. include:: /header.inc In working at the :term:`command line`, you end up typing in a lot of commands. Unfortunately, you type in the same commands over and over, and that breaks a cardinal rule of programming: :term:`DRY`! Don't Repeat Yourself! Back in 1978, Marty Feldmann got sick of repeatedly typing in the commands to build his program and decided to do something about that silliness. He constructed a tool that used a simple text file to describe the commands he needed to type, and taught his tool to do the typing for him. The tool is called Make_, and it has been part of the programming landscape ever since. Hello, Make *********** First, let's check that we have Make_ installed: .. code-block:: bash $ make --version GNU Make 4.1 Built for x86_64-pc-linux-gnu Copyright (C) 1988-2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Looks like it is installed (it was part of the ``build-essential`` package we installed). Navigate into your ``Hello`` project .. code-block:: bash $ cd ~/cosc2325/homework-rblack42/HW2 Let's teach Make_ how to manage this project. What is Make? ************* Simply stated, ``make`` is a program used to "make" programs. It is a tool that issues a set of commands for you when you build a program involving multiple steps, so you do not have to type those commands in manually. If that was all ``make`` did, it might be handy, but hardly "powerful". What makes ``make`` unique is that you describe how programs are constructed out of the component parts, then ``make`` figures out what to do automatically. How it does this is interesting. Dependencies ************ Make_ is driven by something called `dependencies`. If a file is needed to construct anything, that file is a dependency of that anything. For example, you need the source file to build the object file, and ou need object files to build executables. Make manages the actual steps needed to construct things from their dependencies. Take a simple ``C++`` program like "Hello, World". That program is probably present in a single file named something like ``hello.cpp``. To build the executable file from this source file, we have a few steps to work through. Here is our test file: .. literalinclude:: code/hello1/hello.cpp :language: c++ We know it is possible to build an executable directly from this source file, but doing so would not really set things up so make_ can work its magic. Let's build the object file first, then link that file into an executable program. The first thing we need to do is process the source file into an object file named something like ``hello.o``. The command to do this step looks like this: .. code-block:: text g++ -c -o hello.o hello.c Here we invoke the ``C++`` compiler to process the ``hello.cpp`` file and build the ``hello.o`` file from it. The ``-c`` argument tells the compiler to just build the object file and not try to link it into an executable. This only works if the source file has no errors. In this step, we say the ``hello.o`` is ``dependent`` on ``hello.cpp``. With no ``hello.cpp`` around, we cannot build ``hello.o``. In the next step, we link the object file to build the final executable. Here is the command: .. code-block:: text g++ -o hello hello.o .. note:: This is a Linux example, the final executable has no extension Here, the executable ``hello`` is dependent on ``hello.o``. If ``hello.o`` is not around, we cannot build the final executable file. Makefiles ********* To use Make_ to manage all this, we need to create a simple text file containing information needed to describe this process we just went through manually. Here is a start: .. code-block:: make # Makefile for hello.cpp FILES = hello.cpp Pretty simple so far, huh? We name this file ``Makefile`` (no extension) and place it in the directory with the other project files (``hello.cpp`` in this case). Comments can be included, beginning with the sharp character. The next line looks like an assignment statement, but it simply sets up a name for a bunch of text after the equal (and a bit of white space). The text assigned to that name is everything after the whitespace up to the end of the line. .. warning:: When you type lines like this, be careful not to put extra spaces after the last character on the line. That can cause problems. The next line we will add looks a bit strange: .. code-block:: make OBJS = $(FILES:.cpp=.o) In a ``Makefile``, you cause the Make_ program to use a named chunk of text by surrounding the name in parentheses and putting a leading "$" in front. So, $(FILES) would place ``hello.cpp`` at this spot in this file. However, Make_ can do some neat tricks. Here the stuff after the colon is a substitution mechanism. In this example, and text that ends with ".cpp" will become ".o" when the name ``FILES`` is expanded by ``make``. We save the result in a new name called ``OBJS``. The net effect of all this is to create another line that could have been written as follows: .. code-block:: make OBJS = hello.o While this looks silly here, in a more complex program is will save a bunch of typing! Targets ******* Make_ calls something it can build a ``target``. We can ask Make_ to do a bunch of things, but for now, we just want to build our program. The rules we will define next set up that process. Be careful in this section, there is one funny quirk of Make_ we must deal with. Targets begin with the name of something, followed by a colon. Targets are usually some real file name we want to build, but that something can just be a name of some operation we might want to do. Start this section with a target named ``all`` like this: .. code-block:: make .PHONY: all all: hello That ``.PHONY`` marker tells Make_ not to look for a file named ``all`` in the current directory. In this example, the name of the target is ``all``, and it is the first such line in the file. Make_ will automatically try to build the first target it finds in the ``Makefile`` if we launch the program using just ``make`` with no command-line arguments. In this example, we are saying that this name ``depends`` on ``hello``. If ``make`` is managing this project and ``hello`` is already in the current directory, it is possible we have no work to do, so ``make`` might do nothing. More on that later. Suppose ``hello`` does not exist. Make_ knows it is supposed to build it, and we need to describe the process. Here is a new rule that will build the program: .. code-block:: make hello: $(OBJS) g++ -o hello $(OBJS) .. warning:: See that indented line where the real build command is shown? The first character on that line must be a real ``tab`` character, or ``make`` will complain. This is the quirk! If you are editing in the :term:`virtual machine` with ``vim``, you type ``ctrl-v`` followed by the ``tab`` to enter the character assuming you are expanding tabs as I recommended. In this rule, there is only one command needed, but Make_ will let you run a series of commands, as long as all are indented properly (with that silly tab!) This line tells ``make`` how to manufacture ``hello`` out of the dependencies listed on the line after the colon. In this file, the ``$(OBJS)`` expands to just ``hello.o``. If ``hello.o`` is around, all we need to do is to run the command shown, substituting for the ``OBJS`` string. That is exactly what we typed manually to do the last step. As you can see, this is just the link step, and g++ will run the linker to build the executable file. Great, but how do we get ``hello.o`` constructed? Implicit rules ************** One of the most powerful things that ``make`` can do is generate its own rules if you provide a pattern. Here is an example. It does look a bit weird. .. code-block:: make %.o: %.cpp g++ -c $< -o $@ In this funny rule, any file Make_ needs to build that is named ``something.o`` can be constructed out of a dependent file named ``something.cpp``. If ``something.cpp`` is around, the command we execute is shown with two place holders for the expanded names. In this example * ``$<`` will be replaced by ``something.cpp`` * ``$@`` will be replaced by ``something.o`` The cool thing about rules like this is that any time you need a file like ``xxx.o``, the same rule will build it out of ``xxx.cpp``. All you need to do is add ``xxx.o`` as a dependency and ``make`` will take care of things. Make is smart! ************** Once the required rules are in place to build a program, ``make`` does more than just issue the commands to rebuild it. It looks at the time-stamps on all the dependencies of every program component it knows how to build and determines if it really needs to reprocess anything. Think about it. If you did not change the source file, the object built from it will be newer than the source file. The executable built from the object file will be newer than the object file as well. ``Make`` can see that if you delete the executable, all it needs to do is re-link the current object file to rebuild the missing executable. There is no need to recompile the source, since it is older than the current object file. This kind of power speeds up building a complex project involving hundreds of files by only processing the bare minimum needed to get the program constructed! Running Make ************ You launch Make_ by typing ``make`` on the command line. .. note:: Make_ will look in the current directory (where you were working when you typed ``make``), looking for a ``Makefile``. Actually, the name can have either an upper case "m", or a lower-case "m". I stick with upper-case) Make will read the complete ``Makefile`` and build some internal data structures so it can study the build process. It will check the time stamps on all files in the folder, then decide what needs to happen. Make_ starts this process on the first target if no target is named on the command line, or it starts with the named target rule if you provide that target name as a parameter. Normally, developers set up their ``Makefile`` so the first target builds the program. (That is the ``all`` target in our example). If you want to run another target, you can provide the target name after ``make``. For example ``make hello_main.o`` would ask Make_ to just build the object file from ``hello_main.cpp``. .. note:: Normally, I set up four primary targets in a C++ Makefile: * "make [all]" which builds the program (The "all" is optional). * "make clean" which does housekeeping * "make test" which runs unit tests (we will see that soon) * "make run" which launches the program See the next example. A more complex example ********************** Let's break up our simple "Hello, World" program into two parts: Here is ``hello_main.cpp`` .. literalinclude:: code/hello2/hello_main.cpp :language: c++ And, here is ``hello_out.cpp``: .. literalinclude:: code/hello2/hello_out.cpp :language: c++ Here is the ``Makefile`` that builds the program. .. literalinclude:: code/hello2/Makefile :language: make Notice something in this ``Makefile``. I added a line that names the executable file I want to build. (``demo`` in this case) The executable name really does not matter when you are building an application, it might when you release your program to users!) I use that name several places in this file, and naming it makes it easy to change the name later. Look at the line that links the object files into the executable. The pattern here is ``$^`` which expands to the complete list if names on the dependency line. ``$<`` expands to the first name on the dependency line (and we only have one name on the rule that builds an object file. Finally, I added a build target for something called ``clean``. This is very common in ``Makefiles`` and it deletes anything you do not really need to keep around. In this case, it will delete the executable, and all object files. They can always be rebuilt using Make_. You should run "make clean" before telling Git_ to add files to your repository. This example does not do any testing. We will learn about that in our next lectures. Testing the Makefile ******************** Before we leave this step, let's try out our fancy new ``Makefile``: .. command-output:: make clean :cwd: code/hello2 .. command-output:: make :cwd: code/hello2 .. command-output:: ./demo :cwd: code/hello2 Looks like our fingers are going to be happy!