CMake
Introdução
editarO nome CMake é uma abreviação de "cross plataform make”, ou seja, é um sistema multiplataforma que gerencia e automatiza o processo de “build". O CMake, além disso, é um software gratuito e open-source, cujo desenvolvimento iniciou em 1999, patrocinado pela Biblioteca Nacional de Medicina dos Estados Unidos.
O CMake é comparado ao “make utility", no sentido de que ambos providenciam um sistema de “build" que podem ser utilizados para compilar programas. Porém, o CMake é mais recomendado para projetos mais complexos e multiplataformas. O CMake inclusive gera automaticamente um Makefile para o projeto e também pode ser utilizado em conjunto de diversas IDEs como Microsoft Visual Studio e Xcode.
É importante notar que o CMAKE pode gerar um ambiente nativo de compilação que pode ser utilizado não só para construir executáveis, mas também a criação de bibliotecas
A definição de como será realizado o processo de build é definida por um arquivo de configuração chamado “CMakeLists.txt” composto por comandos da forma “COMMAND (args…)“.
CMake vs Make
editarMake é um sistema de compilação. Ele direciona o seu compilador e outras ferramentas de compilação para criar seu código compilado. [1]
CMake é um gerador de sistema de compilação. Pode produzir Makefiles, Ninja Build, KDEvelop ou XCode, Visual Studio. Tudo vindo do mesmo ponto de início, ou melhor, do mesmo "CMakeLists.txt". Então se um projeto é independente da plataforma (Multiplataforma), o CMake é uma maneira de torna-lo independente do sistema também. [2]
Instalação
editarWindows
editarPara instalar o CMake no windows basta utilizar o instalador disponível no site do CMake. Este pode ser encontrado através do link abaixo:
Ubunto/Debian e derivados
editar- sudo apt-get install cmake
Fedora
editar- sudo yum install cmake
Mac OS
editar- Realize o Download do arquivo de instalação ".dmg" disponível no site do CMake. Link abaixo:
- Execute o arquivo baixado, ou seja, clique nele duas vezes.
- Instale o CMake, ou seja, arraste o CMake para a pasta Applications.
- Execute o CMake.
- Adicione CMake aos paths.
- No menu "Tools" selecione "How to Install For Command Line Use". Observe o caminho indicado na caixa de diálogo que aparecerá. Por exemplo, "/Applications/CMake.app/Contents/bin/cmake-gui".
Abra o terminal e digite:
- sudo mkdir -p /usr/local/bin
- sudo /Applications/CMake.app/Contents/bin/cmake-gui --install=/usr/local/bin
Para verificar se tudo ocorreu corretamente digite:
- cmake --version
Utilizando o CMake
editarConfiguração Mínima para Compilar
editarTodo CMakeLists.txt deve conter no mínimo:
- A especificação da versão do CMake:
cmake_minimum_required(''<VERSION NUMBER>'')
- O nome do projeto:
project(hello)
- Alguma ação a ser executada.
Indicar o que será construido pelo CMake, pode também indicar em qual subpasta que estarão os arquivos a serem utilizados.
Exemplos simples de CMakeLists.txt
editar- Aplicação
cmake_minimum_required(VERSION 2.8)
project(hello)
add_executable(Hello hello.cpp)
- Biblioteca
cmake_minimum_required(VERSION 2.8)
project(hello)
add_library(Hello SHARED hello.cpp)
- Shared Object
cmake_minimum_required(VERSION 2.8)
project(hello)
add_library(Hello OBJECT hello.cpp)
Gerando o Makefile a partir do CMakeLists.txt
editarBasta utilizar o comando
cmake <Caminho para o CMakeLists.txt>
Exemplos da Utilização do CMake
editarExemplo 1 - Gerando simples executável
editarSuponha o seguinte arquivo "hello.cpp" e o seguinte arquivo "CMakeLists.txt":
#include <stdio.h>
int main() {
printf("Hello World!\n");
return 0;
}
cmake_minimum_required(VERSION 2.8)
project(hello)
add_executable(Hello hello.cpp)
Se os arquivos "hello.cpp" e "CMakeLists.txt" estivessem em um mesmo diretório, bastaria estar nele e executar o comando
cmake .
Dessa forma, um arquivo Makefile seria gerado, então para obtermos o programa executável, bastaria executar o comando
make
Então o executável Hello seria gerado.
Exemplo 2 - Gerando um executável a partir de dois Object Files
editarSuponha o seguintes arquivos:
- "helloFunc.cpp"
#include <stdio.h>
void myHello(char * msg) {
printf("%s", msg);
}
- "helloUse.cpp"
void myHello( char * msg);
int main () {
myHello("Hello World!\n");
}
- "CMakeLists.txt"
cmake_minimum_required(VERSION 2.8)
project(hello)
add_executable(Hello helloFunc.cpp helloUse.cpp)
Se os arquivos "hello.cpp" e "CMakeLists.txt" estivessem em um mesmo diretório, bastaria estar nele e executar o comando
cmake .
Dessa forma, um arquivo Makefile seria gerado, então para obtermos o programa executável, bastaria executar o comando
make
Então o executável Hello seria gerado.
É interessante notar que o CMake identifica que object files devem ser gerados e posteriormente linkados sem que isso seja especificado.
- Comparação entre o CMAKE e o Makefile para esse exemplo:
Para atingir esse objetivo com o Makefile o seguinte código seria necessário:
hello: helloFunc.o helloUse.o
gcc helloFunc.o helloUse.o -o hello
helloFunc.o: helloFunc.c
gcc -c helloFunc.c
helloUse.o: helloUse.c
gcc -c helloUse.c
Observação
editarEm ambos os exemplos estamos executando o comando cmake no diretório onde os nossos arquivos ("*.cpp" e "CMakeLists.txt") se encontram. Isso não é recomendado pois o cmake gera alguns arquivos e pastas inúteis para nós. Para evitarmos que esses tipos de arquivos se misturem com o conteúdo do nosso projeto, criamos uma nova pasta, normalmente chamada "build" e rodamos o cmake indicando em qual pasta está o "CMakeLists.txt"(nesse exemplo está junto com os arquivos *.cpp). Para realizar o que foi dito acima, basta executar os seguintes comandos:
mkdir build
cd build
cmake ..
Dessa forma, os arquivos relacionados ao processo de build ficam separados do restante dos arquivos.
Exemplo 3
editarÉ bastante comum criarmos uma estrutura em que na pasta raiz do projeto colocamos o arquivo "CMakeLists.txt" e criamos uma pasta "src" e outra pasta "build".
O arquivo "CMakeLists.txt" na pasta raiz do projeto pode conter o seguinte código:
cmake_minimum_required(VERSION 2.8)
project(hello)
add_subdirectory( src )
Dessa forma, quando executarmos o comando cmake, ele procurará o conteúdo necessário dentro da src, indicada na linha 3, para realizar a build.
Dentro da pasta src, podemos adicionar um outro "CMakeLists.txt" que especificará como a build será feita.
set(CMAKE_C_FLAGS "-Wall")
file( GLOB SRCS *.c *.h)
add_executable(hello ${SRCS} )
Neste caso, a primeira linha indica quais flags de compilação serão utilizadas. A linha 2 é responsável por procurar todos os arquivos com extensão ".c" e ".h" e associá-los a variável SRCS, que está sendo utilizada na linha 3. A linha 3 diz para montar o executável "hello" utilizando os arquivos relacionados a SRCS.
Outros Comandos
editarAlguns comandos além dos citados
- set
Manualmente adicionar sources.
set (SRC_DIR "${ROOT}/src")
- message
Mostra uma mensagem na tela.
message( STATUS "mensagem")
- file
file( GLOB SRCS ${SRC_DIR}/*.cpp).
Adiciona a SRCS todos os arquivos terminados com cpp, permitido operações com ele posteriormente.
- option
Possibilita habilitar e desabilitar opções pela linha de comando. Por exemplo:
//CMakeLists.txt
option(MyOption "MyOption" OFF)
- Opção para MyOption.
//Command line
cmake -DMyOption=ON MyProjectFolder
- Setar MyOption para ON.
- include_directories
Adiciona os headers presentes na pasta indicada para o ambiente de Build.
include_directories(include)
- target_link_libraries
target_link_libraries (${PROJ_NAME} ${TE_LIBRARIES})
O exemplo representa que o projeto PROJ_NAME precisa ser linkado com a biblioteca TE_LIBRARIES.
- list
Operações com listas. Opções:
- list(LENGTH <list> <output variable>)
- list(GET <list> <element index> [<element index> ...] <output variable>)
- list(APPEND <list> [<element> ...])
- list(FIND <list> <value> <output variable>)
- list(INSERT <list> <element_index> <element> [<element> ...])
- list(REMOVE_ITEM <list> <value> [<value> ...])
- list(REMOVE_AT <list> <index> [<index> ...])
- list(REMOVE_DUPLICATES <list>)
- list(REVERSE <list>)
- list(SORT <list>)
- foreach
Executa um grupo de comandos para cada elemento em uma lista.
foreach(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endforeach(loop_var)