단일 소스 파일에서 몇 개의 타겟을 생성하는 GNU Makefile 규칙


106

다음을 시도하고 있습니다. foo-bin단일 입력 파일을 받아 두 개의 출력 파일을 생성 하는 프로그램이 있습니다. 이에 대한 멍청한 Makefile 규칙은 다음과 같습니다.

file-a.out file-b.out: input.in
    foo-bin input.in file-a.out file-b.out

그러나 이것은 make두 대상이 동시에 생성된다는 것을 어떤 식으로도 알려주지 않습니다 . make직렬로 실행할 때는 괜찮지 만 시도 make -j16하거나 똑같이 미친 짓을 하면 문제가 발생할 수 있습니다 .

문제는 그러한 경우에 적절한 Makefile 규칙을 작성하는 방법이 있는지 여부입니다. 분명히 DAG를 생성하지만 GNU make 매뉴얼은이 경우를 처리하는 방법을 지정하지 않습니다.

동일한 코드를 두 번 실행하고 하나의 결과 만 생성하는 것은 계산에 시간이 걸리기 때문에 문제가되지 않습니다 (생각 : 몇 시간). 데이터 파일의 일부만 처리하는 방법을 모르는 GNUPLOT의 입력으로 자주 사용되기 때문에 하나의 파일 만 출력하는 것도 다소 어려울 수 있습니다.


1
: Automake는 문서이에있다 gnu.org/software/automake/manual/html_node/...
프랭크

답변:


12

다음과 같이 해결할 것입니다.

file-a.out: input.in
    foo-bin input.in file-a.out file-b.out   

file-b.out: file-a.out
    #do nothing
    noop

이 경우 병렬 make는 a와 b를 만드는 '직렬화'하지만 b를 만드는 것은 아무 작업도하지 않기 때문에 시간이 걸리지 않습니다.


16
사실 이것에 문제가 있습니다. 경우 file-b.out언급하고 신비 삭제 된 것으로 만들었습니다, make때문에 그것을 다시 할 수 없습니다 file-a.out여전히 존재합니다. 이견있는 사람?
makeaurus 2010-06-08

신비하게 삭제 file-a.out하면 위의 솔루션이 잘 작동합니다. 이것은 해킹을 암시합니다. a 또는 b 중 하나만 존재하는 경우 누락 된 파일이 .NET의 출력으로 나타나도록 종속성을 정렬해야합니다 input.in. 몇 $(widcard...)s, $(filter...)s, $(filter-out...)s 등이 트릭을 수행해야합니다. 으.
bobbogo 2011 년

3
PS foo-bin는 분명히 먼저 파일 중 하나를 생성하므로 (마이크로 초 해상도 사용) 두 파일이 모두 존재할 때 올바른 종속성 순서가 있는지 확인해야합니다.
bobbogo 2011 년

2
@bobbogo 또는 호출 touch file-a.out후 두 번째 명령으로 추가하십시오 foo-bin.
Connor Harris

다음은 이에 대한 잠재적 인 수정 사항입니다. touch file-b.out첫 번째 규칙에서 두 번째 명령으로 추가 하고 두 번째 규칙에서 명령을 복제합니다. 파일 중 하나가 누락되었거나보다 오래된 경우 input.in명령이 한 번만 실행되고 file-b.out보다 최신 파일이있는 두 파일을 모두 다시 생성합니다 file-a.out.
chqrlie

128

트릭은 여러 대상이있는 패턴 규칙을 사용하는 것입니다. 이 경우 make는 명령을 한 번 호출하여 두 대상을 모두 생성한다고 가정합니다.

모두 : 파일 -a.out 파일 -b.out
파일 -a % out 파일 -b % out : input.in
    foo-bin input.in 파일 -a $ * out 파일 -b $ * out

패턴 규칙과 일반 ​​규칙 간의 이러한 해석 차이는 정확히 말이되지 않지만 이와 같은 경우에 유용하며 매뉴얼에 문서화되어 있습니다.

이 트릭은 이름에 공통 부분 문자열이있는 한 여러 출력 파일에 사용할 수 있습니다. % 에 일치 할 . (이 경우 공통 하위 문자열은 ".")


3
이것은 실제로 올바르지 않습니다. 규칙은 이러한 패턴과 일치하는 파일이 지정된 명령을 사용하여 input.in에서 만들어 지도록 지정하지만 동시에 만들어 졌다고 말하는 곳은 없습니다. 실제로 병렬로 make실행하면 동일한 명령을 동시에 두 번 실행합니다.
makesaurus

47
작동하지 않는다고 말하기 전에 시도해보십시오 ;-) GNU make 매뉴얼에는 "패턴 규칙에는 둘 이상의 대상이있을 수 있습니다. 일반 규칙과 달리 이것은 동일한 전제 조건 및 명령을 사용하여 여러 다른 규칙으로 작동하지 않습니다. 패턴 규칙에는 여러 대상이 있습니다. 규칙의 명령이 모든 대상을 만드는 역할을한다는 것을 확인합니다. 명령은 모든 대상을 만들기 위해 한 번만 실행됩니다. " gnu.org/software/make/manual/make.html#Pattern-Intro
slowdog 2010-06-26

