본문 바로가기
CMake

[CMake] Properties

by 별준 2021. 11. 2.

References

  • Professional CMake : A Practical Guide

Contents

  • Properties
  • General Property Commands : set_property(), get_property(), define_property()
  • Global Properties : get_cmake_property()
  • Directory Properties : set_directory_properties(), get_directory_property()
  • Target Properties : set_target_properties(), get_target_property()

Properties(속성)는 소스 파일이 object 파일로 컴파일되는 방법부터 빌드된 바이너리의 install 위치까지 빌드 프로세스의 거의 모든 측면에 영향을 줍니다. 이 properties는 디렉토리, Target, 소스 파일, 테스트 케이스, 캐시 변수 또는 전체 빌드 프로세스 자체와 같은 특정 entity에 추가됩니다. 독립된 값을 가지고 있는 변수와는 달리, 속성은 연결된 entity와 관련된 정보들을 제공합니다.

 

CMake를 처음 접하는 경우 속성과 변수를 혼동할 수 있는데, 기능과 특징면에서 비슷해 보일 수 있지만, 속성은 다른 용도로 사용됩니다. 변수는 특정 entity에 연결되지 않으며 프로젝트에서 자체 변수를 정의하고 사용하는 것이 일반적입니다. 둘 사이의 혼란을 야기하는 요인은 속성의 기본값이 때때로 변수에 의해 제공된다는 점인데, CMake가 관련 속성 및 변수에 사용하는 이름은 일반적으로 동일한 패턴을 갖고 있으며, 속성 이름이 되는 변수명은 앞에 CMAKE_가 붙은 속성 이름입니다.

 

지금은 그림이 잘 그려지지 않을 수도 있는데, 조금 더 자세하게 살펴보도록 하겠습니다.

(저도 아직 완전히 이해하지는 못하고 있습니다 ㅠ.ㅠ)

 


General Property Commands

CMake에서는 속성을 조작하기 위해 여러 커맨드를 제공합니다. 이들 중 가장 일반적인 커맨드는 set_property()get_property() 입니다. 이 커맨드를 사용하면 모든 유형의 entity에서 모든 속성을 설정하고 가져올 수 있습니다.

CMake 공식 문서에서는 entity를 property가 설정되는 scope라고 지칭하고 있습니다.
이 글에서는 그냥 entity라고 지칭하도록 하겠습니다.

이 커맨드를 사용하려면 argument로 entity의 타입과 일부 entity에 특화된 정보들을 지정해주어야 합니다.

entitySpecific은 속성이 설정되는 entity를 정의합니다. 이는 아래 중에 하나가 되어야 합니다.

위에서 각 줄의 첫 번째 단어는 속성이 설정되는 entity의 유형을 정의합니다.

GLOBAL은 빌드 자체를 의미하므로 특정 entity의 이름이 필요하지 않습니다. 

DIRECTORY의 경우 dir 이름이 지정되지 않으면 현재 소스 디렉토리가 사용됩니다.

다른 모든 유형의 entity의 경우에는 해당 유형의 항목을 제한없이 나열할 수 있습니다.

 

PROPERTY 키워드는 나머지 모든 argument를 속성 이름과 해당 값을 정의하도록 합니다.

propName은 일반적으로 CMake 문서에 정의된 속성 중 하나와 일치하며, 이는 뒤에서 설명하도록 하겠습니다. 값의 의미는 속성에 따라 다른데, CMake에서 이미 정의한 속성과 별도로 새로운 속성을 생성하는 것도 허용됩니다. 이러한 프로젝트별 속성이 의미하는 바와 빌드에 미치는 영향은 프로젝트에 달려 있습니다.

이 키워드를 사용하면 프로젝트에서 속성 이름에 일부 프로젝트별 접두사를 사용하여 CMake 또는 기타 다른 패키지에서 정의한 속성과 이름 충돌을 방지하는 것이 좋습니다.

 

