C ++ 프로젝트 구성 (gtest, cmake 및 doxygen 사용)


123

저는 일반적으로 프로그래밍을 처음 접했기 때문에 C ++로 간단한 벡터 클래스를 만드는 것으로 시작하기로 결정했습니다. 그러나 나중에 워크 플로를 수정하는 것보다 처음부터 좋은 습관을 들이고 싶습니다.

현재 두 개의 파일 vector3.hppvector3.cpp. 이 프로젝트는 내가 모든 것에 익숙해 짐에 따라 천천히 성장하기 시작할 것입니다 (일반적인 선형 대수 라이브러리에 가깝게 만들 것입니다). 그래서 나중에 삶을 더 쉽게 만들기 위해 "표준"프로젝트 레이아웃을 채택하고 싶습니다. 그래서 둘러 본 후 hpp 및 cpp 파일을 구성하는 두 가지 방법을 찾았습니다. 첫 번째 방법은 다음과 같습니다.

project
└── src
    ├── vector3.hpp
    └── vector3.cpp

두 번째는 :

project
├── inc
   └── project
       └── vector3.hpp
└── src
    └── vector3.cpp

어떤 것을 추천하고 그 이유는 무엇입니까?

두 번째로 사용하기 쉬운 것처럼 보이는 내 코드를 단위 테스트하기 위해 Google C ++ 테스트 프레임 워크를 사용하고 싶습니다. 예를 들어 inc/gtest또는 contrib/gtest폴더 와 같이 내 코드와 함께 번들로 묶으 시겠습니까? 번들로 제공되는 경우 fuse_gtest_files.py스크립트를 사용하여 수 또는 파일 수를 줄이거 나 그대로 두는 것이 좋습니까? 번들로 제공되지 않는 경우이 종속성은 어떻게 처리됩니까?

테스트 작성과 관련하여 일반적으로 어떻게 구성됩니까? 각 클래스 ( test_vector3.cpp예 :)에 대해 하나의 cpp 파일을 갖고 싶었지만 모두 쉽게 함께 실행할 수 있도록 하나의 바이너리로 컴파일 되었습니까?

gtest 라이브러리는 일반적으로 cmake 및 make를 사용하여 빌드되기 때문에 내 프로젝트도 이와 같이 빌드되는 것이 합리적이라고 생각했습니다. 다음 프로젝트 레이아웃을 사용하기로 결정한 경우 :

├── CMakeLists.txt
├── contrib
   └── gtest
       ├── gtest-all.cc
       └── gtest.h
├── docs
   └── Doxyfile
├── inc
   └── project
       └── vector3.cpp
├── src
   └── vector3.cpp
└── test
    └── test_vector3.cpp

CMakeLists.txt라이브러리 만 빌드하거나 라이브러리와 테스트를 빌드 할 수 있도록 어떻게 해야할까요? 또한 a buildbin디렉토리 가있는 꽤 많은 프로젝트를 보았습니다 . 빌드가 빌드 디렉토리에서 발생하고 바이너리가 bin 디렉토리로 이동합니까? 테스트 용 바이너리와 라이브러리가 같은 장소에 있습니까? 또는 다음과 같이 구성하는 것이 더 합리적일까요?

test
├── bin
├── build
└── src
    └── test_vector3.cpp

또한 doxygen을 사용하여 코드를 문서화하고 싶습니다. cmake 및 make와 함께 자동으로 실행되도록 할 수 있습니까?

많은 질문에 대해 죄송하지만 이러한 유형의 질문에 만족스럽게 대답하는 C ++ 책을 찾지 못했습니다.


6
좋은 질문이지만 Stack Overflow 의 Q & A 형식에는 적합하지 않다고 생각 합니다. 그래도 대답에 매우 관심이 있습니다. +1 & fav
Luchian Grigore 2012

1
이것들은 거대한에 대한 많은 질문입니다. 여러 개의 작은 질문으로 나누고 서로 링크를 배치하는 것이 좋습니다. 어쨌든 마지막 부분에 대답하기 위해 : CMake를 사용하면 src 디렉터리 내부 및 외부에서 빌드하도록 선택할 수 있습니다 (외부 권장). 그리고 예, CMake와 함께 doxygen을 자동으로 사용할 수 있습니다.
mistapink 2012

답변:


84

