developer tip

Makefile, 헤더 종속성

optionbox 2020. 9. 11. 07:55
반응형

Makefile, 헤더 종속성


규칙이있는 메이크 파일이 있다고 가정 해 보겠습니다.

%.o: %.c
 gcc -Wall -Iinclude ...

헤더 파일이 변경 될 때마다 * .o가 다시 작성되기를 원합니다. 종속성 목록을 작성하는 대신 헤더 파일이 /include변경 될 때마다 dir의 모든 개체를 다시 빌드해야합니다.

나는 이것을 수용하기 위해 규칙을 변경하는 좋은 방법을 생각할 수 없다. 나는 제안에 개방적이다. 헤더 목록을 하드 코딩 할 필요가없는 경우 보너스 포인트


GNU 컴파일러를 사용하는 경우 컴파일러가 종속성 목록을 조합 할 수 있습니다. Makefile 조각 :

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ -MF  ./.depend;

include .depend

또는

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ > ./.depend;

include .depend

여기서는 SRCS소스 파일의 전체 목록을 가리키는 변수입니다.

도구도 makedepend있지만 나는 그것을 좋아하지 않았습니다.gcc -MM


대부분의 답변은 놀랍도록 복잡하거나 오류가 있습니다. 그러나 간단하고 강력한 예제는 [ codereview ] 다른 곳에 게시되었습니다 . 확실히 gnu 전처리 기가 제공하는 옵션은 약간 혼란 스럽습니다. 그러나 빌드 대상에서 모든 디렉토리를 제거하는 -MM것은 문서화되어 있으며 버그 [ gpp ]가 아닙니다 .

기본적으로 CPP는 기본 입력 파일의 이름을 사용하고 모든 디렉토리 구성 요소 와 '.c'와 같은 파일 접미사를 삭제 하고 플랫폼의 일반적인 개체 접미사를 추가합니다.

(다소 더 새로운) -MMD옵션은 아마도 당신이 원하는 것입니다. 완전성을 위해 여러 src 디렉토리를 지원하고 일부 주석으로 디렉토리를 빌드하는 메이크 파일의 예입니다. 빌드 디렉토리가없는 간단한 버전은 [ codereview ]를 참조하십시오 .

CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow

# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build

# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)

# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)

# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)

# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
    # Create build directories - same structure as sources.
    mkdir -p $(@D)
    # Just link all the object files.
    $(CXX) $(CXX_FLAGS) $^ -o $@

# Include all .d files
-include $(DEP)

# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
    mkdir -p $(@D)
    # The -MMD flags additionaly creates a .d file with
    # the same name as the .o file.
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

This method works because if there are multiple dependency lines for a single target, the dependencies are simply joined, e.g.:

a.o: a.h
a.o: a.c
    ./cmd

is equivalent to:

a.o: a.c a.h
    ./cmd

as mentioned at: Makefile multiple dependency lines for a single target?


As I posted here gcc can create dependencies and compile at the same time:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

The '-MF' parameter specifies a file to store the dependencies in.

The dash at the start of '-include' tells Make to continue when the .d file doesn't exist (e.g. on first compilation).

Note there seems to be a bug in gcc regarding the -o option. If you set the object filename to say obj/_file__c.o then the generated file.d will still contain file.o, not obj/_file__c.o.


How about something like:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...

You could also use the wildcards directly, but I tend to find I need them in more than one place.

Note that this only works well on small projects, since it assumes that every object file depends on every header file.


Martin's solution above works great, but does not handle .o files that reside in subdirectories. Godric points out that the -MT flag takes care of that problem, but it simultaneously prevents the .o file from being written correctly. The following will take care of both of those problems:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<

This will do the job just fine , and even handle subdirs being specified:

    $(CC) $(CFLAGS) -MD -o $@ $<

tested it with gcc 4.8.3


Here's a two-liner:

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

This works with the default make recipe, as long as you have a list of all your object files in OBJS.


I prefer this solution, over the accepted answer by Michael Williamson, it catches changes to sources+inline files, then sources+headers, and finally sources only. Advantage here is that the whole library is not recompiled if only a a few changes are made. Not a huge consideration for a project with a couple of files, bur if you have 10 or a 100 sources, you will notice the difference.

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)

The following works for me:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<

A slightly modified version of Sophie's answer which allows to output the *.d files to a different folder (I will only paste the interesting part that generates the dependency files):

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@

Note that the parameter

-MT $@

is used to ensure that the targets (i.e. the object file names) in the generated *.d files contain the full path to the *.o files and not just the file name.

I don't know why this parameter is NOT needed when using -MMD in combination with -c (as in Sophie's version). In this combination it seems to write the full path of the *.o files into the *.d files. Without this combination, -MMD also writes only the pure file names without any directory components into the *.d files. Maybe somebody knows why -MMD writes the full path when combined with -c. I have not found any hint in the g++ man page.

참고URL : https://stackoverflow.com/questions/2394609/makefile-header-dependencies

반응형