1개의 소스로 서로 다른 운영 체제에서 컴파일 및 실행되는 소프트웨어를 생성하길 원한다면, 각각의 플랫폼(OS)간의 특별한 속성을 파악해야 한다.
서로 다른 플랫폼(운영 체제) 간에는 미묘한 차이가 있다.
예를 들어 FreeBSD에서는 malloc.h를 사용하지 않아야 하고, 반면에 Linux에서 사용할 수 있다.
이러한 차이는 통상 config.h 라고 불리는 각 플랫폼의 특성을 구분짓는 여러개의 define문을 포함하는 헤더파일을 통해 처리된다.
#define HAVE_MALLOC_H 1
/* #undef HAVE_SYS_MNTTAB_H 1 */
/* #undef HAVE_SYS_MNTENT_H 1 */
#define HAVE_SYS_MOUNT_H 1
이 헤더파일은 아래 예제 처럼 소스의 맨 처음에 include 되어 처리 된다.
foo.c:
#include "config.h"
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#else
#include <stdlib.h>
#endif
void do_something()
{
void *buf=malloc(1024);
...
}
config.h의 내용은 소스가 컴파일 되는 플랫폼에 따라 달라야 하므로, 실제로 컴파일 되기 전에 헤더 파일이 생성되어야 한다.
Autotools 기반 소프트웨를 사용중이라면, make 전 단계에서 실행되는 ./configure 단계를 알고 있을 것이다.
./configure 스크립트는 시스템 내부 검사를 수행하고 수집 된 정보로부터 config.h 헤더 파일을 생성한다.
여기서는 CMake서도 ./configure를 사용하는 것과 똑같이 할 수 있는 방법에 대해 설명할 것이다.
또한 내장 명령에 있어서도, CMake는 module이라고 불리는 스크립트 파일을 사용해서 더 많은 명령을 지원한다.
이 파일들은 cmake 모듈 디렉토리에 있으며, UNIX 시스템의 경우 디폴트로 /usr/local/share/CMake/Modules 디렉토리 이다.
이러한 모듈의 명령을 사용하려면 먼저 CMakeLists.txt 파일 안에 include 를 해 줘야 한다.
CMake에는 시스템을 체크하기 위한 몇 가지 모듈이 포함되어 있다.
예를 들면 CHECK_INCLUDE_FILES :
INCLUDE (CheckIncludeFiles)
# usage: CHECK_INCLUDE_FILES (<header> <RESULT_VARIABLE> )
CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H)
CHECK_INCLUDE_FILES ("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
CMake 모듈 CheckIncludeFiles는 CHECK_INCLUDE_FILES() 명령을 사용할 수 있게 해 준다.
이 명령의 첫 번째 인수는 체크하려고 하는 헤더파일이다. 두 번째 인수는 결과를 돌려받을 변수이다.
주어진 헤더가 발견되면 두번째 인수의 변수가 1로 설정되고, 그렇지 않으면 빈 상태로 된다.
헤더를 사용하기 위해 다른 헤더가 필요한 경우 위의 예에서 보듯이 헤더들을 세미콜론으로 구분해서 나열해야 한다.
더 정확한 CHECK_INCLUDE_FILES()의 구현에 대해서는 /usr/local/share/CMake/Modules/CheckIncludeFiles.cmake 을 참고하면 된다.
지정된 헤더 파일에 대한 테스트 결과는 CMakeCache.txt 파일에 아래 예와 같이 저장되므로, 나중에 테스트가 성공했는지 여부를 확인하려는 경우
CMakeCache.txt 파일을 살펴보면 된다.
//Have include HAVE_MALLOC_H
HAVE_MALLOC_H:INTERNAL=1
결과가 캐시에 있으면 테스트가 다시 실행되지 않으므로, 다시 실행하려면 CMakeCache.txt 파일을 삭제하거나 다시 테스트 하려는 변수 항목을 삭제해야만 테스트가 다시 실행될 수 있다.
테스트가 실패한 경우 이유를 찾고 싶다면 CMakeFiles/CMakeError.log 파일을 열어서 헤더 이름 (또는 함수 등)을 찾으면 거기에 실패한 코드가 표시되어 있다.
이제 우리는 예를 들어 malloc.h가 존재하는 지 테스트 하고 cmake 변수 HAVE_MALLOC_H에 결과를 얻었으므로, 이제 헤더 config.h를 만들어야 한다.
이를 위해 cmake 명령 CONFIGURE_FILE()을 사용해야 한다.
이것은 소스 파일을 대상 파일로 복사하면서 원하는 부분을 수정해 주는 역할을 한다.
먼저 config.h.in이라는 이름의 소스 파일을 작성해야 한다. (이름은 아무래도 좋지만 autotools에서는 통상 config.h.in으로 사용한다) :
#cmakedefine HAVE_MALLOC_H 1
#cmakedefine HAVE_SYS_MOUNT_H
cmake가 실행되면 위 구문 중 #cmakedefine이 대체된다. HAVE_MALLOC_H 및 HAVE_SYS_MOUNT_H가 true이면 아래와 같은 config.h가 생성된다.
#define HAVE_MALLOC_H 1
#define HAVE_SYS_MOUNT_H
만약 2개 변수 모두 false 이면 아래와 같은 파일이 생성된다.
/* #undef HAVE_MALLOC_H */
/* #undef HAVE_SYS_MOUNT_H */
이 헤더를 소스 파일에 포함 시키고 #ifdef를 사용해서 속성을 확인할 수 있다.
이러한 체크를 최상위 CMakeLists.txt 만이 아니라 프로젝트의 모든 CMakeLists.txt에 추가할 수 있다.
구성된 헤더가 여러 개인 경우에는 그것들을 모두 config.h라고 부르면 include에 문제가 생길 수 있다.
각 서브 디렉트리에 따라 config.h, foo/ 디렉토리는 config-foo.h, bar/ 디렉토리는 config-bar.h 와 같은 이름을 지정하는 편이 좋다
시스템 검사를 수행하는 cmake의 다른 명령들은 다음과 같다.
Module: INCLUDE (CheckIncludeFiles)
Usage: CHECK_INCLUDE_FILES(headers variable)
Example: CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H)
지정된 헤더파일이 존재하는 지 여부를 판정.
Module: INCLUDE (CheckFunctionExists)
Usage: CHECK_FUNCTION_EXISTS(function variable)
Example: CHECK_FUNCTION_EXISTS(madvise HAVE_MADVISE)
지정된 함수가 존재하는 지 여부를 판정.
Module: INCLUDE (CheckSymbolExists)
Usage: CHECK_SYMBOL_EXISTS(symbol headers variable)
Example: CHECK_SYMBOL_EXISTS((LC_MESSAGES "locale.h" HAVE_LC_MESSAGES)
지정된 헤더가 존재하는 경우에, 지정된 기호가 존재하는 지 여부를 판정
"Symbol"은 Preprocessor 매크로 또는 주소를 사용할 수 있으며, Enumerate 형은 사용 될 수 없음
Module: INCLUDE (CheckLibraryExists)
Usage: CHECK_LIBRARY_EXISTS(library function location variable)
Example: CHECK_LIBRARY_EXISTS(volmgt volmgt_running "" HAVE_VOLMGT)
지정된 라이브러리가 존재하고 제공된 함수를 포함하는지 여부를 확인.
이것은 함수를 사용하는 작은 프로그램과 라이브러리에 대한 링크를 통해서 수행.
필요한 경우 추가 링크 디렉토리 위치 매개 변수(-Ldir)를 지정할 수 있다.
Module: INCLUDE (CheckTypeSize)
Usage: SET(CMAKE_EXTRA_INCLUDE_FILES header)
CHECK_TYPE_SIZE(type variable)
SET(CMAKE_EXTRA_INCLUDE_FILES)
Example: SET(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
CHECK_TYPE_SIZE("struct ucred" STRUCT_UCRED)
SET(CMAKE_EXTRA_INCLUDE_FILES)
지정된 타입이 존재하는 지 여부를 판정 해, 그 타입의 크기를 돌려줌.
변수 안에는 타입의 크기가 설정되며, 타입이 존재한다면 HAVE_STRUCT_UCRED는 true로 설정됨.
타입의 크기에는 관심이 없고, 단순히 타입이 존재하는 지 여부만 알고 싶다면 STRUCT_UCRED 를 직접 사용할 수 있다.
타입이 없을 경우 이 값은 FALSE로 평가된다.(HAVE_STRUCT_UCRED).
Module: INCLUDE (CheckPrototypeExists)
Usage: CHECK_PROTOTYPE_EXISTS(function headers variable)
Example: CHECK_PROTOTYPE_EXISTS(mkstemps "stdlib.h;unistd.h" HAVE_MKSTEMPS_PROTO)
헤더가 주어진 것에 대한 선언을 제공하는지 여부를 확인.
함수가 실제로 정의되었는 지 여부는 확인하지 않음.
Module: INCLUDE (CheckCXXSourceCompiles)
INCLUDE (CheckCSourceCompiles)
Usage: CHECK_CXX_SOURCE_COMPILES(source variable)
CHECK_C_SOURCE_COMPILES(source variable)
소스 코드가 컴파일되고 링크되는지 여부를 확인.
CMAKE_REQUIRED_LIBRARIES, CMAKE_REQUIRED_FLAGS 및
CMAKE_REQUIRE 를 설정할 수 있음