4
그러나 완전히 다른 입력이 있다면 어떨까요?, foo.in 및 bar.src라고 말합니까? 패턴 규칙은 빈 패턴 일치를 지원합니까?
grwlf 2013-07-25

2
@dcow : 출력 파일은 반드시 접두사가 아닌 부분 문자열 ( "."과 같은 단일 문자로도 충분 함) 만 공유하면됩니다. 공통 하위 문자열이없는 경우 richard와 deemer가 설명한대로 중간 대상을 사용할 수 있습니다. @grwlf : 헤이, "foo.in"과 "bar.src"를 수행 할 수 있다는 뜻입니다 : foo%in bar%src: input.in:-)
slowdog

1
각 출력 파일이 변수에 보관되어 있으면 레시피를 다음과 같이 작성할 수 있습니다 $(subst .,%,$(varA) $(varB)): input.in(GNU make 4.1로 테스트 됨).
stefanct

55

Make에는이를 수행하는 직관적 인 방법이 없지만 두 가지 적절한 해결 방법이 있습니다.

첫째, 관련된 대상에 공통 어간이있는 경우 접두사 규칙 (GNU make 사용)을 사용할 수 있습니다. 즉, 다음 규칙을 수정하려는 경우 :

object.out1 object.out2: object.input
    foo-bin object.input object.out1 object.out2

다음과 같이 작성할 수 있습니다.

%.out1 %.out2: %.input
    foo-bin $*.input $*.out1 $*.out2

(패턴의 일치하는 부분을 나타내는 패턴 규칙 변수 $ * 사용)

GNU가 아닌 Make 구현으로 이식하고 싶거나 패턴 규칙과 일치하도록 파일 이름을 지정할 수없는 경우 다른 방법이 있습니다.

file-a.out file-b.out: input.in.intermediate ;

.INTERMEDIATE: input.in.intermediate
input.in.intermediate: input.in
    foo-bin input.in file-a.out file-b.out

이것은 make가 실행되기 전에 input.in.intermediate가 존재하지 않을 것이라는 것을 알려줍니다. 그래서 그것의 부재 (또는 타임 스탬프)는 foo-bin이 가짜로 실행되는 원인이되지 않습니다. 그리고 file-a.out 또는 file-b.out 또는 둘 다 오래된 것이 든 (input.in에 상대적), foo-bin은 한 번만 실행됩니다. .INTERMEDIATE 대신 .SECONDARY를 사용할 수 있으며, 이는 make NOT이 가상 파일 이름 input.in.intermediate를 삭제하도록 지시합니다. 이 방법은 병렬 메이크 빌드에도 안전합니다.

첫 번째 줄의 세미콜론이 중요합니다. 해당 규칙에 대한 빈 레시피를 생성하므로 Make가 실제로 file-a.out 및 file-b.out을 업데이트 할 것임을 알 수 있습니다 (@siulkiulki 및이를 지적한 다른 사람들에게 감사드립니다).


7
.INTERMEDIATE 접근 방식이 잘 작동합니다. 문제를 설명하는 Automake 기사 를 참조하십시오 (그러나 그들은 이식 가능한 방식으로 해결하려고합니다).
grwlf

3
이것이 내가 본 최고의 답변입니다. 다른 특수 타겟도 관심을 가질 수 있습니다. gnu.org/software/make/manual/html_node/Special-Targets.html
Arne Babenhauserheide

2
@deemer 이것은 OSX에서 나를 위해 작동하지 않습니다. (GNU Make 3.81)
Jorge Bucaran 2015

2
나는 이것을 시도해 왔고 병렬 빌드에서 미묘한 문제를 발견했습니다 .-- 종속성이 항상 중간 대상에 제대로 전파되지는 않습니다 ( -j1빌드에서는 모든 것이 괜찮음). 또한 makefile이 중단되고 input.in.intermediate파일이 주변에 남겨 지면 정말 혼란스러워집니다.
다윗은 주어

3
이 답변에는 오류가 있습니다. 병렬 빌드가 완벽하게 작동 할 수 있습니다. 당신은 변경해야 file-a.out file-b.out: input.in.intermediate하는 file-a.out file-b.out: input.in.intermediate ;참조 stackoverflow.com/questions/37873522/...을 자세한 내용은.
siulkilki

10

이것은 패턴 규칙에 의존하지 않는 @deemer의 두 번째 답변을 기반으로하며 해결 방법의 중첩 된 사용으로 발생한 문제를 해결합니다.

file-a.out file-b.out: input.in.intermediate
    @# Empty recipe to propagate "newness" from the intermediate to final targets

.INTERMEDIATE: input.in.intermediate
input.in.intermediate: input.in
    foo-bin input.in file-a.out file-b.out

