Create linux make/build file

前端 未结 10 1353
余生分开走
余生分开走 2020-12-13 01:01

I am moving a C++ project from Windows to Linux and I now need to create a build/make file. I have never created a build/make file before. I also need to include Boost libra

相关标签:
10条回答
  • 2020-12-13 01:36

    What is a Makefile ? (applied to a Boost project)

    The root recursive idea behind Makefile is:

    To build a target we need prerequisites (other targets!) and instructions to build

    Prerequisites

    They are either files, folders or fake targets (usually in .PHONY). Files/folders are tested for existence and modification date.

    The target needs to be rebuilt if it has no prerequisite or if older that any of the prerequisites.

    Instruction

    An Instruction is shell commands, starting with one tab. Each instruction line is one shell instance. The shell command can be continued on next line when the current one ends with backslash \.

    Target definition

    A target is either a dependency or a rule.

    Dependency:

    target : prerequisite1 prerequisite2 prerequisiteN
    

    Rule:

    target : prerequisite1 prerequisite2 prerequisiteN
        instructions1
        @hidden_batch1 ; \
      hidden_batch2  
    

    With tabs in front of instruction start.

    Debug

    Debugging a Makefile can become a real headache. Try the following in your Makefile to show traces (with file and line location for warning):

    $(info Shell: $(SHELL))
    $(warning CXX: $(CXX))
    

    This is helpful when your Makefile contains lots of nested if/else/endif and you're not sure anymore what is the current path.

    Makefile Structure

    The ideal makefile structure is:

    1. variable setup
    2. target/dependency declarations

    The real target-instructions processing starts once the whole Makefile and its include files has been understood (stored in make internal database).

    Example

    Finally, apply theory to this specific example using Boost and create fake source files to illustrate.

    rawr.cpp

    #include "rawr.h"
    

    simple_ls.cpp

    #include "rawr.h"
    

    converter.cpp

    #include <iostream>
    
    #include "rawr.h"
    #include "simple_ls.h"
    #include "2dquicksort.h"
    
    #include <boost/array.hpp>   // Boost! 
    
    int main(int argc, char **argv)
    {
        boost::array<int,4> a = { { 1, 2, 3, 4} };
        std::cout << a[1] << std::endl;
        return 0;
    }
    

    Makefile

    Don't forget to replace spaces with real Tabs if you copy Makefile source from *stack***overflow** :

    sed -i~ -e 's/^    /\t/' Makefile
    

    Makefile source:

    ## Makefile for C++ project using Boost
    #
    # @author Cedric "levif" Le Dillau
    #
    # Some notes:
    # - Using ':=' instead of '=' assign the value at Makefile parsing time,
    #   others are evaluated at usage time. This discards
    # - Use ':set list' in Vi/Vim to show tabs (Ctrl-v-i force tab insertion)
    #
    
    # List to '.PHONY' all fake targets, those that are neither files nor folders.
    # "all" and "clean" are good candidates.
    .PHONY: all, clean
    
    # Define the final program name
    PROGNAME := converter
    
    # Pre-processor flags to be used for includes (-I) and defines (-D) 
    CPPFLAGS := -DUSE_BOOST
    
    # CFLAGS is used for C compilation options.
    CFLAGS := -Wall -O0
    
    # CXXFLAGS is used for C++ compilation options.
    CXXFLAGS += -Wall -O0
    
    # LDFLAGS is used for linker (-g enables debug symbols)
    LDFLAGS  += -g
    
    # Which Boost modules to use (all)
    BOOST_MODULES = \
      date_time     \
      filesystem    \
      graph         \
      iostreams     \
      math_c99      \
      system        \
      serialization \
      regex
    
    # Boost libraries' type (a suffix)
    BOOST_MODULES_TYPE := -mt
    
    # Define library names with their type
    BOOST_MODULES_LIBS := $(addsuffix $(BOOT_MODULES_TYPE),$(BOOST_MODULES))
    
    # Define the linker argument to use the Boost libraries.
    BOOST_LDFLAGS := $(addprefix -lboost_,$(BOOST_MODULES_LIBS))
    
    # Feed compiler/linker flags with Boost's
    CPPFLAGS += $(BOOST_CPPFLAGS)
    LDFLAGS += $(BOOST_LDFLAGS)
    
    # List the project' sources to compile or let the Makefile recognize
    # them for you using 'wildcard' function.
    #
    #SOURCES = simple_ls.cpp rawr.cpp converter.cpp
    SOURCES = $(wildcard *.cpp)
    
    # List the project' headers or let the Makefile recognize
    # them for you using 'wildcard' function.
    #
    #HEADERS = simple_ls.h 2dquicksort.h rawr.h
    HEADERS = $(wildcard %.h)
    
    # Construct the list of object files based on source files using
    # simple extension substitution.
    OBJECTS = $(SOURCES:%.cpp=%.o)
    
    #
    # Now declare the dependencies rules and targets
    #
    # Starting with 'all' make it  becomes the default target when none 
    # is specified on 'make' command line.
    all : $(PROGNAME)
    
    # Declare that the final program depends on all objects and the Makfile
    $(PROGNAME) : $(OBJECTS) Makefile
        $(CXX) -o $@ $(LDFLAGS) $(OBJECTS)
    
    # Now the choice of using implicit rules or not (my choice)...
    #
    # Choice 1: use implicit rules and then we only need to add some dependencies
    #           to each object.
    #
    ## Tells make that each object file depends on all headers and this Makefile.
    #$(OBJECTS) : $(HEADERS) Makefile
    #
    # Choice 2: don't use implicit rules and specify our will
    %.o: %.cpp $(HEADERS) Makefile
        $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(OUTPUT_OPTION) $<
    
    # Simple clean-up target
    # notes:
    # - the '@' before 'echo' informs make to hide command invocation.
    # - the '-' before 'rm' command to informs make to ignore errors.
    clean :
        @echo "Clean."
        -rm -f *.o $(PROGNAME)
    

    File list

    2dquicksort.h
    converter.cpp
    Makefile
    rawr.cpp
    rawr.h
    simple_ls.cpp
    simple_ls.h
    

    Compilation

    make clean all
    Clean.
    rm -f *.o converter
    g++ -Wall -O0 -DUSE_BOOST  -c -o converter.o converter.cpp
    g++ -Wall -O0 -DUSE_BOOST  -c -o rawr.o rawr.cpp
    g++ -Wall -O0 -DUSE_BOOST  -c -o simple_ls.o simple_ls.cpp
    g++ -o converter -g -lboost_date_time -lboost_filesystem -lboost_graph -lboost_iostreams -lboost_math_c99 -lboost_system -lboost_serialization -lboost_regex converter.o rawr.o simple_ls.o
    

    Result

    And now, the result of nearly the tiniest Boost program:

    ./converter
    2
    

    No excuse not to use it ! Boost is really a featured C++ toolbox :)

    0 讨论(0)
  • 2020-12-13 01:37

    Of course you should Read The Fine Manual (specifically gcc and make). However, here are the basics for gcc:

    To compile a source file:

    g++ -c file.cpp
    

    This will create file.o. Then you'll probably want to link them:

    g++ -o app_name file.o main.o other_file.o
    

    This will create an executable called app_name. But since you are using boost and might have header files located all over the place, you'll probably need additional options. Use -I during the compilation to add a directory to the include path:

    g++ -I/usr/local/more_includes/ -c file.cpp
    

    You'll probably also need to link to some libraries. During linking:

    g++ -L/usr/local/more_libraries/ file.o main.o other_file.o -lsome_library
    

    Now onto makefiles. The basics of a makefile are:

    target: dependencies
        command
    

    For example:

    my_app: file.o
        g++ -o my_app file.o
    
    file.o: file.cpp file.h
        g++ -o file.cpp
    
    clean:
        rm file.o my_app
    

    If you type 'make' it will be default try to create the first target. my_app depends on the target file.o, so it will check to see if file.o has been modified since the last time my_app has been modified. If so, it will relink. In checking file.o, it notices that file.o depends on file.cpp and file.h. If either of those files have been modified since the last time file.o was created, it will recompile that file.

    Targets don't always have to be actual files either. The last one is called clean, it doesn't depend on anything and just deletes file.o and my_app. If you type 'make clean' it will run the command.

    There are of course a ton of other options, but that should get you started.

    0 讨论(0)
  • 2020-12-13 01:38

    At least try to read through the official documentation of the product you're trying to use: here. It does explain nearly all of the basics.

    In particular, read chapters 2 and 3, those will get you 99% of the way to where you need to be to use gmake effectively. In addition, carefully read the Catalogue of Implicit Rules. This will tell you what most of those "special variables" are.

    One hint I'd give you is to try out gcc -M *.cpp in your project directory. This will output a list of prerequisite headers for each of your .cpp files in Makefile format. In fact, for a starter makefile, you can just do:

    gcc -M *.cpp > Makefile
    

    Edit this file, more or less prepending sharth's answer to it, and you have a workable Makefile. I would probably advise you remove the large number of system headers that gcc -M is going to add to each build rule, but you don't really have to.

    FWIW, if you start working on a large project (more than one source directory is a good clue), it's time to break out a modern build management tool (cmake fan here). But for small projects, raw make is pretty easy to use.

    0 讨论(0)
  • 2020-12-13 01:38

    It's not exactly what you were requesting but I would highly recommend that you use a build system called premake. The benefit, and difference, between premake and scons and cmake is that it generates makefiles for you. This means that you can use premake as a start and then look at the files it generates to learn more.

    Aside from this premake is an easy to learn and intuitive tool and it has the added benefit of being able to generate visual studio project files from the same configuration.

    My advice would be to use premake if you absolutely have to have makefiles, it will help you learn and is ultimately a flexible replacement for directly writing your own makefiles (which is a major pain is the %*$).

    0 讨论(0)
  • 2020-12-13 01:38

    There is a very good tutorial @ ALP.

    0 讨论(0)
  • 2020-12-13 01:43

    First off, I am not an expert on Makefiles. I know the basics, but the Makefile in this answer will almost certainly have room for improvement. There are a lot of tricks and shortcuts that will allow you to handle dependencies and adding files better than hardcoding everything. However I think this example will be sufficient for your purposes and will possibly be more educational.

    Second, I want to make sure you know the basic stages of building a project. You may already know this, but if you don't the following will be a bit confusing at first. There are essentially two steps: compiling and linking. Compiling converts your code to object code - it will convert a .cpp file to a .o file if successful. The next step is linking. This is where it sticks all the object code together to create an executable and links the function calls from one file to another (so if file1.cpp calls a function defined in file2.cpp, it's at this step that the file1.cpp figures out where the function actually is). There is more to it but that should be enough to make the following clear.

    At this point you can just use g++ to compile and link your project (you can even do it in "one" step). This is quite cumbersome however, especially for any non-trivial project. It also makes it difficult to track files that have changed since you last compiled.

    This is where Makefiles come in. A Makefile is a list of rules in the form:

    target: dependencies
        command
    

    (Make sure to use tabs not spaces, make probably won't work if you use spaces). If you run the command:

    make some_target
    

    Then make will look for the rule with some_target. If the target is a file, it will check the 'last modified' time stamp of the file, and it will check the time stamps of all the dependencies you have listed. If any of the dependencies have a later time stamp, it will run the command.

    I'm going to have to make some assumptions about your project (namely, which files depend on which files) so you will probably have to modify the following, but here is a basic Makefile for your project (and remember, tabs not spaces. If you copy and paste this it won't work):

    CC = g++
    INCLUDE_DIRS = -I/path/to/boost
    
    all: binary_file
    
    clean:
        rm *.o
        rm binary_file
    
    binary_file: simple_ls.o rawr.o converter.o
        $(CC) -o binary_file simple_ls.o rawr.o converter.o
    
    rawr.o: rawr.h rawr.cpp 2dquicksort.h
        $(CC) -c rawr.cpp $(INCLUDE_DIRS)
    
    simple_ls.o: simple_ls.h simple_ls.cpp 2dquicksort.h
        $(CC) -c simple_ls.cpp $(INC*emphasized text*LUDE_DIRS)
    

    Running make or make all or make binary_file will cause all your files to be compiled if necessary and then linked to create an executable called binary_file. There are some improvements you can make, for example:

    %.o: %.cpp %.h 2dquicksort.h
        $(CC) -c $<
    

    Which will find all the .cpp files and compile them in to .o files. The .o files will be dependent on a .cpp file and a .h file of the same name (and 2dquicksort.h).

    0 讨论(0)
提交回复
热议问题