Портирование Библиотеки C/C++ На Javascript (Xml.js)

Статья представляет собой обновленный перевод статьи « HOWTO: перенос библиотеки C/C++ на JavaScript (xml.js) " (автор: азакай ).

Автор оригинальной статьи имеет приличный опыт портирования библиотек C/C++ на JavaScript. В частности, он успешно портировал lzma.js И sql.js .

В своей статье он описывает общую схему портирования кода C/C++ на примере библиотека libxml – открытая библиотека для проверки XML. Кроме того, в этой статье представлена полная последовательность действий, которые потребовались для портирования libxml в среду Ubuntu 12.04. Включая необходимые настройки среды и эмскриптен .



Установка и настройка Эмскриптена

Emscripten — это компилятор байт-кода LLVM в JavaScript. Код C/C++ можно скомпилировать в байт-код LLVM с помощью компилятора.

лязг .

В некоторых других языках также есть компиляторы для байт-кода LLVM. Emscripten генерирует соответствующий код JavaScript на основе байт-кода, который может быть выполнен любым интерпретатором JavaScript, например современным браузером.

Недавно с помощью emscripten ребята из Mozilla успешно портировали Doom. Эмскриптен обеспечивает: emconfigure — утилита для настройки среды и последующего запуска .

/configure; сделать — утилита для настройки среды и последующего запуска make; эмкк – Компилятор LLVM в JavaScript; Итак, настроим среду для работы с emscripten (см.

управление ).

Установите clang+llvm(> =3.0):

wget llvm.org/releases/3.0/clang+llvm-3.0-i386-linux-Ubuntu-11_10.tar.gz tar xfv clang+llvm-3.0-i386-linux-Ubuntu-11_10.tar.gz
Установите node.js (> =0.5.5):
sudo apt-get установить nodejs
Загрузите текущую версию emscripten:
git-клон git://github.com/kripken/emscripten.git CD Эмскриптен
Проверяем работоспособность clang:
.

/clang+llvm-3.0-i386-linux-Ubuntu-11_10/bin/clangtests/hello_world.cpp .

/a.out > > здравствуй, мир!

Проверяем работоспособность node.js:
тесты узла/hello_world.js > > здравствуй, мир!
Запустите emcc в первый раз, чтобы создать файл конфигурации ' ~/.

emscripten ':

.

/эмкк

В файле конфигурации необходимо указать каталог clang+llvm, а также каталог установки emscripten:
EMSCRIPTEN_ROOT = os.path.expanduser(' ~/путь/эмскриптен ') # это помогает проектам, использующим emscripten, найти его LLVM_ROOT = os.path.expanduser(' ~/путь/clang+llvm-3.0-i386-linux-Ubuntu-11_10/bin ')
Вам нужно еще раз запустить emcc, чтобы убедиться, что он настроен правильно.

В этом случае отобразится сообщение « emcc: нет входных файлов ':

.

/эмкк > > emcc: нет входных файлов

Теперь вы можете проверить, что всё работает корректно, скомпилировав hello_wolrd.cpp с помощью emcc:
.

/emcc тесты/hello_world.cpp узел a.out.js > > привет, мир



Часть 1. Компиляция исходников C/C++

Прежде чем приступить к портированию, следует убедиться, что исходные коды проекта скомпилированы без ошибок компилятором C/C++.

Загрузите libxml из репозитория и скомпилируйте:

git-клон git://git.gnome.org/libxml2 компакт-диск libxml2 Git Checkout v2.7.8 CC=~/path/clang+llvm-3.0-i386-linux-Ubuntu-11_10/bin/clang .

/autogen.sh --without-debug --without-ftp --without-http --without-python -- без регулярных выражений --без потоков --без модулей делать

libxml включает консольную утилиту xmllint для проверки схем XML. Его можно использовать для проверки правильности работы скомпилированного кода.

Проводить такого рода проверки необходимо, в том числе, для того, чтобы убедиться, что оригинальная и портированная версии работают одинаково корректно.

Тестирование с помощью xmllint выглядит примерно так:

$.

/xmllint --noout --schema test.xsd test.xml > > test.xml проверяет

