비재 귀적 make에 대한 경험은 무엇입니까?
몇 년 전, 저는 재귀 만들기 Thoughmful 논문을 읽고 제 자신의 빌드 프로세스에서 아이디어를 구현했습니다. 최근에 비 재귀make
를 구현 하는 방법에 대한 아이디어가 담긴 내용을 읽었습니다 . 그래서 make
그냥 몇 개의 프로젝트에서 비재 귀적으로 작동 하는 몇 가지 데이터 포인트가 있습니다.
하지만 다른 사람들의 경험이 궁금합니다. 비 재귀를 시도해 보 경주 make
? 상황을 더 좋게 또는 더 나쁘게 만들 었었습니까? 시간의 가치가 있었습니까?
우리는 내가 일하는 회사에서 비 재귀 GNU Make 시스템을 사용합니다. 이 Miller의 논문과 특히 귀하가 제공하는 "비재 귀적 구현"링크를 기반으로합니다. 우리는 Bergen의 코드를 하위 디렉토리 makefile에 보일러 플레이트가 전혀없는 시스템으로 개선했습니다. 대체로 잘 작동하며 이전 시스템보다 훨씬 낫습니다 (GNU Automake로 수행되는 재귀 적 작업).
우리는 AIX, HP-UX, Linux, OS X, Solaris, Windows, AS / 400 메인 프레임까지 모든 "주요"운영 (상업적)을 지원합니다. 플랫폼 모든 부분을 라이브러리로 분리하여 시스템에 대해 동일한 코드를 수행합니다.
우리 트리에는 약 2000 개의 하위 디렉터리와 20000 개의 파일에 2 백만 개의 줄 이상의 C 코드가 있습니다. 우리는 SCons 사용을 진지하게 고려했지만 충분히 빨리 작동하지. 느린 시스템에서 Python은 GNU Make가 약 1 초 만에 동일한 작업을 수행 한 SCons 파일을 구문 분석하는 데에서 초를 사용합니다. 이는 약 3 년 전 이었거나 그 이후로 상황이 변경되었습니다. 일반적으로 소스 코드를 NFS / CIFS 공유에 유지하고 여러 플랫폼 에서 동일한 코드를 빌드합니다 . 이것은 빌드 도구가 소스 트리에서 변경 사항을 스캔하는 것이 훨씬 느리다는 것을 의미합니다.
우리의 비재 귀적 GNU Make 시스템에는 문제가 없습니다. 다음은 보관 수있는 가장 큰 장애물입니다.
- 특히 Windows에 이식성있게 만드는 것은 많은 작업입니다.
- GNU Make는 거의 사용할 수있는 함수형 프로그래밍 언어이지만 프로그래밍이 적합하지 않습니다. 특히, 조각을 서로 분리하는 데 도움이되는 네임 스페이스, 모듈 또는 이와 동일한 것이 없습니다. 이것은 당신이 생각하는 것만 큼은 문제를 해결할 수 있습니다.
기존의 재귀 적 메이크 파일 시스템에 대한 주요 장점은 다음과 같습니다.
- 그것은이다 빨리 . 트리 (2k 디렉토리, 20k 파일)를 확인하고 최신 상태를 확인하거나 전체 시작하는 데 약 2 초가 전체를 시작합니다. 오래된 재귀적인 것은 아무것도하지 않는 데 1 분 이상 걸릴 것입니다.
- 지역을 처리합니다. 우리의 오래된 시스템은 주문 하위 디렉토리에 의존했습니다. Miller의 논문을 읽었을 때 예상했던 것처럼 전체 트리를 단일로 취급하는 것이 문제를 해결하는 올바른 방법입니다.
- 우리가 모든 노력을 기울인 후 지원되는 모든 시스템에 이식 할 수 있습니다. 꽤 멋지다.
- 추상화 시스템을 사용하면 매우 간결한 메이크 파일을 사용할 수 있습니다. 라이브러리 만 정의하는 일반적인 하위 디렉토리는 두 줄입니다. 한 줄은 라이브러리의 이름을 제공하고 다른 줄은이 라이브러리가 의존하는 라이브러리를 제공합니다.
위 목록의 마지막 항목에 대해. 결국 빌드 시스템 내에서 매크로 확장 기능을 구현했습니다. 서브 디렉토리 메이크 파일은 프로그램, 서브 디렉토리, 라이브러리 및 PROGRAMS, SUBDIRS, LIBS와 같은 변수의 기타 일반적인 업데이트합니다. 그런 다음 이들 각각은 "실제"GNU Make 규칙으로 확장됩니다. 이를 통해 많은 네임 스페이스 문제를 피할 수 있습니다. 예를 들어, 우리 시스템에서는 이름이 같은 여러 소스 파일이 있어도 괜찮습니다.
어쨌든 이것은 많은 작업이되었습니다. 코드에 대해 SCons 또는 이와 유사한 작업을 수행 할 수 있습니다.
RMCH 논문을 읽은 후, 적절한 작업을하고 작은 프로젝트를 위해 적절한 비재 귀에 Makefile을 작성하겠다는 목표를 세웠습니다. 작업을 마친 후, 빌드하려는 최종 대상, 대상 유형 (예 : 라이브러리 또는 실행 파일)을 매우 간단하고 간결하게 만드는 데 사용할 수있는 일반적인 Makefile "프레임 워크"를 만드는 것이 가능해야한다는 것을 깨달았습니다. ) 및이를 만들기 위해 필요한 소스 파일.
몇 번의 반복 끝에 결국 만들었습니다 : 수정이 필요없는 약 150 줄의 GNU Make 구문으로 하나의 단일 상용구 Makefile- 내가 사용하려는 모든 종류의 프로젝트에서 작동하며 빌드 충분히 유연합니다. 각 소스 파일 (원하는 경우)에 대한 정확한 플래그와 각 실행 파일에 대한 정확한 링커 플래그를 지정 대상 세분 할 다양한 유형의 여러 유형의 여러 유형을 지정합니다. 각 프로젝트에 대해 다음과 같은 방식으로 제작되는 Makefile을 제공하기 만하면됩니다.
TARGET := foo
TGT_LDLIBS := -lbar
SOURCES := foo.c baz.cpp
SRC_CFLAGS := -std=c99
SRC_CXXFLAGS := -fstrict-aliasing
SRC_INCDIRS := inc /usr/local/include/bar
위와 같은 프로젝트 Makefile은 "foo"라는 실행 파일을 빌드하고 foo.c (CFLAGS = -std = c99 사용) 및 baz.cpp (CXXFLAGS = -fstrict-aliasing 사용)를 수행합니다. 그리고 "./inc"및 "/ usr / local / include / bar"를 #include
검색 경로에 추가하고 "libbar"라이브러리가 포함 된 최종 링크를 사용합니다. 또한 C ++ 소스 파일이 C 링커 대신 C ++ 링커를 사용하는 방법을 알고 있습니다. 더 많은 것보다 많은 것들이 있습니다.
보일러 플레이트 Makefile은 지정된 대상을 빌드하는 데 필요한 모든 규칙 빌드 및 자동 생성을 수행합니다. 모든 빌드 생성 파일은 별도의 파일 디렉토리 계층 구조에 배치 소스 파일과 섞이지 (VPATH를 사용하지 않고 수행 이름이 같은 소스이 있어도 문제가 없습니다).
나는 지금까지 내가 작업 한 쪽 24 개의 다른 프로젝트에서 동일한 Makefile을 사용했습니다. 이 시스템에 대해 내가 가장 좋아하는 것 중 일부는 ( 새 프로젝트에 적합한 메이크 파일 을 만드는 것이 얼마나 쉬운 지 제외하고 ) 다음과 같습니다.
- 그것은이다 빨리 . 거의 즉시 알 수 있습니다.
- 100 % 충분한 수있는 내용. 이 제로 기회가 병렬 신비 중단됩니다 빌드를하고 , 빌드 항상 정확히 - - 날짜 다시 모든 것을 가지고하는 데 필요한 최소 .
- 다시 완전한 makefile을 다시 사용할 필요 가 없습니다 . : D
마지막으로 재 문제로 인해 발생하는 문제를 해결하는 것이 가능하지 않을 것이라고 생각합니다. 나는 결함있는 메이크 파일을 몇 번이고 다시 작성해야 할 운명에 처했을 것입니다. 실제로 제대로 작동하는 메이크 파일을 만드는 데 헛된 시도를했습니다.
Miller의 논문에서 한 가지 주장을 강조하겠습니다. 서로 다른 모듈 해결하기 시작하고 빌드 순서를 확인하는 데 어려움을 겪고 빌드 순서를 확인하는 데 어려움을 겪습니다. 빌드 시스템을 구축하는 것은 매우 어렵습니다. 실제 프로젝트에는 빌드 순서가 이해하기 어렵지 상호 의존적 인 부분이 많고 작업은 빌드 시스템에 맡겨야합니다. 그러나 시스템에 대한 글로벌 지식이있는 경우에 해당 문제를 해결할 수 있습니다.
또한 재귀 적 빌드 시스템은 여러 프로세서 / 코어에서 동시에 빌드 할 때 분리 할 수 있습니다. 빌드 시스템은 단일 프로세서에서 안정적으로 작동하는 것처럼 보일 수 있고 프로젝트를 빌드하기 시작할 때까지 누락 된 많은 것이 감지되지 않습니다. 나는 최대 4 개의 프로세서에서 작동하는 재귀 적을 빌드 시스템으로 작업했지만 갑자기 2 개의 쿼드 코어가있는 컴퓨터에서 충돌했습니다. 그런 다음 또 다른 문제에 직면했습니다. 지금은 거의 디버그 할 수있는 전체 시스템의 순서도 동시성 문제 는 거의 디버그 할 수 있습니다.
귀하의 질문으로 돌아 가기 위해 재귀 작성을 사용하려는 이유를 생각하기가 어렵습니다. 비재 귀적 GNU Make 빌드 시스템의 런타임 성능은 이길 수 없으며, 반대로 많은 재귀 적 make 시스템에는 심각한 성능 문제가 있습니다 (약한 병렬 빌드 지원이 다시 문제의 일부입니다). 특정 (재귀 적) Make 빌드 시스템을 평가하고 SCons 포트와 비교 한 논문 이 있습니다 . 빌드 시스템이 매우 비표준이기 때문에 성능 결과는 대표적이지 않지만이 경우에는 SCons 포트가 실제로 더 빠릅니다.
결론 : Make를 사용하여 소프트웨어 빌드를 제어 비용 재 귀적 Make를 선택하십시오. 장기적으로는 삶을 훨씬 쉽게 만들어주기 때문입니다. 개인적으로 저는 사용성있는 SCons (또는 Rake- 기본적으로 최신 스크립팅 언어를 사용하고 암시 적 지원이있는 모든 빌드 시스템)을 사용하는 것이 좋습니다.
빌드 시스템 (GNU make 기반)을 완전히 비재 귀적으로 만들기 위해 이전 작업에서 절반 정도의 노력을 기울 였지만 몇 가지 문제가 발생했습니다.
- 아티팩트 (예 : 빌드 된 라이브러리 및 실행 파일)는 소스를 여러 디렉토리에 분산시켜 vpath를 사용하여 찾아 듣습니다.
- 동일한 이름을 가진 여러 소스 파일이 다른 디렉토리에 있습니다.
- 여러 소스 파일이 아티팩트 파일로 공유됩니다.
- 다른 아티팩트에는 종종 다른 컴파일러 플래그, 최적화 설정 등이 있습니다.
비재 귀적 사용을 단순화하는 GNU make의 기능 중 하나는 대상별 변수 값입니다 .
foo: FOO=banana
bar: FOO=orange
즉, 오리지날 "foo"를 빌드 할 때 $ (FOO)는 "banana"로 확장 때부터 "bar"를 빌드 할 때 $ (FOO)는 "orange"로 확장됩니다.
이에 대한 한 가지 제한은 대상별 VPATH 정의를 부여 할 것입니다. 즉, 각 대상에 식별 적으로 VPATH를 고유하게 정의 할 수있는 방법이 없습니다. 이것은 우리의 경우 올바른 소스 파일을 찾기 위해 필요했습니다.
비재 귀성을 지원하기 위해 필요한 GNU make의 주요 누락 된 기능은 네임 스페이스 가 없다는 것 입니다. 대상별 변수는 제한된 방식으로 네임 스페이스를 "시뮬레이션"하는 데 사용할 수 있지만 실제로 필요한 것은 로컬 범위를 사용하여 하위 디렉터리에 Makefile을 포함 할 수있는 것입니다.
편집 :이 맥락에서 GNU make의 또 다른 매우 유용한 (그리고 자주 사용되지 않는) 기능은 매크로 확장 기능입니다 ( 예 : eval 함수 참조 ). 이것은 유사한 규칙 / 목표를 가지고 있지만 일반 GNU make 구문을 사용하여 표현할 수없는 방식이 다른 여러 대상이있을 때 매우 유용합니다.
참조 된 기사의 진술에 동의하지만이 모든 작업을 수행하고 여전히 사용하기 쉬운 좋은 템플릿을 찾는 데 오랜 시간이 걸렸습니다.
Currenty 저는 지속적인 통합을 실험하는 소규모 연구 프로젝트를 진행하고 있습니다. PC에서 자동으로 단위 테스트를 수행 한 다음 (내장 된) 대상에서 시스템 테스트를 실행합니다. 이것은 메이크에서 사소한 것이 아니며 좋은 해결책을 찾았습니다. make는 여전히 휴대용 멀티 플랫폼 빌드를위한 좋은 선택입니다. 마침내 http://code.google.com/p/nonrec-make 에서 좋은 출발점을 찾았습니다 .
이것은 진정한 안도감이었습니다. 이제 내 makefile은
- 수정이 매우 간단합니다 (제작 지식이 제한되어 있어도)
- 빠른 컴파일
- 노력없이 완전히 (.h) 종속성 확인
다음 (큰) 프로젝트에도 확실히 사용할 것입니다 (C / C ++ 가정).
나는 Recursive Make Thoughmful에 설명 된 메커니즘 을 [powerpoint link]를 사용하여 광고하는 대규모 프로젝트 ( ROOT )를 하나 이상 알고 있습니다. 이 프레임 워크는 백만 줄의 코드를 초과하며 매우 스마트하게 컴파일됩니다.
그리고, 물론, 모든 약간 큰 프로젝트는 그와 함께 작동 할 컴파일 할 수 있습니다 천천히 고통스럽게 재귀 메이크업을 사용합니다. ::한숨::
저는 유닉스 계열 시스템 (macs 포함)에서 사용하기위한 중간 크기의 C ++ 프로젝트를위한 비재 귀적 make 시스템을 개발했습니다. 이 프로젝트의 코드는 모두 src / 디렉토리에 뿌리를 둔 디렉토리 트리에 있습니다. 작업 디렉토리에있는 디렉토리 트리의 모든 소스 파일을 컴파일하기 위해 최상위 src / 디렉토리의 하위 디렉토리에서 "make all"을 입력 할 수있는 비 재귀 시스템을 작성하고 싶었습니다. 재귀 적 make 시스템에서. 내 솔루션이 내가 본 다른 솔루션과 약간 다른 것 같기 때문에 여기에 설명하고 반응이 있는지 확인하고 싶습니다.
내 솔루션의 주요 요소는 다음과 같습니다.
1) src / 트리의 각 디렉토리에는 sources.mk라는 파일이 있습니다. 이러한 각 파일은 디렉토리에있는 트리의 모든 소스 파일을 나열하는 makefile 변수를 정의합니다. 이 변수의 이름은 [directory] _SRCS 형식입니다. 여기서 [directory]는 최상위 src / 디렉토리에서 해당 디렉토리로의 경로를 표준 형식으로 나타내며 백 슬래시는 밑줄로 대체됩니다. 예를 들어, src / util / param / sources.mk 파일은 src / util / param 및 하위 디렉토리 (있는 경우)의 모든 소스 파일 목록을 포함하는 util_param_SRCS라는 변수를 정의합니다. 각 sources.mk 파일은 해당 오브젝트 파일 * .o 대상의 목록을 포함하는 [directory] _OBJS라는 변수도 정의합니다. 하위 디렉토리를 포함하는 각 디렉토리에서 sources.mk는 각 하위 디렉토리의 sources.mk 파일을 포함합니다.
2) 모든 경로는 sources.mk 파일에서 src / 디렉토리가 변수 $ (SRC_DIR)로 표현되는 절대 경로로 표현됩니다. 예를 들어 src / util / param / sources.mk 파일에서 src / util / param / Componenent.cpp 파일은 $ (SRC_DIR) /util/param/Component.cpp로 나열됩니다. $ (SRC_DIR)의 값은 sources.mk 파일에 설정되어 있지 않습니다.
3) 각 디렉토리에는 Makefile도 포함되어 있습니다. 모든 Makefile에는 $ (SRC_DIR) 변수의 값을 루트 src / 디렉토리의 절대 경로로 설정하는 전역 구성 파일이 포함되어 있습니다. 필자는 심볼릭 형식의 절대 경로를 사용하기로 결정했습니다. 이는 종속성과 대상에 대한 경로를 동일한 방식으로 해석하는 동시에 원하는 경우 전체 소스 트리를 이동할 수 있도록 허용하는 여러 디렉터리에 여러 메이크 파일을 만드는 가장 쉬운 방법 인 것처럼 보였기 때문입니다. , 하나의 파일에서 $ (SRC_DIR) 값을 변경합니다. 이 값은 패키지가 git 저장소에서 다운로드되거나 복제 될 때 또는 전체 소스 트리가 이동 될 때 사용자에게 실행하도록 지시하는 간단한 스크립트에 의해 자동으로 설정됩니다.
4) 각 디렉토리의 makefile에는 해당 디렉토리에 대한 sources.mk 파일이 포함되어 있습니다. 각 Makefile의 "all"대상은 해당 디렉토리에 대한 [directory] _OBJS 파일을 종속성으로 나열하므로 해당 디렉토리와 하위 디렉토리에있는 모든 소스 파일을 컴파일해야합니다.
5) * .cpp 파일을 컴파일하는 규칙은 여기에 설명 된대로 컴파일의 부작용으로 * .d 접미사를 사용하여 각 소스 파일에 대한 종속성 파일을 만듭니다. http://mad-scientist.net/make/ autodep.html . -M 옵션을 사용하여 종속성 생성을 위해 gcc 컴파일러를 사용하기로 선택했습니다. gcc는 유닉스 계열 시스템에서 거의 항상 사용할 수 있고 빌드 시스템의이 부분을 표준화하는 데 도움이되기 때문에 다른 컴파일러를 사용하여 소스 파일을 컴파일 할 때에도 종속성 생성에 gcc를 사용합니다. 다른 컴파일러를 사용하여 실제로 소스 파일을 컴파일 할 수 있습니다.
6) _OBJS 및 _SRCS 변수의 모든 파일에 대한 절대 경로를 사용하려면 상대 경로가있는 파일을 생성하는 gcc에서 생성 된 종속성 파일을 편집하는 스크립트를 작성해야했습니다. 이 목적으로 파이썬 스크립트를 작성했지만 다른 사람이 sed를 사용했을 수도 있습니다. 결과 종속성 파일의 종속성 경로는 리터럴 절대 경로입니다. 이 컨텍스트에서는 종속성 파일 (source.mk 파일과 달리)이 패키지의 일부로 배포되지 않고 로컬에서 생성되기 때문에 문제가 없습니다.
7) 각 디렉터의 Makefile은 동일한 디렉토리의 sources.mk 파일을 포함하고 모든 소스 파일에 대한 종속성 파일을 포함하려고 시도하는 "-include $ ([directory] _OBJS : .o = .d)"줄을 포함합니다. 위에 제공된 URL에 설명 된대로 디렉토리 및 하위 디렉토리에서.
"make all"이 모든 디렉토리에서 호출되도록 허용하는 다른 스키마와의 주요 차이점은 Make가 다른 디렉토리에서 호출 될 때 동일한 경로가 일관되게 해석 될 수 있도록 절대 경로를 사용한다는 것입니다. 이러한 경로가 최상위 소스 디렉토리를 나타내는 변수를 사용하여 표현되는 한 이는 소스 트리를 이동하는 것을 방해하지 않으며 동일한 목표를 달성하는 다른 방법보다 간단합니다.
현재이 프로젝트의 시스템은 항상 "in-place"빌드를 수행합니다. 각 소스 파일을 컴파일하여 생성 된 개체 파일은 소스 파일과 동일한 디렉토리에 배치됩니다. gcc 종속성 파일을 편집하는 스크립트를 변경하여 src / dirctory의 절대 경로를에 대한 표현식에서 빌드 디렉토리를 나타내는 변수 $ (BUILD_DIR)로 대체하여 외부 빌드를 활성화하는 것은 간단합니다. 각 개체 파일에 대한 규칙의 개체 파일 대상.
지금까지이 시스템을 사용하고 유지하기가 쉽다는 것을 알았습니다. 필요한 메이크 파일 조각은 짧고 공동 작업자가 비교적 쉽게 이해할 수 있습니다.
이 시스템을 개발 한 프로젝트는 외부 종속성없이 완전히 독립적 인 ANSI C ++로 작성되었습니다. 이런 종류의 수제 비 재귀 메이크 파일 시스템은 독립적이고 이식성이 뛰어난 코드를위한 합리적인 옵션이라고 생각합니다. 그러나 외부 프로그램이나 라이브러리 또는 비표준 운영 체제 기능에 사소한 종속성이있는 모든 프로젝트의 경우 CMake 또는 gnu autotools와 같은 더 강력한 빌드 시스템을 고려할 것입니다.
나는 그리 좋지 않은 비재 귀적 make 빌드 시스템을 작성했고 그 이후로 Pd-extended 라는 프로젝트를위한 매우 깨끗한 모듈 식 재귀 적 make 빌드 시스템을 작성했습니다 . 기본적으로 많은 라이브러리가 포함 된 스크립팅 언어와 같습니다. 이제 저는 Android의 비 재귀 시스템도 작업하고 있으므로이 주제에 대한 제 생각의 맥락입니다.
둘 사이의 성능 차이에 대해 많이 말할 수는 없지만 전체 빌드는 실제로 빌드 서버에서만 수행되기 때문에 실제로 관심을 기울이지 않았습니다. 저는 일반적으로 핵심 언어 또는 특정 라이브러리에서 작업하므로 전체 패키지의 하위 집합 만 빌드하는 데 관심이 있습니다. 재귀 적 작성 기술은 빌드 시스템을 독립형으로 만들고 더 큰 전체로 통합 할 수있는 큰 이점이 있습니다. 통합 된 라이브러리 든 외부 작성자가 작성한 라이브러리 든 모든 라이브러리에 대해 하나의 빌드 시스템을 사용하기를 원하기 때문에 이것은 우리에게 중요합니다.
저는 현재 사용자 지정 버전의 Android 내부 (예 : SQLCipher 암호화 된 sqlite를 기반으로하는 Android의 SQLite 클래스 버전)를 빌드하는 중입니다. 그래서 sqlite와 같은 모든 종류의 이상한 빌드 시스템을 래핑하는 비 재귀 Android.mk 파일을 작성해야합니다. Android.mk가 임의의 스크립트를 실행하도록 만드는 방법을 알아낼 수 없지만 제 경험으로는 기존의 재귀 적 make 시스템에서는 쉬울 것입니다.
참고 URL : https://stackoverflow.com/questions/559216/what-is-your-experience-with-non-recursive-make
'ProgramingTip' 카테고리의 다른 글
두 목록에 연산자를 적용하는 C # 관용적 방법은 무엇입니까? (0) | 2020.11.30 |
---|---|
Visual Studio에서 모든 파일을 검색하는 방법 (0) | 2020.11.30 |
OOCalc에서 CONCATENATE 함수로 견적을 인용하는 방법 (0) | 2020.11.30 |
특정에 있는지 확인 (0) | 2020.11.30 |
IMG 태그와 함께 스프라이트를 사용하십니까? (0) | 2020.11.30 |