다른 디렉토리에 소스 파일이있는 Makefile


135

디렉토리 구조가 다음과 같은 프로젝트가 있습니다.

                         $projectroot
                              |
              +---------------+----------------+
              |               |                |
            part1/          part2/           part3/
              |               |                |
       +------+-----+     +---+----+       +---+-----+
       |      |     |     |        |       |         |
     data/   src/  inc/  src/     inc/   src/       inc/

part / src에 있거나 또는 실제로 c / c ++ 소스 파일에서 컴파일 / 링크 할 수있는 makefile을 어떻게 작성해야합니까? / src?

-I $ projectroot / part1 / src -I $ projectroot / part1 / inc -I $ projectroot / part2 / src와 같은 작업을 수행 할 수 있습니까?

그것이 효과가 있다면 더 쉬운 방법이 있습니까? 해당하는 각 부분에 makefile이있는 프로젝트를 보았습니까? 폴더. [이 포스트에서 나는 bash 구문에서와 같이 물음표를 사용했다]



1
Phony Targets 아래 의 원래 gnu 매뉴얼 ( gnu.org/software/make/manual/html_node/Phony-Targets.html )에는 recursive invocationcoule이 매우 우아하다는 샘플이 있습니다.
Frank Nocke

해당 텍스트 그래픽을 만드는 데 어떤 도구를 사용 했습니까?
Khalid Hussain

답변:


114

전통적인 방법은이 것입니다 Makefile하위 디렉토리 (각각 part1, part2당신은 독립적으로 구축 할 수 등). 또한 Makefile프로젝트의 루트 디렉토리에 모든 것을 빌드하십시오. "루트" Makefile는 다음과 같습니다.

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

make 대상의 각 행은 자체 쉘에서 실행되므로 디렉토리 트리 또는 다른 디렉토리로의 순회에 대해 걱정할 필요가 없습니다.

GNU make 매뉴얼 섹션 5.7을 살펴 보는 것이 좋습니다 . 매우 도움이됩니다.


26
이것은 +$(MAKE) -C part1등등 이어야한다 . 이것은 Make의 작업 제어가 서브 디렉토리로 작동하게한다.
ephemient

26
이것은 고전적인 접근 방식이며 널리 사용되지만 프로젝트가 성장함에 따라 여러 가지 측면에서 차선책이됩니다. 데이브 힌튼이 따라야 할 포인터가 있습니다.
dmckee --- 전 운영자 고양이

89

한 서브 디렉토리에 다른 서브 디렉토리의 코드에 종속 된 코드가있는 경우 최상위 레벨의 단일 makefile을 사용하는 것이 좋습니다.

재귀가 유해한 것으로 간주 참조 전체 근거에 대한,하지만 기본적으로는 make가 그것을 다시 작성 여부 파일 요구 사항을 결정하는 데 필요한 모든 정보를 갖고 싶어, 그리고 당신 만의 대략 세 번째를 말한다면 것을 필요가 없습니다 당신의 프로젝트.

위의 링크에 접근 할 수없는 것 같습니다. 동일한 문서를 여기에서 찾을 수 있습니다 :


3
감사합니다, 이것을 몰랐습니다. "일하는"방식이나 표준으로 받아 들여지는 방식 대신 일을하는 "올바른 방식"을 아는 것이 매우 유용합니다.
tjklemz

3
재귀 적 인 제작은 실제로 잘 작동하지 않았을 때 해로운 것으로 간주되었습니다. 요즘 유해하지는 않지만 실제로 autotools / automake가 더 큰 프로젝트를 관리하는 방식입니다.
Edwin Buck 5

36

VPATH 옵션이 유용 할 수 있는데, 이는 소스 코드를 찾을 디렉토리를 지정합니다. 그래도 각 포함 경로에 대해 -I 옵션이 필요합니다. 예를 들면 :

CXXFLAGS=-Ipart1/inc -Ipart2/inc -Ipart3/inc
VPATH=part1/src:part2/src:part3/src

OutputExecutable: part1api.o part2api.o part3api.o

