From a07d32e44a83bb8f9b589e6e5a679b984f1a363f Mon Sep 17 00:00:00 2001 From: Attila Uygun Date: Tue, 29 Aug 2023 12:54:06 +0200 Subject: [PATCH] Add support for GN to gradle config and remove cmake --- README.md | 35 ++++-- build/android/app/CMakeLists.txt | 173 -------------------------- build/android/app/build.gradle | 205 +++++++++++++++++++++++++++---- build/android/build.gradle | 26 ++++ build/android/gradlew.bat | 89 ++++++++++++++ 5 files changed, 325 insertions(+), 203 deletions(-) delete mode 100644 build/android/app/CMakeLists.txt create mode 100644 build/android/gradlew.bat diff --git a/README.md b/README.md index 9425c4f..3dd0152 100644 --- a/README.md +++ b/README.md @@ -8,28 +8,49 @@ based on this engine. Full game code and assets are included in this repository. ## Pre-requisites: -**GN build system** is required for all platforms except Android:\ +GN build system is required for all platforms:\ https://gn.googlesource.com/gn/ ## Building from the command-line: ### All platforms except Android: -Setup: +#### Setup: +Generate build files for Ninja in release and debug modes. ```text gn gen out/release -gn gen --args="is_debug=true" out/debug +gn gen --args='is_debug=true' out/debug ``` -Building and running: +#### Building and running: +Build all games in release mode and run "hello_world". ```text -ninja -C out/debug -./out/debug/hello_world +ninja -C out/release +./out/release/hello_world +``` +Build only "demo" in debug mode and run. +```text +ninja -C out/debug demo ./out/debug/demo ``` ### Android: +Build the default game ("hello_world") in debug mode for all ABIs and install. +GN will be run by Gradle so no setup is required. Both Linux and Windows are +supported as host platforms. The Gradle project can also be +opened in Android Studio. ```text cd build/android ./gradlew :app:installDebug ``` +Build in release mode. The generated APK must be signed before installing. +```text +./gradlew :app:assembleRelease +``` +Build configuration can be changed via project properties. The following command +will build "demo" in debug mode for x86_64 ABI and install. "targetArchs" +property can be set to any combination of Arm7, Arm8, X86_64, X86. Location of +gn and ninja executables can also be specified via "gn" and "ninja" properties. +```text +./gradlew :app:installDebug -PtargetArchs="X86_64" -PtargetGame="demo" +``` ### Generate Visual Studio solution: ```text gn.exe gen --args="is_debug=true" --ide=vs2022 out\vs @@ -53,7 +74,7 @@ devenv out\vs\all.sln ## Hello World example: -Shows a smoothly rotating "Hello Wolrd!". +Shows a smoothly rotating "Hello World!". ```cpp class HelloWorld final : public eng::Game { public: diff --git a/build/android/app/CMakeLists.txt b/build/android/app/CMakeLists.txt deleted file mode 100644 index 8b2d3b8..0000000 --- a/build/android/app/CMakeLists.txt +++ /dev/null @@ -1,173 +0,0 @@ -cmake_minimum_required(VERSION 3.22.1) - -# common_config -add_library(common_config INTERFACE) - -target_compile_options(common_config INTERFACE - $<$:-std=c++20> - #$<$>:-Ofast> - -Wall - -Werror - -Wno-unused-but-set-variable - -Wno-deprecated-enum-enum-conversion - -Wno-unsequenced - -Wno-nullability-completeness - -Wno-unused-variable -) - -target_compile_definitions(common_config INTERFACE - VK_USE_PLATFORM_ANDROID_KHR - VMA_STATIC_VULKAN_FUNCTIONS=1 - $<$:_DEBUG> -) - -target_include_directories(common_config INTERFACE - ../../../src - ../../../src/third_party/glslang - ../../../src/third_party/vulkan/include -) - -# oboe -set (OBOE_DIR ../../../src/third_party/oboe) -add_subdirectory(${OBOE_DIR} ./oboe-bin) - -# cpufeatures -add_library(cpufeatures STATIC ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c) -target_include_directories(cpufeatures PUBLIC ${ANDROID_NDK}/sources/android/cpufeatures) - -# native_app_glue -add_library(native_app_glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) -target_include_directories(native_app_glue PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) -set_property(TARGET native_app_glue PROPERTY POSITION_INDEPENDENT_CODE ON) - -# Export ANativeActivity_onCreate(), -# Refer to: https://github.com/android-ndk/ndk/issues/381. -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") - -# kaliber -project(kaliber) - -add_library(kaliber SHARED - ../../../src/base/collusion_test.cc - ../../../src/base/log.cc - ../../../src/base/mem.cc - ../../../src/base/task_runner.cc - ../../../src/base/thread_pool.cc - ../../../src/demo/credits.cc - ../../../src/demo/demo.cc - ../../../src/demo/enemy.cc - ../../../src/demo/hud.cc - ../../../src/demo/menu.cc - ../../../src/demo/player.cc - ../../../src/demo/sky_quad.cc - ../../../src/engine/animatable.cc - ../../../src/engine/animator.cc - ../../../src/engine/asset/font.cc - ../../../src/engine/asset/image.cc - ../../../src/engine/asset/mesh.cc - ../../../src/engine/asset/shader_source.cc - ../../../src/engine/asset/sound.cc - ../../../src/engine/audio/audio_bus.cc - ../../../src/engine/audio/audio_mixer.cc - ../../../src/engine/audio/audio_sink_oboe.cc - ../../../src/engine/audio/mixer_input.cc - ../../../src/engine/audio/sinc_resampler.cc - ../../../src/engine/drawable.cc - ../../../src/engine/engine.cc - ../../../src/engine/image_quad.cc - ../../../src/engine/persistent_data.cc - ../../../src/engine/platform/asset_file_android.cc - ../../../src/engine/platform/asset_file.cc - ../../../src/engine/platform/platform_android.cc - ../../../src/engine/renderer/geometry.cc - ../../../src/engine/renderer/opengl/renderer_opengl_android.cc - ../../../src/engine/renderer/opengl/renderer_opengl.cc - ../../../src/engine/renderer/renderer_types.cc - ../../../src/engine/renderer/shader.cc - ../../../src/engine/renderer/texture.cc - ../../../src/engine/renderer/vulkan/renderer_vulkan_android.cc - ../../../src/engine/renderer/vulkan/renderer_vulkan.cc - ../../../src/engine/renderer/vulkan/vulkan_context_android.cc - ../../../src/engine/renderer/vulkan/vulkan_context.cc - ../../../src/engine/solid_quad.cc - ../../../src/engine/sound_player.cc - ../../../src/third_party/android/gl3stub.c - ../../../src/third_party/android/GLContext.cpp - ../../../src/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp - ../../../src/third_party/glslang/glslang/GenericCodeGen/Link.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/attribute.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/Constant.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/Initialize.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/intermOut.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/iomapper.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/limits.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/parseConst.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/reflection.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/Scan.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/SpirvIntrinsics.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp - ../../../src/third_party/glslang/glslang/MachineIndependent/Versions.cpp - ../../../src/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp - ../../../src/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp - ../../../src/third_party/glslang/SPIRV/disassemble.cpp - ../../../src/third_party/glslang/SPIRV/doc.cpp - ../../../src/third_party/glslang/SPIRV/GlslangToSpv.cpp - ../../../src/third_party/glslang/SPIRV/InReadableOrder.cpp - ../../../src/third_party/glslang/SPIRV/Logger.cpp - ../../../src/third_party/glslang/SPIRV/SpvBuilder.cpp - ../../../src/third_party/glslang/SPIRV/SpvPostProcess.cpp - ../../../src/third_party/glslang/SPIRV/SPVRemapper.cpp - ../../../src/third_party/glslang/SPIRV/SpvTools.cpp - ../../../src/third_party/jsoncpp/jsoncpp.cpp - ../../../src/third_party/minimp3/minimp3.cc - ../../../src/third_party/minizip/ioapi.c - ../../../src/third_party/minizip/unzip.c - ../../../src/third_party/spirv-reflect/spirv_reflect.c - ../../../src/third_party/stb/stb_image.cc - ../../../src/third_party/texture_compressor/dxt_encoder_internals.cc - ../../../src/third_party/texture_compressor/dxt_encoder.cc - ../../../src/third_party/texture_compressor/texture_compressor_etc1.cc - ../../../src/third_party/texture_compressor/texture_compressor.cc - ../../../src/third_party/vma/vk_mem_alloc.cpp - ../../../src/third_party/volk/volk.c -) - -if (ANDROID_ABI STREQUAL armeabi-v7a) - target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc) - target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc) - set_source_files_properties(../../../src/third_party/texture_compressor/dxt_encoder_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon) - set_source_files_properties(../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc PROPERTIES COMPILE_FLAGS -mfpu=neon) -endif() - -if (ANDROID_ABI STREQUAL arm64-v8a) - target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/dxt_encoder_neon.cc) - target_sources(kaliber PRIVATE ../../../src/third_party/texture_compressor/texture_compressor_etc1_neon.cc) -endif() - -target_link_libraries(kaliber PRIVATE - common_config - android - native_app_glue - oboe - cpufeatures - EGL - GLESv2 - log - z -) diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle index de6dc95..8eef483 100644 --- a/build/android/app/build.gradle +++ b/build/android/app/build.gradle @@ -1,20 +1,103 @@ -apply plugin: 'com.android.application' +@CacheableTask +abstract class WriteFileTask extends DefaultTask { + @Input + abstract Property getContent() -android { - compileSdk 33 - ndkVersion '25.2.9519653' + @OutputFile + abstract RegularFileProperty getTarget() - defaultConfig { - applicationId = 'com.woom.game' - minSdk 24 - targetSdk 33 - externalNativeBuild { - cmake { - arguments '-DANDROID_STL=c++_static' + @TaskAction + void run() { + def file = target.get().asFile + file.parentFile.mkdirs() + def text = content.get() + if (!file.exists() || text != file.text) + file.text = text + } +} + +class Utils implements Plugin { + final def BUILD_TYPES = ['Debug', 'Release'].asImmutable() + final def BUILD_TYPES_REG_EXP = BUILD_TYPES.join('|') + + final def ABI_CODES = ["Arm7": "armeabi-v7a", + "Arm8": "arm64-v8a", + "X86_64": "x86_64", + "X86": "x86"].asImmutable() + final def CPU_CODES = ["Arm7": "arm", + "Arm8": "arm64", + "X86_64": "x64", + "X86": "x86"].asImmutable() + + def project + + @Inject + Utils(Project project) { + this.project = project + } + + void apply(Project project) { + project.extensions.create('utils', Utils) + } + + void addTask(String prefix, Closure taskClosure) { + forEachBuildVariant { String arch, String buildType -> + def taskName = "${prefix}${arch}${buildType}" + taskClosure(taskName, buildType, arch) + } + } + + void forEachBuildVariant(Closure callback) { + project.rootProject.ext.targetArchs.each { arch -> + BUILD_TYPES.each { buildType -> + callback(arch, buildType) } } + } +} + +def generateGnArgsContent(String buildType, String arch) { + def content = 'target_os="android"\n' + content += 'target_cpu="' + utils.CPU_CODES[arch] + '"\n' + content += "is_debug=${buildType != 'Release'}\n" + content += 'ndk="' + android.ndkDirectory + '"\n' + content += "ndk_api=${rootProject.ext.minSdk}\n" + return content +} + +def getOutDir(String buildType) { + return "${project.buildDir}/gn_out/${buildType.toLowerCase()}" +} + +def getAssetsDir(String buildType) { + return "${project.buildDir}/gn_out/${buildType.toLowerCase()}/assets" +} + +def getJniLibsDir(String buildType) { + return "${project.buildDir}/gn_out/jniLibs/${buildType.toLowerCase()}" +} + +apply plugin: 'com.android.application' +apply plugin: Utils + +rootProject.ext.targetArchs.each { arch -> + assert utils.ABI_CODES.containsKey(arch) + assert utils.CPU_CODES.containsKey(arch) +} + +android { + compileSdk rootProject.ext.compileSdk + ndkVersion rootProject.ext.ndkVersion + + defaultConfig { + applicationId rootProject.ext.applicationId + minSdk rootProject.ext.minSdk + targetSdk rootProject.ext.targetSdk ndk { - abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' + abiFilters = [] + rootProject.ext.targetArchs.each { arch -> + abiFilters.add(utils.ABI_CODES[arch]) + } } } buildTypes { @@ -22,24 +105,24 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - ndk { - debugSymbolLevel 'FULL' - } - } - } - externalNativeBuild { - cmake { - version '3.22.1' - path 'CMakeLists.txt' } } sourceSets { main { java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base'] - assets.srcDirs = ['../../../assets'] + utils.BUILD_TYPES.each { buildType -> + "${buildType.toLowerCase()}" { + assets.srcDirs = [getAssetsDir(buildType)] + } + } + } + utils.BUILD_TYPES.each { buildType -> + "${buildType.toLowerCase()}" { + jniLibs.srcDirs = [getJniLibsDir(buildType)] + } } } - namespace 'com.woom.game' + namespace rootProject.ext.applicationId } dependencies { @@ -48,3 +131,79 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'com.google.android.gms:play-services-ads:22.0.0' } + +utils.addTask('generateGnArgsFor') { String taskName, String buildType, String arch -> + task(taskName, type: WriteFileTask) { + content = generateGnArgsContent(buildType, arch) + target = project.layout.file(provider { new File("${getOutDir(buildType)}/${utils.ABI_CODES[arch]}", 'args.gn') }) + } +} + +utils.addTask('runGnFor') { String taskName, String buildType, String arch -> + task(taskName, type: Exec) { + dependsOn "generateGnArgsFor${arch}${buildType}" + + executable rootProject.ext.gn + args '--fail-on-unused-args', 'gen', "${getOutDir(buildType)}/${utils.ABI_CODES[arch]}" + + inputs.file(new File("${getOutDir(buildType)}/${utils.ABI_CODES[arch]}", 'args.gn')) + outputs.file(new File("${getOutDir(buildType)}/${utils.ABI_CODES[arch]}", 'build.ninja')) + } +} + +utils.addTask('runNinjaFor') { String taskName, String buildType, String arch -> + task(taskName, type: Exec) { + dependsOn "runGnFor${arch}${buildType}" + + executable rootProject.ext.ninja + args '-C', "${getOutDir(buildType)}/${utils.ABI_CODES[arch]}", rootProject.ext.targetGame + + inputs.file(new File("${getOutDir(buildType)}/${utils.ABI_CODES[arch]}", 'build.ninja')) + outputs.file(new File("${getOutDir(buildType)}/${utils.ABI_CODES[arch]}", "lib${rootProject.ext.targetGame}.so")) + } +} + +utils.addTask('copyAssetsFor') { String taskName, String buildType, String arch -> + task(taskName, type: Copy) { + dependsOn "runNinjaFor${arch}${buildType}" + + from "${getOutDir(buildType)}/${utils.ABI_CODES[arch]}/assets" + into getAssetsDir(buildType) + } +} + +utils.addTask('copyJniLibsFor') { String taskName, String buildType, String arch -> + task(taskName, type: Copy) { + dependsOn "runNinjaFor${arch}${buildType}" + + from("${getOutDir(buildType)}/${utils.ABI_CODES[arch]}") { + include "lib${rootProject.ext.targetGame}.so" + rename "lib${rootProject.ext.targetGame}.so", "libkaliber.so" + } + into "${getJniLibsDir(buildType)}/${utils.ABI_CODES[arch]}" + } +} + +tasks.configureEach { task -> + def match = task.name =~ /^merge(${utils.BUILD_TYPES_REG_EXP})JniLibFolders$/ + if (match) { + rootProject.ext.targetArchs.each { arch -> + task.dependsOn "copyJniLibsFor${arch}${match.group(1)}" + return + } + } + match = task.name =~ /^merge(${utils.BUILD_TYPES_REG_EXP})Assets$/ + if (match) { + rootProject.ext.targetArchs.each { arch -> + task.dependsOn "copyAssetsFor${arch}${match.group(1)}" + return + } + } + match = task.name =~ /^lintVitalAnalyze(${utils.BUILD_TYPES_REG_EXP})$/ + if (match) { + rootProject.ext.targetArchs.each { arch -> + task.dependsOn "copyAssetsFor${arch}${match.group(1)}" + return + } + } +} diff --git a/build/android/build.gradle b/build/android/build.gradle index 1ca8dd7..0a47ef8 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -19,3 +19,29 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } + +ext { + if (!project.hasProperty('targetGame')) { + targetGame = "hello_world" + } + + if (!project.hasProperty('targetArchs')) { + targetArchs = ['Arm7', 'Arm8', 'X86_64', 'X86'] + } else { + targetArchs = project.getProperty('targetArchs').split(',') + } + + applicationId = 'com.woom.game' + + ndkVersion = '25.2.9519653' + minSdk = 24 + compileSdk = 33 + targetSdk = 33 + + if (!project.hasProperty('gn')) { + gn = "gn" + } + if (!project.hasProperty('ninja')) { + ninja = "ninja" + } +} diff --git a/build/android/gradlew.bat b/build/android/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/build/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega