가장 간단하지만 완전한 CMake 예제


117

어떻게 든 CMake가 작동하는 방식에 완전히 혼란 스럽습니다. CMake가 작성되는 방식을 이해하는 데 가까워 질 때마다 내가 읽은 다음 예제에서 사라집니다. 내가 알고 싶은 것은 내 프로젝트를 어떻게 구성해야하는지, 그래서 내 CMake는 앞으로 최소한의 유지 관리를 필요로한다는 것입니다. 예를 들어, 다른 모든 src 폴더와 똑같이 작동하는 src 트리에 새 폴더를 추가 할 때 CMakeList.txt를 업데이트하고 싶지 않습니다.

이것이 내 프로젝트의 구조를 상상하는 방법이지만 이것은 단지 예일뿐입니다. 권장 방법이 다른 경우, 방법을 알려주십시오.

myProject
    src/
        module1/
            module1.h
            module1.cpp
        module2/
            [...]
        main.cpp
    test/
        test1.cpp
    resources/
        file.png
    bin
        [execute cmake ..]

그건 그렇고, 내 프로그램이 리소스가 어디에 있는지 아는 것이 중요합니다. 권장되는 리소스 관리 방법을 알고 싶습니다. "../resources/file.png"로 내 리소스에 액세스하고 싶지 않습니다.


1
For example I don't want to update my CMakeList.txt when I am adding a new folder in my src tree소스를 자동으로 수집하는 IDE의 예를 들어 주시겠습니까?

7
no ide는 필요하지 않기 때문에 일반적으로 소스를 자동으로 수집하지 않습니다. 새 파일이나 폴더를 추가하면 ide 내에서 추가하면 프로젝트가 업데이트됩니다. 다른 쪽의 빌드 시스템은 일부 파일을 변경해도 인식하지 못하므로 모든 소스 파일을 자동으로 수집하는 것이 바람직한 동작입니다.
Arne

4
이 링크를 보면 CMake가 해결하고자하는 가장 중요한 작업 인 크로스 플랫폼 빌드 시스템을 쉽게 만드는 데 실패했다는 인상을 받았습니다.
아르네

답변:


94

몇 가지 연구 끝에 이제는 가장 간단하지만 완전한 cmake 예제의 자체 버전을 얻었습니다. 여기에 있으며 리소스 및 패키지를 포함한 대부분의 기본 사항을 다루려고합니다.

비표준을 수행하는 한 가지는 리소스 처리입니다. 기본적으로 cmake는 이들을 / usr / share /, / usr / local / share / 및 Windows에서 이와 동등한 위치에 저장하려고합니다. 어디서나 압축을 풀고 실행할 수있는 간단한 zip / tar.gz를 갖고 싶었습니다. 따라서 리소스는 실행 파일에 상대적으로로드됩니다.

cmake 명령을 이해하기위한 기본 규칙은 <function-name>(<arg1> [<arg2> ...])쉼표 또는 반색없이 다음 구문 입니다. 각 인수는 문자열입니다. foobar(3.0)그리고 foobar("3.0")동일합니다. 을 사용하여 목록 / 변수를 설정할 수 있습니다 set(args arg1 arg2). 이 변수 세트로foobar(${args})foobar(arg1 arg2)효과적으로 동일하다. 존재하지 않는 변수는 빈 목록과 동일합니다. 목록은 내부적으로 요소를 구분하는 세미콜론이있는 문자열입니다. 따라서 하나의 요소 만 포함 된 목록은 정의에 따라 해당 요소 일 뿐이며 권투는 발생하지 않습니다. 변수는 전역 적입니다. 내장 함수는 또는 같은 ID를 기대한다는 사실에 의해 명명 된 인수의 형태를 제공합니다.PUBLICDESTINATION인수 목록에서 인수를 그룹화합니다. 그러나 그것은 언어 기능이 아니며, 이러한 ID는 문자열 일 뿐이며 함수 구현에 의해 구문 분석됩니다.

github 에서 모든 것을 복제 할 수 있습니다.

cmake_minimum_required(VERSION 3.0)
project(example_project)

###############################################################################
## file globbing ##############################################################
###############################################################################