APPEND 및 APPEND_STRING 키워드는 명명된 속성에 이미 값이 있는 경우 업데이트되는 방법을 제어하는데 사용됩니다. 키워드를 지정하지 않으면 지정된 값이 이전 값을 대체합니다. APPEND 키워드는 기존 값에 값을 추가하여 리스트를 형성하는 것처럼 동작하고, APPEND_STRING 키워드는 기존 값에 리스트가 아닌 문자열로 연결하여 새로운 값으로 추가합니다.

아래에 키워드가 없는 경우와 APPEND 및 APPEND_STRING에 따른 속성 값의 변화를 보여주고 있습니다.

 

get_property()는 아래처럼 사용됩니다.

PROPERTY 키워드는 항상 필수이며, 항성 검색할 속성의 이름이 뒤에 와야합니다. 검색 결과는 resultVar라는 이름의 변수에 저장됩니다.

entitySpecific은 set_property()와 유사하며 다음 중 하나가 와야합니다.

이전과 마찬가지로 GLOBAL은 빌드 전체를 의미하므로 특정 entity의 이름을 지정할 필요가 없습니다.

DIRECTORY는 특정 디렉토리를 지정하거나 지정하지 않는다면 현재 소스 디렉토리가 디폴트로 지정됩니다.

대부분의 다른 entity의 경우에는 해당 scope 안에서의 특정 entity 이름을 지정해야하며, 해당 entity에 연결된 속성이 검색됩니다.

VARIABLE은 조금 다른데, 변수 이름이 VARIABLE 키워드에 첨부되지 않고 propName에 변수 이름이 지정됩니다. 이는 다소 직관적이지 않을 수 있는데, 다른 entity 유형 키워드와 마찬가지로 변수가 VARIABLE 키워드와 함께 entity로 명명된 상황을 생각해보면 이해가 빠를 수 있습니다. 이러한 상황에서는 속성 이름(propName)에 대해 지정할 것이 없습니다. VARIABLE이 현재 특정 scope로 지정한다는 것으로 생각하면, 알고자하는 속성은 propName 이름으로 명명된 변수라고 볼 수 있습니다. 다만, get_property()로 VARIABLE을 사용하는 것은 흔하진 않습니다. 변수 값은 ${} 문법으로 쉽게 접근이 가능하기 때문입니다.

Optional 키워드인 DEFINED / SET / BRIEF_DOCS / FULL_DOCS 중에 아무것도 설정되지 않는다면, 명명된 속성의 값이 검색됩니다. 이 키워드가 설정되면 값 이외의 속성에 대한 세부 정보를 검색합니다.

  • DEFINED : 이 옵션을 사용하면 검색 결과는 명명된 속성이 정의되었는지 여부를 나타내는 부울값입니다. 만약 VARIABLE에서 이 옵션을 사용하면 명명된 변수가 define_property() 명령으로 명시적으로 정의된 경우에만 결과가 true입니다. (아래 참조)
  • SET : 이 옵션을 사용하면 검색 결과는 명명된 속성이 설정되었는지 여부를 나타내는 부울값입니다. 명명된 속성에 실제로 값이 설정되었는지 여부를 판단하기 때문에 DEFINED보다 더 강력한 테스트입니다. DEFINE에 대해서는 TRUE, SET에 대해서는 FALSE를 반환할 수 있으며, 반대도 가능합니다.
  • BRIEF_DOCS : 명명된 속성에 대한 간단한 docstring을 검색합니다. 속성에 대해 간단한 문서가 정의되지 않은 경우 결과는 NOTFOUND 문자열이 됩니다.
  • FULL_DOCS : 명명된 속성에 대한 전체 문서를 검색합니다. 정의되지 않은 경우에 결과는 NOTFOUND 문자열이 됩니다.

4개의 Optional 키워드 중에서 SET을 제외한 모든 키워드는 프로젝트가 명시적으로 define_property()를 호출하여 특정 entity에 대해 정보를 설정하지 않는 이상 거의 가치가 없습니다.