C ++ 빌드 시스템은 약간의 검은 예술이며 프로젝트가 오래 될수록 더 이상한 것을 찾을 수 있으므로 많은 질문이 나오는 것은 놀라운 일이 아닙니다. 질문을 하나씩 살펴보고 C ++ 라이브러리 구축과 관련된 몇 가지 일반적인 사항을 언급하겠습니다.

디렉토리에서 헤더와 cpp 파일을 분리합니다. 이것은 실제 응용 프로그램이 아닌 라이브러리로 사용되는 구성 요소를 빌드하는 경우에만 필수적입니다. 헤더는 사용자가 제공하는 제품과 상호 작용할 수있는 기반이며 설치해야합니다. 즉, 하위 디렉토리에 있어야하며 (아무도 최상위 수준으로 끝나는 많은 헤더를 원하지 않음 /usr/include/) 헤더가 이러한 설정에 자신을 포함 할 수 있어야합니다.

└── prj
    ├── include
       └── prj
           ├── header2.h
           └── header.h
    └── src
        └── x.cpp

포함 경로가 잘 작동하고 설치 대상에 쉽게 globbing을 사용할 수 있기 때문에 잘 작동합니다.

번들링 종속성 : 이것은 크게 종속성을 찾고 구성하는 빌드 시스템의 능력과 단일 버전에 대한 코드의 의존성에 달려 있다고 생각합니다. 또한 사용자의 능력과 플랫폼에 의존성이 얼마나 쉬운 지에 따라 다릅니다. CMake는 find_packageGoogle 테스트 용 스크립트 와 함께 제공됩니다 . 이것은 일을 훨씬 쉽게 만듭니다. 나는 필요할 때만 번들링을하고 그렇지 않으면 피할 것입니다.

빌드 방법 : 소스 내 빌드를 피하십시오. CMake는 소스 빌드를 쉽게 만들고 삶을 훨씬 쉽게 만듭니다.

또한 CTest를 사용하여 시스템에 대한 테스트를 실행하고 싶다고 가정합니다 (GTest에 대한 기본 지원도 함께 제공됨). 디렉터리 레이아웃 및 테스트 구성에 대한 중요한 결정은 다음과 같습니다. 하위 프로젝트로 끝나나요? 그렇다면 CMakeLists를 설정할 때 추가 작업이 필요하며 하위 프로젝트를 각각 자체 includesrc파일이있는 하위 디렉터리로 분할해야 합니다. 아마도 그들 자신의 doxygen 실행 및 출력 (여러 개의 doxygen 프로젝트를 결합하는 것은 가능하지만 쉽지 않거나 예쁘지는 않음) 일 수도 있습니다.

다음과 같은 결과를 얻게됩니다.

└── prj
    ├── CMakeLists.txt <-- (1)
    ├── include
       └── prj
           ├── header2.hpp
           └── header.hpp
    ├── src
       ├── CMakeLists.txt <-- (2)
       └── x.cpp
    └── test
        ├── CMakeLists.txt <-- (3)
        ├── data
           └── testdata.yyy
        └── testcase.cpp

어디

  • (1) 종속성, 플랫폼 사양 및 출력 경로 구성
  • (2) 빌드 할 라이브러리를 구성합니다.
  • (3) 테스트 실행 파일 및 테스트 케이스 구성

하위 구성 요소가있는 경우 다른 계층 구조를 추가하고 각 하위 프로젝트에 대해 위의 트리를 사용하는 것이 좋습니다. 그러면 하위 구성 요소가 종속성을 검색하고 구성할지 아니면 최상위 수준에서 수행할지 결정해야하기 때문에 문제가 발생합니다. 이는 사례별로 결정되어야합니다.

Doxygen : doxygen의 구성 댄스를 수행 한 후 CMake add_custom_command를 사용 하여 문서 대상을 추가하는 것은 간단합니다 .

이것이 내 프로젝트가 끝나는 방식이며 매우 유사한 프로젝트를 보았지만 물론 이것이 모든 것을 치료할 수는 없습니다.

부록 어떤 시점 config.hpp 에서 버전 정의 및 버전 제어 식별자 (Git 해시 또는 SVN 개정 번호)에 대한 정의를 포함 하는 파일 을 생성하려고 할 것 입니다. CMake에는 해당 정보를 자동으로 찾고 파일을 생성하는 모듈이 있습니다. CMake를 사용 configure_file하여 템플릿 파일의 변수를 CMakeLists.txt.