Если все работает правильно, внесите несколько изменений в файл test.xml, и тогда xmllint выведет сообщение об ошибке.



Часть 2: Конфигурация

Вы можете настроить проект для компиляции с помощью emscripten с помощью команды:
~/path/emscripten/emconfigure .

/autogen.sh --without-debug --without-ftp --without-http --without-python --without-regexps --without-threads --without-modules

emconfigure устанавливает переменные среды так, чтобы .

/configure использовал компилятор emcc вместо gcc или clang. Он настраивает среду так, чтобы .

/configure работал правильно, включая тесты конфигурации (которые компилируют собственный код).

Результаты конфигурации по умолчанию (без флагов) включают в себя множество функций, которые не нужны на данном этапе, например, поддержку HTTP и FTP. Мы просто хотим проверить XML-схемы, поэтому нам следует настроить проект, исключив ненужный функционал.

В целом, при портировании желательно исключить ненужный функционал.

Благодаря этому код будет меньше по размеру, что важно для сетевого окружения.

Кроме того, некоторые файлы заголовков могут потребовать редактирования вручную (те файлы, которые используют newlib, а не glibc).



Часть 3: Создание проекта

Сборка выполняется командой:
~/путь/emscripten/emmake make
emmake аналогичен emconfigure: он также устанавливает переменные среды.

Благодаря emmake во время сборки вместо собственного кода генерируется байт-код LLVM. Это сделано для того, чтобы избежать генерации кода JavaScript для каждого объектного файла и его последующего связывания.

Вместо этого он используется компоновщик Байт-код LLVM. В результате сборки создается множество различных файлов.

Но они не могут быть выполнены.

Как говорилось выше, это байт-код LLVM (можно посмотреть с помощью BC), поэтому нам нужен следующий шаг.



Часть 4. Преобразование в JavaScript

xmllint зависит от xmllint.o и libxml2.a. Компоновщик LLVM не поддерживает динамическое связывание (позднее связывание), и emcc игнорирует его.

Поэтому вам придется вручную указать статическую библиотеку libxml2.a для компоновки.

Чуть менее очевидна зависимость от библиотека (открытая библиотека сжатия).

Если вы создадите ссылку без libz.a, во время выполнения возникнет ошибка при попытке вызвать функцию «gzopen».

Соответственно, вам необходимо собрать libz.a:

компакт-диск ~/путь wget zlib.net/zlib-1.2.7.tar.gz tar xfv zlib-1.2.7.tar.gz компакт-диск zlib-1.2.7 ~/путь/emscripten/emconfigure .

/configure --static ~/путь/emscripten/emmake make

Теперь вы можете скомпилировать код JavaScript:
компакт-диск ~/путь/libxml2 ~/path/emscripten/emcc -O2 xmllint.o .

libs/libxml2.a .

/zlib-1.2.7/libz.a -o xmllint.test.js --embed-file test.xml --embed-file test.xsd

Где:
  • emcc – замена gcc или clang (см.

    выше);

  • -O2 – флаг оптимизации.

    LLVM и дополнительные оптимизации выполняются на уровне JavaScript, включая Closure Compiler (в расширенном режиме);

  • файлы, которые нужно связать;
  • -o — результирующий файл xmllint.test.js. Суффикс «js» указывает emcc формат сгенерированного кода, в данном случае JavaScript;
  • --embed-file – сообщает emcc включить содержимое указанного файла в сгенерированный код и настроить виртуальную файловую систему так, чтобы эти файлы были доступны через стандартные вызовы stdio (fopen, fread и т. д.).

    Это самый простой способ доступа к файлам из скомпилированного кода.



Часть 5. Тестирование JavaScript

Консоль JavaScript, предоставляемая Node.js, SpiderMonkey или V8, можно использовать для запуска этого кода:
узел xmllint.test.js --noout --schema test.xsd test.xml > > test.xml проверяет
Результат должен быть точно таким же, как в собственном коде.

Аналогично, если вы внесете ошибки в схему xml, xmllint должен их обнаружить.

Важный : все аргументы, используемые для встроенных сборок и сборок JavaScript, должны быть абсолютно одинаковыми.