그러면 VPATH 지정 디렉토리에서 일치하는 partXapi.cpp 파일을 자동으로 찾아서 컴파일합니다. 그러나 이것은 src 디렉토리가 서브 디렉토리로 분리 될 때 더 유용합니다. 다른 사람들이 말했듯이, 특히 각 부분이 독립적 일 수 있다면 각 부분에 대한 makefile을 사용하는 것이 좋습니다.


2
이 간단한 완벽한 답변이 더 많은 표를 얻지 못했다고 믿을 수 없습니다. 그것은 +1입니다.
Nicholas Hamilton

2
하위 폴더의 여러 개별 프로젝트를 위해 상위 디렉토리에 공통 소스 파일이 있었고 VPATH=..저에게 도움이되었습니다!
EkriirkE

23

다른 디렉토리에서 필요한 cpp 파일을 컴파일하기 위해 루트 Makefile에 규칙을 추가 할 수 있습니다. 아래의 Makefile 예제는 원하는 곳으로가는 좋은 출발점이되어야합니다.

CC = g ++
목표 = cppTest
OTHERDIR = .. / .. / someotherpath / in / project / src

소스 = cppTest.cpp
소스 = $ (OTHERDIR) /file.cpp

## 최종 소스 정의
INCLUDE = -I./ $ (AN_INCLUDE_DIR)  
INCLUDE = -I. $ (OTHERDIR) /../ inc
## 더 포함