라이브러리를 빌드하는 경우에는 컴파일러 (예 : __declspecMSVC 및 visibilityGCC / clang의 속성) 간의 차이를 올바르게 가져 오기 위해 내보내기 정의도 필요합니다 .


2
좋은 대답이지만, 왜 헤더 파일을 추가 프로젝트 이름의 하위 디렉토리 인 "/prj/include/prj/foo.hpp"에 넣어야하는지 명확하지 않습니다. "/prj/include/foo.hpp"가 아닌 이유는 무엇입니까? 나는 설치시 설치 디렉토리를 다시 지그 할 기회가 있다고 가정하고 설치시 <INSTALL_DIR> /include/prj/foo.hpp를 얻거나 CMake에서 어렵습니까?
William Payne

6
@William 실제로 CPack으로 수행하기가 어렵습니다. 또한 소스 파일 내부 포함은 어떻게 보일까요? 설치된 버전의 "header.hpp"인 경우 "/ usr / include / prj /"는 "/ usr / include"가 아닌 include 경로에 있어야합니다.
pmr

37

우선, 무시할 수없는 일반적인 디렉토리 이름이 있습니다. 이는 Unix 파일 시스템의 오랜 전통을 기반으로합니다. 이것들은:

trunk
├── bin     : for all executables (applications)
├── lib     : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src     : for source files
└── doc     : for documentation

최소한 최상위 수준에서는이 기본 레이아웃을 고수하는 것이 좋습니다.

헤더 파일과 소스 파일 (cpp) 분할에 대해서는 두 가지 방식이 모두 매우 일반적입니다. 그러나 나는 그것들을 함께 보관하는 것을 선호하는 경향이 있으며, 파일을 함께 보관하는 것이 일상적인 작업에서 더 실용적입니다. 또한 모든 코드가 하나의 최상위 폴더 (예 : 폴더) 아래에있을 때 trunk/src/다른 모든 폴더 (bin, lib, include, doc 및 일부 테스트 폴더)가 최상위 수준에 있음을 알 수 있습니다. 소스 외부 빌드의 "build"디렉토리는 빌드 프로세스에서 생성 된 파일 만 포함하는 모든 폴더입니다. 따라서 src 폴더 만 백업하거나 버전 제어 시스템 / 서버 (예 : Git 또는 SVN)에 보관하면됩니다.

그리고 대상 시스템에 헤더 파일을 설치할 때 (결국 라이브러리를 배포하려는 경우) CMake에는 파일 설치 명령이 있습니다 (암시 적으로 "설치"대상을 생성하여 "make install"수행). 모든 헤더를 /usr/include/디렉토리 에 넣는 데 사용할 수 있습니다 . 이 목적을 위해 다음 cmake 매크로를 사용합니다.

# custom macro to register some headers as target for installation:
#  setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
  foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
    install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
  endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)

어디 SRCROOT내가 src 폴더로 설정하는 cmake 변수이며, INCLUDEROOT내가 헤더에 갈 필요가 어디든지 구성하는 것이 cmake 변수입니다. 물론이 작업을 수행하는 다른 많은 방법이 있으며 내 방식이 최선이 아니라고 확신합니다. 요점은 헤더 만 대상 시스템에 설치하면된다는 이유만으로 헤더와 소스를 분할 할 이유가 없다는 것입니다. 특히 CMake (또는 CPack)에서는 헤더를 선택하고 구성하는 것이 매우 쉽기 때문입니다. 별도의 디렉토리에 두지 않고도 설치할 수 있습니다. 그리고 이것은 제가 대부분의 도서관에서 본 것입니다.

인용구 : 두 번째로 사용하기 쉬운 것처럼 보이는 코드 단위 테스트를 위해 Google C ++ 테스트 프레임 워크를 사용하고 싶습니다. 예를 들어 "inc / gtest"또는 "contrib / gtest"폴더에이 코드를 내 코드와 함께 묶는 것이 좋습니다. 번들로 제공되는 경우 fuse_gtest_files.py 스크립트를 사용하여 수 또는 파일을 줄이거 나 그대로 두는 것이 좋습니까? 번들로 제공되지 않는 경우이 종속성은 어떻게 처리됩니까?

