Привет, Хабр!
В наших прошлой и позапрошлой статьях мы рассказывали о создании нашего инструмента для стеганографии и стеганоанализа ChameleonLab. Мы получили массу ценных отзывов, и один из самых частых запросов был: «А когда версия для macOS?». Что ж, по вашим многочисленным просьбам, мы сделали это — ChameleonLab теперь доступен для macOS!
Этот путь оказался не просто «взять и пересобрать». Нам пришлось столкнуться с рядом специфичных для macOS проблем, начиная от сборки .app
-пакетов и заканчивая борьбой с «замороженной» иконкой в Dock при запуске. В этой статье мы хотим поделиться нашим опытом, рассказать о сложностях переноса, показать конкретные решения в коде и заглянуть в будущее.

Наш технологический стек: почему Python?
С самого начала проект ChameleonLab разрабатывался на Python с использованием фреймворка PyQt6. Наш выбор был неслучайным. Python, благодаря своей гибкости и огромной экосистеме научных библиотек (NumPy
, SciPy
, Matplotlib
), идеально подошел для сложных вычислений в стеганоанализе. Но главным преимуществом стала его кросс-платформенность.
Именно эта возможность — писать код один раз и с минимальными изменениями адаптировать его под Windows, macOS, а в будущем и под другие системы — позволила нам изначально заложить правильную архитектуру и относительно безболезненно взяться за портирование.
Почему macOS и есть ли там жизнь без нас?
Первый вопрос, который мы себе задали: а нужен ли вообще наш инструмент на Mac? Разве нет аналогов?
Краткий анализ рынка показал интересную картину. Да, для macOS существуют утилиты для стеганографии, но большинство из них либо старые и давно не обновлялись, либо работают только из командной строки, либо являются проприетарными и не предлагают тех образовательных и аналитических возможностей, которые мы заложили в ChameleonLab. Мы увидели нишу для современного, наглядного и кросс-платформенного инструмента с открытым исходным кодом. К тому же, большая часть разработчиков и исследователей в области ИБ, которых мы знаем, использует именно Mac. Для нас это стало решающим фактором.
Главные сложности переноса: от .exe к .app
Переход с Windows на macOS — это не просто смена операционной системы, это смена философии сборки и дистрибуции.
1. Сборка и распространение: Прощай, version_info.txt
В Windows мы привыкли использовать .ico
для иконок и простой текстовый файл version_info.txt
для метаданных (версия, компания, копирайт). В macOS все иначе. Здесь правит бал пакет .app
и его внутренний файл Info.plist
.
Еще один сюрприз для Windows-разработчика: файл .app
, который создает PyInstaller, — это на самом деле не файл, а папка (или, как говорят в мире Apple, «пакет» — bundle). Вы не можете просто загрузить его на сайт и дать ссылку на скачивание. Веб-сервер отдаст ее как папку, и браузер пользователя попытается открыть ее и показать содержимое, а не скачать единым файлом. Поэтому перед загрузкой на сайт приложение обязательно нужно упаковать в архив (.zip
) или, что более правильно для macOS, в образ диска (.dmg
).
Пошаговое руководство: создаем кастомный .dmg-установщик
Создание качественного .dmg
— стандарт для дистрибуции под macOS. Процесс состоит из нескольких шагов, которые удобнее всего выполнять через Терминал для полного контроля.
Шаг 1: Подготовка ресурсов Сначала создаем временную папку, которая будет шаблоном для образа, и копируем в нее все необходимое.Bash
# Создаем папку на Рабочем столе
mkdir ~/Desktop/dmg_build
# Копируем наше приложение из папки сборки
cp -R /путь/к/проекту/dist/ChameleonLab.app ~/Desktop/dmg_build/
# Создаем символическую ссылку на системную папку "Программы"
ln -s /Applications ~/Desktop/dmg_build/Applications
Шаг 2: Создание временного образа диска На этом шаге создается пустой, но доступный для записи .dmg
файл.
# Создаем образ из нашей папки-шаблона
hdiutil create -volname "ChameleonLab Installer" -srcfolder ~/Desktop/dmg_build -fs HFS+ -size 500m ~/Desktop/ChameleonLab_temp.dmg
Шаг 3: Кастомизация внешнего вида Монтируем образ и с помощью AppleScript автоматически настраиваем внешний вид окна установщика: размер иконок, их расположение и отсутствие лишних элементов интерфейса.Bash
# Монтируем временный образ
hdiutil attach ~/Desktop/ChameleonLab_temp.dmg
# Запускаем скрипт для настройки окна Finder
osascript <<'END'
tell application "Finder"
tell disk "ChameleonLab Installer"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set the bounds of container window to {400, 100, 950, 450}
set viewOptions to the icon view options of container window
set arrangement of viewOptions to not arranged
set icon size of viewOptions to 128
set position of item "ChameleonLab.app" of container window to {150, 175}
set position of item "Applications" of container window to {400, 175}
close
open
update without registering applications
delay 2
close
end tell
end tell
END
Шаг 4: Финализация Отмонтируем настроенный образ и конвертируем его в финальный, сжатый .dmg
только для чтения, после чего удаляем временные файлы.Bash
# Отмонтируем диск
hdiutil detach /Volumes/"ChameleonLab Installer"
# Конвертируем в финальный сжатый образ
hdiutil convert ~/Desktop/ChameleonLab_temp.dmg -format UDZO -o ~/Desktop/ChameleonLab.dmg
# Удаляем временные файлы
rm ~/Desktop/ChameleonLab_temp.dmg
rm -rf ~/Desktop/dmg_build
После этих шагов на Рабочем столе у вас появится готовый ChameleonLab.dmg
, который можно загружать на сайт.

