.. highlight:: makefile ******** GNU Make ******** The GNU Make project provides a command-line tool and a configuration system to facilitate the building of complex projects and generate executables. It is also employed to automate command-line tasks. Read the the `official documentation`_ for a more detailed view. .. _official documentation: https://www.gnu.org/software/make/manual/html_node/index.html make command ============ The command-line tool ``make`` is how you run all tasks. makefile ======== A makefile is the configuration file for the make command. It contains all the logics to guide the make to build the executables. A makefile is usually named ``makefile`` or ``Makefile`` and locate in top-level directory in a project. When you run the ``make`` command, it will search for ``makefile`` or ``Makefile`` in the current directory. You can use a command-line argument ``-f `` with the ``make`` command to specify the makefile of another name or in another directory. The core component called rule in a makefile looked like this .. code-block:: text target: prerequisites recipe .. glossary:: Target It is usually the file you want to generate. However, virtual targets such as ``main``, ``clean`` are extensively used in many makefiles. They do not bind to a real file but can be used in the make command-line like ``make main`` or ``make clean`` Prerequisites It is a list of dependent files/targets needed to build the target. When any of the files or targets are updated, the target will be re-built. Recipe An indented block including command-line commands in each line in order to generate the target. .. warning:: The indentation for commands **must be a single character** for older versions of GNU make. The newer versions can take spaces for indentation. If you do not know g++ command well, refer to the :doc:`g++ document`. One important thing to remember is **never list header files in the g++ command-line!** Provide cpp files only! Modular Compilation =================== Let's assume that we are writing a makefile for a project with multiple files. The relationship among files are demonstrated in the diagram. Arrows means include relationship: ``main.cpp -> student.hpp`` means the ``student.hpp`` file is included by the ``main.cpp`` so the ``main.cpp`` is depending on the ``student.hpp`` file. .. graphviz:: digraph dep { "main.cpp" -> {"grade-book.hpp", "student.hpp"}; "student.cpp" -> "student.hpp"; "grade-book.cpp" -> {"grade-book.hpp", "student.hpp", "grades.hpp"}; "grades.cpp" -> "grades.hpp"; } For projects with multiple files, modular compilation is usually preferred. In this approach, .cpp source code files are compiled to .o object files first and then all object files are linked to provide the executable(s). It has many benefits over direct compilation to output executables from source code files: + Specify different g++ flags + Easier to debug + No need to recompile everything after a partial modification .. warning:: Examples in this pages has all indentations as spaces. It is due to the limitation of the website generating system. You should change them to real ``tab`` characters to ensure the backward compatibility. Hard-coded rules ---------------- A simple makefile with hard coded g++ commands:: # declare virtual targets .PHONY: clean # Linking to provide the executable main: main.o grade-book.o student.o grades.o g++ -o main main.o grade-book.o student.o grades.o # Compile. One cpp file a time. main.o: main.cpp grade-book.hpp student.hpp g++ -std=c++14 -Wall -g -c main.cpp student.o: student.cpp student.hpp g++ -std=c++14 -Wall -g -c student.cpp grades.o: grades.cpp grades.hpp g++ -std=c++14 -Wall -g -c grades.cpp grade-book.o: grade-book.cpp grade-book.hpp student.hpp grades.hpp g++ -std=c++14 -Wall -g -c grade-book.cpp # the 'clean' virtual target to remove temporary files clean: rm -rf *.o main *.gc* *.dSYM The first rule is to produce the ``main`` executable. It calls g++ to link a list of object files to build the final program. The targets of .o files are compilations rules to generate every object file. The clean target is used to remove generated temporary files and the executable. You may now build your executable ``main`` by the command ``make`` or ``make main``. The ``make`` command without parameters will take the first target in the makefile as the target to make. Introducing variables --------------------- You may noticed a lot of redundant texts such as the g++ flags (``-std=c++14 -Wall -g``), list of file names, etc. Many of the redundancy can be remove by introducing variables. With variables, you can also extract configurations out of the commands for efficient management of configurations. A makefile using variables:: CXX=g++ CXXFLAGS=-std=c++14 -Wall -g RM=rm -rf .PHONY: clean main: main.o grade-book.o student.o grades.o $(CXX) -o main main.o grade-book.o student.o grades.o main.o: main.cpp grade-book.hpp student.hpp $(CXX) $(CXXFLAGS) -c main.cpp student.o: student.cpp student.hpp $(CXX) $(CXXFLAGS) -c student.cpp grades.o: grades.cpp grades.hpp $(CXX) $(CXXFLAGS) -c grades.cpp grade-book.o: grade-book.cpp grade-book.hpp student.hpp grades.hpp $(CXX) $(CXXFLAGS) -c grade-book.cpp clean: $(RM) *.o main *.gc* *.dSYM With variables, we can easily make modifications to the compiler command and compilation flags by modifying variables. Introducing automatic variables ------------------------------- You may notice that we still have redundancy. The file names listed as part of the target or dependencies are hard-coded in the g++ commands. It can be improved using the **automatic variables** like $@, $^, $<. .. glossary:: $@ the target $^ all prerequisites $< the first prerequisite The further simplified makefile:: CXX=g++ CXXFLAGS=-std=c++14 -Wall -g RM=rm -rf .PHONY: clean main: main.o grade-book.o student.o grades.o $(CXX) -o $@ $^ main.o: main.cpp grade-book.hpp student.hpp $(CXX) $(CXXFLAGS) -c $< student.o: student.cpp student.hpp $(CXX) $(CXXFLAGS) -c $< grades.o: grades.cpp grades.hpp $(CXX) $(CXXFLAGS) -c $< grade-book.o: grade-book.cpp grade-book.hpp student.hpp grades.hpp $(CXX) $(CXXFLAGS) -c $< clean: $(RM) *.o main *.gc* *.dSYM .. note:: The commands in the .o file rules used $< to avoid including header files in the g++ command-line. Make sure to put the .cpp file as the first dependency in the list! Use Pattern Match ----------------- It is still verbose. By sacrificing the header prerequisites in the object file rules, we can merge these rules into one:: CXX=g++ CXXFLAGS=-std=c++14 -Wall -g RM=rm -rf .PHONY: clean main: main.o grade-book.o student.o grades.o $(CXX) -o $@ $^ %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< clean: $(RM) -rf *.o main *.gc* *.dSYM .. note:: The drawback is that the .o file will not depend on certain header files and the modification of the header files will not trigger the recompilation of the .o file. A workaround is to force recompilation by running ``make clean main``. This will be slow if your project is huge. No easy solution without heavy coding. It is a limitation of make any. Further Improvement ------------------- Now we have a very concise makefile. You can learn potential improvement from the : + Store generated files in a subdirectory + Use wildcard to catch file names automatically Pitfalls ======== :: # use the wrong variable # the use of $^ with -c will include the hpp file in the g++ command # use $< to only include the first prerequisite, which should be the cpp file lib1.o: lib1.cpp lib1.hpp g++ -o $@ $^ # the order of prerequisites are wrong # it will cause the g++ to run with hpp file as its argument # the cpp file should go first so $< will refer to it lib1.o: lib1.hpp lib1.cpp g++ -o $@ $< # Typo in variables main.o: main.cpp $(CXX) $(CXXFLAG) -c $@ $< .. _make-diag: Common Error Messages From make =============================== .. note:: The terms 'xxx', 'yyy', etc. are placeholders of names. The term '#' is a placeholder for numbers. + :code:`No rule to make target 'xxx'.` Wrong target name. For example, you try to run ``make foo`` but ``foo`` is not defined as a target name in the ``makefile`` + :code:`No rule to make target 'xxx.o', needed by 'yyy'.` This is a common error when make require a ``xxx.cpp`` file to compile ``xxx.o`` but the file does not exist. Check for missing file or bad file name! Names like ``Xxx.cpp`` is wrong because Linux is case sensitive with file names! + :code:`missing separator. Stop.` It is likely that you have used the space characters for indentation. It is not supported in some old version of make. **Use tab for indentation for best compatibility!** + :code:`make: *** [makefile:#: xxx] Error 1` This is caused by a bad command runs by make. * Scroll up to see the last command that went wrong (mostly g++) * Also refer to the #th line in the makefile to see which command caused the problem. There are sometimes useful hints in the comments in the makefile. + **warning: overriding recipe for target `xxx'** It means that you have multiple rules for a same target. It can be dangerous if you do not know which one is finally employed to build your target. Debug makefile ============== You may use the make command with option `-d` to show debugging information. E.g. `make -d main` to show the details about target and rule matching process.