라이브러리와 종속성을 번들로 묶지 마십시오. 이것은 일반적으로 매우 끔찍한 아이디어이며, 그렇게하는 라이브러리를 구축하려고 할 때 항상 싫어합니다. 마지막 수단이되어야하며 함정에주의하십시오. 종종 사람들은 끔찍한 개발 환경 (예 : Windows)을 대상으로하거나 해당 라이브러리의 이전 (더 이상 사용되지 않는) 버전 (종속성) 만 지원하기 때문에 라이브러리와 종속성을 번들로 묶습니다. 주된 함정은 번들 된 종속성이 동일한 라이브러리 / 애플리케이션의 이미 설치된 버전과 충돌 할 수 있다는 것입니다 (예 : gtest를 번들로 제공했지만 라이브러리를 빌드하려는 사람이 이미 최신 (또는 이전) 버전의 gtest를 설치 한 경우) 두 사람이 충돌하여 그 사람에게 매우 심한 두통을 줄 수 있습니다.) 그러니 제가 말했듯이 위험을 감수하고 그리고 나는 마지막 수단으로 만 말할 것입니다. 라이브러리를 컴파일하기 전에 사람들에게 몇 가지 종속성을 설치하도록 요청하는 것은 번들 된 종속성과 기존 설치 간의 충돌을 해결하려는 것보다 훨씬 덜 악합니다.

인용구 : 테스트 작성과 관련하여 일반적으로 어떻게 구성됩니까? 각 클래스에 대해 하나의 cpp 파일 (예 : test_vector3.cpp)을 가질 생각 이었지만 모두 쉽게 함께 실행할 수 있도록 하나의 바이너리로 컴파일 되었습니까?

제 생각에는 클래스 당 하나의 cpp 파일 (또는 작은 응집력있는 클래스 및 함수 그룹)이 더 일반적이고 실용적입니다. 그러나 "모두 함께 실행될 수 있도록"모든 파일을 하나의 바이너리로 컴파일하지 마십시오. 정말 나쁜 생각입니다. 일반적으로 코딩에 관해서는 합당한만큼 분할하고 싶을 것입니다. 단위 테스트의 경우 하나의 바이너리가 모든 테스트를 실행하는 것을 원하지는 않습니다. 이는 라이브러리의 내용을 조금만 변경해도 해당 단위 테스트 프로그램을 거의 전체적으로 재 컴파일 할 수 있음을 의미하기 때문입니다. , 재 컴파일을 기다리는 데 몇 분 / 시간이 소요됩니다. 간단한 계획을 고수하십시오 : 1 단위 = 1 단위 테스트 프로그램. 그때,

인용구 : gtest 라이브러리는 일반적으로 cmake 및 make를 사용하여 빌드되므로 내 프로젝트도 이와 같이 빌드하는 것이 합리적이라고 생각했습니다. 다음 프로젝트 레이아웃을 사용하기로 결정한 경우 :

차라리이 레이아웃을 제안합니다.

trunk
├── bin
├── lib
   └── project
       └── libvector3.so
       └── libvector3.a        products of installation / building
├── docs
   └── Doxyfile
├── include
   └── project
       └── vector3.hpp
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

├── src
   └── CMakeLists.txt
   └── Doxyfile.in
   └── project                 part of version-control / source-distribution
       └── CMakeLists.txt
       └── vector3.hpp
       └── vector3.cpp
       └── test
           └── test_vector3.cpp
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

├── build
└── test                        working directories for building / testing
    └── test_vector3

여기서 주목해야 할 몇 가지 사항이 있습니다. 첫째, src 디렉토리의 하위 디렉토리는 include 디렉토리의 하위 디렉토리를 미러링해야합니다. 이것은 단지 일을 직관적으로 유지하기위한 것입니다 (또한 폴더의 깊은 중첩으로 인해 하위 디렉토리 구조를 합리적으로 평평하게 (얕게) 유지하려고합니다.) 다른 무엇보다 번거 롭습니다.) 둘째, "include"디렉토리는 설치 디렉토리 일 뿐이며 그 내용은 src 디렉토리에서 헤더를 선택하는 것과 같습니다.

