Skip to content

Build and CI

Владислав edited this page Sep 5, 2026 · 1 revision

Сборка и CI

Собрать порт можно четырьмя путями, и они не равноценны: один кроссплатформенный и главный, три остальных - короткие дороги под конкретную машину. Плюс два скрипта, которые игру не собирают вообще, а проверяют.

CMake - основной путь

CMakeLists.txt - то, чем пользуется и CI, и любой сторонний сборщик:

set_target_properties(jumpyball PROPERTIES C_STANDARD 90 C_EXTENSIONS ON)

Стандарт C90, но расширения включены нарочно: snprintf появился в C99, и в чистом режиме -std=c89 системные заголовки его просто не объявляют. Отсюда gnu89 вместо c89 - единственное послабление, и оно про заголовки, а не про код. Строгость никуда не делась, её проверяют отдельно (ниже).

SDL2 ищется в три приёма, по убыванию точности:

  1. find_package(SDL2 CONFIG QUIET) - vcpkg, Conan, любой пакет с SDL2Config.cmake;
  2. find_package(SDL2 MODULE QUIET) - классический FindSDL2.cmake;
  3. pkg_check_modules(SDL2 REQUIRED sdl2) - Linux/BSD с pkg-config.
Опция По умолчанию Что делает
JUMPYBALL_WARNINGS ON /W4 на MSVC, -Wall -Wextra на остальных
JUMPYBALL_BUILD_TOOLS OFF собирает tools/modtest.c - плеер .tkm без игры
JUMPYBALL_COPY_ASSETS ON POST_BUILD копирует BITMAP/, Sounds/, Musics/ к бинарнику

Цель объявлена как add_executable(jumpyball WIN32 ...), на Windows линкуется shell32, на остальных системах ищется libm, install кладёт бинарник в bin.

Три короткие дороги

  • build.cmd - прямой cl без всякого cmake, x86: /W4 /O2 /TC /MT, 24 файла в одной строке, /SUBSYSTEM:WINDOWS и /NODEFAULTLIB:msvcrt.lib - иначе /MT и SDL2.lib затащат две разные копии рантайма. /TC значит «это всё C», а не «догадайся по расширению». Дальше скрипт сам копирует SDL2.dll и три папки ассетов в build\.
  • build-cmake.cmd - тот же cmake, но с жёстко прописанными путями к vcvars32.bat, cmake.exe, ninja.exe и тулчейну vcpkg, VCPKG_TARGET_TRIPLET=x86-windows.
  • tools/android_prepare.sh - Android. Скачивает SDL 2.32.10 с GitHub, разворачивает его android-project в build-android/, копирует весь SDL в app/jni/SDL, все jb_*.c и jb_*.h в app/jni/src, ассеты в app/src/main/assets, и правит build.gradle через sed: applicationId io.github.katamaran21.jumpyball, versionCode 3, versionName 1.2, minSdkVersion 21, APP_ABI := armeabi-v7a arm64-v8a x86 x86_64. Дальше - ./gradlew assembleDebug.

android/Android.mk набирает исходники как $(notdir $(wildcard $(LOCAL_PATH)/*.c)), поэтому новый файл порта попадает в APK сам, без правки скрипта.

Две проверки, которые ничего не собирают

  • tools/c89check.cmd - clang-tidy из Build Tools с -std=c89 -pedantic-errors -Wall по каждому .c. Объектных файлов нет, только разбор: ищется то, что MSVC молча принимает, а строгий C89 отвергает.
  • tools/modtest.cmd - собирает modtest.exe из modtest.c + jb_mod.c + jb_mod_effects.c, чтобы гонять .tkm без игры и без SDL, см. Audio-and-TKM.

Аргументы командной строки

Разбор - в jb_main.c: четыре именованных ключа и два позиционных аргумента.

Аргумент Смысл
--level=N сразу в уровень N, минуя меню
--screen=N стартовый экран меню (screen_id, см. Menu-and-controls)
--index=N стартовый пункт
--unlocked=N max_unlocked
первое число масштаб окна, < 1 -> 2
второе слово путь для дампа кадра

Дамп - это и есть механизм проверки в CI:

if (dump_path && ticks > 1500u) {
    FILE *fp = fopen(dump_path, "wb");
    if (fp) { fwrite(back->pixels, sizeof(uint16_t), JB_VIEW_W * JB_VIEW_H, fp); fclose(fp); }
    break;
}

Полторы секунды на прогрев (за это время таймер успевает выйти из отрицательной секунды, см. Levels), один кадр в файл, выход. Размер файла всегда один и тот же: 240 * 320 * 2 = 153600 байт, потому что бекбуфер 16-битный.

Ещё две переменные окружения:

  • JUMPYBALL_ASSETS - папка, где лежат BITMAP/, Sounds/, Musics/ (jb_assets.c);
  • JUMPYBALL_TOUCH - включить сенсорную панель на десктопе. На Android она включена всегда (#ifdef __ANDROID__), а на десктопе переменная дополнительно ставит SDL_HINT_MOUSE_TOUCH_EVENTS=1 (мышь работает как палец) и делает окно SDL_WINDOW_RESIZABLE, потому что раскладка панели считается от размеров окна.

Что проверяет CI

.github/workflows/build.yml - три задачи. Триггеры: push в main, любой pull request, ручной workflow_dispatch. Задачи release здесь нет: артефакты собираются и выкладываются как artifacts прогона, но релиз не публикуется.

linux

Матрица из двух раннеров: ubuntu-24.04 (x86_64) и ubuntu-24.04-arm (arm64).

  1. Строгий C89 - цикл по всем *.c: gcc -std=c89 -pedantic-errors -Wall -Wextra $sdl -c "$f" -o /dev/null. Здесь именно c89, без gnu, и -pedantic-errors - предупреждение становится ошибкой. Это и есть та проверка, которую cmake себе позволить не может.
  2. Обычная сборка через cmake.
  3. Два безголовых дампа: SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout 60 ./jumpyball 1 frame.raw, проверяется код возврата 0 и размер файла ровно $((240 * 320 * 2)). Второй прогон - то же самое с JUMPYBALL_TOUCH=1.

windows

Матрица windows-latest (x86 / Win32 / x86-windows) и windows-11-arm (arm64 / ARM64 / arm64-windows), SDL2 из vcpkg. Запуск через Start-Process .\jumpyball.exe -ArgumentList '1','frame.raw' -Wait -PassThru, те же две проверки - код выхода и длина файла.

android

JDK temurin 17, sh tools/android_prepare.sh, ./gradlew --no-daemon assembleDebug, а затем проверка содержимого APK через unzip -l. В архиве обязаны быть:

  • lib/<abi>/libmain.so и lib/<abi>/libSDL2.so для всех четырёх ABI;
  • assets/BITMAP/204.bmp, assets/Sounds/sblam.wav, assets/Musics/music1.tkm.

CI ловит не только «скомпилировалось», но и «ассеты действительно попали в APK» - самая частая поломка при сборке под Android.

Чего дамп не видит

Дамп берётся из бекбуфера (Platform_BackBuffer()), а сенсорная панель рисуется в окно уже после того, как кадр скопирован: SDL_RenderCopy -> Touch_Draw -> SDL_RenderPresent в jb_platform_sdl2.c. Поэтому прогон с JUMPYBALL_TOUCH=1 проверяет, что игра не падает с включённой панелью и что раскладка не ломает кадр, но саму панель в файле не увидеть - см. Menu-and-controls.

Clone this wiki locally