# these instructions search the directory tree when cmake is
# invoked and put all files that match the pattern in the variables 
# `sources` and `data`
file(GLOB_RECURSE sources      src/main/*.cpp src/main/*.h)
file(GLOB_RECURSE sources_test src/test/*.cpp)
file(GLOB_RECURSE data resources/*)
# you can use set(sources src/main.cpp) etc if you don't want to
# use globing to find files automatically

###############################################################################
## target definitions #########################################################
###############################################################################

# add the data to the target, so it becomes visible in some IDE
add_executable(example ${sources} ${data})

# just for example add some compiler flags
target_compile_options(example PUBLIC -std=c++1y -Wall -Wfloat-conversion)

# this lets me include files relative to the root src dir with a <> pair
target_include_directories(example PUBLIC src/main)

# this copies all resource files in the build directory
# we need this, because we want to work with paths relative to the executable
file(COPY ${data} DESTINATION resources)

###############################################################################
## dependencies ###############################################################
###############################################################################

# this defines the variables Boost_LIBRARIES that contain all library names
# that we need to link to
find_package(Boost 1.36.0 COMPONENTS filesystem system REQUIRED)

target_link_libraries(example PUBLIC
  ${Boost_LIBRARIES}
  # here you can add any library dependencies
)

###############################################################################
## testing ####################################################################
###############################################################################

# this is for our testing framework
# we don't add REQUIRED because it's just for testing
find_package(GTest)

if(GTEST_FOUND)
  add_executable(unit_tests ${sources_test} ${sources})

  # we add this define to prevent collision with the main
  # this might be better solved by not adding the source with the main to the
  # testing target
  target_compile_definitions(unit_tests PUBLIC UNIT_TESTS)

  # this allows us to use our executable as a link library
  # therefore we can inherit all compiler options and library dependencies
  set_target_properties(example PROPERTIES ENABLE_EXPORTS on)

  target_link_libraries(unit_tests PUBLIC
    ${GTEST_BOTH_LIBRARIES}
    example
  )

  target_include_directories(unit_tests PUBLIC
    ${GTEST_INCLUDE_DIRS} # doesn't do anything on Linux
  )
endif()

###############################################################################
## packaging ##################################################################
###############################################################################

# all install commands get the same destination. this allows us to use paths
# relative to the executable.
install(TARGETS example DESTINATION example_destination)
# this is basically a repeat of the file copy instruction that copies the
# resources in the build directory, but here we tell cmake that we want it
# in the package
install(DIRECTORY resources DESTINATION example_destination)

# now comes everything we need, to create a package
# there are a lot more variables you can set, and some
# you need to set for some package types, but we want to
# be minimal here
set(CPACK_PACKAGE_NAME "MyExample")
set(CPACK_PACKAGE_VERSION "1.0.0")

# we don't want to split our program up into several things
set(CPACK_MONOLITHIC_INSTALL 1)

# This must be last
include(CPack)

8
@SteveLorimer 저는 동의하지 않습니다. 파일 globbing은 나쁜 스타일입니다. 파일 트리를 CMakeLists.txt에 수동으로 복사하는 것은 중복되기 때문에 나쁜 스타일이라고 생각합니다. 하지만 사람들이이 주제에 동의하지 않는다는 것을 알고 있으므로 코드에 주석을 남겼습니다. 여기서 글 로빙을 모든 소스 파일을 명시 적으로 포함하는 목록으로 바꿀 수 있습니다. 를 검색합니다 set(sources src/main.cpp).
Arne

3
@SteveLorimer 예, 자주 cmake를 다시 호출해야했습니다. 디렉토리 트리에 무언가를 추가 할 때마다 cmake를 수동으로 다시 호출해야 globbing이 재평가됩니다. 에 파일을 넣으면 CMakeLists.txt일반 make (또는 ninja)가 cmake의 재 호출을 트리거하므로 잊지 못할 수 있습니다. 또한 팀원들도 cmake를 실행하는 것을 잊을 수 없기 때문에 약간의 팀 친화적입니다. 하지만 누군가가 파일을 추가했기 때문에 makefile을 건드릴 필요가 없다고 생각합니다. 한 번 작성하면 아무도 그것에 대해 다시 생각할 필요가 없습니다.
Arne

3
@SteveLorimer 나는 또한 프로젝트의 모든 디렉토리에 하나의 CMakeLists.txt를 넣는 패턴에 동의하지 않습니다. 프로젝트의 구성을 모든 곳에 분산시킵니다. 하나의 파일로 충분해야한다고 생각합니다. 그렇지 않으면 개요를 잃어 버립니다. 실제로 빌드 프로세스에서 수행됩니다. 그렇다고 자체 CMakeLists.txt가있는 하위 디렉터리가있을 수 없다는 의미는 아닙니다. 예외 여야한다고 생각합니다.
Arne

2
"VCS""버전 제어 시스템"의 줄임말 이라고 가정하면 이는 관련이 없습니다. 문제는 아티팩트가 소스 제어에 추가되지 않는다는 것입니다. 문제는 CMake가 추가 된 소스 파일을 재평가하지 못한다는 것입니다. 빌드 시스템 입력 파일을 다시 생성하지 않습니다. 빌드 시스템은 오류 (운이 좋다면)로 이어 지거나 운이 떨어지면 눈에 띄지 않게 오래된 입력 파일을 기꺼이 고수합니다. GLOBbing은 종속성 계산 체인에 공백을 생성합니다. 이것은 이다 중대한 문제와 코멘트는 적절하게이 문제를 인정하지 않습니다.
IInspectable

2
CMake와 VCS는 완전히 분리되어 작동합니다. VCS는 CMake를 인식하지 못하고 CMake는 VCS를 인식하지 못합니다. 그들 사이에는 링크가 없습니다. 개발자가 수동 단계를 수행하고 VCS에서 정보를 가져오고 일부 휴리스틱 정리 및 CMake를 다시 실행하도록 제안하지 않는 한. 그것은 분명히 확장되지 않으며 인간 특유의 오류에 민감합니다. 아니요, 죄송합니다. 지금까지 GLOBbing 파일에 대해 설득력있는 지점을 만들지 않았습니다.
IInspectable

39

가장 기본적이지만 완전한 예제는 CMake 튜토리얼 에서 찾을 수 있습니다 .

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)

프로젝트 예의 경우 다음이있을 수 있습니다.

cmake_minimum_required (VERSION 2.6)
project (MyProject)
add_executable(myexec src/module1/module1.cpp src/module2/module2.cpp src/main.cpp)
add_executable(mytest test1.cpp)

추가 질문에 대한 한 가지 방법은 자습서에서 다시 : 코드에 포함 할 구성 가능한 헤더 파일을 만드는 것입니다. 이를 위해 configuration.h.in다음 내용 으로 파일 을 만드십시오 .

#define RESOURCES_PATH "@RESOURCES_PATH@"

그런 다음 CMakeLists.txt추가 :

set(RESOURCES_PATH "${PROJECT_SOURCE_DIR}/resources/"
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/configuration.h.in"
  "${PROJECT_BINARY_DIR}/configuration.h"
)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

마지막으로 코드에서 경로가 필요한 곳에서 다음을 수행 할 수 있습니다.

#include "configuration.h"

...

string resourcePath = string(RESOURCE_PATH) + "file.png";

특히 RESOURCE_PATH에 대해 대단히 감사합니다. 어떻게 든 내가 찾던 configure_file을 얻지 못했습니다. 그러나 프로젝트의 모든 파일을 수동으로 추가했습니다. src 트리에서 모든 파일이 추가되는 패턴을 간단히 정의하는 더 좋은 방법이 있습니까?
Arne

Dieter의 답변을 참조하고 왜 사용하지 말아야하는지에 대한 의견도 참조하십시오. 정말로 자동화하고 싶다면 소스 파일 목록을 재생성하기 위해 실행할 수있는 스크립트를 작성하는 것이 더 나은 방법 일 수 있습니다 (또는이 작업을 수행하는 cmake 인식 IDE를 사용합니다.
sgvd

3
@sgvd string resourcePath = string(RESOURCE_PATH) + "file.png"IMHO 소스 디렉토리의 절대 경로를 하드 코딩하는 것은 나쁜 생각 입니다. 프로젝트를 설치해야하는 경우 어떻게합니까?

2
자동으로 소스를 수집하는 것이 좋게 들리지만 모든 종류의 문제를 일으킬 수 있다는 것을 알고 있습니다. 간단한 토론을 위해 얼마 전에이 질문을 참조하십시오 : stackoverflow.com/q/10914607/1401351 .
Peter

2
cmake를 실행하지 않으면 똑같은 오류가 발생합니다. 수동으로 파일을 추가하는 데 1 초가 걸리고 컴파일 할 때마다 cmake를 실행하는 데는 1 초가 걸립니다. 실제로 cmake의 기능을 깨뜨립니다. 같은 프로젝트에서 작업하고 변경 사항을 가져 오는 사람은 다음과 같이 할 것입니다. make 실행-> 정의되지 않은 참조 가져 오기-> cmake를 다시 실행하는 것을 기억하거나 파일 버그-> cmake 실행-> make 성공적으로 실행하는 반면 파일을 추가하면 그는 손으로합니다 : 성공적으로 실행-> 가족과 함께 시간을 보냅니다. 이를 요약하고 게으르지 말고 미래에 자신과 다른 사람들의 두통을 피하십시오.
sgvd

2

여기서는 가장 간단하지만 완전한 CMakeLists.txt 파일 샘플을 작성합니다.

소스 코드

  1. Hello World에서 크로스 플랫폼 Android / iOS / Web / Desktop에 이르는 튜토리얼.
  2. 각 플랫폼은 샘플 애플리케이션을 출시했습니다.
  3. 08-cross_platform 파일 구조체는 내 작업에 의해 확인됩니다.
  4. 완벽하지는 않지만 나 혼자 팀을위한 유용하고 모범 사례

그 후 세부 사항에 대한 문서를 제공했습니다.

질문이 있으시면 저에게 연락해 주시면 설명하겠습니다.

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