셋째, CMake 시스템은 최상위 수준에서 하나의 CMakeLists.txt 파일이 아닌 소스 하위 디렉터리에 배포되도록되어 있습니다. 이것은 사물을 지역적으로 유지하고 좋은 것입니다 (물건을 독립적 인 조각으로 나누는 정신에서). 새 소스, 새 헤더 또는 새 테스트 프로그램을 추가하는 경우 다른 항목에 영향을주지 않고 해당 하위 디렉토리에서 작고 간단한 CMakeLists.txt 파일 하나를 편집하기 만하면됩니다. 이를 통해 디렉토리를 쉽게 재구성 할 수 있습니다 (CMakeList는 로컬이며 이동중인 하위 디렉토리에 포함됨). 최상위 CMakeLists에는 대상 디렉터리 설정, 사용자 지정 명령 (또는 매크로), 시스템에 설치된 패키지 찾기와 같은 대부분의 최상위 구성이 포함되어야합니다. 하위 수준 CMakeList에는 헤더, 소스,

인용구 : CMakeLists.txt는 라이브러리 또는 라이브러리와 테스트 만 빌드 할 수 있도록 어떻게 표시되어야합니까?

기본적인 대답은 CMake를 사용하면 "all"( "make"를 입력 할 때 빌드 된 것)에서 특정 대상을 구체적으로 제외 할 수 있으며 특정 대상 번들을 만들 수도 있다는 것입니다. 여기서 CMake 자습서를 수행 할 수는 없지만 직접 알아내는 것은 상당히 간단합니다. 그러나이 특정 경우에 권장되는 솔루션은 물론 CTest를 사용하는 것입니다. CTest는 CMakeLists 파일에서 단위로 표시된 여러 대상 (프로그램)을 등록하는 데 사용할 수있는 추가 명령 집합입니다. 테스트. 따라서 CMake는 모든 테스트를 특수한 빌드 범주에 넣을 것이며, 이것이 정확히 요청한 것이므로 문제가 해결되었습니다.

인용구 : 또한 bin 디렉토리에 빌드 광고가있는 꽤 많은 프로젝트를 보았습니다. 빌드가 빌드 디렉토리에서 발생하고 바이너리가 bin 디렉토리로 이동합니까? 테스트 용 바이너리와 라이브러리가 같은 장소에 있습니까? 또는 다음과 같이 구성하는 것이 더 합리적일까요?

소스 외부에 빌드 디렉토리를 갖는 것 ( "out-of-source"빌드)은 실제로 할 수있는 유일한 정상적인 일이며 요즘에는 사실상 표준입니다. 따라서 CMake 사람들이 권장하는 것처럼 소스 디렉토리 외부에 별도의 "build"디렉토리를 갖고 내가 만난 모든 프로그래머가 그렇듯이 확실히해야합니다. bin 디렉토리에 관해서는, 그것은 관습이며,이 게시물의 시작 부분에서 말했듯이 그것에 충실하는 것이 아마도 좋은 생각 일 것입니다.

인용구 : 또한 doxygen을 사용하여 내 코드를 문서화하고 싶습니다. cmake 및 make와 함께 자동으로 실행되도록 할 수 있습니까?

예. 가능 이상으로 굉장합니다. 얼마나 멋지게 얻고 싶은지에 따라 몇 가지 가능성이 있습니다. CMake에는 find_package(Doxygen)일부 파일에서 Doxygen을 실행할 대상을 등록 할 수있는 Doxygen 용 모듈 (예 :)이 있습니다. Doxyfile에서 버전 번호를 업데이트하거나 소스 파일에 대한 날짜 / 작성자 스탬프를 자동으로 입력하는 것과 같은 더 멋진 작업을 수행하려면 약간의 CMake kung-fu로 모두 가능합니다. 일반적으로 이렇게하려면 토큰을 찾아 CMake의 구문 분석 명령으로 대체 할 수있는 소스 Doxyfile (예 : 위의 폴더 레이아웃에 넣은 "Doxyfile.in")을 유지해야합니다. 에서 내 최상위 CMakeLists 파일 , 당신은 cmake-doxygen이의 함께 몇 가지 멋진 일을 CMake 쿵후의 하나 개의 조각을 찾을 수 있습니다.


그래서 main.cpp가야 trunk/bin합니까?
Ugnius Malūkas

17

프로젝트 구조화

일반적으로 다음을 선호합니다.