define_property() 명령은 아래의 방법으로 사용됩니다.

중요한 것은 이 커맨드는 속성의 값은 설정하지 않고, 오직 이 속성의 docstring만 설정하거나 설정되지 않은 경우 다른 곳에서 이 값을 상속하는지 여부만 설정합니다. entityType은 [GLOBAL / DIRECTORY / TARGET / SOURCE / TEST / VARIABLE / CACHE_VARIABLE] 중 하나여야 하고, propName은 정의된 속성의 이름입니다. get_property() 커맨드와 마찬가지로 VARIABLE인 경우에는 변수 이름이 propName으로 설정되어야 합니다.

 

속성을 정의할 때, INHERITED 옵션을 사용하는 경우 해당 속성이 명명된 범위에 설정되지 않은 경우 get_property() 명령은 상위 scope까지 연결됩니다. 예를 들어, DIRECTORY 속성이 요청되었지만, 지정된 디렉토리에 대해 설정되지 않은 경우 속성을 찾거나 소스 트리의 최상위 scope에 도달할 때까지 재귀적으로 해당 속성을 찾습니다. 최상위 디렉토리까지 추적했음에도 해당 속성이 없다면 GLOBAL scope에서 검색됩니다. 마찬가지로, TARGET, SOURCE, TEST 속성이 요청되었지만 지정된 entity에 설정되지 않은 경우, DIRECTORY entity가 검색됩니다(필요시 디렉토리 계층과 GLOBAL scope를 재귀적으로 포함). VARIABLE이나 CACHE_VARIABLE은 이러한 기능이 제공되지 않습니다. (이미 설계상 상위 변수 scope에 연결되어 있기 때문)

 

INHERITED 속성의 상속은 get_property() 커맨드와 특정 속성 유형에 대한 'get_...' 함수에만 적용됩니다.(아래에서 다시 다루도록 하겠습니다.)

또한 상속 옵션을 가진 속성에서 APPEND APPEND_STRING 옵션으로 set_property()를 호출하면 속성의 원래 값만 고려되고, 값을 추가할 때 어떠한 상속도 발생하지 않습니다. (확인 필요)

 

CMake에는 미리 정의된 각 타입의 속성들이 많이 존재합니다. 개발자는 사용 가능한 속성 및 의도된 목적을 위해서 CMake 레퍼런스 문서를 참조해야할 것입니다.

 


Global Properties

Global 속성은 빌드에 관련이 있습니다. 일반적으로 빌드 도구가 실행되는 방법 또는 빌드 도구의 동작, 프로젝트 파일이 구조화, 어느 정도의 빌드 수준 정보를 제공할 것인지와 같은 측면을 정의하는데 사용됩니다.

일반적인 set_property()와 get_property() 이외에도 CMake에서는 Global entity를 쿼리하기 위해 get_cmake_property()를 제공합니다. 이는 get_property()에 GLOBAL entity를 사용하는 단축 명령어이며, Global 속성을 검색하는데 간단히 사용할 수 있습니다.

get_property()와 마찬가지로 resultVar는 커맨드가 반환될 때 요청된 속성의 값이 저장될 변수의 이름이고, property는 Global 속성의 이름이거나 다음 pseudo 속성일 수 있습니다.

  • VARIABLES : (캐시가 아닌) 모든 일반 변수들의 리스트를 반환 (CMake에서 미리 정의하는 변수들도 검색된다)
  • CACHE_VARIABLES : 모든 캐시 변수들의 리스트를 반환
  • COMMANDS : 정의된 모든 커맨드, 함수, 매크로들의 리스트를 반환. 커맨드는 CMake에서 미리 정의된 것이고, 함수와 매크로는 CMake(보통 modules)에서 정의되거나 프로젝트 내에서 정의된 것들을 의미합니다. 반환된 것들 중의 일부는 문서화되지 않았거나 프로젝트에서 직접 사용하도록 의도하지 않은 내부 entity일 수 있습니다. 반환된 이름은 원래 정의된 이름이 아닌 대/소문자가 다를 수 있습니다.
  • MACROS : 정의된 모든 매크로의 리스트를 반환. 이는 COMMANDS가 반환하는 리스트의 subset이지만, COMMANDS가 반환할 때와는 달리 정의될 때의 대/소문자를 정확하게 표현합니다.
  • COMPONENTS : install() 커맨드로 정의된 모든 component들의 리스트를 반환. (install() 커맨드는 추후에 알아보는 시간을 갖겠습니다.)