Часть 6: Рефакторинг и повторное использование

На данный момент в скрипте есть два файла, жестко закодированные для проверки.

Нам нужна обобщенная функция для проверки любого XML-файла на соответствие схеме.

На самом деле сделать это несложно, хотя нужно учитывать, что код оптимизируется с помощью Closure Compiler, что добавляет работы.

Первое, что вам нужно сделать, это вызвать emcc с опцией --pre-js. Он добавляет код JavaScript перед сгенерированным кодом (post-js соответственно после).

Важно то, что --pre-js добавляет больше кода.

перед выполнением оптимизации.

Это означает, что код будет оптимизирован вместе с сгенерированным кодом, что необходимо для корректной оптимизации.

С другой стороны, оптимизатор Closure Compiler может отбросить нужные нам функции как неиспользуемые.

Вот скрипт, который необходимо включить с помощью опции --pre-js:

   Module['preRun'] = function() {
     FS.createDataFile(
       '/',
       'test.xml',
       Module['intArrayFromString'](Module['xml']),
       true,
       true);
     FS.createDataFile(
       '/',
       'test.xsd',
       Module['intArrayFromString'](Module['schema']),
       true,
       true);
   };
   Module['arguments'] = ['--noout', '--schema', 'test.xsd', 'test.xml'];
   Module['return'] = '';
   Module['print'] = function(text) {
     Module['return'] += text + '\n';
   };
Рассмотрим этот сценарий:
  • Модуль — это объект, посредством которого код, созданный с помощью emscripten, взаимодействует с другим кодом JavaScript.
  • Для доступа к модулю важно использовать строковые имена, например Module['name'] вместо Module.name. В этом случае Closure оставит имя без изменений.

  • Первое, что нужно сделать, это изменить Module.preRun, который запускается непосредственно перед сгенерированным кодом (но после настройки среды).

    Функция preRun создает два файла, используя API файловой системы ( Эмскриптен API файловой системы ).

    Для простоты используются те же имена файлов, что и в предыдущих тестах (test.xml и test.xsd).

    Содержимому этих файлов присвоены значения Module['xml'] и Module['xsd'].

    Эти переменные должны содержать XML и схему XML. Строки преобразуются в массив с помощью intArrayFromString.

  • Устанавливаем Module.arguments — эквивалент списка аргументов консольной команды.

    Аргументы должны быть точно такими же, как те, которые мы использовали ранее при тестировании.

    Единственное отличие состоит в том, что файлы test.xml и test.xsd будут содержать пользовательские данные.

  • Module.print вызывается, когда код пытается вызвать операцию из stdio. Мы сохраняем весь вывод в буфер, чтобы иметь возможность прочитать его позже.

Таким образом, мы добились того, что входные файлы test.xml и test.xsd будут содержать введенную пользователем информацию, а результаты проверки будут сохранены в буфере.

Однако это еще не все.

Давайте скомпилируем код:

~/path/emscripten/emcc -O2 xmllint.o .

libs/libxml2.a .

/zlib-1.2.7/libz.a -o xmllint.raw.js --pre-js pre.js

Команда для компиляции такая же, как и раньше, за исключением того, что нам больше не нужно включать файлы.

Вместо этого используйте флаг --pre-js, чтобы включить файл pre.js. После компиляции xmllint.raw.js содержит оптимизированный и минимизированный код. Для простоты использования обернем это функцией JavaScript:

   function validateXML(xml, schema) {
     var Module = {
       xml: xml,
       schema: schema
     };
     {{{ GENERATED_CODE }}}
     return Module.return;
   }
GENERATED_CODE необходимо заменить результатом компиляции (xmllint.raw.js).

Функция validateXML присваивает соответствующие аргументы полям xml и схеме.

Сделав это, мы гарантируем, что файлы test.xml и test.xsd содержат пользовательские данные.

После выполнения сгенерированного кода функция вернет результаты проверки.

Вот и все! xml.js можно использовать из обычного кода JavaScript. Все, что вам нужно, это просто подключить файл js и вызвать функцию validateXML с XML и схемой.

Теги: #emscipten #C++ #JavaScript #JavaScript #C++

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.