Para vincular um programa nativo do macOS, cada fatia de biblioteca selecionada deve corresponder à arquitetura da CPU e à plataforma Apple, além de ter uma ABI de linguagem compatível. Este guia mostra como compilar e inspecionar uma pequena biblioteca C usada por uma ferramenta de linha de comando para desktop escrita em C++.
A tarefa concreta é produzir um executável do macOS a partir do código-fonte local e diagnosticar uma dependência nativa incompatível. O exemplo usa operações aritméticas sintéticas com inteiros, não requer bibliotecas baixadas e mantém separados os resultados específicos de cada arquitetura.
O que precisa corresponder para que uma biblioteca nativa possa ser vinculada?
Verifique três aspectos de forma independente: arquitetura, plataforma e a interface binária entre linguagens. A especificação de destino do Clang inclui mais do que o processador; o destino e o ambiente influenciam a geração de código e a seleção de bibliotecas. As dependências também precisam ser compiladas para o destino pretendido. Compilação cruzada com Clang.
| Verificação | Pergunta para esta tarefa no macOS | O que significa uma incompatibilidade |
|---|---|---|
| Arquitetura da CPU | A dependência contém a fatia arm64 ou x86_64 que está sendo vinculada? |
Obtenha ou compile a arquitetura ausente. |
| Plataforma Apple | Essa fatia foi compilada para o macOS nativo? | Obtenha uma compilação para macOS; uma compilação para iOS ou para o Simulador é uma variante separada. |
| ABI da linguagem | As declarações, convenções de chamada e os ambientes de execução necessários são compatíveis? | Corrija a interface ou use o caminho de interoperabilidade compatível com a linguagem. |
Uma lista de arquiteturas não responde à pergunta sobre a plataforma. O formato Mach-O da Apple registra as informações da CPU separadamente das informações da plataforma para a qual o binário foi compilado e define identificadores de plataforma distintos para macOS, iOS, Mac Catalyst e Simulador do iOS. Portanto, uma dependência descrita apenas como arm64 precisa ser inspecionada mais a fundo. Definições do formato Mach-O da Apple.
Neste exemplo, o contrato de compilação pretendido é macOS nativo, destino de implantação macOS 13.0 e uma compilação por arquitetura. A biblioteca C expõe uma interface C; o executável usa C++17. A versão é uma escolha ilustrativa, não um requisito mínimo universal para software de Mac.
Compile um exemplo completo para macOS com destinos explícitos
Use um Mac com uma instalação atual do Xcode selecionada para o desenvolvimento pela linha de comando. A Apple documenta xcrun como o mecanismo para localizar ou executar ferramentas na instalação de desenvolvedor selecionada. Verifique a seleção antes de investigar alterações no código-fonte. Perguntas frequentes sobre compilação pela linha de comando com o Xcode.
Os comandos abaixo foram criados para o Bash no macOS. Eles foram inspecionados, mas não executados aqui, pois o ambiente de pesquisa é Linux e não tem as ferramentas da Apple.
xcode-select --print-path
xcrun --sdk macosx --find clang
xcrun --sdk macosx --find clang++
xcrun --sdk macosx --show-sdk-path
Se essas verificações falharem, resolva primeiro a seleção ou a instalação das ferramentas de desenvolvedor. A compilação precisa do SDK do macOS, além de um compilador. Um SDK fornece os cabeçalhos e as bibliotecas da plataforma usados na compilação do produto. Referência de configurações de compilação do Xcode: SDK base.
Crie os arquivos de código-fonte
Execute este bloco de configuração no Bash. Ele cria um novo diretório temporário e imprime sua localização, mantendo intactos os arquivos existentes do projeto. Continue com os blocos posteriores no mesmo shell.
set -euo pipefail
example_dir="$(mktemp -d "${TMPDIR:-/tmp}/mac-link-demo.XXXXXX")"
cd "$example_dir"
printf 'Example directory: %s\n' "$example_dir"
mkdir -p include src build/arm64 build/x86_64
cat > include/tile.h <<'EOF'
#ifndef TILE_H
#define TILE_H
#ifdef __cplusplus
extern "C" {
#endif
int tile_total(int rows, int columns);
#ifdef __cplusplus
}
#endif
#endif
EOF
cat > src/tile.c <<'EOF'
#include "tile.h"
int tile_total(int rows, int columns)
{
return rows * columns;
}
EOF
cat > src/main.cpp <<'EOF'
#include <cstdio>
#include "tile.h"
int main()
{
const int total = tile_total(3, 4);
std::printf("Tiles: %d\n", total);
return total == 12 ? 0 : 1;
}
EOF
O cabeçalho declara a vinculação da linguagem C quando uma unidade de tradução C++ o inclui. Isso impede que o chamador C++ solicite um nome de função C++ com uma decoração de nome diferente. O FAQ do C++ também recomenda deixar o compilador C++ conduzir a vinculação final para incluir as bibliotecas necessárias. FAQ do C++ padrão: como misturar C e C++.
Compile, arquive e vincule cada arquitetura
O bloco a seguir compila as duas variantes do macOS. Ele passa o mesmo destino e SDK para a compilação da biblioteca e a vinculação do executável de cada arquitetura. O Clang documenta -c como opção para produzir um arquivo objeto; sem uma opção de seleção de etapa, o driver prossegue até a vinculação. Guia de comandos do compilador Clang.
mac_sdk="$(xcrun --sdk macosx --show-sdk-path)"
for cpu in arm64 x86_64; do
mac_target="${cpu}-apple-macos13.0"
xcrun --sdk macosx clang \
-target "$mac_target" -isysroot "$mac_sdk" \
-std=c17 -Wall -Wextra -Werror \
-I include -c src/tile.c \
-o "build/$cpu/tile.o"
xcrun ar rcs "build/$cpu/libtile.a" "build/$cpu/tile.o"
xcrun --sdk macosx clang++ \
-target "$mac_target" -isysroot "$mac_sdk" \
-std=c++17 -stdlib=libc++ -Wall -Wextra -Werror \
-I include src/main.cpp "build/$cpu/libtile.a" \
-o "build/$cpu/tile-demo"
done
Aqui, -target escolhe a arquitetura e o destino de implantação do macOS, enquanto -isysroot fornece a raiz do SDK. São entradas distintas. O caminho explícito do arquivo de arquivo também deixa visível no comando qual dependência foi selecionada. Orientações do Clang sobre compilação cruzada, Referência de argumentos de linha de comando do Clang.
O comando ar rcs empacota o objeto compilado em um arquivo estático e fornece um índice de símbolos. O arquivamento empacota objetos existentes; não os compila para outro destino. O LLVM documenta as operações de arquivamento e sua compatibilidade com outras implementações de ar. Documentação do arquivador LLVM.
As entradas de exemplo 3 e 4 devem produzir Tiles: 12. Esta pequena função serve apenas como exemplo de vinculação; sua interface não define o comportamento para dimensões arbitrárias cujo produto exceda o intervalo de int.
Como inspecionar a arquitetura, a plataforma e os símbolos?
Inspecione o executável, a biblioteca ou o arquivo objeto em si. A Apple documenta lipo -archs para listar as arquiteturas e observa que o executável de um app fica dentro do diretório Contents/MacOS/ do pacote. Compilação de um binário universal para macOS.
xcrun lipo -archs build/arm64/tile-demo
xcrun lipo -archs build/x86_64/tile-demo
xcrun otool -l build/arm64/tile.o
xcrun otool -l build/arm64/tile-demo
xcrun nm -g build/arm64/libtile.a
Os dois primeiros comandos devem identificar suas respectivas arquiteturas. Para verificar a plataforma, inspecione os comandos de carregamento exibidos por otool -l; a ferramenta compatível do LLVM, llvm-otool, documenta a mesma opção. Ferramenta de exibição de Mach-O do LLVM.
Procure por LC_BUILD_VERSION. Seu campo platform deve identificar o macOS, e minos registra a versão mínima do sistema operacional. O campo sdk é separado. Alguns binários mais antigos usam, em vez disso, um comando LC_VERSION_MIN_*. As definições de formato da Apple documentam as duas representações, incluindo o identificador de plataforma 1 do macOS. Estruturas de versão de compilação do Mach-O.
No caso de uma biblioteca estática, inspecione os objetos membros em vez de tratar o invólucro do arquivo como prova de compatibilidade com a plataforma. Este exemplo mantém tile.o para esse fim. Para um arquivo desconhecido, use xcrun ar t para listar seus membros e inspecione-os em um diretório de trabalho separado. O LLVM documenta as operações de listagem e extração de arquivos. Documentação do arquivador LLVM.
Por fim, verifique os símbolos. A biblioteca de exemplo deve definir _tile_total no Mach-O. Na saída tradicional de nm, T indica um símbolo de código global e U indica uma referência não definida. Uma definição e uma referência são evidências diferentes. Documentação da tabela de símbolos do LLVM.
Use esta sequência de diagnóstico para escolher a próxima ação:
- Falta a fatia de CPU necessária: recompile a dependência para essa arquitetura ou obtenha o binário correspondente.
- A CPU corresponde, mas a plataforma é diferente: obtenha uma variante nativa para macOS. Alterar a arquitetura do chamador não comprova a compatibilidade da plataforma.
- A CPU e a plataforma correspondem, mas a função não é resolvida: compare a função declarada com o símbolo exportado pela biblioteca e confirme que o arquivo de arquivo pretendido aparece no comando de vinculação.
- Os nomes diferem entre C e C++: corrija a vinculação da linguagem no cabeçalho compartilhado e recompile o chamador.
- As versões de implantação diferem: inspecione a versão mínima da dependência e, se houver suporte, recompile-a para o destino de implantação pretendido, ou eleve deliberadamente o requisito do aplicativo.
Esta sequência é um auxílio à decisão derivado da documentação sobre destino, Mach-O e vinculação apresentada acima. Ela separa a falta de código de máquina de metadados de plataforma incompatíveis e de problemas na interface da linguagem, para que cada recompilação responda a uma constatação específica.
O que muda quando o Swift chama a biblioteca nativa?
Uma CPU e uma plataforma correspondentes não tornam toda interface de linguagem invocável. O Swift oferece interoperabilidade com C por padrão; é preciso habilitar a interoperabilidade direta com C++, que oferece suporte a um subconjunto documentado dos recursos de C++. O cabeçalho voltado para C neste exemplo oferece uma opção de interface compatível, desde que a compilação do Swift o importe por meio do mecanismo configurado de cabeçalho ou módulo. Misturar Swift e C++.
Para uma interface C++ direta, o Swift documenta -cxx-interoperability-mode=default para compilações pela linha de comando, módulos Clang para importar cabeçalhos e -Xcc para passar configurações do importador de C++. A configuração da importação e a vinculação da implementação compilada continuam sendo requisitos separados da compilação. Configuração de projetos Swift e C++ em linguagens mistas.
Respeite os limites documentados: a interoperabilidade com C++ requer Swift 5.9 ou posterior e C++14 ou posterior. O código misto Swift e C++ deve usar a mesma biblioteca padrão de C++; as plataformas Apple usam libc++. Chamar Swift a partir de C++ requer a interface gerada e um compilador Clang compatível. Os tipos C++ de referência importados também exigem macOS 13.3 como versão mínima de implantação. Antes de escolher esse caminho, confira a API específica na documentação de compatibilidade atual. Recursos compatíveis e restrições da interoperabilidade com C++.
Quando usar um binário universal ou um XCFramework?
Para este executável exclusivo do macOS, combine as duas compilações concluídas para macOS em um binário universal. A Apple documenta a compilação e a vinculação de cada arquitetura antes de combinar os executáveis resultantes com lipo. Compilação de um binário universal para macOS.
xcrun lipo -create \
build/arm64/tile-demo \
build/x86_64/tile-demo \
-output build/tile-demo
xcrun lipo -archs build/tile-demo
./build/tile-demo
Em um Mac compatível com macOS 13.0 ou posterior, o comando final deve imprimir Tiles: 12 e terminar com sucesso. Compilar as duas fatias é diferente de testar ambas: um Mac Intel não pode executar a fatia arm64. Orientações da Apple sobre binários universais.
Para uma biblioteca compatível com várias plataformas Apple ou variantes de plataforma, use um XCFramework para empacotar variantes compiladas separadamente. A Apple instrui explicitamente os desenvolvedores a manter separadas as bibliotecas estáticas do iOS e do Simulador do iOS, e permite incluir variantes nativas do macOS e do Mac Catalyst no mesmo XCFramework. Nos fluxos de trabalho de arquivamento do Xcode, a abordagem documentada usa um destino para selecionar a plataforma e o SDK. Criação de um pacote de framework binário multiplataforma.
Na próxima falha de vinculação no macOS, anote o destino pretendido, inspecione a arquitetura e a plataforma da dependência selecionada e depois verifique a interface da linguagem. Mantenha essas constatações junto ao comando exato de vinculação. Assim, você terá uma base concreta para selecionar o binário correto ou recompilá-lo a partir do código-fonte.
Fontes
Compilação cruzada com Clang — Projeto LLVM.
Perguntas frequentes sobre compilação pela linha de comando com o Xcode — Apple.
clang — o compilador Clang para C, C++ e Objective-C — Projeto LLVM.
Referência de argumentos de linha de comando do Clang — Projeto LLVM.
C++ padrão: como misturar C e C++ — Fundação Standard C++.
llvm-ar — arquivador LLVM — Projeto LLVM.
llvm-otool — ferramenta de exibição de Mach-O — Projeto LLVM.
llvm-nm — listar a tabela de símbolos de bitcode LLVM e arquivos objeto — Projeto LLVM.
Misturar Swift e C++ — Swift.org.
Configuração de projetos Swift e C++ em linguagens mistas — Swift.org.
Recursos compatíveis e restrições da interoperabilidade com C++ — Swift.org.
Criação de um pacote de framework binário multiplataforma — Apple.