[makefile] make 파일을 사용하여 디렉토리 생성

저는 makefiles를 처음 접했고 makefile을 사용하여 디렉토리를 만들고 싶습니다. 내 프로젝트 디렉토리는 다음과 같습니다.

+--Project
   +--output
   +--source
     +Testfile.cpp
   +Makefile

모든 개체와 출력을 각 출력 폴더에 넣고 싶습니다. 컴파일 후 다음과 같은 폴더 구조를 만들고 싶습니다.

+--Project
   +--output
     +--debug (or release)
       +--objs
         +Testfile.o
       +Testfile (my executable file)
   +--source
     +Testfile.cpp
   +Makefile

몇 가지 옵션을 시도했지만 성공하지 못했습니다. make 파일을 사용하여 디렉토리를 만들도록 도와주세요. 나는 당신의 고려를 위해 내 Makefile을 게시하고 있습니다.

#---------------------------------------------------------------------
# Input dirs, names, files
#---------------------------------------------------------------------
OUTPUT_ROOT := output/

TITLE_NAME := TestProj

ifdef DEBUG
    TITLE_NAME += _DEBUG
else
ifdef RELEASE
    TITLE_NAME += _RELEASE
endif
endif


# Include all the source files here with the directory tree
SOURCES := \
        source/TestFile.cpp \

#---------------------------------------------------------------------
# configs
#---------------------------------------------------------------------
ifdef DEBUG
OUT_DIR     := $(OUTPUT_ROOT)debug
CC_FLAGS    := -c -Wall
else
ifdef RELEASE
OUT_DIR     := $(OUTPUT_ROOT)release
CC_FLAGS    := -c -Wall
else
$(error no build type defined)
endif
endif

# Put objects in the output directory.
OUT_O_DIR   := $(OUT_DIR)/objs

#---------------------------------------------------------------------
# settings
#---------------------------------------------------------------------
OBJS = $(SOURCES:.cpp=.o)
DIRS = $(subst /,/,$(sort $(dir $(OBJS))))
DIR_TARGET = $(OUT_DIR)

OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME)

CC_FLAGS +=

LCF_FLAGS :=

LD_FLAGS :=

#---------------------------------------------------------------------
# executables
#---------------------------------------------------------------------
MD := mkdir
RM := rm
CC := g++

#---------------------------------------------------------------------
# rules
#---------------------------------------------------------------------
.PHONY: all clean title

all: title

clean:
    $(RM) -rf $(OUT_DIR)

$(DIR_TARGET):
    $(MD) -p $(DIRS)

.cpp.o:
    @$(CC) -c $< -o $@

$(OBJS): $(OUT_O_DIR)/%.o: %.cpp
    @$(CC) -c $< -o $@

title: $(DIR_TARGET) $(OBJS)

미리 감사드립니다. 저도 실수를했다면 안내 해주세요.



답변

이것은 유닉스와 같은 환경을 가정하여 그렇게 할 것입니다.

MKDIR_P = mkdir -p

.PHONY: directories

all: directories program

directories: ${OUT_DIR}

${OUT_DIR}:
        ${MKDIR_P} ${OUT_DIR}

이것은 최상위 디렉토리에서 실행되어야합니다. 그렇지 않으면 $ {OUT_DIR}의 정의가 실행되는 위치에 상대적으로 정확해야합니다. 물론 Peter Miller의 ” Recursive Make Thoughmful “문서 의 칙령을 따른다면 어쨌든 최상위 디렉토리에서 make를 실행하게 될 것입니다.

나는 지금이 (RMCH)를 가지고 놀고있다. 테스트 그라운드로 사용중인 소프트웨어 제품군에 약간의 적응이 필요했습니다. 이 제품군에는 15 개의 디렉토리에 분산 된 소스로 구축 된 12 개의 개별 프로그램이 있으며, 일부는 공유됩니다. 그러나 약간의주의를 기울이면 가능합니다. OTOH, 초보자에게는 적합하지 않을 수 있습니다.


주석에서 언급했듯이 ‘디렉토리’에 대한 작업으로 ‘mkdir’명령을 나열하는 것은 잘못되었습니다. 주석에서 언급했듯이 ‘출력 / 디버그를 만드는 방법을 모릅니다’오류를 수정하는 다른 방법이 있습니다. 하나는 ‘디렉토리’줄에 대한 종속성을 제거하는 것입니다. 이것은 ‘mkdir -p’가 생성하도록 요청 된 모든 디렉토리가 이미 존재하는 경우 오류를 생성하지 않기 때문에 작동합니다. 다른 하나는 표시된 메커니즘으로, 디렉토리가없는 경우에만 생성을 시도합니다. ‘수정 된’버전은 내가 어젯밤 염두에 두었던 것입니다.하지만 두 기술 모두 작동합니다 (출력 / 디버그가 존재하지만 디렉토리가 아닌 파일 인 경우 둘 다 문제가 있습니다).


답변

제 생각에, 디렉토리는 기술적 또는 디자인 적 측면에서 Makefile의 대상으로 간주되어서는 안됩니다. 당신은 만들어야합니다 파일 및 파일 생성은 새로운 디렉토리를 필요로하는 경우 다음 조용히 관련 파일에 대한 규칙 내에서 디렉토리를 생성합니다.

