У фреймворка Android есть небольшой инструмент под названием validatekeymaps. Это утилита для проверки синтаксиса конфигурационных файлов устройств ввода (*.kl, *.kcm, *.idc или virtualkeys.*). В документации предлагается собрать её самостоятельно: скачать исходники Android, установить необходимую систему сборки и т.д. И это в почти 2023. Неужели нельзя сделать это в вэбе, чтоб без заморочек и удобно для пользователя? Спойлер: можно. Этим и займёмся.

Подробнее про validatekeymaps

В Android для переопределения клавиш или добавления новых раскладок используются различные конфигурационные файлы. Такие конфигурационные файлы либо нужно заливать на устройство с доступом к root, либо ставить приложение с дополнительными раскладками, если доступа к root нет. Подробнее про это можно почитать в статье Кастомизируем раскладку внешней клавиатуры на Android без root. Удобнее сразу проверить, что файл конфигурации правильный, чем обнаружить это уже после заливки/установки. Для этого и существует утилита validatekeymaps. Ей в качестве аргументов передаются файлы (*.kl, *.kcm, *.idc или virtualkeys.*), синтаксис которых она проверяет на правильность.

Собираем нативно

Скачивать исходники Android и ставить специфичные системы сборки мне не хотелось, поэтому я тупо скачал только те файлы, которые необходимы для компиляции, чтобы это не пришлось делать вам (на самом деле нет, т.к. проверял я только под MacOS). Взглянуть на них можно в директории platform проекта. Это заняло некоторое время, зато теперь можно собрать программу простым вызовом компилятора.

Т.к. я не настоящий сварщик разработчик C/C++, а времени на раскачку не было, сборку осуществил такой командой (Look ma, no hands make):

clang++ -O2 -o validatekeymaps -std=c++17 platform/**/*.cc platform/**/*.cpp \
  -isystem platform/external/fmtlib/include \
  -isystem platform/frameworks/native/include \
  -isystem platform/frameworks/native/libs/gui/include \
  -isystem platform/frameworks/native/libs/math/include \
  -isystem platform/frameworks/native/libs/ui/include \
  -isystem platform/libnativehelper/include_jni \
  -isystem platform/system/core/libcutils/include \
  -isystem platform/system/core/libsystem/include \
  -isystem platform/system/core/libutils/include \
  -isystem platform/system/libbase/include \
  -isystem platform/system/logging/liblog/include

И да, тут ещё возникают проблемы с globbing, но всё это не важно, т.к. дальше мы скомпилируем это в WASM, запустим в браузере и заживём долго и счастливо.

Собираем для вэба

Начнём с конца. Финальный результат доступен здесь. Там есть поля ввода типа файла, содержимого файла, кнопка проверки и поле вывода работы программы. Теперь перейдём к тому, как это сделать.

Для компиляции в JS/WASM будем использовать Emscripten. Это набор инструментов для компиляции C/C++ проектов или LLVM в JavaScript/WebAssembly. В документации утверждается, что можно просто взять и заменить вызов gcc на emcc, но на практике не всё так просто.

Во-первых, пришлось пропатчить некоторые файлы в исходниках Android, чтобы компиляция прошла успешно. Патч можно посмотреть здесь. Ничего особо интересного, просто авторы Android не предполагали компиляции в WASM.

Во-вторых, в исходниках есть #include <sys/cdefs.h>, а Emscripten таких заголовочных файлов не поставляет. Пришлось добавить минимальную необходимую реализацию самому.

В-третьих, Emscripten может компилировать в WASM, JS или HTML. Обычная замена компилятора на emcc выдаст нам на выходе .wasm файл, а его не так-то просто использовать. Компиляция в HTML выдаст .html, .js и .wasm которые позволят запустить программу в браузере, но нам нужно запустить программу несколько раз, с разными аргументами и разными файлами. В итоге команда для компиляции с помощью emcc выглядит так:

emcc -O2 -o validatekeymaps.js -std=c++17 platform/**/*.cc platform/**/*.cpp \
  -isystem platform/external/fmtlib/include \
  -isystem platform/frameworks/native/include \
  -isystem platform/frameworks/native/libs/gui/include \
  -isystem platform/frameworks/native/libs/math/include \
  -isystem platform/frameworks/native/libs/ui/include \
  -isystem platform/libnativehelper/include_jni \
  -isystem platform/system/core/libcutils/include \
  -isystem platform/system/core/libsystem/include \
  -isystem platform/system/core/libutils/include \
  -isystem platform/system/libbase/include \
  -isystem platform/system/logging/liblog/include \
  -isystem emscripten/include \
  -sENVIRONMENT=web \
  -sINVOKE_RUN=0 \
  -sEXPORTED_RUNTIME_METHODS=callMain,FS

Разберём по частям что изменилось:

  • Замена компилятора на emcc.

  • -o validatekeymaps.js - нужно для компиляции в JS (на выходе .js и .wasm).

  • -isystem emscripten/include - тут лежит наш самописный sys/cdefs.h.

  • -sENVIRONMENT=web - нас интересует только вэб. Код для запуска, например, на Node.js нам не нужен.

  • -sINVOKE_RUN=0 - отключаем вызов main при запуске.

  • -sEXPORTED_RUNTIME_METHODS=callMain,FS - нам понадобятся вызов main и доступ к файловой системе.

Дело за малым - использовать полученный validatekeymaps.js. Код всей страницы доступен здесь. Часть кода для вызова main:

Module.FS.writeFile(filename, content);
const result = Module.callMain([filename]);

Всё достаточно просто: создаём файл, который будем проверять и передаём его имя в качестве аргумента в списке аргументов.

Итог

Портировать нативную утилиту в вэб оказалось весьма просто. Результат доступен тут. Исходники проекта тут.

P.S.: проект собран "на коленке", но выполняет поставленные перед ним задачи. Если, на ваш взгляд, что-то можно улучшить, жду PR на GitHub.

Комментарии (0)