나는 이것을 @deemer의 답변에 대한 코멘트로 추가했을 것이지만, 방금이 계정을 만들었고 평판이 없기 때문에 할 수 없습니다.

설명 : Make가 적절한 부기를 표시 file-a.out하고 file-b.out재건 된 것으로 표시 할 수 있도록하려면 빈 레시피가 필요합니다 . 에 의존하는 또 다른 중간 대상이있는 file-a.out경우 Make는 다음을 주장하면서 외부 중간을 빌드하지 않도록 선택합니다.

No recipe for 'file-a.out' and no prerequisites actually changed.
No need to remake target 'file-a.out'.

9

GNU Make 4.3 (2020 년 1 월 19 일) 이후에는 "그룹화 된 명시 적 대상"을 사용할 수 있습니다. 교체 :와 함께 &:.

file-a.out file-b.out &: input.in
    foo-bin input.in file-a.out file-b.out

GNU Make의 NEWS 파일은 다음과 같이 말합니다.

새로운 기능 : 그룹화 된 명시 적 대상

패턴 규칙은 항상 레시피를 한 번만 호출하여 여러 대상을 생성 할 수있었습니다. 이제 명시 적 규칙이 단일 호출로 여러 대상을 생성한다고 선언 할 수 있습니다. 이를 사용하려면 규칙에서 ":"토큰을 "& :"로 바꾸십시오. 이 기능을 감지하려면 .FEATURES 특수 변수에서 'grouped-target'을 검색하십시오. 구현 제공 : Kaz Kylheku <kaz@kylheku.com>


2

이것이 내가하는 방법이다. 먼저 저는 항상 사전 요청과 레시피를 분리합니다. 그런 다음이 경우 레시피를 수행 할 새 대상입니다.

all: file-a.out file-b.out #first rule

file-a.out file-b.out: input.in

file-a.out file-b.out: dummy-a-and-b.out

.SECONDARY:dummy-a-and-b.out
dummy-a-and-b.out:
    echo creating: file-a.out file-b.out
    touch file-a.out file-b.out

처음으로 :
1. 우리는 파일 a.out을 빌드하려고하지만, dummy-a-and-b.out은 먼저해야하므로 make는 dummy-a-and-b.out 레시피를 실행합니다.
2. 우리는 file-b.out을 빌드하려고합니다. dummy-a-and-b.out은 최신 상태입니다.

두 번째 및 그 이후 :
1. 우리는 file-a.out을 빌드하려고합니다. 전제 조건을 살펴보고 일반 전제 조건이 최신 상태이며 보조 전제 조건이 누락되어 무시됩니다.
2. file-b.out을 빌드하려고합니다. 전제 조건을 확인하고, 일반 전제 조건이 최신 상태이며, 보조 전제 조건이 누락되어 무시됩니다.


1
이것은 고장났습니다. 삭제 file-b.out하면 make가 다시 만들 수 없습니다.
bobbogo 2011 년

모두 추가 : 첫 번째 규칙으로 file-a.out file-b.out. file-b.out이 제거되고 make가 대상없이 실행 된 것처럼 명령 줄에서 다시 빌드하지 않습니다.
ctrl-alt-delor

@bobbogo 수정 됨 : 위 주석 참조.
ctrl-alt-delor

0

@deemer의 답변에 대한 확장으로 나는 그것을 함수로 일반화했습니다.

sp :=
sp +=
inter = .inter.$(subst $(sp),_,$(subst /,_,$1))

ATOMIC=\
    $(eval s1=$(strip $1)) \
    $(eval target=$(call inter,$(s1))) \
    $(eval $(s1): $(target) ;) \
    $(eval .INTERMEDIATE: $(target) ) \
    $(target)

$(call ATOMIC, file-a.out file-b.out): input.in
    foo-bin input.in file-a.out file-b.out

고장:

$(eval s1=$(strip $1))

첫 번째 인수에서 선행 / 후행 공백을 제거하십시오.

$(eval target=$(call inter,$(s1)))

중간 대상으로 사용할 고유 한 값에 대한 변수 대상을 만듭니다. 이 경우 값은 .inter.file-a.out_file-b.out이됩니다.

$(eval $(s1): $(target) ;)

고유 한 대상을 종속성으로 사용하여 출력에 대한 빈 레시피를 만듭니다.

$(eval .INTERMEDIATE: $(target) ) 

고유 한 대상을 중간으로 선언하십시오.

$(target)

이 기능을 레시피에서 직접 사용할 수 있도록 고유 한 대상에 대한 참조로 완료합니다.

또한 여기서 eval을 사용하는 것은 eval이 아무것도 확장하지 않기 때문에 함수의 전체 확장이 유일한 대상이되기 때문입니다.

이 함수에서 영감을 얻은 GNU Make의 Atomic Rules에 기여해야 합니다.


0

make -jN사용 하여 여러 출력이있는 규칙의 병렬 다중 실행을 방지하려면을 사용하십시오 .NOTPARALLEL: outputs. 귀하의 경우 :

.NOTPARALLEL: file-a.out file-b.out

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