História de sucesso da transferência do ScreenPlay de QMake para CMake

ScreenPlay é um aplicativo de código aberto para Windows (e em breve também para Linux e macOS) projetado para funcionar com papéis de parede e widgets. Foi criado com ferramentas modernas (C ++ / Qt / QML) e está trabalhando ativamente nele desde o primeiro semestre de 2017. O código do projeto é armazenado na plataforma GitLab . O autor do artigo, cuja tradução publicamos hoje, está desenvolvendo o ScreenPlay. Ele enfrentou uma série de problemas, que foram ajudados pela transição de QMake para CMake.











QMake e desenvolvimento de grandes projetos



▍ O compartilhamento de código no QMake é muito inconveniente



Ao desenvolver aplicativos bastante complexos, geralmente é melhor dividi-los em partes pequenas e gerenciáveis. Por exemplo, se você deseja apresentar seu aplicativo como um arquivo executável principal ao qual as bibliotecas estão conectadas, então, usando QMake, você pode fazer isso apenas usando um projeto baseado em um modelo subdirs. Este é um projeto, representado por um arquivo, digamos com um nome MyApp.pro, que contém uma entrada para o modelo usado e uma lista de pastas do projeto:



  TEMPLATE = subdirs
 
  SUBDIRS = \
            src/app \   #  
            src/lib \
            src/lib2


Com essa abordagem, temos vários subprojetos à nossa disposição nos quais precisamos organizar o compartilhamento de código. Para informar o compilador sobre onde exatamente em outros projetos ele precisa procurar arquivos de cabeçalho e arquivos com código-fonte, você precisa informar ao vinculador quais bibliotecas ele precisa incluir e onde procurar por arquivos compilados. QMake faz isso criando enormes arquivos .pri que são usados ​​exclusivamente para descrever o que incluir no projeto. Isso é semelhante ao uso de construções de visualização C ++ regulares #include <xyz.h>. Como resultado, verifica-se que, por exemplo, o arquivo está MyProjectName.priincluído na composição MyProjectName.pro. E para corrigir o problema do caminho relativo, adicione o caminho absoluto atual a cada linha.



▍ Dependências externas



Trabalhar com dependências externas destinadas a vários sistemas operacionais se reduz principalmente a copiar os caminhos para as dependências correspondentes e colá-los em um arquivo .pro. Este é um trabalho enfadonho e tedioso, pois cada SO tem suas peculiaridades a esse respeito. Por exemplo, o Linux não possui subpastas debuge release.



▍CONFIG + = ordenado é um assassino de desempenho de compilação



Outra desvantagem do QMake é que ele tem problemas de compilação intermitente. Portanto, se um projeto tiver muitos subprojetos, que são bibliotecas usadas em outros subprojetos, a compilação falha periodicamente. O motivo do erro pode ser algo assim: a biblioteca libAdepende das bibliotecas libBe libC. Mas na hora da montagem, a libAbiblioteca libCainda não está pronta. Normalmente, o problema desaparece quando o projeto é recompilado. Mas o fato de isso estar acontecendo indica um problema sério com o QMake. E esses problemas não podem ser resolvidos usando algo comolibA.depends = libB... Provavelmente (e talvez seja verdade), estou fazendo algo errado, mas nem eu nem meus colegas conseguiram resolver o problema. A única maneira de resolver o problema com a ordem de construção das bibliotecas é usar uma customização CONFIG += ordered, mas por causa disso, ao eliminar a construção paralela, o desempenho sofre muito.



QBS e CMake



▍Por que o QBS está perdendo para o CMake?



A mensagem sobre o fim do suporte para QBS (Qt Build System, Qt build system) foi um verdadeiro choque para mim. Eu fui até um dos iniciadores de uma tentativa de mudar isso. QBS usa uma sintaxe agradável que é familiar para qualquer pessoa que já escreveu código QML. Não posso dizer o mesmo sobre o CMake, mas depois de trabalhar com este sistema de construção de projeto por vários meses, posso afirmar com segurança que mudar para ele do QBS foi a decisão certa e que continuarei a usar o CMake ...



CMake, embora tenha algumas falhas sintáticas, funciona de forma confiável. E os problemas da QBS são mais políticos do que técnicos.



Este é um dos principais fatores que força os programadores que estão insatisfeitos com o tamanho do Qt (tanto em termos do número de linhas de código quanto do tamanho da biblioteca) a procurar uma alternativa. Além disso, muitas pessoas não gostam muito de MOC. É um compilador de meta-objeto que converte código C ++ escrito usando Qt em C ++ normal. Graças a este compilador, você pode, por exemplo, usar construções convenientes, como aquelas que permitem que você trabalhe com sinais.



▍Alternativas para QBS



Além do QBS, temos à nossa disposição sistemas de construção de projeto como build2, CMake, Meson, SCons. Eles são, fora do ecossistema Qt, usados ​​em muitos projetos.



▍ Suporte insuficiente para QBS no IDE



Até onde eu sei, o único IDE que oferece suporte a QBS é o QtCreator.



▍ União brilhante de vcpkg e CMake



Lembra-se de como me ressenti dos problemas com dependências externas acima? Portanto, não é surpreendente quantas emoções positivas o gerenciador de pacotes vcpkg me deu. Um comando é suficiente para instalar a dependência! Acho que o vcpkg pode ser útil para qualquer programador C ++.



Sintaxe CMake aparentemente pouco atraente



