A05 - Compilação, vinculação e execução
Questão
Como você pode adicionar etapas personalizadas ao seu sistema de compilação com CMake?
Objetivos
Aprenda como e quando usar
execute_processAprenda a usar
add_custom_commandcom alvos.Aprenda a testar a compilação, a vinculação e a execução.
CMake permite que você execute comandos arbitrários em qualquer estágio no ciclo de vida do projeto. Este é mais um mecanismo de personalização e discutiremos algumas das opções neste tutorial.
Executando comandos personalizados
O método mais simples é explicitamente executar uma (ou mais)
processo(s) filhos ao invocar o cmake. Isso é feito com o
comando execute_process.
execute_process(COMMAND <cmd1> [args1...]]
[COMMAND <cmd2> [args2...] [...]]
[WORKING_DIRECTORY <directory>]
[TIMEOUT <seconds>]
[RESULT_VARIABLE <variable>]
[RESULTS_VARIABLE <variable>]
[OUTPUT_VARIABLE <variable>]
[ERROR_VARIABLE <variable>]
[INPUT_FILE <file>]
[OUTPUT_FILE <file>]
[ERROR_FILE <file>]
[OUTPUT_QUIET]
[ERROR_QUIET]
[OUTPUT_STRIP_TRAILING_WHITESPACE]
[ERROR_STRIP_TRAILING_WHITESPACE]
[ENCODING <name>])
Executa um ou mais processos. A saída padrão e erro padrão
são registrados nas variáveis OUTPUT_VARIABLE e ERROR_VARIABLE,
respectivamente. O resultado do último processo é salvo em
RESULT_VARIABLE.
É importante notar que qualquer comando invocado pelo execute_process
só será executado na execução do cmake.
Exercício 17: Encontre um módulo Python
Neste exercício, usaremos execute_process para verificar se o módulo Python cffi
está instalado em seu ambiente. Na linha de comando, você faria:
$ python -c "import cffi; print(cffi.__version__)"Seu objetivo é replicar o mesmo no CMake. O código base está em
source/code/day-1/17_find_cffi. Você terá que modificar a chamada deexecute_processpara executar o comando acima.Um exemplo funcional está na subpasta
solution.
Observe o uso de find_package(Python REQUIRED) para obter o
executável python. O CMake vem com muitos módulos dedicados à
detecção de dependências, como Python. Eles são convencionalmente
chamados de Find<dependency>.cmake e você pode inspecionar
sua documentação com:
$ cmake --help-module FindPython | more
Revisitaremos os usos de find_package mais tarde em A07 - Encontrando e usando dependências.
Comandos personalizados para seus alvos (targets)
Como mencionado, o principal problema de execute_process é que vai rodar
o processo somente quando o comando cmake é invocado pela primeira vez.
Portanto, não é uma alternativa viável se pretendermos realizar algumas
ações específicas dependendo dos alvos ou tornar o resultado dos comandos
personalizados uma dependência para outros alvos.
Ambos os casos têm exemplos do mundo real, como ao usar código gerado automaticamente.
O comando CMake add_custom_command pode ser usado em alguns desses casos.
add_custom_command(TARGET <target>
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL])
Adicione um ou mais comandos customizados a um destino, como uma biblioteca ou um executável.
Os comandos podem ser executados antes da vinculaçao (com PRE_BUILD e PRE_LINK)
ou depois (com POST_BUILD)
Exercício 18: Antes e depois da montagem
Queremos realizar alguma ação antes e depois de construir um alvo, neste caso um executável Fortran:
Antes de construir, queremos ler a linha de link, conforme produzida pelo CMake, e ecoá-la na saída padrão. Usamos o script Python
echo-file.py.Após a construção, queremos verificar o tamanho das alocações estáticas no binário, invocando o comando
size. Usamos o script Pythonstatic-size.py.
O código base está em sources/code/day-1/18_pre_post-f.
Adicione comandos do CMake para construir o executável
exampledas fontes Fortran. Encontre o arquivo de texto com a linha de link na pasta de compilação. Dica: dê uma olhada emCMakeFilese tenha em mente o nome que você deu ao alvo.Chame
add_custom_commandcomPRE_LINKpara invocar o script Pythonecho-file.py.Chame
add_custom_commandcomPOST_BUILDpara invocar o script Pythonstatic-size.py.
Um exemplo funcional está na subpasta solution.
Testar a compilação, vinculação e execução
Também queremos poder executar verificações em nossos compiladores e vinculadores. Ou verificar se uma determinada biblioteca pode ser usada corretamente antes de tentar construir nossos próprios artefatos. O CMake fornece módulos e comandos para estes fins:
Check<LANG>CompilerFlagfornece a funçãocheck_<LANG>_compiler_flag, paraverificar se um sinalizador do compilador é válido para o compilador em uso.
Check<LANG>SourceCompilesfornece a funçãocheck_<LANG>_source_compilesa qual verificase um determinado arquivo de origem compila com o compilador em uso.
Check<LANG>SourceRunsfornece a funçãocheck_<LANG>_source_runsque certifica seum determinado trecho de origem compila, vincula e executa.
Em todos os casos, <LANG> pode ser um de CXX, C ou Fortran.
Exercício 19: Verifique se um compilador aceita uma flag de compilador
Os compiladores evoluem: eles adicionam e/ou removem sinalizadores e às vezes você terá que testar se alguns sinalizadores estão disponíveis antes de usá-los em sua compilação.
O código base está em
source/code/day-1/19_check_compiler_flag.
Implemente um
CMakeLists.txtpara construir um executável a partir do arquivo fonteasan-example.cpp.Verifique se os sinalizadores de limpeza de endereço estão disponíveis com
check_cxx_compiler_flag. As flags a serem verificadas são-fsanitize=address -fno-omit-frame-pointer. Encontre a assinatura do comando com:$ cmake --help-module CMakeCXXCompilerFlagSe as flags funcionarem, adicione-os aos usados para compilar o destino executável com
target_compile_options.
Um exemplo funcional está na subpasta solution.
Exercício 20: Testando recursos de tempo de execução
Testar se alguns recursos funcionarão corretamente para seu código requer não apenas compilar um arquivo de objeto, mas também vincular um executável e executá-lo com êxito.
O código base está em source/code/day-1/20_check_source_runs.
Crie um destino executável a partir do arquivo fonte
use-uuid.cpp.Adicione uma verificação de que a vinculação à biblioteca produz executáveis funcionais. Use o seguinte código C como teste:
#include <uuid/uuid.h> int main(int argc, char * argv[]) { uuid_t uuid; uuid_generate(uuid); return 0; }
check_c_source_runsrequer que o código-fonte de teste seja passado comouma string. Encontre a assinatura do comando com:
$ cmake --help-module CheckCSourceRuns
#.Se o teste for bem-sucedido, vincule o destino executável à biblioteca UUID: use o destino PkgConfig::UUID como argumento para target_link_libraries.
Um exemplo funcional está na subpasta solution.
Resumo
Você pode personalizar o sistema de compilação executando comandos personalizados.
O CMake oferece comandos para testar a compilação, vinculação e execução.