일반 또는 “패턴 화 된”파일을 대상으로하는 경우 make‘s 내부 변수 $(@D)를 사용하십시오. 이는 “현재 대상이 상주하는 디렉토리”(대상의 경우 cmp. $@)를 의미합니다. 예를 들면

$(OUT_O_DIR)/%.o: %.cpp
        @mkdir -p $(@D)
        @$(CC) -c $< -o $@

title: $(OBJS)

그런 다음 효과적으로 동일한 작업을 수행합니다. 모두를위한 디렉토리를 생성 $(OBJS)하지만 덜 복잡한 방식으로 수행합니다.

다양한 애플리케이션에서 동일한 정책 (파일은 대상, 디렉토리는 절대로 사용되지 않음)이 사용됩니다. 예를 들어 git개정 제어 시스템은 디렉토리를 저장하지 않습니다.


참고 : 사용하려는 경우 편의 변수를 도입하고 make의 확장 규칙을 활용하는 것이 유용 할 수 있습니다 .

dir_guard=@mkdir -p $(@D)

$(OUT_O_DIR)/%.o: %.cpp
        $(dir_guard)
        @$(CC) -c $< -o $@

$(OUT_O_DIR_DEBUG)/%.o: %.cpp
        $(dir_guard)
        @$(CC) -g -c $< -o $@

title: $(OBJS)


답변

또는 KISS.

DIRS=build build/bins

...

$(shell mkdir -p $(DIRS))

Makefile이 구문 분석 된 후 모든 디렉토리가 생성됩니다.


답변

makein 및 off 자체는 파일 대상과 동일하게 디렉토리 대상을 처리합니다. 따라서 다음과 같은 규칙을 작성하는 것은 쉽습니다.

outDir/someTarget: Makefile outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

유일한 문제는 디렉토리 타임 스탬프가 내부 파일에 수행되는 작업에 따라 달라진다는 것입니다. 위의 규칙의 경우 다음과 같은 결과가 나타납니다.

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget

이것은 확실히 당신이 원하는 것이 아닙니다. 파일을 터치 할 때마다 디렉토리도 터치합니다. 그리고 파일이 디렉토리에 의존하기 때문에 파일이 오래되어 다시 빌드되어야합니다.

그러나 make에게 디렉토리의 타임 스탬프를 무시하도록 지시 하여이 루프를 쉽게 끊을 수 있습니다 . 이는 디렉토리를 주문 전용 전제 조건으로 선언하여 수행됩니다.

# The pipe symbol tells make that the following prerequisites are order-only
#                           |
#                           v
outDir/someTarget: Makefile | outDir
    touch outDir/someTarget

outDir:
    mkdir -p outDir

이것은 올바르게 다음을 산출합니다.

$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.

TL; DR :

디렉터리를 만드는 규칙을 작성합니다.

$(OUT_DIR):
    mkdir -p $(OUT_DIR)

그리고 내부의 물건에 대한 대상은 디렉토리 주문에만 의존합니다.

$(OUT_DIR)/someTarget: ... | $(OUT_DIR)


답변

수락 된 솔루션을 포함한 모든 솔루션에는 각각의 의견에 명시된 몇 가지 문제가 있습니다. 조나단 – 레플러 @에 의해 허용 대답은 이미 꽤 좋은 그러나 전제 조건이 반드시 (중 순서에 건설되지 않도록 효력을지지 않습니다 make -j예를 들어). 그러나 단순히 directories전제 조건을에서 all로 이동하면 programAFAICT가 실행될 때마다 재 구축 이 유발됩니다. 다음 솔루션에는 해당 문제가 없으며 AFAICS가 의도 한대로 작동합니다.

MKDIR_P := mkdir -p
OUT_DIR := build

.PHONY: directories all clean

all: $(OUT_DIR)/program

directories: $(OUT_DIR)

$(OUT_DIR):
    ${MKDIR_P} $(OUT_DIR)

$(OUT_DIR)/program: | directories
    touch $(OUT_DIR)/program

clean:
    rm -rf $(OUT_DIR)


답변

빌드 할 파일을 정의하고 디렉토리를 자동으로 생성 할 수있는 상당히 합리적인 솔루션을 찾았습니다. 먼저 ALL_TARGET_FILESmakefile이 빌드 될 모든 파일의 파일 이름을 보유 하는 변수 를 정의 하십시오. 그런 다음 다음 코드를 사용하십시오.

define depend_on_dir
$(1): | $(dir $(1))

ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
    mkdir -p $$@

$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef

$(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file))))

작동 방식은 다음과 같습니다. depend_on_dir파일 이름을 가져 와서 파일이 포함 된 디렉토리에 종속되는 규칙을 생성 하는 함수 를 정의한 다음 필요한 경우 해당 디렉토리를 생성하는 규칙을 정의합니다. 그런 다음 각 파일 이름과 결과 에이 기능을 사용 foreach합니다 .calleval

를 지원하는 GNU make 버전이 필요합니다 eval.이 버전은 3.81 이상이라고 생각합니다.


답변

당신이 초보자라는 것을 감안할 때, 나는 이것을 아직 시도하지 말라고 말하고 싶습니다. 확실히 가능하지만 Makefile을 불필요하게 복잡하게 만듭니다. make에 더 익숙해 질 때까지 간단한 방법을 고수하십시오.

즉, 소스 디렉토리와 다른 디렉토리에 빌드하는 한 가지 방법은 VPATH입니다 . 나는 패턴 규칙을 선호한다