Se você julgar o CMake pelos dez principais links encontrados pelo Google, pode parecer que este sistema usa uma sintaxe muito pouco atraente. Mas o problema aqui é que o Google é o primeiro a exibir coisas antigas do CMake do Stack Overflow, datado de 2008. Links para a documentação antiga da versão 2.8 do CMake também podem ser encontrados. A sintaxe usada ao trabalhar com CMake pode ser muito bonita. O fato é que o uso do CMake envolve principalmente o uso das construções mostradas abaixo (esta é uma versão abreviada do arquivo CMakeList.txt do projeto ScreenPlay).



#   
cmake_minimum_required(VERSION 3.16.0)

#   .       
#       ${PROJECT_NAME}
project(ScreenPlay)

#   Qt,    MOC
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOMOC ON)

#  -   .    src,
#   .       add_executable
set(src main.cpp
        app.cpp
        #  - 
        src/util.cpp
        src/create.cpp)

set(headers app.h
        src/globalvariables.h
        #   - 
        src/util.h
        src/create.h)

#  Qt     
qt5_add_big_resources(resources  resources.qrc)

#  CMake  qml  C++   release
#   !
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    set(qml qml.qrc)
else()
    qtquick_compiler_add_resources(qml qml.qrc )
endif()

#  CMake   .  ,   ,  CMAKE_TOOLCHAIN_FILE
#         !
find_package(
  Qt5
  COMPONENTS Quick
             QuickCompiler
             Widgets
             Gui
             WebEngine
  REQUIRED)

#   vcpkg
find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(libzippp CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)

#  CMake    : 
# add_executable    
# add_library   
add_executable(${PROJECT_NAME} ${src} ${headers} ${resources} ${qml})

#        Windows
# https://stackoverflow.com/questions/8249028/how-do-i-keep-my-qt-c-program-from-opening-a-console-in-windows
set_property(TARGET ${PROJECT_NAME} PROPERTY WIN32_EXECUTABLE true)

#     .    
#     vcpkg.      
# dll/lib/so/dynlib  vcpkg/installed
#      , 
#   project(MyLib)  target_link_libraries.
#        .
target_link_libraries(${PROJECT_NAME}
    PRIVATE
    Qt5::Quick
    Qt5::Gui
    Qt5::Widgets
    Qt5::Core
    Qt5::WebEngine
    nlohmann_json::nlohmann_json
    libzippp::libzippp
    ScreenPlaySDK
    QTBreakpadplugin)

#  CMake      build   ,   .
# ${CMAKE_BINARY_DIR} -   build!
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin/assets/fonts)
configure_file(assets/fonts/NotoSansCJKkr-Regular.otf ${CMAKE_BINARY_DIR}/bin/assets/fonts COPYONLY)


Ninja acelera CMake



A função do CMake é apenas gerar instruções para o sistema de construção do projeto escolhido pelo desenvolvedor. Isso pode ser uma grande vantagem ao trabalhar com pessoas que usam o Visual Studio em vez do Qt Creator. Ao usar o CMake, você pode (e deve) escolher o Ninja como seu sistema de compilação padrão. Compilar projetos usando o pacote CMake + Ninja é muito bom. Ambos podem ser encontrados na caixa de ferramentas Qt Maintenance. Entre outras coisas, essas ferramentas podem lidar com mudanças muito rapidamente em uma abordagem de desenvolvimento iterativa. Na verdade, tudo funciona tão rápido que quando uso Godot com SCons, eu realmente quero usar CMake aqui também.



Vcpkg permite que o CMake brilhe



Gerenciar dependências em projetos C ++ não é uma tarefa fácil. Para resolver isso, muitos projetos até colocam as DLLs necessárias em seus repositórios Git. E isso é ruim, já que aumenta desnecessariamente o tamanho dos repositórios (não tocamos no Git LFS aqui). A única desvantagem do vcpkg é que este gerenciador de pacotes suporta apenas uma versão global de um pacote (ou seja, você mesmo precisa instalar diferentes versões do vcpkg, mas isso é um pouco um hack e raramente é necessário). É verdade que nos planos de desenvolvimento do projeto você pode ver que ele está indo na direção certa.



Para instalar pacotes, use o seguinte comando:



vcpkg install crashpad


Enquanto trabalhava em ScreenPlay, nós simplesmente criou os install_dependencies_windows.bat e install_dependencies_linux_mac.sh roteiros para clonar o repositório vcpkg, construí-lo e instalar todas as nossas dependências. Ao trabalhar com o Qt Creator, você precisa escrever no CMAKE_TOOLCHAIN_FILEcaminho relativo ao vcpkg. Além disso, o vcpkg precisa ser informado sobre qual sistema operacional e qual arquitetura estamos usando.



    #  QtCreator. Extras -> Tools -> Kits ->  -> CMake Configuration.    :
    CMAKE_TOOLCHAIN_FILE:STRING=%{CurrentProject:Path}/Common/vcpkg/scripts/buildsystems/vcpkg.CMake
    VCPKG_TARGET_TRIPLET:STRING=x64-windows


Você precisa instalar alguma outra biblioteca? Para fazer isso, basta usar o comando do formulário vcpkg install myLibToInstall.



Resultado



A abordagem de usar o mais novo e mais popular tem suas vantagens. Mas o que fazer, por exemplo, quando construir sistemas com grande potencial como o QBS de repente se vêem à margem? Em última análise, o próprio desenvolvedor toma decisões sobre o que usar em seus projetos. Por isso decidi transferir meu projeto para o CMake. E, devo dizer, foi a decisão certa. Hoje, em 2020, o CMake parece muito bom.



Você está usando CMake e vcpkg?










All Articles