path include and src directory makefile

后端 未结 3 1357
不知归路
不知归路 2021-01-30 07:58

Following this tutorial:

http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/

It has 3 files 2 of which are .c files and 1 .h fil

相关标签:
3条回答
  • 2021-01-30 08:16

    the make utility, with no specific 'target' will make the first target in the file.

    The first target is usually named 'all'

    For the posted file, will make the object files and will not continue to make the executable when the target is not given in the command line

    Suggest the following:

    SHELL := /bin/sh
    
    # following so could define executable name on command line
    # using the '-D' parameter
    #ifndef $(NAME)
        NAME := hellomake
    #endif
    
    # use ':=' so macros only evaluated once
    
    
    MAKE    :=  /usr/bin/make
    CC      :=  /usr/bin/gcc
    
    CFLAGS  := -g -Wall -Wextra -pedantic
    LFLAGS  :=
    
    ODIR    := obj
    IDIR    := ../include
    LIBS    :=
    LIBPATH := ../lib
    
    DEPS    := $(wildcard $(IDIR)/*.h)
    SRCS    := $(wildcard *.c)
    OBJS    := $(SRCS:.c=.o)
    
    .PHONY: all
    all: $(NAME) $(OBJS)
    
    $(ODIR)/%.o: %.c $(DEPS)
        $(CC) $(CFLAGS) -c -o $@ $< -I$(DEPS)
    
    $(NAME): $(OBJS)
        $(CC) $(LFLAGS) -o $@ $^ -L$(LIBPATH) -l$(LIBS)
    
    .PHONY: clean
    clean:
        rm -f $(ODIR)/*.o
        rm -f $(NAME)
    
    
    however, in your proposed project,
    not every source file needs every header file
    so should use either gcc or sed to generate the dependency files
    then use makefile rules similar to the following,
    which may need a little 'tweaking' for your project
    because the include files are not in the same directory
    as the source files:
    
    DEP := $(SRCS:.c=.d)
    
    #
    #create dependency files
    #
    %.d: %.c 
        # 
        # ========= START $< TO $@ =========
        $(CC) -M $(CPPFLAGS) $< > $@.$$$$;                      \
        sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;     \
        rm -f $@.$$$$
        # ========= END $< TO $@ =========
    
    # 
    # compile the .c files into .o files using the compiler flags
    #
    %.o: %.c %.d 
         # 
         # ========= START $< TO $@ =========
         $(CC) $(CCFLAGS) -c $< -o $@ -I$(IDIR) 
         # ========= END $< TO $@ =========
         # 
    
    # include the contents of all the .d files
    # note: the .d files contain:
    # <filename>.o:<filename>.c plus all the dependencies for that .c file 
    # I.E. the #include'd header files
    # wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
    #
    ifneq "$(MAKECMDGOALS)" "clean"
    -include $(DEP)
    endif
    
    0 讨论(0)
  • 2021-01-30 08:20

    Your tutorial promotes old and bad practices, you should avoid it IMHO.

    In your rule here:

    $(ODIR)/%.o: %.c $(DEPS)
    

    You're telling make to look for sources in the current directory while they actually reside in the src directory, thus this pattern is never used and you have no suitable one.


    Make sure you organize your project directory like this :

    root
    ├── include/
    │   └── all .h files here
    ├── lib/
    │   └── all third-party library files (.a/.so files) here
    ├── src/
    │   └── all .c files here
    └── Makefile
    

    Then let's take the process step by step, using good practices.

    Firstly, don't define anything if you don't need to. Make has a lot of predefined variables and functions that you should use before trying to do it manually. In fact, he has so many that you can compile a simple file without even having a Makefile in the directory at all!

    1. List your source and build output directories:

       SRC_DIR := src
       OBJ_DIR := obj
       BIN_DIR := bin # or . if you want it in the current directory
      
    2. Name your final target, that is, your executable:

       EXE := $(BIN_DIR)/hellomake
      
    3. List your source files:

       SRC := $(wildcard $(SRC_DIR)/*.c)
      
    4. From the source files, list the object files:

       OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
       # You can also do it like that
       OBJ := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC))
      
    5. Now let's handle the flags

       CPPFLAGS := -Iinclude -MMD -MP # -I is a preprocessor flag, not a compiler flag
       CFLAGS   := -Wall              # some warnings about bad code
       LDFLAGS  := -Llib              # -L is a linker flag
       LDLIBS   := -lm                # Left empty if no libs are needed
      

    The -MMD -MP flags are used to generate the header dependencies automatically. We will use this later on to trigger a compilation when only a header changes.

    Ok, time to roll some recipes now that our variables are correctly filled.

    It is widely spread that the default target should be called all and that it should be the first target in your Makefile. Its prerequisites shall be the target you want to build when writing only make on the command line:

    all: $(EXE)
    

    One problem though is Make will think we want to actually create a file or folder named all, so let's tell him this is not a real target:

    .PHONY: all
    

    Now list the prerequisites for building your executable, and fill its recipe to tell make what to do with these:

    $(EXE): $(OBJ)
        $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
    

    Note that your $(BIN_DIR) might not exist yet so the call to the compiler might fail. Let's tell make that you want it to check for that first:

    $(EXE): $(OBJ) | $(BIN_DIR)
        $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
    
    $(BIN_DIR):
        mkdir -p $@
    

    Some quick additional notes:

    • $(CC) is a built-in variable already containing what you need when compiling and linking in C
    • To avoid linker errors, it is strongly recommended to put $(LDFLAGS) before your object files and $(LDLIBS) after
    • $(CPPFLAGS) and $(CFLAGS) are useless here, the compilation phase is already over, it is the linking phase here

    Next step, since your source and object files don't share the same prefix, you need to tell make exactly what to do since its built-in rules don't cover your specific case:

    $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
        $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
    

    Same problem as before, your $(OBJ_DIR) might not exist yet so the call to the compiler might fail. Let's update the rules:

    $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
        $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
    
    $(BIN_DIR) $(OBJ_DIR):
        mkdir -p $@
    

    Ok, now the executable should build nicely. We want a simple rule to clean the build artifacts though:

    clean:
        @$(RM) -rv $(BIN_DIR) $(OBJ_DIR) # The @ disables the echoing of the command
    

    (Again, clean is not a target that needs to be created, so add it to the .PHONY special target!)

    Last thing. Remember about the automatic dependency generation? GCC and Clang will create .d files corresponding to your .o files, which contains Makefile rules for us to use, so let's include that in here:

    -include $(OBJ:.o=.d) # The dash is used to silence errors if the files don't exist yet
    

    Final result:

    SRC_DIR := src
    OBJ_DIR := obj
    BIN_DIR := bin
    
    EXE := $(BIN_DIR)/hellomake
    SRC := $(wildcard $(SRC_DIR)/*.c)
    OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
    
    CPPFLAGS := -Iinclude -MMD -MP
    CFLAGS   := -Wall
    LDFLAGS  := -Llib
    LDLIBS   := -lm
    
    .PHONY: all clean
    
    all: $(EXE)
    
    $(EXE): $(OBJ) | $(BIN_DIR)
        $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
    
    $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
        $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
    
    $(BIN_DIR) $(OBJ_DIR):
        mkdir -p $@
    
    clean:
        @$(RM) -rv $(BIN_DIR) $(OBJ_DIR)
    
    -include $(OBJ:.o=.d)
    
    0 讨论(0)
  • 2021-01-30 08:25

    The simple Makefile definitions seem OK to me as they appear in your question. Try specifying the compiler options before the file names:

    $(ODIR)/%.o: %.c $(DEPS)
        $(CC) $(CFLAGS) -c -o $@ $<
    
    hellomake: $(OBJ)
        gcc $(CFLAGS) -o $@ $^
    

    You need to run make from the source directory.

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