이러한 pseudo 속성은 읽기 전용이며, 구현상 Global 속성은 아니지만(이 속성들은 get_property()로 검색할 수 없습니다), 개념적으로는 Global 속성과 유사합니다. 이 속성들은 get_cmake_property() 커맨드를 통해서만 검색할 수 있습니다.

 


Directory Properties

디렉토리 속성은 모든 곳에 적용되는 Global 속성과 개별 Target에만 영향을 주는 Target 속성 사이에 위치한다고 볼 수 있습니다. 따라서 디렉토리 속성은 대부분 현재 디렉토리에서 Target 속성의 기본값을 설정하고 Global 속성이나 기본값을 재정의하는데 중점을 둡니다. 몇 가지 읽기 전용 디렉토리 속성은 해당 지점에서 정의된 것들, 빌드가 어떻게 디렉토리에 도달하는 지에 대한 정보 등을 제공합니다.

 

set_property() 커맨드로도 설정할 수 있지만 편의를 위해서 디렉토리 속성 전용 set_directory_properties()를 제공합니다.

set_property() 커맨드보다 간결하고, APPEND와 APPEND_STRING 옵션이 없습니다. 즉, 속성을 설정하거나 바꿀 때만 사용할 수 있고, 기존 속성에 직접 추가하는 데에는 사용할 수 없습니다. 일반적인 set_property()와 비교하여 이 커맨드의 추가 제약사항은 항상 현재 디렉토리에 적용된다는 점입니다. 편리함을 위해서 이 커맨드를 사용할 수도 있고, 일관성을 위해 set_property()를 사용할 수도 있습니다. (선호도의 차이입니다.)

 

get_directory_property()는 두 가지 방법으로 사용됩니다.

첫 번째 방법은 특정 디렉토리 또는 현재 디렉토리(DIRECTORY argument가 사용되지 않을때)로 부터 속성 값을 얻을 때 사용됩니다.

두 번째 방법은 변수 값을 검색하는데, 현재 디렉토리가 아닌 다른 디렉토리 scope(DIRECTORY argument가 사용되는 경우)에서 변수 값을 가져오는데 사용합니다. 하지만, 이 방법은 거의 필요하지 않으며, 빌드 디버깅 이외에서 사용을 피하는 것이 좋습니다. (자세한 이유는 잘 모르겠습니다 ㅠ.ㅠ)

 

마지막으로 get_directory_property() 커맨드에서 DIRECTORY argument가 사용되는 경우 명명된 디렉토리는 CMake에서 이미 처리되어 있어야 합니다. (CMake는 아직 만나지 못한 디렉토리 scope의 속성은 알 수 없습니다.)

 


Target Properties

Target 속성은 빌드에 아주 강력하고 직접적으로 영향을 끼치는 속성입니다. 소스 파일을 컴파일하는데 사용되는 플래그부터 빌드된 바이너리 및 중간 파일의 유형과 위치에 이르기까지 모든 것에 대한 정보를 제어하고 제공합니다. 일부 Target 속성은 개발자의 IDE 프로젝트에서 Target이 표시되는 방식에 영향을 미치는 반면, 다른 Target 속성은 컴파일/링크할 때 사용되는 도구에 영향을 줍니다. 간단히 말하자면, Target 속성은 실제로 소스 파일을 바이너리로 변환하는 방법에 대한 대부분의 세부 정보를 설정하고 적용하는 것입니다.

 

