CMake에서 GLOB 또는 각 파일을 사용하여 소스 파일을 개별적으로 지정하는 것이 더 좋습니까?


157

CMake는 대상의 소스 파일을 지정하는 여러 가지 방법을 제공합니다. 하나는 예를 들어 globbing ( documentation ) 을 사용하는 것입니다 .

FILE(GLOB MY_SRCS dir/*)

다른 방법은 각 파일을 개별적으로 지정하는 것입니다.

어느 방법이 선호됩니까? 글 로빙은 쉬워 보이지만 단점이 있다고 들었습니다.

답변:


185

전체 공개 : 원래는 단순함을 위해 글 로빙 방식을 선호했지만, 수년에 걸쳐 파일을 명시 적으로 나열하는 것은 대규모의 다중 개발자 프로젝트에서 오류가 덜 발생한다는 것을 인식했습니다.

원래 답변 :


globbing의 장점은 다음과 같습니다.

  • 디스크에 한 곳에만 나열되므로 새 파일을 쉽게 추가 할 수 있습니다. globbing하지 않으면 복제가 발생합니다.

  • CMakeLists.txt 파일이 더 짧습니다. 파일이 많으면 큰 장점입니다. globbing하지 않으면 거대한 파일 목록에서 CMake 논리가 손실됩니다.

하드 코드 된 파일 목록을 사용하면 다음과 같은 장점이 있습니다.

  • CMake는 디스크에서 새 파일의 종속성을 올바르게 추적합니다. glob를 사용하면 CMake를 실행할 때 처음으로 glob되지 않은 파일이 선택되지 않습니다.

  • 원하는 파일 만 추가해야합니다. 글 로빙은 원하지 않는 길잃은 파일을 선택할 수 있습니다.

첫 번째 문제를 해결하려면 touch 명령을 사용하거나 변경없이 파일을 작성하여 glob을 수행하는 CMakeLists.txt를 "터치"하면됩니다. 그러면 CMake가 새 파일을 다시 실행하고 선택합니다.

두 번째 문제를 해결하려면 코드를주의해서 디렉토리에 정리하면됩니다. 최악의 경우 list(REMOVE_ITEM)명령을 사용 하여 globbed 파일 목록을 정리할 수 있습니다.

file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})

이것이 물릴 수있는 유일한 실제 상황은 git-bisect 와 같은 것을 사용 하여 동일한 빌드 디렉토리에서 코드의 이전 버전을 시도하는 경우입니다. 이 경우 목록에서 올바른 파일을 확보하기 위해 필요 이상으로 정리하고 컴파일해야 할 수도 있습니다. 이것은 코너링 케이스이며 이미 발끝에있어 실제로 문제가되지 않습니다.


1
또한 globbing에 좋지 않습니다 : git의 difftool 파일은 $ basename. $ ext. $ type. $ pid. $ ext로 저장되어 단일 병합 해결 후 컴파일 할 때 재미있는 오류가 발생할 수 있습니다.
mathstuf

9
나는이 답변이 cmake가 새 파일을 잃어 버린 단점에 대해 생각한다고 생각 Simply "touch" the CMakeLists.txt합니다. 개발자라면 괜찮습니다.하지만 소프트웨어를 구축하는 다른 사람들에게는 업데이트 후 빌드가 실패 하고 조사 해야 할 부담이 있습니다. 왜.
ideasman42

36
그거 알아? 6 년 전이 답변을 작성한 이후로 , 나는 마음을 조금 바꾸었고 이제 파일을 명시 적으로 나열하는 것을 선호합니다. 단지 단점은 "파일을 추가하는 작업이 조금 더 많다"는 점이지만 모든 종류의 두통을 줄여줍니다. 그리고 많은 방법으로 명시 적이 암시 적보다 낫습니다.
richq

1
@richq 고리 가 현재 위치를 재고하게 하시겠습니까 ? :)
Antonio

8
안토니오가 말한 것처럼 "글 로빙 (globbing)"접근법을지지 한 표가 주어졌다. 답변의 본질을 바꾸는 것은 유권자들에게해야 할 미끼와 전환입니다. 타협으로 나는 변경된 의견을 반영하기 위해 편집을 추가했습니다. 인터넷에서 찻잔에 그런 폭풍을 일으킨 것에 대해 사과드립니다
:-P

113

CMake에서 소스 파일을 지정하는 가장 좋은 방법은 명시 적으로 나열하는 것 입니다.

CMake의 제작자는 스스로 globbing을 사용 하지 말 것을 권고 합니다.

참조 : https://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file

(GLOB를 사용하여 소스 트리에서 소스 파일 목록을 수집하지 않는 것이 좋습니다. 소스를 추가하거나 제거 할 때 CMakeLists.txt 파일이 변경되지 않으면 생성 된 빌드 시스템이 CMake의 재생성 시점을 알 수 없습니다.)

물론 단점이 무엇인지 알고 싶을 수도 있습니다 .


글 로빙이 실패 할 때 :

globbing의 가장 큰 단점은 파일 생성 / 삭제가 빌드 시스템을 자동으로 업데이트하지 않는다는 것입니다.

파일을 추가하는 사람이라면이 방법이 허용되는 타협으로 보일 수 있지만 다른 사람들이 코드를 작성하는 데 문제가 발생하면 버전 관리에서 프로젝트를 업데이트하고 빌드를 실행 한 다음 사용자에게 연락하여
"빌드가 부서진".

설상가상으로, 실패는 일반적으로 문제의 원인에 대한 힌트를 제공하지 않는 링크 오류를 발생시키고 문제 해결에 시간이 손실됩니다.

내가 작업 한 프로젝트에서 globbing을 시작했지만 새 파일을 추가 할 때 불만이 너무 많아서 globbing 대신 파일을 명시 적으로 나열 해야하는 충분한 이유가있었습니다.

또한 일반적인 git 워크 플로우
( git bisect및 기능 분기 간 전환) 가 중단 됩니다.

그래서 나는 이것을 추천 할 수 없었습니다. 이로 인해 발생하는 문제는 편리함을 능가합니다. 이로 인해 누군가 소프트웨어를 빌드 할 수 없을 때 문제를 추적하거나 포기하는 데 많은 시간이 걸릴 수 있습니다.

또 다른 메모, 터치 만 기억하는 것만으로 CMakeLists.txt는 충분하지 않습니다. 글 로빙을 사용하는 자동화 된 빌드에서는 마지막 빌드 이후 파일 이 추가 / 제거 될 수 있기 때문에 모든 빌드cmake 전에 실행 해야했습니다 *.

규칙의 예외 :

globbing이 바람직한 경우가 있습니다.

  • CMakeLists.txtCMake를 사용하지 않는 기존 프로젝트 의 파일을 설정합니다 .
    모든 소스를 참조하는 빠른 방법입니다 (빌드 시스템이 실행되면 명시 적 파일 목록으로 globbing 대체).
  • CMake가 기본 빌드 시스템 으로 사용되지 않는 경우, 예를 들어 CMake를 사용하지 않는 프로젝트를 사용하고 있고 자체 빌드 시스템을 유지하려는 경우.
  • 파일 목록이 너무 자주 변경되어 유지 관리가 불가능한 경우. 이 경우 유용 수 있지만 신뢰할 수 있고 올바른 빌드를 얻을 때마다cmake 빌드 파일을 생성하기 위해 실행을 수락해야합니다 (CMake의 의도와 반대 함) .

* 예, 업데이트 전후 디스크의 파일 트리를 비교하는 코드를 작성할 수 있었지만 이는 훌륭한 해결 방법이 아니며 빌드 시스템에 더 나은 것으로 남았습니다.


9
"글 로빙의 가장 큰 단점은 새 파일을 만들면 빌드 시스템이 자동으로 업데이트되지 않는다는 것입니다." 그러나 glob하지 않아도 cakeLists.txt를 수동으로 업데이트해야하는데 cmake가 여전히 빌드 시스템을 자동으로 업데이트하지 않는다는 의미입니까? 새 파일을 빌드하기 위해 수동으로 무언가를 수행 해야하는 방법 중 하나 인 것 같습니다. CMakeLists.txt를 터치하면 파일을 열고 편집하여 새 파일을 추가하는 것보다 쉽습니다.
Dan

17
@Dan, 당신의 시스템을 위해 -물론, 만약 당신이 혼자서 개발한다면 이것은 괜찮지 만, 프로젝트를 만드는 다른 사람들은 어떻습니까? CMake 파일로 이동하여 수동으로 터치하도록 이메일을 보내시겠습니까? 파일이 추가되거나 제거 될 때마다? -CMake에 파일 목록을 저장하면 빌드가 항상 vcs가 알고있는 것과 동일한 파일을 사용하게됩니다. 저를 믿으십시오-이것은 단지 미묘한 세부 사항이 아닙니다-많은 개발자에게 빌드가 실패하면 목록을 메일로 보내고 IRC에 코드가 손상되었음을 요청합니다. 참고 : (자신의 시스템에서도 예를 들어, git history로 되돌아 갈 수 있으며 CMake 파일에 들어가거나 터치하지 않을 수도 있습니다)
ideasman42

2
아 나는 그 사건을 생각하지 않았다. 그것이 내가 글러브에 대해 들었던 가장 좋은 이유입니다. 사람들이 글러브를 피하도록 권장하는 이유에 대한 cmake 문서가 확장되기를 바랍니다.
Dan

1
마지막 cmake 실행의 타임 스탬프를 파일에 쓰는 솔루션에 대해 생각했습니다. 유일한 문제는 다음과 같습니다 : 1) 아마도 cmake가 크로스 플랫폼이되어야하므로 cmake가 두 번째로 실행되는 것을 피해야합니다. 2) 아마도 더 많은 병합 충돌 (아직 파일 목록 btw에서 발생)은 나중에 타임 스탬프를 가져 와서 사소하게 해결할 수 있습니다.
Predelnik

2
@ tim-mb, "그러나 CMake가 체크인 할 수있는 filetree_updated 파일을 만들면 좋을 것입니다. 파일이 업데이트 될 때마다 자동으로 변경됩니다." -당신은 내 대답이 정확히 무엇인지 설명했습니다.
Glen Knowles

21

CMake 3.12에서 file(GLOB ...)andfile(GLOB_RECURSE ...) 명령은 CONFIGURE_DEPENDSglob의 값이 변경되면 cmake를 다시 실행 하는 옵션을 얻었습니다 . 그것이 소스 파일을 가져 오는 데있어 가장 큰 단점이되었으므로 이제 그렇게해도됩니다.

# Whenever this glob's value changes, cmake will rerun and update the build with the
# new/removed files.
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")

add_executable(my_target ${sources})

그러나 일부 사람들은 여전히 ​​출처를 파악하지 않는 것이 좋습니다. 실제로 설명서 에는 다음 과 같이 명시되어 있습니다.

GLOB를 사용하여 소스 트리에서 소스 파일 목록을 수집하지 않는 것이 좋습니다. ... CONFIGURE_DEPENDS플래그가 모든 생성기에서 안정적으로 작동하지 않을 수도 있고, 향후 생성 할 수없는 새 생성기가 추가되면이를 사용하는 프로젝트가 중단됩니다. CONFIGURE_DEPENDS안정적으로 작동 하더라도 모든 재 구축에 대해 점검을 수행하는 데 여전히 비용이 있습니다.

개인적으로 소스 파일 목록을 수동으로 관리하지 않아도 가능한 단점을 능가하는 이점을 고려합니다. 수동으로 나열된 파일로 다시 전환해야하는 경우, globbed 소스 목록을 인쇄하고 다시 붙여 넣기 만하면 쉽게 얻을 수 있습니다.


빌드 시스템이 완전한 cmake 및 빌드주기를 수행하는 경우 (빌드 디렉토리를 삭제하고 거기에서 cmake를 실행 한 다음 makefile을 호출), 원치 않는 파일을 가져 오지 않으면 GLOBbed 소스를 사용하는 데 단점이없는 것입니까? 이 오버 헤드가 어쨌든 많이하지 그래서 내 경험에서 cmake 부분은 빌드보다 훨씬 더 빠르게 실행
덴 - 제이슨

9

의존성을 유지하기 위해 추가 파일을 희생하여 안전하게 구할 수 있습니다.

다음과 같은 기능을 어딘가에 추가하십시오.

# Compare the new contents with the existing file, if it exists and is the 
# same we don't want to trigger a make by changing its timestamp.
function(update_file path content)
    set(old_content "")
    if(EXISTS "${path}")
        file(READ "${path}" old_content)
    endif()
    if(NOT old_content STREQUAL content)
        file(WRITE "${path}" "${content}")
    endif()
endfunction(update_file)

# Creates a file called CMakeDeps.cmake next to your CMakeLists.txt with
# the list of dependencies in it - this file should be treated as part of 
# CMakeLists.txt (source controlled, etc.).
function(update_deps_file deps)
    set(deps_file "CMakeDeps.cmake")
    # Normalize the list so it's the same on every machine
    list(REMOVE_DUPLICATES deps)
    foreach(dep IN LISTS deps)
        file(RELATIVE_PATH rel_dep ${CMAKE_CURRENT_SOURCE_DIR} ${dep})
        list(APPEND rel_deps ${rel_dep})
    endforeach(dep)
    list(SORT rel_deps)
    # Update the deps file
    set(content "# generated by make process\nset(sources ${rel_deps})\n")
    update_file(${deps_file} "${content}")
    # Include the file so it's tracked as a generation dependency we don't
    # need the content.
    include(${deps_file})
endfunction(update_deps_file)

그런 다음 globbing로 이동하십시오.

file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})

이전과 같이 명시 적 종속성을 계속 사용하고 모든 자동화 된 빌드를 트리거하고 있습니다. 하나가 아닌 두 개의 파일 만 있습니다.

새 파일을 만든 후에 만 ​​절차가 변경됩니다. glob을 사용하지 않으면 워크 플로는 Visual Studio 내부에서 CMakeLists.txt를 수정하고 다시 작성하는 것입니다. glob을 수행하는 경우 cmake를 명시 적으로 실행하거나 CMakeLists.txt를 터치하면됩니다.


처음에는 이것이 소스 파일이 추가 될 때 Makefile을 자동으로 업데이트하는 도구라고 생각했지만 이제 그 값이 무엇인지 알 수 있습니다. 좋은! 이렇게하면 누군가 저장소에서 업데이트하고 make이상한 링커 오류가 발생 하는 문제가 해결 됩니다.
Cris Luengo

1
나는 이것이 좋은 방법 일 수 있다고 생각합니다. 물론 파일을 추가하거나 제거한 후에 cmake를 트리거해야한다는 점도 기억해야하며이 종속성 파일을 커밋해야하므로 사용자 측에 대한 교육이 필요합니다. 가장 큰 단점은이 의존성 파일이 개발자에게 메커니즘에 대한 이해를 다시 요구할 필요없이 어려운 병합 충돌을 야기 할 수 있다는 것입니다.
Antonio

1
프로젝트에 조건부로 포함 된 파일 (예 : 기능이 활성화 된 경우에만 사용되거나 특정 운영 체제에서만 사용되는 일부 파일)이있는 경우에는 작동하지 않습니다. 휴대용 소프트웨어에는 일부 파일이 특정 플랫폼에만 사용되는 것이 일반적입니다.
ideasman42

0

각 파일을 개별적으로 지정하십시오!

기존 CMakeLists.txt와 python 스크립트를 사용하여 업데이트합니다. 파일을 추가 한 후 파이썬 스크립트를 수동으로 실행합니다.

내 대답보기 : https://stackoverflow.com/a/48318388/3929196

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