├── CMakeLists.txt
|
├── docs/
│   └── Doxyfile
|
├── include/
│   └── project/
│       └── vector3.hpp
|
├── src/
    └── project/
        └── vector3.cpp
        └── test/
            └── test_vector3.cpp

즉, 라이브러리에 대해 매우 명확하게 정의 된 API 파일 세트가 있으며 구조는 라이브러리의 클라이언트가

#include "project/vector3.hpp"

덜 명시적인 것보다는

#include "vector3.hpp"


나는 / include 트리의 구조와 일치하는 / src 트리의 구조를 좋아하지만 실제로는 개인적 선호도입니다. 그러나 프로젝트가 / include / project 내의 하위 디렉터리를 포함하도록 확장되는 경우 일반적으로 / src 트리 내의 하위 디렉터리와 일치하는 것이 도움이됩니다.

테스트의 경우 테스트하는 파일에 "가까운"상태를 유지하는 것이 좋습니다. / src 내에 하위 디렉토리가있는 경우 다른 사용자가 주어진 파일의 테스트 코드를 찾고자 할 때 따를 수있는 매우 쉬운 패러다임입니다.


테스팅

두 번째로 사용하기 쉬운 것처럼 보이는 내 코드를 단위 테스트하기 위해 Google C ++ 테스트 프레임 워크를 사용하고 싶습니다.

Gtest는 실제로 사용하기 쉽고 기능면에서 상당히 포괄적입니다. 그 기능을 확장하기 위해 gmock 과 함께 매우 쉽게 사용할 수 있지만, gmock에 대한 저의 경험은 그다지 좋지 않았습니다. 나는 이것이 내 자신의 결점으로 귀결 될 수 있다는 것을 받아 들일 준비가되어 있지만, gmock 테스트는 생성하기가 더 어렵고 훨씬 더 취약하고 유지하기 어려운 경향이 있습니다. gmock 관의 큰 못은 스마트 포인터로 제대로 작동하지 않는다는 것입니다.

이것은 거대한 질문에 대한 매우 사소하고 주관적인 대답입니다 (아마도 SO에 속하지 않을 것입니다)

예를 들어 "inc / gtest"또는 "contrib / gtest"폴더에이 코드를 내 코드와 함께 묶는 것이 좋습니다. 번들로 제공되는 경우 fuse_gtest_files.py 스크립트를 사용하여 수 또는 파일을 줄이거 나 그대로 두는 것이 좋습니까? 번들로 제공되지 않는 경우이 종속성은 어떻게 처리됩니까?

나는 CMake의 ExternalProject_Add모듈을 선호 합니다. 이렇게하면 저장소에 gtest 소스 코드를 보관하거나 어디에나 설치할 필요가 없습니다. 빌드 트리에 자동으로 다운로드되고 빌드됩니다.

여기에서 세부 사항을 다루는대답을 참조 하십시오 .

테스트 작성과 관련하여 일반적으로 어떻게 구성됩니까? 각 클래스에 대해 하나의 cpp 파일 (예 : test_vector3.cpp)을 가질 생각 이었지만 모두 쉽게 함께 실행할 수 있도록 하나의 바이너리로 컴파일 되었습니까?

좋은 계획.


건물

저는 CMake의 팬이지만 테스트 관련 질문과 마찬가지로 SO는 그러한 주관적인 문제에 대한 의견을 구하기에 가장 좋은 곳이 아닐 것입니다.

CMakeLists.txt가 라이브러리 또는 라이브러리와 테스트 만 빌드 할 수 있도록 어떻게 표시되어야합니까?

add_library(ProjectLibrary <All library sources and headers>)
add_executable(ProjectTest <All test files>)
target_link_libraries(ProjectTest ProjectLibrary)

라이브러리는 대상 "ProjectLibrary"로 표시되고 테스트 스위트는 대상 "ProjectTest"로 표시됩니다. 라이브러리를 테스트 exe의 종속성으로 지정하면 테스트 exe를 빌드하면 오래된 경우 라이브러리가 자동으로 다시 빌드됩니다.

또한 bin 디렉토리에 빌드 광고가있는 꽤 많은 프로젝트를 보았습니다. 빌드가 빌드 디렉토리에서 발생하고 바이너리가 bin 디렉토리로 이동합니까? 테스트 용 바이너리와 라이브러리가 같은 장소에 있습니까?