다른 속성과 마찬가지로 편의성을 위해 set_target_properties()get_target_property() 커맨드를 제공합니다.

set_directory_properties() 커맨드처럼, set_target_properties()도 set_property()보다 옵션은 적지만, 일반적으로 더 간편한 문법을 제공합니다. set_target_properties() 커맨드는 기존 속성 값에 추가하는 기능을 제공하지 않으며, 주어진 속성에 대해 리스트 값으로 전달하는 경우 해당 값을 문자열 형식으로 지정해야합니다.(ex, "this;is;a;list")

 

get_target_property() 커맨드는 get_property()의 단순화된 버전입니다. Target 속성의 값을 가져오는 간단한 방법을 제공하는데 중점을 두고 있으며, 기본적으로 get_property() 커맨드의 축약형 버전이라고 할 수 있습니다.

 

위의 두 커맨드 외에서 CMake에서는 Target 속성을 설정하는 여러가지 다른 커맨드들이 있습니다.

특히 'target_...()' 의 커맨드들은 CMake에서 중요한 부분이며, 거의 대부분의 프로젝트에서 이 커맨드를 일반적으로 사용합니다. 이 커맨드는 특정 Target에 대한 속성을 정의할 뿐만 아니라 다른 Target에 링크하는 방법도 정의합니다.

이번 글에서는 이러한 커맨드가 존재한다는 것만 체크하고, 이 후에 실제로 프로젝트를 구성해보면서 더 자세하게 알아보도록 하겠습니다.

 


Source Properties

CMake에서는 개별 소스 파일의 속성도 지원하는데, 이를 통해 모든 Target 소스가 아닌 파일별로 컴파일 플래그를 세밀하게 설정할 수 있습니다. 또한, CMake 또는 빌드 도구가 파일을 처리하는 방식을 설정하기 위해서 해당 소스 파일에 추가 정보를 설정할 수 있습니다. 예를 들어, 빌드의 일부로 생성되었는지 여부, 파일과 함께 사용할 컴파일러, 컴파일러가 아닌 도구 사용을 위한 옵션 등을 설정합니다.

 

마찬가지로 전용 커맨드 set_source_files_properties()get_source_file_property() 커맨드를 제공합니다. 

이는 위에서 설명한 다른 속성의 setter/getter 커맨드와 유사한 패턴을 갖습니다. 즉, APPEND나 APPEND_STRING 기능은 제공되지 않습니다.

소스 속성은 오직 같은 디렉토리 scope에 정의된 Target에만 보인다는 것에 주의해야합니다. 소스 속성의 설정이 다른 디렉토리 scope에서 이루어지는 경우 Target은 해당 속성의 변경을 체크하지 않으므로, 해당 소스 파일의 컴파일 등이 영향을 받지 않습니다. 또한, 하나의 소스 파일을 여러 대상으로 컴파일할 수 있으므로 설정되는 어떠한 소스 속성들도 이 소스 파일이 추가된 모든 Target에 적용되어야 의미가 있습니다.

 


속성(Property)과 속성을 다루는 커맨드에 대해서 알아봤는데, 아직 와닿지 않는 부분들이 많은 것 같습니다. 저 같은 경우 Target은 그나마 무엇을 의미하고 어떤 역할을 하는지 대강 감은 오지만, Property는 아직 감이 잘 안오는 것 같습니다. 그래도 교재 중반부부터 실제 프로젝트를 구성하면서 더 설명을 할 것으로 보이는데, 기초부터 차근차근 알아가보도록 하겠습니다.

'CMake' 카테고리의 다른 글

[CMake] Modules  (6) 2021.11.04
[CMake] Generator Expressions  (0) 2021.11.03
[CMake] Functions and Macros  (0) 2021.11.01
[CMake] add_subdirectory() 와 변수 Scope  (0) 2021.10.31
[CMake] Looping - foreach, while  (0) 2021.10.31

댓글