Суббота, 2017-11-25, 5:01 PM

Поиск
Меню
Категории раздела
Поддержи проект!
Рекомендуем


Сборка libgdx из исходного кода

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

Простой путь

Система сборки libgdx основана на Ant. Различные Ant скрипты отвечают за сборку разный частей libgdx.

Основной сценарий сборки называется build.xml и находиться в корневой директории libgdx репозитория. Он собирается дистрибутив libgdx, включая core API, back-end, все расширения и Javadoc. Чтобы построить дистрибутив выполните следующие команды в shell.

ant -f fetch.xml
ant
Этим самым загрузятся последние версии нативных библиотек (скомпилированный C/C++ коды для всех платформ) из сервера сборки, так что вам не нужно делать их сборку самостоятельно. Затем будет вызван основной сценарий для сборки всех Java частей.

Конечным результатом будет zip архив с названием libgdx-версия.zip и директория под названием dist, содержащая распакованный контент архива, который будет по сути таким же, как и тот, который можно загрузить с nightly сервера сборки, плюс изменения сделанные вами в исходниках.

Файл build.xml имеет target для каждого модуля. Каждая target настраивает несколько параметров (classpath, конечная директория и т.д.), которые затем передаются на вход build-template.xml файлу. Файл build-template.xml отвечает за компиляцию исходного Java кода, а также исходников нативного кода. Последнее делается вызовом Ant сценария, называемого build.xml и находящегося в jni директории модуля. Если хотите использовать данный метод для компилирования нативных кода, то это не удастся. Как скомпилировать нативные исходники смотрите информацию ниже.

Сложный путь

Существует несколько проблем при сборки исходников нативного кода libgdx:

  • C/C++ код может быть доступен из Java только через JNI, для которого есть специальное решение, называемое gdx-jnigen.
  • Нам нужно сборка для различных платформ, Windows (32/64 бит), Linux (32/64 бит), Mac OS X (32/64 бит), Android(arm6/arm7) и iOS(i386/arm7).
  • The Mac OS X и iOS сборки могут быть выполнены только на Mac.
  • В дополнение к компиляции нативного C/C++ нам нужно конвертировать различные jar файлы в .Net assemblies с помощью IKVM для iOS.
Давайте обсудим каждую часть, прежде чем погружаться в настройку цепи средств кросс-компиляции и посмотрим на процесс компиляции.

Jnigen

Jnigen — это расширение, которое позволяет поместить C/C++ код прямо в исходники Java кода. Это повышает локальность кода, которая концептуально относиться к друг другу (нативные методы Java класса и фактическая реализация) и делает рефакторинг кода намного проще по сравнению с обычным процессом JNI.

jnigen имеет две функции:

  • Проверять исходные Java файлы и определенной директории, обнаруживать нативные методы и прилагаемую C++ реализацию и отбрасывать исходные С++ файлы и заголовки, схожа как бы вы делали вручную с помощью JNI.
  • Обеспечивать генерацию Ant сценариев сборки, которые собирают нативные исходники для каждой платформы.

Генерация нативного кода

Вот пример из смешивания Java/C++ в один Java исходный файл понятный для jnigen.

private static native ByteBuffer newDisposableByteBuffer (int numBytes); /*
   char* ptr = (char*)malloc(numBytes);
   return env->NewDirectByteBuffer(ptr, numBytes);
*/


private native static void copyJni (float[] src, Buffer dst, int numFloats, int offset); /*
   memcpy(dst, src + offset, numFloats << 2 );
*/
Код C++ содержится в блоке комментария после описания нативного Java метода. Со стороны Java вводятся параметры, которые будут доступны для C++ кода по их Java именам, и если возможно преобразование для ограниченного подмножества типов. Происходит следующее преобразование:
  • Примитивные типы передаются как есть, использую jint, jshort, jboolean как их типы.
  • Одномерные массивы примитивных типов преобразуются в типизированные указатели с прямым доступом. Массивы автоматически блокируются и разблокируются через JNIEnv::GetPrimitiveArrayCritical и JNIEnv::ReleasePrimitiveArrayCritical.
  • Прямые буферы конвертируются в unsigned сhar* указатели через JNIEnv::GetDirectBufferAddress. Следует отметить, что позиция буфера не учитывается.
  • Любой другой тип будет передан с соответствующем ему JNI типом, например обычные Java объекты будут переданы как объекты.
