[recovery mode] Детективная история с участием CMake 3.10 и Android Studio

geek.png

Disclaimer: всё описанное ниже не является хорошей практикой. Не следует читать этот текст как руководство к действию — его роль, скорее, развлекательная. По этой же причине, не имеет смысла советовать автору (мне) сменить язык, инструменты, ОС, железо, пол и страну пребывания.

У меня есть один проект. Для сборки он использует CMake, а также менеджер пакетов для C++ под названием Hunter, хорошо с CMake интегрированный. Проекту необходимо собираться для нескольких платформ, одна из которых — Android. Hunter собирает зависимости под Android без проблем —, но ему нужна версия CMake >= 3.7, поскольку именно в 3.7 была добавлена улучшенная поддержка этой платформы. Эта очень важная деталь.
CMake поддерживает генерацию проектов для Android под Windows/Visual Studio при помощи плагина NVIDIA Nsight Tegra. К сожалению, этот плагин не ставится на Visual Studio 2017, а ставить 2015ую ради одного проекта не хотелось. С другой стороны, в VS2017 есть поддержка сборки под Android —, но нет генерации таких проектов из CMake. Точнее, она есть, но требует самодельной версии CMake от Microsoft, разработка которой была заброшена в районе версии 3.4, то есть, мне она не подходит.

Хорошо, если не Visual Studio, то Android Studio! CMake, конечно, её не поддерживает, зато она поддерживает его, аж с версии 2.x. Указываешь путь к CMakeLists.txt в build.gradle, запускаешь синхронизацию, и всё должно работать. Но не работает, а говорит «У вас не установлен CMake». Почему —, а потому, что она работает тоже со своим собственным форком CMake, который надо качать из репозитория Android, и который застыл на версии 3.6.

Казалось бы тупик. Но на самом деле нет, потому что начиная с версии 3.0, Android Studio умеет работать с внешними версиями CMake старше 3.6, за счёт той самой «улучшенной поддержки Android». Достаточно прописать в local.properties поле cmake.dir, и всё должно работать. Но не работает, а говорит… Хм, вот честно говоря я уже забыл, что он там говорил, но суть в том, что текст ошибки, забитый в Google, привёл меня к разговору в баг-треккере, где упоминалось, что в версиях 3.7 и 3.8 CMake с Android Studio как-то неправильно общаются, и это всё должно быть исправлено в версии 3.9.

А у меня как раз стоит CMake 3.8. Ну что же, не беда, идём на сайт и качаем свежайший CMake, который имеет версию 3.10 (ну правда же 3.10 должна быть лучше, чем 3.9, или во всяком случае не хуже?). Устанавливаем, и всё должно работать. Но не работает, а говорит «Ошибка общения с CMake Server — смотрите логи». Логи же лаконично заявляют:

CMAKE SERVER: [== "CMake Server" ==[

CMAKE SERVER: {"supportedProtocolVersions":[{"isExperimental":true,"major":1,"minor":1}],"type":"hello"}

CMAKE SERVER: ]== "CMake Server" ==]


Как видите, решительно никаких ошибок. Кстати, да, а что вообще за CMake Server? Первый раз слышу. Идём в документацию, и читаем, что это такой новый режим работы CMake, который позволяет внешним программам получать информацию о CMake-проекте в машино-читаемом виде, что упрощает интеграцию с разными IDE и дополнительными инструментами.

Вот тут и начинается детектив. Первая улика — поля «major» и «minor» в ответе сервера. Раз есть версия протокола — её кто-нибудь может проверять, и не работать с «неправильными» версиями. Предположение очень вероятное, но лишь предположение — хорошо бы получить его подтверждение.

На самом деле в этот момент я решил сделать паузу и передать дело в полицию завести баг в баг-треккере Android Studio, как они сами предлагают в своей документации. К сожалению, реакции на баг не последовало, а через несколько дней он пропал из треккера — не был закрыт, пусть даже и с WONTFIX, а именно пропал без следа. Обозлившись, я решил довести собственное расследование до конца.

Возможно, среди читателей есть люди, активно работающие с Android Open Source Project, или хотя бы просто знакомые с его устройством. Пусть они посмеются над моими дальнейшими приключениями.

Мне надо было найти исходники плагина Android для Gradle, который и ответственен за интеграцию с CMake. Как обычный порядочный гражданин, я забил фразу «android gradle plugin source» в Google, и получил в ответ страницу репозитория. Однако, краткий поиск по ней при помощи веб-интерфейса, показал, что последний коммит в master был 2 года назад. А где же свежие сорцы? Не понятно!

Вторая ссылка из того же поиска привела меня на инструкцию «как скачать исходный код AOSP». Сделать это, надо сказать, не просто: у Google, как всегда, всё не как у людей, и нельзя просто выкачать конкретный проект из Git. Надо взять их собственный инструмент под названием repo, и использовать его.

