У фреймворка 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.