CMake는 "out-of-source"빌드를 권장합니다. 즉, 프로젝트 외부에 자체 빌드 디렉터리를 만들고 거기에서 CMake를 실행합니다. 이는 빌드 파일로 소스 트리를 "오염"시키는 것을 방지하며 vcs를 사용하는 경우 매우 바람직합니다.

당신은 할 수 있습니다 내장되면 바이너리를 이동하거나 다른 디렉토리에 복사하도록 지정하거나 다른 디렉토리에 기본적으로 생성됩니다,하지만 필요는 일반적으로 없다. CMake는 원하는 경우 프로젝트를 설치하는 포괄적 인 방법을 제공하거나 다른 CMake 프로젝트가 프로젝트의 관련 파일을 쉽게 "찾을"수 있도록합니다.

gtest 테스트를 찾고 실행하기위한 CMake의 자체 지원 과 관련 하여 프로젝트의 일부로 gtest를 빌드하는 경우 이는 대체로 부적절합니다. 이 FindGtest모듈은 실제로 gtest가 프로젝트 외부에서 별도로 빌드 된 경우에 사용되도록 설계되었습니다.

CMake는 자체 테스트 프레임 워크 (CTest)를 제공하며 이상적으로는 모든 gtest 케이스가 CTest 케이스로 추가됩니다.

그러나 개별 ctest 케이스로 gtest 케이스를 쉽게 추가 할 수 있도록에서 GTEST_ADD_TESTS제공하는 FindGtest매크로는 TEST및 이외의 gtest 매크로에서는 작동하지 않는다는 점에서 다소 부족 TEST_F합니다. 부가가치 또는 타입 매개 변수화 시험 사용 TEST_P, TYPED_TEST_P등 전혀 처리되지 않습니다.

문제는 내가 아는 쉬운 해결책이 없습니다. gtest 케이스 목록을 얻는 가장 강력한 방법은 플래그와 함께 테스트 exe를 실행하는 것 --gtest_list_tests입니다. 그러나 이것은 exe가 빌드 된 후에 만 ​​수행 될 수 있으므로 CMake는이를 사용할 수 없습니다. 두 가지 선택이 남습니다. CMake는 테스트의 이름을 추론하기 위해 C ++ 코드를 구문 분석해야합니다 (모든 gtest 매크로, 주석 처리 된 테스트, 비활성화 된 테스트를 고려하려는 경우 극히 중요하지 않음) 또는 테스트 케이스가 수동으로 추가됩니다. CMakeLists.txt 파일.

또한 doxygen을 사용하여 코드를 문서화하고 싶습니다. cmake 및 make와 함께 자동으로 실행되도록 할 수 있습니까?

예-비록 나는이 분야에 대한 경험이 없습니다. CMake는 FindDoxygen이를 위해 제공합니다 .


6

다른 (훌륭한) 답변 외에도 비교적 대규모 프로젝트 에 사용하고있는 구조를 설명 할 것 입니다.
나는 다른 답변에서 말한 것을 반복 할 것이기 때문에 Doxygen에 대한 하위 질문을 다루지 않을 것입니다.


이론적 해석

모듈 성과 유지 보수성을 위해 프로젝트는 작은 단위 세트로 구성됩니다. 명확성을 위해 X = A, B, C, ... (그러나 일반적인 이름을 가질 수 있음)를 사용하여 UnitX의 이름을 지정하겠습니다. 그런 다음 디렉토리 구조가이 선택 사항을 반영하도록 구성되며 필요한 경우 단위를 그룹화 할 수 있습니다.

해결책

기본 디렉토리 레이아웃은 다음과 같습니다 (단위 내용은 나중에 자세히 설명합니다).

project
├── CMakeLists.txt
├── UnitA
├── UnitB
├── GroupA
   └── CMakeLists.txt
   └── GroupB
       └── CMakeLists.txt
       └── UnitC
       └── UnitD
   └── UnitE

project/CMakeLists.txt 다음을 포함 할 수 있습니다.

cmake_minimum_required(VERSION 3.0.2)
project(project)
enable_testing() # This will be necessary for testing (details below)

add_subdirectory(UnitA)
add_subdirectory(UnitB)
add_subdirectory(GroupA)

project/GroupA/CMakeLists.txt:

add_subdirectory(GroupB)
add_subdirectory(UnitE)

project/GroupB/CMakeLists.txt:

