guides:software:make:start
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
guides:software:make:start [2020/07/20 13:58] – [Simple Use] wikiadmin | guides:software:make:start [2024/07/25 15:01] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 3: | Line 3: | ||
Make is a build automation tool that is designed to automatically construct executable programs from source code. | Make is a build automation tool that is designed to automatically construct executable programs from source code. | ||
+ | |||
+ | Make simplifies the build process for projects at all levels. | ||
===== Simple Use ===== | ===== Simple Use ===== | ||
Make has a set of built in rules that allow you to compile single file programs with no additional configuration. | Make has a set of built in rules that allow you to compile single file programs with no additional configuration. | ||
+ | |||
+ | ==== A simple Example ==== | ||
Make recognizes files ending with .cpp, .C and several other extensions as c++ source code. To build an executable from a file with an appropriate extension type //make progname// | Make recognizes files ending with .cpp, .C and several other extensions as c++ source code. To build an executable from a file with an appropriate extension type //make progname// | ||
Line 25: | Line 29: | ||
- This caused make to compile hello.cpp and produce the output file //hello//. | - This caused make to compile hello.cpp and produce the output file //hello//. | ||
- The user typed //ls// again to show that the file //hello// had been produced. | - The user typed //ls// again to show that the file //hello// had been produced. | ||
+ | |||
+ | ==== Make Only Rebuilds when Necessary ==== | ||
Make will only rebuild an executable if it is necessary. | Make will only rebuild an executable if it is necessary. | ||
Line 49: | Line 55: | ||
* Change the timestamp on hello.cpp (//touch hello.cpp// | * Change the timestamp on hello.cpp (//touch hello.cpp// | ||
* Edit the file hello.cpp and save the new file. | * Edit the file hello.cpp and save the new file. | ||
+ | |||
+ | ==== Make Can Not Build an Unknown Target ==== | ||
Unless directed otherwise, make uses the files in the current directory when attempting to build an executable. | Unless directed otherwise, make uses the files in the current directory when attempting to build an executable. | ||
Line 63: | Line 71: | ||
- The user types //make bad// to attempt to build an executable //bad// | - The user types //make bad// to attempt to build an executable //bad// | ||
- Make does not detect a source code file that can be used to construct bad, so it reports an error and exits. | - Make does not detect a source code file that can be used to construct bad, so it reports an error and exits. | ||
- | ===== Basic Make Files ===== | + | ===== Setting Compiler Flags for Make ===== |
- | Make uses a file called either // | + | Make uses a file called either // |
+ | |||
+ | ==== Using a Makefile to Provide Compiler Flags ==== | ||
A relatively powerful yet simple use of a Makefile is to provide the proper command line arguments to build your executable. | A relatively powerful yet simple use of a Makefile is to provide the proper command line arguments to build your executable. | ||
+ | |||
+ | Assume that your instructor wishes you to compile your program with the following [[guides: | ||
+ | < | ||
+ | -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | </ | ||
+ | |||
+ | You could | ||
+ | * Memorize these flags and type them every time you compile. | ||
+ | * Type in these flags once for each session and use command line history to recompile each time. | ||
+ | * Write a shell script to compile your programs for you. | ||
+ | * Create a // | ||
+ | |||
+ | The last option is fairly easy. Simple edit // | ||
+ | |||
+ | <code make> | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | </ | ||
+ | |||
+ | // | ||
+ | |||
+ | < | ||
+ | $ ls | ||
+ | Makefile | ||
+ | $ cat Makefile | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | $ make hello | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | $ ls | ||
+ | Makefile | ||
+ | </ | ||
+ | |||
+ | In this example: | ||
+ | - The user types //ls// to show the contents of the directory. | ||
+ | - Note in this case, both hello.cpp and Makefile are present. | ||
+ | - The user types //cat Makefile// to display the contents of the // | ||
+ | - The user types //make hello// to build the executable | ||
+ | - Make uses the contents of the // | ||
+ | |||
+ | ==== Specifying a Single Target in a Makefile ==== | ||
+ | |||
+ | A second easy use of the makefile is to specify what you wish to build when you type make. In all of the examples so far, the user has typed //make hello// | ||
+ | |||
+ | To solve this problem //make// allows you to specify **default targets**. | ||
+ | |||
+ | A common way to specify a default target is to declare a **rule** to build the target // | ||
+ | |||
+ | < | ||
+ | $ ls | ||
+ | Makefile | ||
+ | $ cat Makefile | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | all: hello | ||
+ | $ make | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | </ | ||
+ | |||
+ | In the example: | ||
+ | - The user types //ls// to show the contents of the directory | ||
+ | - The user types //cat Makefile// to show the contents of the makefile. | ||
+ | - Notice that the line //all: hello// has been added. | ||
+ | - The user types //make// to build the project | ||
+ | |||
+ | In the example above, //all// is a target. | ||
+ | |||
+ | A target consists of a target name, in this case //all// and a dependency list. The dependency list consists of a single dependency // | ||
+ | |||
+ | |||
+ | ==== Adding a Rule to Remove Executable Files and Other Compiler Generated Files ==== | ||
+ | |||
+ | Another standard target is // | ||
+ | |||
+ | < | ||
+ | $ ls | ||
+ | Makefile | ||
+ | $ cat Makefile | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | all: hello | ||
+ | |||
+ | clean: | ||
+ | rm -f hello | ||
+ | $ make | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | $ ls | ||
+ | Makefile | ||
+ | $ make clean | ||
+ | rm -f hello | ||
+ | $ ls | ||
+ | Makefile | ||
+ | </ | ||
+ | |||
+ | In the example: | ||
+ | - The user types //ls// to show the contents of the directory | ||
+ | - The user types //cat Makefile// to show the contents of the Makefile | ||
+ | - The user types //make// to build the default target //hello// | ||
+ | - The user types //ls// to show that hello was constructed | ||
+ | - The user types //make clean// to remove project executable files. | ||
+ | - The user types //ls// to show that the executable files have been removed. | ||
+ | |||
+ | The Makefile contains the following: | ||
+ | <code make> | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | all: hello | ||
+ | |||
+ | clean: | ||
+ | rm -f hello | ||
+ | </ | ||
+ | |||
+ | Note that an additional target, //clean//, has been added to the Makefile. | ||
+ | - There are no dependencies | ||
+ | - There is a //rule// to be applied when building the target. | ||
+ | |||
+ | The rule is //rm -f hello// | ||
+ | |||
+ | Note that when constructing rules in make, the rules need to be indented by a tab. The following example demonstrates what would happen if //rm -f hello// were not preceded by a tab. | ||
+ | |||
+ | < | ||
+ | $ cat Makefile | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | all: hello | ||
+ | |||
+ | clean: | ||
+ | rm -f hello | ||
+ | $ make | ||
+ | Makefile:6: *** missing separator. | ||
+ | </ | ||
+ | |||
+ | In this example: | ||
+ | - The user types //cat Makefile// to show the contents of the Makefile | ||
+ | - Notice that the rule for //clean// has a space and not a tab. | ||
+ | - The user types //make// to build the project | ||
+ | - Make reports an error because it can not parse the Makefile due to the missing tab. | ||
+ | |||
+ | It is extremely important that all rules for a target begin with a tab. | ||
+ | |||
+ | ===== Building Multiple Targets ===== | ||
+ | |||
+ | < | ||
+ | $ ls | ||
+ | Makefile | ||
+ | $ cat Makefile | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | OBJS = hello greeting salute | ||
+ | |||
+ | all: ${OBJS} | ||
+ | |||
+ | clean: | ||
+ | rm -f ${OBJS} | ||
+ | $ make | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | $ ls | ||
+ | Makefile | ||
+ | $ make clean | ||
+ | rm -f hello greeting salute | ||
+ | $ ls | ||
+ | Makefile | ||
+ | </ | ||
+ | |||
+ | In this example: | ||
+ | - The user types //ls// to show the contents of the directory | ||
+ | - Note that three independent source code files exist // | ||
+ | - The user types //cat Makefile// to show the contents of the makefile. | ||
+ | - The changes will be explained below. | ||
+ | - The user types //make// to build all primary targets. | ||
+ | - Make constructs three executables, | ||
+ | - The user types //ls// to show the contents of the directory after the build. | ||
+ | - The user types //make clean// to clean up the project directory | ||
+ | - Make removes all of the executable files produced in the previous step. | ||
+ | - The user types //ls// to show the contents of the directory. | ||
+ | |||
+ | The makefile has been modified on three lines. | ||
+ | |||
+ | First, a variable called //OBJS// has been added. | ||
+ | |||
+ | <code make> | ||
+ | OBJS = hello greeting salute | ||
+ | </ | ||
+ | |||
+ | Secondly, the default target has been changed. | ||
+ | |||
+ | <code make> | ||
+ | all: ${OBJS} | ||
+ | </ | ||
+ | |||
+ | Finally, the rule to make clean has been altered to remove all of the files listed in the variable //OBJS//. | ||
+ | |||
+ | <code make> | ||
+ | clean: | ||
+ | rm -f ${OBJS} | ||
+ | </ | ||
+ | |||
+ | The final makefile for this section is: | ||
+ | <code make> | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion | ||
+ | |||
+ | OBJS = hello greeting salute | ||
+ | |||
+ | all: ${OBJS} | ||
+ | |||
+ | clean: | ||
+ | rm -f ${OBJS} | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
===== Makefiles for Multiple File Compilation ===== | ===== Makefiles for Multiple File Compilation ===== | ||
+ | |||
+ | One of the most powerful uses of make is to compile multiple files into a single executable. | ||
+ | |||
+ | ==== Example ==== | ||
+ | |||
+ | For this example the code is decomposed into the following files: | ||
+ | |||
+ | * // | ||
+ | * <code c++> | ||
+ | #ifndef STRING_TOOLS | ||
+ | #define STRING_TOOLS | ||
+ | |||
+ | void LowerCase(std:: | ||
+ | |||
+ | #endif | ||
+ | </ | ||
+ | * // | ||
+ | * <code c++> | ||
+ | include < | ||
+ | #include < | ||
+ | #include " | ||
+ | |||
+ | using namespace std; | ||
+ | |||
+ | void LowerCase(string & word){ | ||
+ | for(auto &x : word) { | ||
+ | x = static_cast< | ||
+ | } | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | * // | ||
+ | * <code c++> | ||
+ | #ifndef GREETING | ||
+ | #define GREETING | ||
+ | |||
+ | std::string GreetingString(std:: | ||
+ | |||
+ | #endif | ||
+ | </ | ||
+ | * // | ||
+ | * <code c++> | ||
+ | |||
+ | #include < | ||
+ | |||
+ | #include " | ||
+ | #include " | ||
+ | |||
+ | using namespace std; | ||
+ | |||
+ | string GreetingString(string language){ | ||
+ | string greeting = "Hello World"; | ||
+ | |||
+ | LowerCase(language); | ||
+ | |||
+ | if (language == " | ||
+ | | ||
+ | } else if (language == " | ||
+ | | ||
+ | } | ||
+ | |||
+ | return greeting; | ||
+ | } | ||
+ | </ | ||
+ | * // | ||
+ | * <code c++> | ||
+ | #include < | ||
+ | |||
+ | #include " | ||
+ | |||
+ | using namespace std; | ||
+ | |||
+ | int main() { | ||
+ | string languages[] = {" | ||
+ | |||
+ | for (auto lang: languages) { | ||
+ | cout << " The greeting in \"" | ||
+ | cout << GreetingString(lang) << " | ||
+ | } | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | * // | ||
+ | * <code make> | ||
+ | CXXFLAGS = -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wu | ||
+ | ninitialized -Wshadow -Wconversion -std=c++14 | ||
+ | |||
+ | OBJS = hello | ||
+ | |||
+ | all: ${OBJS} | ||
+ | |||
+ | hello: greeting.o stringTools.o | ||
+ | |||
+ | greeting.o: greeting.h stringTools.h | ||
+ | |||
+ | stringTools.o: | ||
+ | |||
+ | clean: | ||
+ | rm -f ${OBJS} *.o | ||
+ | </ | ||
+ | |||
+ | To build this code, you are required to compile stringTools.cpp into stringTools.o, | ||
+ | |||
+ | This could be accomplished by hand by executing the following | ||
+ | < | ||
+ | g++ -c greeting.cpp | ||
+ | g++ -c stringTools.cpp | ||
+ | g++ -o hello hello.cpp stringTools.o greeting.o | ||
+ | </ | ||
+ | |||
+ | Of course, if you have command line flags, this becomes much more complex. | ||
+ | |||
+ | Using the above // | ||
+ | |||
+ | < | ||
+ | $ make | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -std=c++14 | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -std=c++14 | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -std=c++14 | ||
+ | $ ls | ||
+ | Makefile | ||
+ | greeting.cpp | ||
+ | </ | ||
+ | |||
+ | Note that all components are compiled and the final executable is produced. | ||
+ | |||
+ | < | ||
+ | $ touch greeting.cpp | ||
+ | $ make | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -std=c++14 | ||
+ | g++ -g -O3 -Wpedantic -Wall -Wextra -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -std=c++14 | ||
+ | </ | ||
+ | |||
+ | ==== The Makefile ==== | ||
+ | |||
+ | In order to build this project using make, you need to inform make of the dependencies built into the project. | ||
+ | |||
+ | <code make> | ||
+ | hello: | ||
+ | </ | ||
+ | |||
+ | You do not need to tell make that //hello// depends on // | ||
+ | |||
+ | You also do not need to tell make that //hello// depends on // | ||
+ | |||
+ | <code make> | ||
+ | stringTools.o: | ||
+ | </ | ||
+ | |||
+ | This line tells make that // | ||
+ | |||
+ | <code make> | ||
+ | greeting.o: greeting.h stringTools.h | ||
+ | </ | ||
+ | |||
+ | This line tells make that // | ||
guides/software/make/start.1595253508.txt.gz · Last modified: 2024/07/25 15:01 (external edit)