# # Copyright (c) 2008-2021 the Urho3D project. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # task default: :build desc 'Show info for the specified target platform' task :info, [:kind] => [:init] do |_, args| case args[:kind] when 'build_tree' print build_tree when 'install_dir' print install_dir else abort %q{Please specify the type of info requested: 'build_tree', 'install_dir'} end end desc 'Invoke CMake to configure and generate a build tree' task :cmake => [:init] do if ENV['CI'] system 'cmake --version' or abort 'Failed to find CMake' if ENV['USE_CCACHE'] && ENV['GITHUB_EVENT_NAME'] != 'repository_dispatch' && /\[cache clear\]/ =~ `git log --format=%B -n1 2>/dev/null` system 'bash', '-c', 'rm -rf ~/.{ccache,gradle}' or abort 'Failed to clear the build cache' end end next if ENV['PLATFORM'] == 'android' || (Dir.exists?(build_tree) and not ARGV.include?('cmake')) ['CMAKE_INSTALL_PREFIX', 'URHO3D_HOME'].each { |var| if ENV[var] == 'system' ENV.delete(var) elsif !ENV[var] ENV[var] = install_dir if var == 'CMAKE_INSTALL_PREFIX' || Dir.exists?(install_dir) end } script = "script/cmake_#{ENV['GENERATOR']}#{ENV['OS'] ? '.bat' : '.sh'}" build_options = /linux|macOS|win/ =~ ENV['PLATFORM'] ? '' : "-D #{ENV['PLATFORM'].upcase}=1" File.readlines('script/.build-options').each { |var| var.chomp! build_options = "#{build_options} -D #{var}=#{ENV[var]}" if ENV[var] } system %Q{#{script} "#{build_tree}" #{build_options}} or abort end desc 'Clean the build tree' task :clean => [:init] do if ENV['PLATFORM'] == 'android' Rake::Task[:gradle].invoke('clean') next end system build_target('clean') or abort end desc 'Build the software' task :build, [:target] => [:cmake] do |_, args| system "ccache -z" if ENV['USE_CCACHE'] if ENV['PLATFORM'] == 'android' Rake::Task[:gradle].invoke('build -x test') system "ccache -s" if ENV['USE_CCACHE'] next end filter = '' case ENV['GENERATOR'] when 'xcode' concurrent = '' # Assume xcodebuild will do the right things without the '-jobs' filter = '|xcpretty -c && exit ${PIPESTATUS[0]}' if system('xcpretty -v >/dev/null 2>&1') when 'vs' concurrent = '/maxCpuCount' else concurrent = "-j #{$max_jobs}" filter = "2>#{lint_err_file}" if ENV['URHO3D_LINT'] end system "#{build_target(args[:target])} -- #{concurrent} #{ENV['BUILD_PARAMS']} #{filter}" or abort system "ccache -s" if ENV['USE_CCACHE'] end desc 'Test the software' task :test => [:init] do if ENV['PLATFORM'] == 'android' Rake::Task[:gradle].invoke('test') next elsif ENV['URHO3D_LINT'] == '1' Rake::Task[:lint].invoke next elsif ENV['URHO3D_STYLE'] == '1' Rake::Task[:style].invoke next end wrapper = ENV['CI'] && ENV['PLATFORM'] == 'linux' ? 'xvfb-run' : '' test = /xcode|vs/ =~ ENV['GENERATOR'] ? 'RUN_TESTS' : 'test' system build_target(test, wrapper) or abort end desc 'Generate documentation' task :doc => [:init] do if ENV['PLATFORM'] == 'android' Rake::Task[:gradle].invoke('documentationZip') next end system build_target('doc') or abort end desc 'Install the software' task :install, [:dest_dir] => [:init] do |_, args| if ENV['PLATFORM'] == 'android' Rake::Task[:gradle].invoke('publishToMavenLocal') next end wrapper = args[:dest_dir] && !ENV['OS'] ? "DESTDIR=#{verify_path(args[:dest_dir], true)}" : '' system build_target('install', wrapper) or abort end desc 'Package build artifact' task :package => [:init] do next if ENV['PLATFORM'] == 'android' wrapper = /linux|rpi|arm/ =~ ENV['PLATFORM'] && ENV['URHO3D_64BIT'] == '0' ? 'setarch i686' : '' system build_target('package', wrapper) or abort end desc 'Publish build artifact' task :publish => [:init] do if ENV['PLATFORM'] == 'android' Rake::Task[:gradle].invoke("publish #{/refs\/tags\// =~ ENV['GITHUB_REF'] ? 'bintrayUpload' : ''}") next end Rake::Task[$publish_task.to_sym].invoke if $publish_task end desc 'Create a new project' task :new, [:name, :parent_dir, :use_copy] => [:init] do |_, args| abort 'The "new" task can only be invoked in the Urho3D project root!' unless first_match(/^# (Urho3D)$/, 'README.md') args.with_defaults(:name => 'UrhoApp', :parent_dir => '~/projects', :use_copy => false) name = args[:name] parent_dir = verify_path(args[:parent_dir]) dir = "#{parent_dir}/#{name}" use_copy = args[:use_copy] || dockerized? abort "The directory '#{dir}' already exists!" if Dir.exists?(dir) puts "Creating a new project in #{dir}..." func = FileUtils.method(use_copy ? :cp_r : :ln_s) source_tree(name).split("\n").each do |it| dirname, basename = /\// =~ it.split(/\s*<-\s*/).first ? it.match(/([^\s<]+)\/(.+)/).captures : ['.', it] FileUtils.mkdir_p("#{dir}/#{dirname}") if (matched = basename.match(/(?<basename>\S+)\s*<-\s*:(?<symbol>\S+)/)) File.write("#{dir}/#{dirname}/#{matched[:basename]}", method(matched[:symbol].to_sym).call(name)) elsif (matched = basename.match(/(?<basename>\S+)\s*<-\s*(?<dirname>\S+)/)) func.call(verify_path("#{matched[:dirname]}/#{matched[:basename]}"), "#{dir}/#{dirname}") else func.call(verify_path(it), "#{dir}/#{dirname}") end end puts "Done!" end ### Internal tasks ### task :check_license do commit = 0 # Automatically bump copyright when crossing a new year if /2008-([0-9]{4}) the Urho3D project/.match(File.read('rakefile'))[1].to_i != Time.now.year system %Q{ git config user.name #{ENV['PUBLISHER_NAME']} && \\ git config user.email #{ENV['PUBLISHER_EMAIL']} && \\ git add #{bump_copyright_year.join ' '} && \\ git commit -qm 'GH Actions: Bump copyright year to #{Time.now.year}.\n[cache clear]' } or abort "Failed to commit copyright year update" commit = 1 end # TODO: Check and merge any new 3rd-party license into 'Source/ThirdParty/LICENSES' puts "::set-output name=commit::#{commit}" end task :ci do ENV['URHO3D_PCH'] = '0' if ENV['PLATFORM'] == 'linux-gcc' # TODO - PCH causes cache miss on initial build for Linux/GCC, why? platform_modifier = /(.+?)-(.+)/.match(ENV['PLATFORM']) if platform_modifier ENV['PLATFORM'] = platform_modifier[1] ENV['MODIFIER'] = platform_modifier[2] end case ENV['HOST'] when 'linux' ENV['URHO3D_DEPLOYMENT_TARGET'] = 'generic' if /linux|mingw/ =~ ENV['PLATFORM'] if ENV['MODIFIER'] == 'clang' ENV['CC'] = 'clang' ENV['CXX'] = 'clang++' end when 'windows' if ENV['MODIFIER'] == 'gcc' ENV['URHO3D_DEPLOYMENT_TARGET'] = 'generic' ENV['GENERATOR'] = 'mingw' end else # Do nothing end ENV['BUILD_TREE'] = 'build/ci' ENV['CMAKE_BUILD_TYPE'] = ENV['BUILD_TYPE'] == 'dbg' ? 'Debug' : 'Release' if /dbg|rel/ =~ ENV['BUILD_TYPE'] case ENV['GRAPHICS_API'] when 'DX11' ENV['URHO3D_D3D11'] = '1' when 'DX9' ENV['URHO3D_OPENGL'] = '0' # Need to make this explicit because 'MINGW' default to use OpenGL otherwise when 'OpenGL' ENV['URHO3D_OPENGL'] = '1' else # Do nothing end case ENV['PLATFORM'] when 'web' ENV['EMSCRIPTEN_SHARE_DATA'] = '1' $max_jobs = 1 if ENV['BUILD_TYPE'] == 'dbg' $publish_task = 'ci_publish_web' else # Do nothing end ENV['URHO3D_LIB_TYPE'] = ENV['LIB_TYPE'].upcase if /static|shared/ =~ ENV['LIB_TYPE'] ENV['URHO3D_TESTING'] = '1' if /linux|macOS|win/ =~ ENV['PLATFORM'] ENV['URHO3D_LINT'] = '1' if ENV['MODIFIER'] == 'clang-tidy' ENV['URHO3D_STYLE'] = '1' if ENV['MODIFIER'] == 'clang-format' # Enable all the bells and whistles %w[URHO3D_DATABASE_SQLITE URHO3D_EXTRAS].each { |it| ENV[it] = '1' } end task :ci_publish_web do require 'json' system 'git clone --depth 1 -q https://github.com/urho3d/urho3d.github.io.git build/urho3d.github.io' or abort 'Failed to clone urho3d/urho3d.github.io' system "rsync -a --delete --exclude tool --exclude *.pak --exclude index.md build/ci/bin/ build/urho3d.github.io/samples" or abort 'Failed to rsync Web samples' Dir.chdir('build/urho3d.github.io/samples') { next unless system 'git diff --quiet Urho3D.js.data' uuid = `git diff --color=never --word-diff-regex='\\w+' --word-diff=porcelain Urho3D.js`.split.grep(/^[+-]\w+-/).map { |it| it[0] = ''; it } system %Q(ruby -i.bak -pe "gsub '#{uuid.last}', '#{uuid.first}'" Urho3D.js) or abort 'Failed to substitute UUID' if system 'git diff --quiet Urho3D.js' File.unlink 'Urho3D.js.bak' Dir['*.js'].each { |file| system %Q(ruby -i -pe "gsub '#{uuid.last}', '#{uuid.first}'" #{file}) } else File.rename 'Urho3D.js.bak', 'Urho3D.js' end } web = {'samples' => {}} Dir.chdir('build/urho3d.github.io/samples') { web['samples']['Native'] = Dir['*.html'].sort } web['player'] = web['samples']['Native'].pop # Assume the last sample after sorting is the Urho3DPlayer.html {'AngelScript' => 'Scripts', 'Lua' => 'LuaScripts'}.each { |lang, subdir| Dir.chdir("bin/Data/#{subdir}") { script_samples = Dir['[0-9]*'].sort deleted_samples = [] # Delete samples that do not have their native counterpart script_samples.each { |sample| deleted_samples.push sample unless web['samples']['Native'].include? "#{sample.split('.').first}.html" } web['samples'][lang] = (script_samples - deleted_samples).map { |sample| "#{subdir}/#{sample}" } } } File.open('build/urho3d.github.io/_data/web.json', 'w') { |file| file.puts web.to_json } system %Q{ cd build/urho3d.github.io && \\ git config user.name #{ENV['PUBLISHER_NAME']} && \\ git config user.email #{ENV['PUBLISHER_EMAIL']} && \\ git remote set-url --push origin https://#{ENV['PUBLISHER_TOKEN']}@github.com/urho3d/urho3d.github.io.git && \\ git add -A . && \\ ( git commit -qm "GH Actions: Web samples update at #{Time.now.utc}.\n\nCommit: https://github.com/#{ENV['GITHUB_REPOSITORY']}/commit/#{ENV['GITHUB_SHA']}\n\nMessage: #{`git log --format=%B -n 1`}" || true) && \\ git push -q >/dev/null 2>&1 } or abort 'Failed to update Web samples' end task :gradle, [:task] do |_, args| system "#{ENV['OS'] ? 'gradlew.bat' : './gradlew'} #{args[:task]} #{ENV['CI'] ? '--console plain' : ''}" or abort end task :init do next if $max_jobs Rake::Task[:ci].invoke if ENV['CI'] case build_host when /linux/ $max_jobs = `grep -c processor /proc/cpuinfo`.chomp unless $max_jobs ENV['GENERATOR'] = 'generic' unless ENV['GENERATOR'] unless ENV['PLATFORM'] if /x86/ =~ `uname -m` ENV['PLATFORM'] = 'linux' elsif Dir.exists?('/opt/vc') ENV['PLATFORM'] = 'rpi' else ENV['PLATFORM'] = 'arm' end end when /darwin|macOS/ $max_jobs = `sysctl -n hw.logicalcpu`.chomp unless $max_jobs ENV['GENERATOR'] = 'xcode' unless ENV['GENERATOR'] ENV['PLATFORM'] = 'macOS' unless ENV['PLATFORM'] when /win32|mingw|mswin|windows/ unless $max_jobs require 'win32ole' WIN32OLE.connect('winmgmts://').ExecQuery("select NumberOfLogicalProcessors from Win32_ComputerSystem").each { |it| $max_jobs = it.NumberOfLogicalProcessors } end ENV['GENERATOR'] = 'vs' unless ENV['GENERATOR'] ENV['PLATFORM'] = 'win' unless ENV['PLATFORM'] else abort "Unsupported host system: #{build_host}" end # The 'ARCH' env-var, when set, has higher precedence than the 'URHO3D_64BIT' env-var ENV['URHO3D_64BIT'] = ENV['ARCH'] == '32' ? '0' : '1' if /32|64/ =~ ENV['ARCH'] end task :lint do lint_err = File.read(lint_err_file) puts lint_err # TODO: Tighten the check by failing the job later # abort 'Failed to pass linter checks' unless lint_err.empty? # puts 'Passed the linter checks' end task :style do system 'bash', '-c', %q{ git diff --name-only HEAD~ -- Source \ |grep -v ThirdParty \ |grep -P '\.(?:c|cpp|h|hpp)' \ |xargs clang-format -n -Werror 2>&1 \ |tee build/clang-format.out \ && exit ${PIPESTATUS[3]} } or abort 'Failed to pass style checks' puts 'Passed the style checks' end task :source_checksum do require 'digest' sha256_final = Digest::SHA256.new sha256_iter = Digest::SHA256 Dir['Source/**/*.{c,h}*'].each { |it| sha256_final << sha256_iter.file(it).hexdigest } puts "::set-output name=hexdigest::#{sha256_final.hexdigest}" end task :update_dot_files do system 'bash', '-c', %q{ perl -ne 'undef $/; print $1 if /(Build Option.*?(?=\n\n))/s' Docs/GettingStarted.dox \ |tail -n +3 |cut -d'|' -f2 |tr -d [:blank:] >script/.build-options && \ echo URHO3D_LINT >>script/.build-options && \ cat script/.build-options <(perl -ne 'while (/([A-Z_]+):.+?/g) {print "$1\n"}' .github/workflows/main.yml) \ <(perl -ne 'while (/ENV\[\x27(\w+)\x27\]/g) {print "$1\n"}' rakefile) \ <(perl -ne 'while (/System.getenv\\("(\w+)"\\)/g) {print "$1\n"}' android/urho3d-lib/build.gradle.kts) \ |sort |uniq |grep -Ev '^(HOME|PATH)$' >script/.env-file } or abort 'Failed to update dot files' if /schedule|workflow_dispatch/ =~ ENV['GITHUB_EVENT_NAME'] system %Q{ git config user.name #{ENV['PUBLISHER_NAME']} && \\ git config user.email #{ENV['PUBLISHER_EMAIL']} && \\ git add script/.build-options script/.env-file && \\ commit=0 && \\ if git commit -qm 'GH Actions: Update dot files for DBE.'; then commit=1; fi && \\ echo ::set-output name=commit::$commit } or abort "Failed to commit dot files update" end end ### Internal methods ### def build_host ENV['HOST'] || RUBY_PLATFORM end def build_tree ENV['BUILD_TREE'] || "build/#{dockerized? ? 'dockerized-' : ''}#{default_path}" end def build_config /xcode|vs/ =~ ENV['GENERATOR'] ? "--config #{ENV.fetch('CONFIG', 'Release')}" : '' end def build_target(tgt, wrapper = '') %Q{#{wrapper} cmake --build "#{build_tree}" #{build_config} #{tgt ? "--target #{tgt}" : ''}} end def bump_copyright_year(regex = '2008-[0-9]{4} the Urho3D project') begin copyrighted = `git grep -El '#{regex}'`.split copyrighted.each { |filename| replaced_content = File.read(filename).gsub(/#{regex}/, regex.gsub('[0-9]{4}', Time.now.year.to_s)) File.open(filename, 'w') { |file| file.puts replaced_content } } return copyrighted rescue abort 'Failed to bump copyright year' end end def default_path "#{ENV['PLATFORM'].downcase}" \ "#{ENV['CC'] ? "-#{ENV['CC']}" : ''}" \ "#{ENV['GENERATOR'] && /generic|xcode|vs/ =~ ENV['GENERATOR'] ? '' : "-#{ENV['GENERATOR']}"}" end def dockerized? File.exists?('/entrypoint.sh') end def install_dir "#{Dir.home}/.urho3d/install/#{default_path}" end def lint_err_file 'build/clang-tidy.out' end def verify_path(path, auto_create = false) require 'pathname' begin expanded_path = File.expand_path(path) FileUtils.mkdir_p(expanded_path) if (auto_create && !Dir.exists?(expanded_path)) Pathname.new(expanded_path).realdirpath.to_s rescue abort "The specified path '#{path}' is invalid!" end end def first_match(regex, from) begin if from.instance_of?(Array) array = from else array = File.exists?(from) ? File.readlines(from) : from.split("\n") end array.grep(regex).first.match(regex).captures.first rescue nil end end def source_tree(name) <<-EOF bin/CoreData bin/Data/Materials/Mushroom.xml bin/Data/Models/Mushroom.mdl bin/Data/Music/Ninja Gods.ogg bin/Data/Textures/Mushroom.dds bin/Data/Textures/UrhoIcon.icns bin/Data/Textures/UrhoIcon.png cmake gradle script app/src/main/cpp/#{name}.cpp <- :urho_app_cpp app/src/main/cpp/#{name}.h <- :urho_app_h app/src/main/java/io/urho3d/#{name.downcase}/MainActivity.kt <- :main_activity_kt #{Dir.chdir('android/launcher-app') { Dir['src/main/res/{drawable,mipmap}*'].map { |it| "app/#{it} <- android/launcher-app/src/main/res" }.join("\n") }} app/src/main/res/values/strings.xml <- :strings_xml app/src/main/AndroidManifest.xml <- :android_manifest_xml app/build.gradle.kts <- :app_build_gradle_kts app/CMakeLists.txt <- :app_cmake_lists_txt app/proguard-rules.pro <- android/launcher-app build.gradle.kts <- :root_build_gradle_kts CMakeLists.txt <- :root_cmake_lists_txt gradle.properties gradlew gradlew.bat rakefile settings.gradle.kts <- :settings_gradle_kts .clang-format .clang-tidy .gitattributes <- :gitattributes .gitignore <- :gitignore EOF end def settings_gradle_kts(name) <<-EOF rootProject.name = "#{name}" include(":app") EOF end def app_build_gradle_kts(name) template = File.readlines('android/launcher-app/build.gradle.kts') sdk_version = first_match(/compileSdkVersion\((\d+)\)/, template) min_sdk_version = first_match(/minSdkVersion\((\d+)\)/, template) aar_version = ENV['CI'] && ENV['PLATFORM'] != 'android' ? 'unknown' : # Skip using gradle all together when on CI, unless for Android build first_match(/AAR version: (.+)/, `#{ENV['OS'] ? 'gradlew.bat' : './gradlew'} aarVersion 2>#{ENV['OS'] ? 'null' : '/dev/null'}`) type = ENV.fetch('URHO3D_LIB_TYPE', 'STATIC').downcase <<-EOF plugins { id("com.android.application") kotlin("android") kotlin("android.extensions") } val kotlinVersion: String by ext val ndkSideBySideVersion: String by ext val cmakeVersion: String by ext val buildStagingDir: String by ext android { ndkVersion = ndkSideBySideVersion compileSdkVersion(#{sdk_version}) defaultConfig { minSdkVersion(#{min_sdk_version}) targetSdkVersion(#{sdk_version}) applicationId = "io.urho3d.#{name}" versionCode = 1 versionName = "1.0" testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { arguments.apply { System.getenv("ANDROID_CCACHE")?.let { add("-D ANDROID_CCACHE=$it") } add("-D JNI_DIR=${project.file(buildStagingDir)}") // Pass along matching env-vars as CMake build options addAll(project.file("../script/.build-options") .readLines() .mapNotNull { variable -> System.getenv(variable)?.let { "-D $variable=$it" } } ) } } } splits { abi { isEnable = project.hasProperty("ANDROID_ABI") reset() include( *(project.findProperty("ANDROID_ABI") as String? ?: "") .split(',') .toTypedArray() ) } } } buildTypes { named("release") { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } lintOptions { isAbortOnError = false } externalNativeBuild { cmake { version = cmakeVersion path = project.file("../CMakeLists.txt") setBuildStagingDirectory(buildStagingDir) } } sourceSets { named("main") { assets.srcDir(project.file("../bin")) } } } val urhoReleaseImpl by configurations.creating { isCanBeResolved = true } configurations.releaseImplementation.get().extendsFrom(urhoReleaseImpl) val urhoDebugImpl by configurations.creating { isCanBeResolved = true } configurations.debugImplementation.get().extendsFrom(urhoDebugImpl) dependencies { urhoReleaseImpl("io.urho3d:urho3d-lib-#{type}:#{aar_version}") urhoDebugImpl("io.urho3d:urho3d-lib-#{type}-debug:#{aar_version}") implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar")))) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") implementation("androidx.core:core-ktx:#{first_match(/"androidx.core:core-ktx:(.+)"/, template)}") implementation("androidx.appcompat:appcompat:#{first_match(/"androidx.appcompat:appcompat:(.+)"/, template)}") implementation("androidx.constraintlayout:constraintlayout:#{first_match(/"androidx.constraintlayout:constraintlayout:(.+)"/, template)}") testImplementation("junit:junit:#{first_match(/"junit:junit:(.+)"/, template)}") androidTestImplementation("androidx.test:runner:#{first_match(/"androidx.test:runner:(.+)"/, template)}") androidTestImplementation("androidx.test.espresso:espresso-core:#{first_match(/"androidx.test.espresso:espresso-core:(.+)"/, template)}") } afterEvaluate { android.buildTypes.forEach { buildType -> val config = buildType.name.capitalize() val unzipTaskName = "unzipJni$config" tasks { "generateJsonModel$config" { dependsOn(unzipTaskName) } register<Copy>(unzipTaskName) { val aar = configurations["urho${config}Impl"].resolve().first { it.name.startsWith("urho3d-lib") } from(zipTree(aar)) include("urho3d/**") into(android.externalNativeBuild.cmake.buildStagingDirectory) } } } } tasks { register<Delete>("cleanAll") { dependsOn("clean") delete = setOf(android.externalNativeBuild.cmake.buildStagingDirectory) } } EOF end def root_build_gradle_kts(_) template = File.readlines('build.gradle.kts') <<-EOF buildscript { extra["kotlinVersion"] = "#{first_match(/extra\["kotlinVersion"\] = "(.+)"/, template)}" val kotlinVersion: String by extra repositories { google() jcenter() } dependencies { classpath("com.android.tools.build:gradle:#{first_match(/"com.android.tools.build:gradle:(.+)"/, template)}") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") } } val kotlinVersion: String by ext allprojects { repositories { google() jcenter() // Remove below two repos if you are only using released AAR from JCenter mavenLocal() if (System.getenv("GITHUB_ACTOR") != null && System.getenv("GITHUB_TOKEN") != null) { maven { name = "GitHubPackages" url = uri("https://maven.pkg.github.com/urho3d/Urho3D") credentials { username = System.getenv("GITHUB_ACTOR") password = System.getenv("GITHUB_TOKEN") } } } } buildscript { ext { set("kotlinVersion", kotlinVersion) set("ndkSideBySideVersion", "#{first_match(/set\("ndkSideBySideVersion", "(.+)"\)/, template)}") set("cmakeVersion", "#{first_match(/set\("cmakeVersion", "(.+)"\)/, template)}") set("buildStagingDir", "#{first_match(/set\("buildStagingDir", "(.+)"\)/, template)}") } } } tasks { wrapper { distributionType = Wrapper.DistributionType.ALL } "prepareKotlinBuildScriptModel" { listOf("Debug", "Release").forEach { dependsOn(":app:unzipJni$it") } } register<Delete>("clean") { // Clean the build artifacts generated by the Gradle build system only, but keep the buildDir rootProject.buildDir.listFiles { _, name -> name == "intermediates" || name == "kotlin" }?.let { delete = it.toSet() } } register<Delete>("cleanAll") { dependsOn("clean") } } EOF end def strings_xml(name) <<-EOF <resources> <string name="app_name">#{name}</string> </resources> EOF end def android_manifest_xml(name) <<-EOF <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.urho3d.#{name.downcase}"> <application android:allowBackup="true" android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> <activity android:name=".MainActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> EOF end def main_activity_kt(name) <<-EOF package io.urho3d.#{name.downcase} import io.urho3d.UrhoActivity class MainActivity : UrhoActivity() EOF end def app_cmake_lists_txt(name) <<-EOF set(TARGET_NAME #{name}) define_source_files(GLOB_CPP_PATTERNS src/main/cpp/*.cpp GLOB_H_PATTERNS src/main/cpp/*.h RECURSE GROUP) setup_main_executable() setup_test() EOF end def root_cmake_lists_txt(name) <<-EOF cmake_minimum_required(VERSION #{first_match(/cmake_minimum_required\s*\(VERSION (.+)\)/, 'CMakeLists.txt')}) project(#{name}) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) include(UrhoCommon) add_subdirectory(app) EOF end def urho_app_h(name) <<-EOF #pragma once #include <Urho3D/Urho3DAll.h> class #{name} : public Application { URHO3D_OBJECT(#{name}, Application); public: explicit #{name}(Context* context); void Start() override; private: SharedPtr<Scene> scene_; }; EOF end def urho_app_cpp(name) <<-EOF #include "#{name}.h" URHO3D_DEFINE_APPLICATION_MAIN(#{name}) #{name}::#{name}(Context* context) : Application(context) {} void #{name}::Start() { auto* cache = GetSubsystem<ResourceCache>(); auto* graphics = GetSubsystem<Graphics>(); graphics->SetWindowIcon(cache->GetResource<Image>("Textures/UrhoIcon.png")); graphics->SetWindowTitle("#{name}"); scene_ = new Scene(context_); scene_->CreateComponent<Octree>(); Node* objectNode = scene_->CreateChild(); auto* object = objectNode->CreateComponent<StaticModel>(); object->SetModel(cache->GetResource<Model>("Models/Mushroom.mdl")); object->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml")); auto* sound = scene_->CreateComponent<SoundSource>(); sound->SetSoundType(SOUND_MUSIC); auto* music = cache->GetResource<Sound>("Music/Ninja Gods.ogg"); music->SetLooped(true); sound->Play(music); Node* lightNode = scene_->CreateChild(); auto* light = lightNode->CreateComponent<Light>(); light->SetLightType(LIGHT_DIRECTIONAL); lightNode->SetDirection(Vector3(0.6f, -1.f, 0.8f)); Node* cameraNode = scene_->CreateChild(); auto* camera = cameraNode->CreateComponent<Camera>(); cameraNode->SetPosition(Vector3(0.f, 0.3f, -3.f)); GetSubsystem<Renderer>()->SetViewport(0, new Viewport(context_, scene_, camera)); SubscribeToEvent(E_KEYUP, [&](StringHash, VariantMap&) { engine_->Exit(); }); SubscribeToEvent(E_UPDATE, [=](StringHash, VariantMap& eventData) { objectNode->Yaw(eventData[Update::P_TIMESTEP].GetFloat()); }); } EOF end def gitattributes(_) <<-EOF *.h linguist-language=C++ EOF end def gitignore(_) <<-EOF # Code::Blocks project settings /*.cbp # Codelite project settings /*.project /*.workspace # Gradle project settings /local.properties .gradle/ build/ .cxx/ # JetBrains IDE project settings /.idea/ /cmake-build-*/ *.iml # KDevelop project settings /*.kdev? # Qt Creator project settings /CMakeLists.txt.user # Visual Studio project settings /CMakeSettings.json /.vs/ /out/ # Misc. *~ *.swp .DS_Store *.log *.bak Thumbs.db .directory EOF end # Load custom rake scripts Dir['.rake/*.rake'].each { |r| load r } # vi: set ts=2 sw=2 expandtab: