É notoriamente difícil obter informações úteis sobre o CMake como um iniciante. Até agora, vi alguns tutoriais sobre como configurar um ou outro projeto muito básico. No entanto, nenhum deles explica o raciocínio por trás de tudo o que é mostrado neles, sempre deixando muitos buracos para preencher.
O que chama CMake em um CMakeLists significa ? É suposto ser chamado uma vez por árvore de construção ou o quê? Como faço para usar configurações diferentes para cada compilação se todos eles usam o mesmo arquivo CMakeLists.txt da mesma fonte? Por que cada subdiretório precisa de seu próprio arquivo CMakeLists? Faria sentido usar o CMake em um CMakeLists.txt diferente daquele na raiz do projeto? Em caso afirmativo, em que casos? Qual é a diferença entre especificar como construir um executável ou biblioteca do arquivo CMakeLists em seu próprio subdiretório e fazer isso no arquivo CMakeLists na raiz de todas as fontes? Posso fazer um projeto para Eclipse e outro para Visual Studio, mudando apenas a -G
opção ao chamar o CMake? É assim mesmo que é usado?
Nenhum dos tutoriais, páginas de documentação ou perguntas / respostas que vi até agora fornecem qualquer visão útil para a compreensão de como usar o CMake. Os exemplos simplesmente não são completos. Não importa quais tutoriais eu leio, sinto que estou perdendo algo importante.
Existem muitas perguntas feitas por novatos do CMake como eu que não perguntam isso explicitamente, mas tornam óbvio o fato de que, como novatos, não temos ideia de como lidar com o CMake ou o que fazer com ele.
Respostas:
Para que serve o CMake?
De acordo com a Wikipedia:
Com CMake, você não precisa mais manter configurações separadas específicas para seu ambiente de compilação / construção. Você tem uma configuração que funciona para muitos ambientes.
CMake pode gerar uma solução Microsoft Visual Studio, um projeto Eclipse ou um labirinto Makefile a partir dos mesmos arquivos sem alterar nada neles.
Dado um monte de diretórios com código neles, CMake gerencia todas as dependências, pedidos de construção e outras tarefas que seu projeto precisa ser feito antes de poder ser compilado. Na verdade, ele NÃO compila nada. Para usar o CMake, você deve dizer a ele (usando arquivos de configuração chamados CMakeLists.txt) quais executáveis você precisa compilar, a quais bibliotecas eles se vinculam, quais diretórios existem em seu projeto e o que está dentro deles, bem como quaisquer detalhes como sinalizadores ou qualquer outra coisa que você precise (CMake é bastante poderoso).
Se estiver configurado corretamente, você usa o CMake para criar todos os arquivos que seu "ambiente de construção nativo" de escolha precisa para fazer seu trabalho. No Linux, por padrão, isso significa Makefiles. Assim, uma vez que você execute o CMake, ele criará vários arquivos para seu próprio uso, além de alguns programas
Makefile
. Tudo o que você precisa fazer depois disso é digitar "make" no console a partir da pasta raiz toda vez que terminar de editar seu código e, bam, um executável compilado e vinculado é feito.Como funciona o CMake? O que isso faz?
Aqui está um exemplo de configuração de projeto que usarei ao longo de:
O conteúdo de cada arquivo é mostrado e discutido posteriormente.
CMake configura seu projeto de acordo com a raiz
CMakeLists.txt
do seu projeto, e o faz em qualquer diretório que você executoucmake
no console. Fazer isso de uma pasta que não é a raiz de seu projeto produz o que é chamado de compilação fora do código-fonte , o que significa que os arquivos criados durante a compilação (arquivos obj, arquivos lib, executáveis, você sabe) serão colocados nessa pasta , mantido separado do código real. Ajuda a reduzir a desordem e também é preferido por outros motivos, que não discutirei.Não sei o que acontece se você executar
cmake
em qualquer outro que não seja o rootCMakeLists.txt
.Neste exemplo, como quero que tudo seja colocado dentro da
build/
pasta, primeiro tenho que navegar até lá e depois passar ao CMake o diretório no qualCMakeLists.txt
reside a raiz .Por padrão, isso configura tudo usando Makefiles como eu disse. Esta é a aparência da pasta de construção agora:
O que são todos esses arquivos? A única coisa com que você precisa se preocupar é o Makefile e as pastas do projeto .
Observe as pastas
src/
elib/
. Eles foram criados porquesimple/CMakeLists.txt
apontam para eles usando o comandoadd_subdirectory(<folder>)
. Este comando diz ao CMake para procurar outroCMakeLists.txt
arquivo na referida pasta e executar esse script, de modo que cada subdiretório adicionado desta forma deve ter umCMakeLists.txt
arquivo dentro. Neste projeto,simple/src/CMakeLists.txt
descreve como construir o executável real esimple/lib/CMakeLists.txt
descreve como construir a biblioteca. Cada destino que umCMakeLists.txt
descreve será colocado por padrão em seu subdiretório dentro da árvore de construção. Então, depois de um rápidono console feito de
build/
, alguns arquivos são adicionados:O projeto está construído e o executável está pronto para ser executado. O que você faz se quiser que os executáveis sejam colocados em uma pasta específica? Defina a variável CMake apropriada ou altere as propriedades de um destino específico . Mais sobre variáveis CMake posteriormente.
Como posso dizer ao CMake como construir meu projeto?
Aqui está o conteúdo, explicado, de cada arquivo no diretório de origem:
simple/CMakeLists.txt
:A versão mínima necessária deve sempre ser definida, de acordo com o aviso que o CMake lança quando você não o faz. Use qualquer que seja a sua versão do CMake.
O nome do seu projeto pode ser usado posteriormente e indica o fato de que você pode gerenciar mais de um projeto a partir dos mesmos arquivos CMake. Não vou me aprofundar nisso, no entanto.
Como mencionado antes,
add_subdirectory()
adiciona uma pasta ao projeto, o que significa que o CMake espera que ele tenha umCMakeLists.txt
dentro, que será executado antes de continuar. A propósito, se acontecer de você ter uma função CMake definida, você pode usá-la de outrosCMakeLists.txt
s em subdiretórios, mas você tem que defini-la antes de usaradd_subdirectory()
ou ela não a encontrará. O CMake é mais inteligente com relação a bibliotecas, portanto, esta é provavelmente a única vez em que você encontrará esse tipo de problema.simple/lib/CMakeLists.txt
:Para fazer sua própria biblioteca, você dá um nome a ela e lista todos os arquivos a partir dos quais ela foi construída. Direto. Se fosse necessário outro arquivo
foo.cxx
,, para ser compilado, você deveria escreveradd_library(TestLib TestLib.cxx foo.cxx)
. Isso também funciona para arquivos em outros diretórios, por exemploadd_library(TestLib TestLib.cxx ${CMAKE_SOURCE_DIR}/foo.cxx)
. Mais sobre a variável CMAKE_SOURCE_DIR posteriormente.Outra coisa que você pode fazer com isso é especificar que deseja uma biblioteca compartilhada. O exemplo:
add_library(TestLib SHARED TestLib.cxx)
. Não tenha medo, é aqui que CMake começa a tornar sua vida mais fácil. Quer seja compartilhada ou não, agora tudo que você precisa fazer para usar uma biblioteca criada dessa maneira é o nome que você deu a ela aqui. O nome desta biblioteca agora é TestLib e você pode fazer referência a ela de qualquer lugar no projeto. CMake vai encontrar.Existe uma maneira melhor de listar dependências? Definitivamente sim . Verifique abaixo para mais informações sobre isso.
simple/lib/TestLib.cxx
:simple/lib/TestLib.h
:simple/src/CMakeLists.txt
:O comando
add_executable()
funciona exatamente da mesma formaadd_library()
, exceto, é claro, que irá gerar um executável. Este executável agora pode ser referenciado como um destino para coisas comotarget_link_libraries()
. Como tutorial.cxx usa o código encontrado na biblioteca TestLib, você aponta isso para o CMake conforme mostrado.Da mesma forma, qualquer arquivo .h # incluído por qualquer fonte
add_executable()
que não esteja no mesmo diretório da fonte deve ser adicionado de alguma forma. Se não fosse pelotarget_include_directories()
comando,lib/TestLib.h
não seria encontrado ao compilar o Tutorial, então alib/
pasta inteira é adicionada aos diretórios de inclusão a serem pesquisados por #includes. Você também pode ver o comandoinclude_directories()
que age de maneira semelhante, exceto que não precisa que você especifique um destino, já que o define globalmente, para todos os executáveis. Mais uma vez, explicarei CMAKE_SOURCE_DIR mais tarde.simple/src/tutorial.cxx
:Observe como o arquivo "TestLib.h" está incluído. Não há necessidade de incluir o caminho completo: CMake cuida de tudo isso nos bastidores graças a
target_include_directories()
.Tecnicamente falando, em uma árvore de código-fonte simples como essa você pode fazer sem o
CMakeLists.txt
s soblib/
esrc/
e apenas adicionar algo semelhanteadd_executable(Tutorial src/tutorial.cxx)
asimple/CMakeLists.txt
. Depende de você e das necessidades do seu projeto.O que mais devo saber para usar corretamente o CMake?
(Tópicos também relevantes para a sua compreensão)
Encontrar e usar pacotes : a resposta a esta pergunta explica isso melhor do que eu jamais poderia.
Declarando variáveis e funções, usando controle de fluxo, etc .: confira este tutorial que explica o básico do que o CMake tem a oferecer, além de ser uma boa introdução em geral.
Variáveis CMake : existem muitas, então o que se segue é um curso intensivo para colocá-lo no caminho certo. O wiki CMake é um bom lugar para obter informações mais detalhadas sobre variáveis e, aparentemente, outras coisas também.
Você pode querer editar algumas variáveis sem reconstruir a árvore de construção. Use ccmake para isso (edite o
CMakeCache.txt
arquivo). Lembre-se dec
configurar quando terminar as mudanças e, em seguida,g
gerar makefiles com a configuração atualizada.Leia o tutorial mencionado anteriormente para aprender sobre o uso de variáveis, mas é uma longa história curta:
set(<variable name> value)
para alterar ou criar uma variável.${<variable name>}
para usá-lo.CMAKE_SOURCE_DIR
: O diretório raiz da fonte. No exemplo anterior, isso é sempre igual a/simple
CMAKE_BINARY_DIR
: O diretório raiz da construção. No exemplo anterior, isso é igual asimple/build/
, mas se você executoucmake simple/
de uma pasta comofoo/bar/etc/
, todas as referências aCMAKE_BINARY_DIR
nessa árvore de construção se tornariam/foo/bar/etc
.CMAKE_CURRENT_SOURCE_DIR
: O diretório no qual a correnteCMakeLists.txt
está. Isso significa que ela muda: imprimindo a partir dassimple/CMakeLists.txt
safras/simple
e imprimindo a partir dassimple/src/CMakeLists.txt
safras/simple/src
.CMAKE_CURRENT_BINARY_DIR
: Você entendeu a ideia. Esse caminho dependeria não apenas da pasta em que o build está, mas tambémCMakeLists.txt
da localização do script atual .Por que isso é importante? Os arquivos fonte obviamente não estarão na árvore de construção. Se você tentar algo como
target_include_directories(Tutorial PUBLIC ../lib)
no exemplo anterior, esse caminho será relativo à árvore de construção, ou seja, será como escrever${CMAKE_BINARY_DIR}/lib
, que olhará para dentrosimple/build/lib/
. Não há arquivos .h lá; no máximo você encontrarálibTestLib.a
. Você quer em${CMAKE_SOURCE_DIR}/lib
vez disso.CMAKE_CXX_FLAGS
: Sinalizadores para passar para o compilador, neste caso o compilador C ++. Também vale a pena observarCMAKE_CXX_FLAGS_DEBUG
qual será usado seCMAKE_BUILD_TYPE
estiver definido como DEBUG. Existem mais como estes; verifique o wiki CMake .CMAKE_RUNTIME_OUTPUT_DIRECTORY
: Diga ao CMake onde colocar todos os executáveis quando compilados. Esta é uma configuração global. Você pode, por exemplo, configurá-lobin/
e ter tudo bem colocado lá.EXECUTABLE_OUTPUT_PATH
é semelhante, mas obsoleto, caso você tropece nele.CMAKE_LIBRARY_OUTPUT_DIRECTORY
: Da mesma forma, uma configuração global para dizer ao CMake onde colocar todos os arquivos de biblioteca.Propriedades do destino : você pode definir propriedades que afetam apenas um destino, seja um executável ou uma biblioteca (ou um arquivo ... essa é a ideia). Aqui está um bom exemplo de como usá-lo (com
set_target_properties()
.Existe uma maneira fácil de adicionar fontes a um destino automaticamente? Use GLOB para listar tudo em um determinado diretório sob a mesma variável. A sintaxe de exemplo é
FILE(GLOB <variable name> <directory>/*.cxx)
.Você pode especificar diferentes tipos de construção? Sim, embora eu não tenha certeza de como isso funciona ou das limitações disso. Provavelmente requer algum if / then'ning, mas o CMake oferece algum suporte básico sem configurar nada, como padrões para o
CMAKE_CXX_FLAGS_DEBUG
, por exemplo. Você pode definir seu tipo de construção de dentro doCMakeLists.txt
arquivo viaset(CMAKE_BUILD_TYPE <type>)
ou chamando CMake do console com os sinalizadores apropriados, por exemplocmake -DCMAKE_BUILD_TYPE=Debug
.Algum bom exemplo de projetos que usam CMake? A Wikipedia tem uma lista de projetos de código aberto que usam CMake, se você quiser dar uma olhada nisso. Os tutoriais online não têm sido nada além de uma decepção para mim a esse respeito, no entanto, esta questão do Stack Overflow tem uma configuração de CMake muito legal e fácil de entender. Vale a pena dar uma olhada.
Usando variáveis do CMake em seu código : Aqui está um exemplo rápido e sujo (adaptado de algum outro tutorial ):
simple/CMakeLists.txt
:simple/TutorialConfig.h.in
:O arquivo resultante gerado pelo CMake,
simple/src/TutorialConfig.h
:Com o uso inteligente deles, você pode fazer coisas legais como desligar uma biblioteca e tal. Eu recomendo dar uma olhada nesse tutorial, pois há algumas coisas um pouco mais avançadas que serão muito úteis em projetos maiores, mais cedo ou mais tarde.
Para todo o resto, Stack Overflow está repleto de perguntas específicas e respostas concisas, o que é ótimo para todos, exceto para os não iniciados.
fonte
make
no bash para, bem, fazer seu projeto.