Hoje lancei uma versão do RaidenVideoRipper para dispositivos Apple com macOS e processadores M1/M2/M3/M4 (Apple Silicon). RaidenVideoRipper é um aplicativo rápido de edição de vídeo que permite cortar parte de um arquivo de vídeo em um novo arquivo. Você também pode criar gifs e exportar a trilha de áudio para mp3.
A seguir, descreverei brevemente quais comandos usei para fazer isso. A teoria do que está acontecendo aqui, a documentação das concessionárias, pode ser lida nos seguintes links:
https://www.unix.com/man-page/osx/1/otool/
https://www.unix.com/man-page/osx/1/install_name_tool/
https://llvm.org/docs/CommandGuide/llvm-nm.html
https://linux.die.net/man/1/file
https://www.unix.com/man-page/osx/8/SPCTL/
https://linux.die.net/man/1/chmod
https://linux.die.net/man/1/ls
https://man7.org/linux/man-pages/man7/xattr.7.html
https://doc.qt.io/qt-6/macos-deployment.html
Primeiro, instale o Qt no seu macOS e também instale o ambiente de desenvolvimento Qt Desktop. Depois disso, construa seu projeto, por exemplo, no Qt Creator. Em seguida, descreverei o que é necessário para que as dependências com bibliotecas dinâmicas externas funcionem corretamente ao distribuir o aplicativo para usuários finais.
Crie um diretório Frameworks na pasta YOUR_APP.app/Contents do seu aplicativo e coloque dependências externas nele. Por exemplo, esta é a aparência dos Frameworks para o aplicativo RaidenVideoRipper:
Frameworks
├── DullahanFFmpeg.framework
│ ├── dullahan_ffmpeg.a
│ ├── libavcodec.60.dylib
│ ├── libavdevice.60.dylib
│ ├── libavfilter.9.dylib
│ ├── libavformat.60.dylib
│ ├── libavutil.58.dylib
│ ├── libpostproc.57.dylib
│ ├── libswresample.4.dylib
│ └── libswscale.7.dylib
├── QtCore.framework
│ ├── Headers -> Versions/Current/Headers
│ ├── QtCore -> Versions/Current/QtCore
│ ├── Resources -> Versions/Current/Resources
│ └── Versions
├── QtGui.framework
│ ├── Headers -> Versions/Current/Headers
│ ├── QtGui -> Versions/Current/QtGui
│ ├── Resources -> Versions/Current/Resources
│ └── Versions
├── QtMultimedia.framework
│ ├── Headers -> Versions/Current/Headers
│ ├── QtMultimedia -> Versions/Current/QtMultimedia
│ ├── Resources -> Versions/Current/Resources
│ └── Versions
├── QtMultimediaWidgets.framework
│ ├── Headers -> Versions/Current/Headers
│ ├── QtMultimediaWidgets -> Versions/Current/QtMultimediaWidgets
│ ├── Resources -> Versions/Current/Resources
│ └── Versions
├── QtNetwork.framework
│ ├── Headers -> Versions/Current/Headers
│ ├── QtNetwork -> Versions/Current/QtNetwork
│ ├── Resources -> Versions/Current/Resources
│ └── Versions
└── QtWidgets.framework
├── Headers -> Versions/Current/Headers
├── QtWidgets -> Versions/Current/QtWidgets
├── Resources -> Versions/Current/Resources
└── Versions
Para simplificar, imprimi apenas o segundo nível de aninhamento.
A seguir, imprimimos as dependências dinâmicas atuais da sua aplicação:
otool -L RaidenVideoRipper
Saída para o binário RaidenVideoRipper, localizado em RaidenVideoRipper.app/Contents/MacOS:
RaidenVideoRipper:
@rpath/DullahanFFmpeg.framework/dullahan_ffmpeg.a (compatibility version 0.0.0, current version 0.0.0)
@rpath/QtMultimediaWidgets.framework/Versions/A/QtMultimediaWidgets (compatibility version 6.0.0, current version 6.8.1)
@rpath/QtWidgets.framework/Versions/A/QtWidgets (compatibility version 6.0.0, current version 6.8.1)
@rpath/QtMultimedia.framework/Versions/A/QtMultimedia (compatibility version 6.0.0, current version 6.8.1)
@rpath/QtGui.framework/Versions/A/QtGui (compatibility version 6.0.0, current version 6.8.1)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2575.20.19)
/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 367.4.0)
@rpath/QtNetwork.framework/Versions/A/QtNetwork (compatibility version 6.0.0, current version 6.8.1)
@rpath/QtCore.framework/Versions/A/QtCore (compatibility version 6.0.0, current version 6.8.1)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers (compatibility version 1.0.0, current version 709.0.0)
/System/Library/Frameworks/AGL.framework/Versions/A/AGL (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1800.101.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)
Como pode ser visto no RaidenVideoRipper nas dependências Qt e Dullahan_ffmpeg. Dullahan FFmpeg é um fork do FFmpeg que encapsula sua funcionalidade em uma biblioteca dinâmica, com a capacidade de obter o progresso e cancelamento da execução atual usando rotinas C.
Em seguida, substitua os caminhos do aplicativo e todas as bibliotecas necessárias usando install_name_tool.
O comando para isso é:
install_name_tool -change old_path new_path target
Exemplo de uso:
install_name_tool -change /usr/local/lib/libavfilter.9.dylib @rpath/DullahanFFmpeg.framework/libavfilter.9.dylib dullahan_ffmpeg.a
Depois de inserir todos os caminhos corretos, o aplicativo deverá iniciar corretamente. Verifique se todos os caminhos para bibliotecas são relativos, transfira o binário e abra-o novamente.
Se você encontrar algum erro, verifique os caminhos via otool e altere novamente via install_name_tool.
Também ocorre um erro com confusão de dependências, quando a biblioteca que você substituiu não possui um símbolo na tabela você pode verificar a presença ou ausência de um símbolo assim:
nm -gU path
Uma vez executado, você verá toda a tabela de símbolos da biblioteca ou aplicativo.
Também é possível que você tenha copiado as dependências da arquitetura errada, você pode verificar isso usando o arquivo:
file path
O utilitário de arquivo mostrará a qual arquitetura a biblioteca ou aplicativo pertence.
O Qt também requer uma pasta Plugins na pasta Contents do seu diretório YOUR_APP.app, copie os plugins do Qt para Contents. A seguir, verifique a funcionalidade do aplicativo, após o qual você poderá começar a otimizar a pasta Plugins, excluindo elementos desta pasta e testando o aplicativo.
Segurança do macOS
Depois de copiar todas as dependências e corrigir os caminhos para vinculação dinâmica, você precisará assinar o aplicativo com a assinatura do desenvolvedor e, adicionalmente, enviar a versão do aplicativo à Apple para reconhecimento de firma.
Se você não tem US$ 100 para uma licença de desenvolvedor ou não deseja assinar nada, escreva instruções para seus usuários sobre como iniciar o aplicativo.
Esta instrução também funciona para RaidenVideoRipper:
- Desativar o Gatekeeper: spctl –master-disable
- Permitir inicialização de qualquer fonte em Privacidade e segurança: permitir que aplicativos mudem para qualquer lugar
- Remova o sinalizador de quarentena após fazer download de um aplicativo zip ou dmg: xattr -d com.apple.quarantine app.dmg
- Verifique se o sinalizador de quarentena (com.apple.quarantine) está faltando: ls -l@ app.dmg
- Adicione confirmação para iniciar o aplicativo, se necessário, em Privacidade e segurança
Um erro com o sinalizador de quarentena geralmente é reproduzido pelo erro “O aplicativo está corrompido” que aparece na tela do usuário. Nesse caso, você precisa remover o sinalizador de quarentena dos metadados.
Link para construir o RaidenVideoRipper para Apple Silicon:
https://github.com/demensdeum/RaidenVideoRipper/releases/download/1.0.1.0/RaidenVideoRipper-1.0.1.0.dmg