ChameleonLab Installer
2. Универсальные бинарники: Intel vs ARM
Сегодня нельзя просто собрать приложение под «macOS». Нужно учитывать, что существуют две архитектуры: старая x86_64
(Intel) и новая arm64
(Apple Silicon). Идеальное приложение должно быть универсальным (Universal 2), то есть содержать код для обеих архитектур.
К сожалению, PyInstaller не умеет делать кросс-компиляцию. Это значит, что для создания универсального бинарника нужно:
Собрать Intel-версию на Mac с процессором Intel.
Собрать ARM-версию на Mac с процессором ARM (M1/M2/M3).
Объединить их с помощью утилиты
pyi-bundle-universal2
.
Поскольку у нас в распоряжении пока только Intel-машина, мы остановились на сборке x86_64
версии. Она отлично работает на ARM-компьютерах через встроенный транслятор Rosetta 2, что является приемлемым компромиссом на данном этапе.
3. Код со сложностями: борьба с медленным запуском
Самой большой и неприятной проблемой стала огромная задержка при запуске. После клика иконка в Dock начинала прыгать, потом могла «зависнуть» на 10-15 секунд, и только потом появлялась заставка и окно программы. Это создавало впечатление, что приложение намертво зависло.
Решение — отложенный («ленивый») импорт. Мы перестроили наш главный файл main.py
так, чтобы при старте импортировался только сверхлегкий PyQt6
. Он тут же показывает заставку, а затем, с помощью QTimer
, запускает функцию, которая уже подгружает все тяжелые модули и создает главное окно.
Вот ключевая часть кода из main.py
, которая решила проблему:
# main.py
import sys
import os
# Импортируем ТОЛЬКО PyQt для старта
from PyQt6 import QtWidgets, QtGui, QtCore
# ... (прочий код) ...
class AppController(QtCore.QObject):
def __init__(self, app, splash):
# ...
def initialize_main_window(self):
"""Этот метод выполняется ПОСЛЕ того, как сплэш-скрин уже виден."""
# 1. ТЯЖЕЛЫЙ ИМПОРТ ПРОИСХОДИТ ЗДЕСЬ
import matplotlib
matplotlib.use('QtAgg') # <-- Еще одна специфика macOS
from ui_components import MainWindow
# 2. Создаем и показываем главное окно
self.window = MainWindow()
self.window.show()
# 3. Закрываем сплэш-скрин
if self.splash:
self.splash.finish(self.window)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
# Сразу же показываем сплэш-скрин
splash = QtWidgets.QSplashScreen(...)
splash.show()
# Создаем контроллер
controller = AppController(app, splash)
# Запускаем тяжелую инициализацию через 50 мс после старта
QtCore.QTimer.singleShot(50, controller.initialize_main_window)
sys.exit(app.exec())
Что дальше? Привет, Linux и Android!
Портирование на macOS стало для нас ценным опытом. Мы решили множество проблем и сделали приложение значительно стабильнее и быстрее на всех платформах.
Наши горизонты расширяются. Следующими логичными шагами для нас станут Linux и, как это ни амбициозно звучит, Android. Мы хотим, чтобы ChameleonLab стал по-настоящему универсальным инструментом, доступным на всех ключевых платформах. Адаптация под мобильную ОС потребует серьезной переработки интерфейса, но благодаря выбору Python и PyQt, основная бизнес-логика останется практически неизменной.
А пока — приглашаем всех пользователей macOS скачивать и тестировать ChameleonLab. Мы будем рады вашим отзывам, отчетам об ошибках и предложениям здесь, на Хабре, или в нашем Telegram-канале.
Спасибо за ваше внимание и поддержку!
Скачать последнюю версию на macOS: ChameleonLab 1.3.0.0
Скачать последнюю версию на Windows: ChameleonLab 1.3.0.0
Telegram-канал: t.me/ChameleonLab
Надеюсь, эта статья была для вас интересной. Спасибо за внимание! Буду рад ответить на вопросы в комментариях.
Demmidovich
Спасибо. Скачал сборку на Intel. Тестирую. Таких аналогов программ просто нет на macOS и Windows