Выше указанные два jnigen нативных метода будут транслированы в следующих C++ исходный код (будет соответствующий и header файл).
JNIEXPORT jobject JNICALL Java_com_badlogic_gdx_utils_BufferUtils_newDisposableByteBuffer(JNIEnv* env, jclass clazz, jint numBytes) {
//@line:334
   char* ptr = (char*)malloc(numBytes);
   return env->NewDirectByteBuffer(ptr, numBytes);
}

JNIEXPORT void JNICALL Java_com_badlogic_gdx_utils_BufferUtils_copyJni___3FLjava_nio_Buffer_2II(JNIEnv* env, jclass clazz, jfloatArray obj_src, jobject obj_dst, jint numFloats, jint offset) {
   unsigned char* dst = (unsigned char*)env->GetDirectBufferAddress(obj_dst);
   float* src = (float*)env->GetPrimitiveArrayCritical(obj_src, 0);


//@line:348
   memcpy(dst, src + offset, numFloats << 2 );

   env->ReleasePrimitiveArrayCritical(obj_src, src, 0);
}
Как вы можете видеть в в copyJni() преобразование вставляется в верхнюю и нижнюю часть метода автоматически. Если вы вернетесь из JNI метода в другое место, отличное от конца вашего метода, то jnigen обернет вашу функцию со второй функцией, которая делает все преобразование, подобно здесь: Перевод исходников Javaи C++.

Jnigen также выводит номера строк, говоря где в исходный Java файле появился C++. Это полезно, если собираем генерируемый jnigen C++ код, так как сценарий Ant не показывает ошибки с номерам строки в Java, на которую можно перейти нажав на строку в консоли.

Для того, чтобы позволить Jnigen пройтись по исходному коду и сгенерировать C/C++ заголовки и исходные файлы, выполните следующие действия:

new NativeCodeGenerator().generate("src", "bin", "jni", new String[] {"**/*"}, null);
Укажите директорию с исходниками, директорию содержащею скомпилированные .class файлы Java классов, файлы которые вы хотите включить и исключить. Смотрите NativeCodeGenerator для дополнительной информации.

Генерация сценария сборки

Как только были сгенерированны нативные файлы, хотеться создать сценарий сборки для всех поддерживаемых платформ. В настоящий момент включена поддержка Windows (32/64-бит), Linux (32/64-бит), Mac OS X (x86, 32/64-бит), Android (arm6/arm7) and iOS (i386, arm7). Генератор сценария сборки jnigen имеет шаблонный Ant сценарий, который может быть параметризован для каждой платформы. Параметры задаются через BuiltTarget. Можно создать BuiltTarget для конкретной платформы подобным образом:

BuildTarget linux32 = BuildTarget.newDefaultTarget(TargetOS.Linux, false);
Это создает target сборку по умолчанию для Linux 32-бит. Можно добавить к BuildTarget дополнительные параметры для конкретной платформы. Повторите этот процесс для других BuildTarget.

Как только все target настроены, поместите их вместе в BuildConfig. Укажите имя shared/static библиотеки, например gdx, которая будет в итоге gdx.dll в Windows, libgdx.so в Linux и Android, libgdx.dylib d Mac OS X и libgdx.a в iOS. Так же можно указать файлы будут в итоге и т.д. Самый простой способ использования конфигурации выглядит следующим образом:

BuildConfig config = new BuildConfig("gdx");
Когда все target и настройки находятся в одном месте, настает время генерации Ant сценария при помощи AntScriptGenerator.
new AntScriptGenerator().generate(config, linux32, linux64, windows32, windows64, macosx, android, ios)