CMake는 대상의 소스 파일을 지정하는 여러 가지 방법을 제공합니다. 하나는 예를 들어 globbing ( documentation ) 을 사용하는 것입니다 .
FILE(GLOB MY_SRCS dir/*)
다른 방법은 각 파일을 개별적으로 지정하는 것입니다.
어느 방법이 선호됩니까? 글 로빙은 쉬워 보이지만 단점이 있다고 들었습니다.
CMake는 대상의 소스 파일을 지정하는 여러 가지 방법을 제공합니다. 하나는 예를 들어 globbing ( documentation ) 을 사용하는 것입니다 .
FILE(GLOB MY_SRCS dir/*)
다른 방법은 각 파일을 개별적으로 지정하는 것입니다.
어느 방법이 선호됩니까? 글 로빙은 쉬워 보이지만 단점이 있다고 들었습니다.
답변:
전체 공개 : 원래는 단순함을 위해 글 로빙 방식을 선호했지만, 수년에 걸쳐 파일을 명시 적으로 나열하는 것은 대규모의 다중 개발자 프로젝트에서 오류가 덜 발생한다는 것을 인식했습니다.
원래 답변 :
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 와 같은 것을 사용 하여 동일한 빌드 디렉토리에서 코드의 이전 버전을 시도하는 경우입니다. 이 경우 목록에서 올바른 파일을 확보하기 위해 필요 이상으로 정리하고 컴파일해야 할 수도 있습니다. 이것은 코너링 케이스이며 이미 발끝에있어 실제로 문제가되지 않습니다.
Simply "touch" the CMakeLists.txt
합니다. 개발자라면 괜찮습니다.하지만 소프트웨어를 구축하는 다른 사람들에게는 업데이트 후 빌드가 실패 하고 조사 해야 할 부담이 있습니다. 왜.
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.txt
CMake를 사용하지 않는 기존 프로젝트 의 파일을 설정합니다 . cmake
빌드 파일을 생성하기 위해 실행을 수락해야합니다 (CMake의 의도와 반대 함) .* 예, 업데이트 전후 디스크의 파일 트리를 비교하는 코드를 작성할 수 있었지만 이는 훌륭한 해결 방법이 아니며 빌드 시스템에 더 나은 것으로 남았습니다.
CMake 3.12에서 file(GLOB ...)
andfile(GLOB_RECURSE ...)
명령은 CONFIGURE_DEPENDS
glob의 값이 변경되면 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 소스 목록을 인쇄하고 다시 붙여 넣기 만하면 쉽게 얻을 수 있습니다.
의존성을 유지하기 위해 추가 파일을 희생하여 안전하게 구할 수 있습니다.
다음과 같은 기능을 어딘가에 추가하십시오.
# 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를 터치하면됩니다.
make
이상한 링커 오류가 발생 하는 문제가 해결 됩니다.
각 파일을 개별적으로 지정하십시오!
기존 CMakeLists.txt와 python 스크립트를 사용하여 업데이트합니다. 파일을 추가 한 후 파이썬 스크립트를 수동으로 실행합니다.