Diagnóstico e Correção de Falha de Linker QtCore em Projetos Rust-Qt no macOS

Contexto do Problema

O cenário envolve o desenvolvimento de uma aplicação que integra Qt/QML com Rust utilizando a ponte cxx-qt. O ambiente de build é composto por CMake e Cargo rodando em macOS (arquitetura ARM64). Durante a fase de linkagem do projeto, o processo de compilação interrompe com um erro crítico indicando que a biblioteca QtCore não pode ser localizada.

Análise do Log de Compilação

Ao inspecionar a saída do CMake, identifica-se a falha na etapa de linkagem via clang. O erro específico retornado pelo linker é:

ld: framework 'QtCore' not found
clang: error: linker command failed with exit code 1

Os logs indicam que o CMake já identificou o diretório das bibliotecas do Qt instalado via Homebrew:

-L /opt/homebrew/Cellar/qt@5/5.15.12_1/lib

Além disso, as flags de framework estão presentes no comando:

-framework QtCore -framework DiskArbitration -framework IOKit

Uma verificação no sistema de arquivos confirma que o framework existe na estrutura esperada:

tree -L 1 /opt/homebrew/Cellar/qt@5/5.15.12_1/lib | grep QtCore
# output: QtCore.framework

Isolamento do Caso de Teste

Para descartar problemas específicos da integração Rust, criou-se um arquivo C++ mínimo (test_qt_link.cpp) para validar a configuração do linker manualmente:

#include <QCoreApplication>
#include <iostream>

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    std::cout << "Verificando link do Qt..." << std::endl;
    return 0;
}

A tentativa de compilação direta com clang utilizando as mesmas flags extraídas do log falhou com um erro de cabeçalho não encontrado, mesmo com o path -L especificado:

/usr/bin/clang -arch arm64 -o test_qt_link test_qt_link.cpp \
  -L/opt/homebrew/Cellar/qt@5/5.15.12_1/lib \
  -framework QtCore -framework DiskArbitration -framework IOKit \
  -lc++

Investigação das Flags de Framework

Comparando com um projeto Qt gerado nativamente pelo Qt Creator que compila com sucesso, observou-se uma diferença crucial nos parâmetros passados ao compilador. O projeto funcional utiliza a flag -iframework para especificar o diretório de busca, em vez de apenas -L.

Consultando a documentação do clang, confirma-se que:

  • -L: Adiciona diretório ao path de busca de bibliotecas padrão.
  • -iframework: Adiciona diretório ao path de busca de sistemas framework (específico do macOS).
  • -F: Adiciona diretório ao path de include de frameworks.

Testes locais demonstraram que o uso de -F ou -iframework resolve a compilação do exemplo C++ isolado. O desafio então tornou-se passar essa configuração corretamente através da cadeia de build Rust-CMake.

Tentativas de Integração via CMake e Cargo

O projeto utiliza a crate corrosion para importar pacotes Rust dentro do fluxo do CMake. A configuração inicial no CMakeLists.txt era semelhante a:

set(RUST_TARGET rust_qt_bridge)
corrosion_import_crate(MANIFEST_PATH rust/Cargo.toml CRATES ${RUST_TARGET})
set(CXXQT_EXPORT_DIR "${CMAKE_CURRENT_BINARY_DIR}/cxxqt")
corrosion_set_env_vars(${RUST_TARGET}
    "CXXQT_EXPORT_DIR=${CXXQT_EXPORT_DIR}"
    "QMAKE=${QMAKE}"
)

Várias abordagens foram testadas para injetar as flags de framework sem sucesso inicial:

  1. Modificação direta nas variáveis de ambiente do CMake para o target Rust.
  2. Adição de rustflags no arquivo .cargo/config na raiz do projeto.
  3. Inserção de instruções de link no script build.rs do pacote Rust sem a especificação correta do tipo de busca.

Nenhuma dessas alterações refletiu no comando final invocado pelo clang durante o processo de build gerenciado pelo Cargo.

Solução Definitiva no Build Script

A documentação oficial do Cargo especifica instruções para scripts de build que controlam a linkagem. A instrução cargo:rustc-link-search permite adicionar diretórios ao path de busca, suportando um modificador KIND. Para frameworks no macOS, o kind deve ser explícito.

A correção foi implementada dentro do arquivo build.rs da crate que depende do Qt. Ao invés de depender apenas do CMake para passar as flags, o script de build do Rust instrui o compilador diretamente:

fn main() {
    // Define o caminho onde os frameworks do Qt estão localizados
    let qt_framework_path = "/opt/homebrew/Cellar/qt@5/5.15.12_1/lib";
    
    // Instrui o rustc a buscar frameworks neste diretório específico
    println!("cargo:rustc-link-search=framework={}", qt_framework_path);
}

Com essa instrução, o Cargo passa a flag correta para o linker, permitindo que o QtCore e demais módulos sejam resolvidos durante a etapa final de compilação.

Tags: Rust Qt5 CMake macOS linker

Publicado em 9-9 12:45