add_subdirectory(UnitC)
add_subdirectory(UnitD)

이제 다른 단위의 구조로 이동합니다 (예를 들어 UnitD를 보겠습니다).

project/GroupA/GroupB/UnitD
├── README.md
├── CMakeLists.txt
├── lib
   └── CMakeLists.txt
   └── UnitD
       └── ClassA.h
       └── ClassA.cpp
       └── ClassB.h
       └── ClassB.cpp
├── test
   └── CMakeLists.txt
   └── ClassATest.cpp
   └── ClassBTest.cpp
   └── [main.cpp]

다른 구성 요소에 :

  • 같은 폴더에 소스 ( .cpp)와 헤더 ( .h)가있는 것을 좋아 합니다. 이렇게하면 디렉터리 계층이 중복되는 것을 방지하고 유지 관리가 더 쉬워집니다. 설치의 경우 헤더 파일 만 필터링하는 것은 문제가되지 않습니다 (특히 CMake의 경우).
  • 디렉토리의 역할은 UnitD나중에 #include <UnitD/ClassA.h>. 또한이 장치를 설치할 때 디렉토리 구조를 그대로 복사 할 수 있습니다. 소스 파일을 하위 디렉터리로 구성 할 수도 있습니다.
  • 나는 README 단위의 내용을 요약하고 유용한 정보를 지정 파일을 합니다.
  • CMakeLists.txt 다음을 포함 할 수 있습니다.

    add_subdirectory(lib)
    add_subdirectory(test)
  • lib/CMakeLists.txt:

    project(UnitD)
    
    set(headers
        UnitD/ClassA.h
        UnitD/ClassB.h
        )
    
    set(sources
        UnitD/ClassA.cpp
        UnitD/ClassB.cpp    
        )
    
    add_library(${TARGET_NAME} STATIC ${headers} ${sources})
    
    # INSTALL_INTERFACE: folder to which you will install a directory UnitD containing the headers
    target_include_directories(UnitD
                               PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                               PUBLIC $<INSTALL_INTERFACE:include/SomeDir>
                               )
    
    target_link_libraries(UnitD
                          PUBLIC UnitA
                          PRIVATE UnitC
                          )

    여기서는 단위를 구성 할 때 이미 지정되었으므로 UnitAUnitC에 대한 포함 디렉터리를 원한다고 CMake에 알릴 필요가 없습니다 . 또한 PUBLIC의존하는 모든 대상에게 종속성을 UnitD자동으로 포함해야 UnitA하지만 UnitC필요하지 않은 경우 ( PRIVATE) 를 알립니다 .

  • test/CMakeLists.txt (GTest를 사용하려면 아래를 참조하십시오) :

    project(UnitDTests)
    
    add_executable(UnitDTests
                   ClassATest.cpp
                   ClassBTest.cpp
                   [main.cpp]
                   )
    
    target_link_libraries(UnitDTests
                          PUBLIC UnitD
    )
    
    add_test(
            NAME UnitDTests
            COMMAND UnitDTests
    )

GoogleTest 사용

Google Test의 경우 가장 쉬운 방법은 소스가 소스 디렉토리 어딘가에 있지만 실제로 직접 추가 할 필요가없는 경우입니다. 나는 이 프로젝트를 사용 하고있다 를 자동으로 다운로드했으며 여러 테스트 대상이 있더라도 한 번만 다운로드되도록 함수에 사용을 래핑했습니다.

이 CMake 함수는 다음과 같습니다.

function(import_gtest)
  include (DownloadProject)
  if (NOT TARGET gmock_main)
    include(DownloadProject)
    download_project(PROJ                googletest
                     GIT_REPOSITORY      https://github.com/google/googletest.git
                     GIT_TAG             release-1.8.0
                     UPDATE_DISCONNECTED 1
                     )
    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent GoogleTest from overriding our compiler/linker options when building with Visual Studio
    add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
  endif()
endfunction()

그런 다음 테스트 대상 중 하나에서 사용하려는 경우 다음 줄을에 추가합니다 CMakeLists.txt(위 예제의 경우 test/CMakeLists.txt).

import_gtest()
target_link_libraries(UnitDTests gtest_main gmock_main)

Gtest와 cmake로 멋진 "해킹"을하셨습니다! 유능한! :)
Tanasis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.