Берём repo, убеждаемся, что это обычный скрипт на Питоне, запускаем, и получаем ошибку — не найден импорт fcntl. Да, этот скрипт не работает под Windows. Ура, я люблю тебя, Google. Запускаем случайно завалявшуюся в углу виртуальную машину с Ubuntu, скачиваем repo туда, запускаем, и, видя, что что-то происходит, уходим обедать.

По возвращение, меня встретила ошибка «no space left on device». Ну да, там его было 5Gb, но нужный мне проект никак не может столько весить, даже со всеми зависимостями! Однако, короткое расследование показало, что repo выкачивает весь, или почти весь репозиторий AOSP, и указать ему отдельный проект — нет совершенно никакой возможности. Частично, как я понимаю, это вина Git’a, а точнее — того, как его использует Google. Вместо того, чтобы засунуть каждый проект в свой репозиторий, они всё засунули в один. А Git, в отличие от SVN, не умеет выкачивать суб-директории репозитория от слова «совсем».

«Нет, — решил я, — выкачивать весь Андроид с цирком и конями ради одного-двух файлов, которые мне надо посмотреть, я не хочу.»

Интуиция подсказывала, что где-то всё-таки должны быть исходники нужной мне версии плагина. Но разум зашёл в тупик. Поэтому я решил поискать помощи на официальных ресурсах для разработчиков Android. Группу в Google+ отвергаем сразу — это не платформа для серьёзных разговоров. А вот старые добрые Mailing List’ы Google Groups — это то что нам надо! Их есть два — «просто Android (android-developers)» и «для тех кто NDK (android-ndk)». Моя проблема к NDK отношения, вроде бы, имеет мало, поэтому открываем первую ссылку… И получаем сообщение — «Эта группа рассылает malware, поэтому мы её закрыли нафиг. Подпись, Ваш Google.».

Сцена немого удивления.

Ладно, открываем вторую группу, для разработчиков NDK. Она рабочая. В поиск вбиваем слово CMake, надеясь зацепить что-нибудь интересное — и находим! Тема называется «Как указать Android Studio внешний CMake?». Её краткий обзор показывает, что это как раз то место, где обсуждалось изменение, приведшее к появлению в плагине версии 3.0.0. этой возможности. В тему был призван человек, что-то понимающий в плагине. Он дал автору темы ссылку на репозиторий (всё ту же бесполезную, где написано использовать repo), но самое главное — не утерпел, и скопировал в одно из писем часть кода из файла, указав его имя!

Этого мне было достаточно. Я забил имя в поиск Google, и тут же нашёл его в их репозитории. Правда, это был файл из ветки studio-2.2-preview3, то есть, более ранней, не имеющей интеграции с внешними версиями CMake.

Тут до меня дошло то, что должно было дойти гораздо раньше — два года назад разработчики плагина перестали коммитить в master, и стали всю разработку вести в ветках, никогда не заливая результаты в основную. Меня оправдывает лишь то, что я никогда не работал с Git в таком стиле, и вообще, ВСЕ проекты, которые я видел, обычно в конце таки мержили изменения в master. Впрочем, даже если бы я знал, что мне надо копаться в списке веток, не факт, чтобы я тогда нашёл бы нужную — их там сотни, дат создания не видно, а об имени правильной я понятия не имел.

Меняем прямо в URL имя ветки на studio-3.0, и… Находим, но не совсем то: в этом файле нет никаких упоминаний про версию протокола CMake Server. Но это уже не страшно, благо через веб-интерфейс, в отличие от repo, можно скачать в tgz отдельную папку репозитория, и в ней искать нужное.

Просмотр кода показал, что дело действительно было в версии протокола. Плагин сравнивал её на чёткое соответствие 1.0 (в функции getSupportedVersion), а свежий CMake использовал 1.1 (в следующей версии, кстати, уже будет 1.2). Из исследования кода CMake, я знал, что версия 1.1 не добавляет ничего принципиально ломающего, поэтому не было никаких препятствий к тому, чтобы Android Studio могла с ней работать, кроме этой дурацкой проверки. Поэтому, я нашёл у себя на диске файл gradle-core-3.0.0.jar, достал из него нужный класс, и при помощи Java Bytecode Editor, изменил проверку так, чтобы всё работало (disclaimer: не повторяйте дома (а там более на рабочем месте); трюк выполнен отчаявшимся и очень обозлённым программистом).

И всё заработало.

P.S. На самом деле не совсем: почему-то при сборке под Android, CMake использует некорректные относительные пути к исходным файлам, которые потом идут в объектные файлы, и в результате — в отладочную информацию внутри библиотеки. А отладчик потом по этим путям ничего найти не может, потому что библиотека лежит совсем не там. Но это уже другая, и гораздо более короткая история.

© Geektimes