VPATH = $ (OTHERDIR)
OBJ = $ (가입 $ (addsuffix ../obj/, $ (dir $ (SOURCE))), $ (notdir $ (SOURCE : .cpp = .o)) 

## 의존 목적지를 src 디렉토리에 대해 ../.dep로 수정
DEPENDS = $ (가입 $ (addsuffix ../.dep/, $ (dir $ (SOURCE))), $ (notdir $ (SOURCE : .cpp = .d)))

## 기본 규칙 실행
모두 : $ (TARGET)
        @진실

## 깨끗한 규칙
깨끗한:
        @ -rm -f $ (대상) $ (OBJ) $ (DEPENDS)


## 실제 목표를 만들기위한 규칙
$ (대상) : $ (OBJ)
        @echo "============="
        @echo "대상 연결 $ @"
        @echo "============="
        @ $ (CC) $ (CFLAGS) -o $ @ $ ^ $ (LIBS)
        @echo-링크 완료-

## 일반 컴파일 규칙
% .o : % .cpp
        @mkdir -p $ (디렉토리 $ @)
        @echo "============="
        @echo "$ <"컴파일
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @


## cpp 파일의 객체 파일 규칙
## 각 파일의 오브젝트 파일은 obj 디렉토리에 있습니다
## 실제 소스 디렉토리에서 한 단계 위
../obj/%.o : % .cpp
        @mkdir -p $ (디렉토리 $ @)
        @echo "============="
        @echo "$ <"컴파일
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

# "other directory"에 대한 규칙 "other"디렉토리마다 하나씩 필요합니다.
$ (OTHERDIR) /../ obj / %. o : % .cpp
        @mkdir -p $ (디렉토리 $ @)
        @echo "============="
        @echo "$ <"컴파일
        @ $ (CC) $ (CFLAGS) -c $ <-o $ @

## 의존성 규칙 만들기
../.dep/%.d : % .cpp
        @mkdir -p $ (디렉토리 $ @)
        @echo "============="
        $ *. o의 @echo Building 의존성 파일
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ .. / obj / $ *. o ^"> $ @ '

## "기타"디렉토리에 대한 종속성 규칙
$ (OTHERDIR) /../. dep / %. d : % .cpp
        @mkdir -p $ (디렉토리 $ @)
        @echo "============="
        $ *. o의 @echo Building 의존성 파일
        @ $ (SHELL) -ec '$ (CC) -M $ (CFLAGS) $ <| sed "s ^ $ *. o ^ $ (OTHERDIR) /../ obj / $ *. o ^"> $ @ '

## 의존성 파일 포함
-포함 $ (DEPENDS)


7
나는 이것이 지금까지 꽤 오래되었다는 것을 알고 있지만, 한 줄 후에 다른 할당으로 SOURCE와 INCLUDE를 모두 덮어 쓰지 않습니까?
skelliam

@skelliam 예, 그렇습니다.
nabroyan

1
이 접근 방식은 DRY 원칙을 위반합니다. 각 "다른 디렉토리"에 대한 코드를 복제하는 것은 좋지 않습니다.
Jesus H

20

소스가 많은 폴더에 분산되어 있고 개별 Makefile을 갖는 것이 합리적이라면 이전에 제안한 것처럼 재귀 적 make가 좋은 접근 방법이지만 소규모 프로젝트의 경우 Makefile의 모든 소스 파일을 상대 경로와 함께 나열하는 것이 더 쉽습니다. 받는 사람 메이크 같은 :

# common sources
COMMON_SRC := ./main.cpp \
              ../src1/somefile.cpp \
              ../src1/somefile2.cpp \
              ../src2/somefile3.cpp \

그런 다음 VPATH이 방법 을 설정할 수 있습니다 :

VPATH := ../src1:../src2

그런 다음 객체를 만듭니다.

COMMON_OBJS := $(patsubst %.cpp, $(ObjDir)/%$(ARCH)$(DEBUG).o, $(notdir $(COMMON_SRC)))

이제 규칙은 간단합니다.

# the "common" object files
$(ObjDir)/%$(ARCH)$(DEBUG).o : %.cpp Makefile
    @echo creating $@ ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<

그리고 출력을 만드는 것이 훨씬 쉽습니다.

# This will make the cbsdk shared library
$(BinDir)/$(OUTPUTBIN): $(COMMON_OBJS)
    @echo building output ...
    $(CXX) -o $(BinDir)/$(OUTPUTBIN) $(COMMON_OBJS) $(LFLAGS)

다음을 통해 VPATH세대를 자동화 할 수도 있습니다 .

VPATH := $(dir $(COMMON_SRC))

또는 sort중복 을 제거 한다는 사실을 사용하십시오 (중요하지는 않지만).

VPATH := $(sort  $(dir $(COMMON_SRC)))

이것은 일부 사용자 정의 라이브러리를 추가하고 별도의 폴더에 저장하려는 소규모 프로젝트에 효과적입니다. 매우 감사합니다
zitroneneis

6

오늘날 도구와 비교할 때 배우고 유지 관리하고 확장하기가 어렵 기 때문에 Make (재귀 적이거나 그렇지 않은)를 사용하는 것이 일반적으로 피하고 싶을 것이라고 지적하는 것이 좋습니다.

훌륭한 도구이지만 2010 년에는 직접 사용하지 않는 것으로 간주됩니다.

물론, 당신은 레거시 프로젝트 등과 같은 특별한 환경에서 일하고 있지 않다면.

IDE, CMake 또는 하드 코어 된 경우 Autotools를 사용하십시오 .

(공포로 인해 편집 됨, ty Honza 지적)


1
downvotes를 설명하는 것이 좋을 것입니다. 나는 Makefiles도 직접 만들었습니다.
IlDan

2
KDevelop에는 Make 및 기타 빌드 시스템을 구성 할 수있는 그래픽 인터페이스가 있으며 Makefile을 직접 작성하는 동안 KDevelop는 동료에게주는 것입니다. Autotools 링크가 도움이되지 않는다고 생각하십시오. Autotools를 이해하려면 m4, libtool, automake, autoconf, shell, make 및 기본적으로 전체 스택을 이해해야합니다.
ephemient

7
downvotes는 아마도 당신이 당신의 자신의 질문에 대답하기 때문일 것입니다. 문제는 Makefile을 사용해야하는지에 관한 것이 아닙니다. 그것들을 쓰는 법에 관한 것이 었습니다.
혼자

8
몇 개의 문장으로 현대 도구가 Makefile을 작성하는 것보다 인생을 더 쉽게 만드는 방법을 설명 할 수 있다면 좋을 것입니다. 그것들은 더 높은 수준의 추상화를 제공합니까? 또는 무엇을?
Abhishek Anand

1
xterm, vi, bash, makefile == 세상을 지배, cmake는 코드를 해킹 할 수없는 사람들을위한 것입니다.
μολὼν.λαβέ

2

RC의 게시물은 매우 유용했습니다. 나는 $ (dir $ @) 함수를 사용하는 것에 대해 생각하지 않았지만, 내가해야 할 일을 정확하게 수행했습니다.

parentDir에는 dirA, dirB, dirC와 같은 소스 파일이있는 디렉토리가 많이 있습니다. 다양한 파일은 다른 디렉토리의 객체 파일에 의존하므로 한 디렉토리에서 하나의 파일을 만들고 해당 종속성과 관련된 makefile을 호출하여 해당 파일을 만들 수 있기를 원했습니다.

본질적으로, 나는 많은 다른 것들 중에서도 RC와 비슷한 일반적인 규칙을 가진 하나의 Makefile을 parentDir에 만들었습니다.

%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

각 서브 디렉토리에는이 일반 규칙을 상속하기 위해이 상위 레벨 makefile이 포함되었습니다. 각 하위 디렉토리의 Makefile에서 각 파일에 대한 사용자 지정 규칙을 작성하여 각 개별 파일이 의존하는 모든 것을 추적 할 수있었습니다.

파일을 만들어야 할 때마다 (필수적으로)이 규칙을 사용하여 모든 / 모든 종속성을 재귀 적으로 만들었습니다. 완전한!

참고 :이 작업을 훨씬 직관적으로 수행하는 것처럼 보이는 "makepp"라는 유틸리티가 있지만 다른 도구에 의존하지 않고 이식성을 위해이 방법으로 선택했습니다.

도움이 되었기를 바랍니다!


2

Make의 재귀 적 사용

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

이를 통해 make작업으로 분할하고 여러 코어를 사용할 수 있습니다


2
어떻게 이런 일을하는 것보다 낫 make -j4습니까?
devin

1
내가보기로 @devin, 아니에요 더 나은 , 그냥 make 모두에서 작업 제어를 사용 할 수 있습니다. 별도의 make 프로세스를 실행할 때는 작업 제어 대상이 아닙니다.
Michael Pankov

1

나는 이것과 같은 것을 찾고 있었고, 몇 번의 시도와 실패 후에 나는 내 자신의 makefile을 만들지 만, 그것이 "관용적 인 방법"이 아니라는 것을 안다.

PROJ_NAME=mono

CPP_FILES=$(shell find . -name "*.cpp")

S_OBJ=$(patsubst %.cpp, %.o, $(CPP_FILES))

CXXFLAGS=-c \
         -g \
        -Wall

all: $(PROJ_NAME)
    @echo Running application
    @echo
    @./$(PROJ_NAME)

$(PROJ_NAME): $(S_OBJ)
    @echo Linking objects...
    @g++ -o $@ $^

%.o: %.cpp %.h
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS) -o $@

main.o: main.cpp
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS)

clean:
    @echo Removing secondary things
    @rm -r -f objects $(S_OBJ) $(PROJ_NAME)
    @echo Done!

나는 그것이 간단하고 어떤 사람들에게는 내 깃발이 잘못되었다는 것을 알고 있지만, 이것이 내 프로젝트를 여러 디렉토리로 컴파일하고 함께 묶어서 내 쓰레기통을 만드는 첫 번째 Makefile이라고 말했듯이.

나는 수용을 받아들이고있다 : D


-1

나는 사용하는 것이 좋습니다 autotools:

//## 비재 귀적 make가 사용될 때 충돌을 피하기 위해 생성 된 오브젝트 파일 (.o)을 소스 파일과 동일한 디렉토리에 배치하십시오.

AUTOMAKE_OPTIONS = subdir-objects

Makefile.am다른 아주 간단한 것들과 함께 포함 시키면됩니다 .

튜토리얼 은 다음과 같습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.