rakefile 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. #
  2. # Copyright (c) 2008-2020 the Urho3D project.
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in
  12. # all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. # THE SOFTWARE.
  21. #
  22. task default: :build
  23. desc 'Invoke CMake to configure and generate a build tree'
  24. task :cmake => [:init] do
  25. if ENV['CI']
  26. system 'cmake --version' or abort 'Failed to find CMake'
  27. if ENV['USE_CCACHE'] && /\[cache clear\]/ =~ `git log --format=%B -n1 2>/dev/null`
  28. system 'bash', '-c', 'rm -rf ~/.{ccache,gradle}' or abort 'Failed to clear the build cache'
  29. end
  30. end
  31. next if ENV['PLATFORM'] == 'android' || (Dir.exist?("#{build_tree}") and not ARGV.include?('cmake'))
  32. script = "script/cmake_#{ENV['GENERATOR']}#{ENV['OS'] ? '.bat' : '.sh'}"
  33. build_options = /linux|macOS|win/ =~ ENV['PLATFORM'] ? '' : "-D #{ENV['PLATFORM'].upcase}=1"
  34. File.readlines('script/.build-options').each { |var|
  35. var.chomp!
  36. build_options = "#{build_options} -D #{var}=#{ENV[var]}" if ENV[var]
  37. }
  38. system %Q{#{script} "#{build_tree}" #{build_options}} or abort
  39. end
  40. desc 'Clean the build tree'
  41. task :clean => [:init] do
  42. if ENV['PLATFORM'] == 'android'
  43. Rake::Task[:gradle].invoke('clean')
  44. next
  45. end
  46. system build_target('clean') or abort
  47. end
  48. desc 'Build the software'
  49. task :build, [:target] => [:cmake] do |_, args|
  50. system "ccache -z" if ENV['USE_CCACHE']
  51. if ENV['PLATFORM'] == 'android'
  52. Rake::Task[:gradle].invoke('build -x test')
  53. system "ccache -s" if ENV['USE_CCACHE']
  54. next
  55. end
  56. filter = ''
  57. case ENV['GENERATOR']
  58. when 'xcode'
  59. concurrent = '' # Assume xcodebuild will do the right things without the '-jobs'
  60. filter = '|xcpretty -c && exit ${PIPESTATUS[0]}' if system('xcpretty -v >/dev/null 2>&1')
  61. when 'vs'
  62. concurrent = '/maxCpuCount'
  63. else
  64. concurrent = "-j #{$max_jobs}"
  65. filter = "2>#{lint_err_file}" if ENV['URHO3D_LINT']
  66. end
  67. system "#{build_target(args[:target])} -- #{concurrent} #{ENV['BUILD_PARAMS']} #{filter}" or abort
  68. system "ccache -s" if ENV['USE_CCACHE']
  69. end
  70. desc 'Test the software'
  71. task :test => [:init] do
  72. if ENV['PLATFORM'] == 'android'
  73. Rake::Task[:gradle].invoke('test')
  74. next
  75. elsif ENV['URHO3D_LINT'] == '1'
  76. Rake::Task[:lint].invoke
  77. next
  78. elsif ENV['URHO3D_STYLE'] == '1'
  79. Rake::Task[:style].invoke
  80. next
  81. end
  82. wrapper = ENV['CI'] && ENV['PLATFORM'] == 'linux' ? 'xvfb-run' : ''
  83. test = /xcode|vs/ =~ ENV['GENERATOR'] ? 'RUN_TESTS' : 'test'
  84. system build_target(test, wrapper) or abort
  85. end
  86. desc 'Generate documentation'
  87. task :doc => [:init] do
  88. if ENV['PLATFORM'] == 'android'
  89. Rake::Task[:gradle].invoke('documentationZip')
  90. next
  91. end
  92. system build_target('doc') or abort
  93. end
  94. desc 'Install the software'
  95. task :install, [:prefix] => [:init] do |_, args|
  96. if ENV['PLATFORM'] == 'android'
  97. Rake::Task[:gradle].invoke('publishToMavenLocal')
  98. next
  99. end
  100. wrapper = args[:prefix] && !ENV['OS'] ? "DESTDIR=#{verify_path(args[:prefix])}" : ''
  101. system build_target('install', wrapper) or abort
  102. end
  103. desc 'Package build artifact'
  104. task :package => [:init] do
  105. next if ENV['PLATFORM'] == 'android'
  106. wrapper = /linux|rpi|arm/ =~ ENV['PLATFORM'] && ENV['URHO3D_64BIT'] == '0' ? 'setarch i686' : ''
  107. system build_target('package', wrapper) or abort
  108. end
  109. desc 'Publish build artifact'
  110. task :publish => [:init] do
  111. if ENV['PLATFORM'] == 'android'
  112. Rake::Task[:gradle].invoke("publish #{/refs\/tags\// =~ ENV['GITHUB_REF'] ? 'bintrayUpload' : ''}")
  113. next
  114. end
  115. Rake::Task[$publish_task.to_sym].invoke if $publish_task
  116. end
  117. desc 'Create a new project'
  118. task :new, [:name, :parent_dir, :use_copy] => [:init] do |_, args|
  119. abort 'The "new" task can only be invoked in the Urho3D project root!' unless first_match(/^# (Urho3D)$/, 'README.md')
  120. args.with_defaults(:name => 'UrhoApp', :parent_dir => '~/projects', :use_copy => false)
  121. name = args[:name]
  122. parent_dir = verify_path(args[:parent_dir])
  123. dir = "#{parent_dir}/#{name}"
  124. use_copy = args[:use_copy] || dockerized?
  125. abort "The directory '#{dir}' already exists!" if Dir.exists?(dir)
  126. puts "Creating a new project in #{dir}..."
  127. func = FileUtils.method(use_copy ? :cp_r : :ln_s)
  128. source_tree(name).split("\n").each do |it|
  129. dirname, basename = /\// =~ it.split(/\s*<-\s*/).first ? it.match(/([^\s<]+)\/(.+)/).captures : ['.', it]
  130. FileUtils.mkdir_p("#{dir}/#{dirname}")
  131. if (matched = basename.match(/(?<basename>\S+)\s*<-\s*:(?<symbol>\S+)/))
  132. File.write("#{dir}/#{dirname}/#{matched[:basename]}", method(matched[:symbol].to_sym).call(name))
  133. elsif (matched = basename.match(/(?<basename>\S+)\s*<-\s*(?<dirname>\S+)/))
  134. func.call(verify_path("#{matched[:dirname]}/#{matched[:basename]}"), "#{dir}/#{dirname}")
  135. else
  136. func.call(verify_path(it), "#{dir}/#{dirname}")
  137. end
  138. end
  139. puts "Done!"
  140. end
  141. ### Internal tasks ###
  142. task :ci do
  143. ENV['URHO3D_PCH'] = '0' if ENV['PLATFORM'] == 'linux-gcc' # TODO - PCH causes cache miss on initial build for Linux/GCC, why?
  144. platform_modifier = /(.+?)-(.+)/.match(ENV['PLATFORM'])
  145. if platform_modifier
  146. ENV['PLATFORM'] = platform_modifier[1]
  147. ENV['MODIFIER'] = platform_modifier[2]
  148. end
  149. case ENV['HOST']
  150. when 'macOS'
  151. # Do nothing
  152. when 'windows'
  153. if ENV['MODIFIER'] == 'gcc'
  154. ENV['URHO3D_DEPLOYMENT_TARGET'] = 'generic'
  155. ENV['GENERATOR'] = 'mingw'
  156. end
  157. else
  158. ENV['URHO3D_DEPLOYMENT_TARGET'] = 'generic' if /linux|mingw/ =~ ENV['PLATFORM']
  159. if /clang/ =~ ENV['MODIFIER']
  160. ENV['CC'] = 'clang'
  161. ENV['CXX'] = 'clang++'
  162. end
  163. end
  164. ENV['BUILD_TREE'] = 'build/ci'
  165. ENV['CMAKE_BUILD_TYPE'] = ENV['BUILD_TYPE'] == 'dbg' ? 'Debug' : 'Release' if /dbg|rel/ =~ ENV['BUILD_TYPE']
  166. case ENV['GRAPHICS_API']
  167. when 'DX11'
  168. ENV['URHO3D_D3D11'] = '1'
  169. when 'DX9'
  170. ENV['URHO3D_OPENGL'] = '0' # Need to make this explicit because 'MINGW' default to use OpenGL otherwise
  171. when 'OpenGL'
  172. ENV['URHO3D_OPENGL'] = '1'
  173. else
  174. # Do nothing
  175. end
  176. case ENV['PLATFORM']
  177. when 'web'
  178. ENV['EMSCRIPTEN_SHARE_DATA'] = '1'
  179. $max_jobs = 1 if ENV['BUILD_TYPE'] == 'dbg'
  180. $publish_task = 'ci_publish_web'
  181. else
  182. # Do nothing
  183. end
  184. ENV['URHO3D_LIB_TYPE'] = ENV['LIB_TYPE'].upcase if /static|shared/ =~ ENV['LIB_TYPE']
  185. ENV['URHO3D_TESTING'] = '1' if /linux|macOS|win/ =~ ENV['PLATFORM']
  186. ENV['URHO3D_LINT'] = '1' if ENV['MODIFIER'] == 'clang-tidy'
  187. ENV['URHO3D_STYLE'] = '1' if ENV['MODIFIER'] == 'clang-format'
  188. # Enable all the bells and whistles
  189. %w[URHO3D_DATABASE_SQLITE URHO3D_EXTRAS].each { |it| ENV[it] = '1' }
  190. end
  191. task :ci_publish_web do
  192. require 'json'
  193. 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'
  194. 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'
  195. Dir.chdir('build/urho3d.github.io/samples') {
  196. next unless system 'git diff --quiet Urho3D.js.data'
  197. uuid = `git diff --color=never --word-diff-regex='\\w+' --word-diff=porcelain Urho3D.js`.split.grep(/^[+-]\w+-/).map { |it| it[0] = ''; it }
  198. system %Q(ruby -i.bak -pe "gsub '#{uuid.last}', '#{uuid.first}'" Urho3D.js) or abort 'Failed to substitute UUID'
  199. if system 'git diff --quiet Urho3D.js'
  200. File.unlink 'Urho3D.js.bak'
  201. Dir['*.js'].each { |file| system %Q(ruby -i -pe "gsub '#{uuid.last}', '#{uuid.first}'" #{file}) }
  202. else
  203. File.rename 'Urho3D.js.bak', 'Urho3D.js'
  204. end
  205. }
  206. web = {'samples' => {}}
  207. Dir.chdir('build/urho3d.github.io/samples') { web['samples']['Native'] = Dir['*.html'].sort }
  208. web['player'] = web['samples']['Native'].pop # Assume the last sample after sorting is the Urho3DPlayer.html
  209. {'AngelScript' => 'Scripts', 'Lua' => 'LuaScripts'}.each { |lang, subdir|
  210. Dir.chdir("bin/Data/#{subdir}") {
  211. script_samples = Dir['[0-9]*'].sort
  212. deleted_samples = [] # Delete samples that do not have their native counterpart
  213. script_samples.each { |sample| deleted_samples.push sample unless web['samples']['Native'].include? "#{sample.split('.').first}.html" }
  214. web['samples'][lang] = (script_samples - deleted_samples).map { |sample| "#{subdir}/#{sample}" }
  215. }
  216. }
  217. File.open('build/urho3d.github.io/_data/web.json', 'w') { |file| file.puts web.to_json }
  218. system %Q{
  219. cd build/urho3d.github.io && \\
  220. git config user.name #{ENV['PUBLISHER_NAME']} && \\
  221. git config user.email #{ENV['PUBLISHER_EMAIL']} && \\
  222. git remote set-url --push origin https://#{ENV['PUBLISHER_TOKEN']}@github.com/urho3d/urho3d.github.io.git && \\
  223. git add -A . && \\
  224. ( 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) && \\
  225. git push -q >/dev/null 2>&1
  226. } or abort 'Failed to update Web samples'
  227. end
  228. task :source_checksum do
  229. require 'digest'
  230. sha256_final = Digest::SHA256.new
  231. sha256_iter = Digest::SHA256
  232. Dir['Source/**/*.{c,h}*'].each { |it| sha256_final << sha256_iter.file(it).hexdigest }
  233. puts "::set-output name=hexdigest::#{sha256_final.hexdigest}"
  234. end
  235. task :update_dot_files do
  236. system 'bash', '-c', %q{
  237. perl -ne 'undef $/; print $1 if /(Build Option.*?(?=\n\n))/s' Docs/GettingStarted.dox \
  238. |tail -n +3 |cut -d'|' -f2 |tr -d [:blank:] >script/.build-options && \
  239. echo URHO3D_LINT >>script/.build-options && \
  240. cat script/.build-options <(perl -ne 'while (/([A-Z_]+):.+?/g) {print "$1\n"}' .github/workflows/main.yml) \
  241. <(perl -ne 'while (/ENV\[\x27(\w+)\x27\]/g) {print "$1\n"}' rakefile) \
  242. <(perl -ne 'while (/System.getenv\\("(\w+)"\\)/g) {print "$1\n"}' android/urho3d-lib/build.gradle.kts) \
  243. |sort |uniq |grep -Ev '^(HOME|PATH)$' >script/.env-file
  244. } or abort 'Failed to update dot files'
  245. end
  246. task :init do
  247. next if $max_jobs
  248. Rake::Task[:ci].invoke if ENV['CI']
  249. case build_host
  250. when /linux/
  251. $max_jobs = `grep -c processor /proc/cpuinfo`.chomp unless $max_jobs
  252. ENV['GENERATOR'] = 'generic' unless ENV['GENERATOR']
  253. ENV['PLATFORM'] = 'linux' unless ENV['PLATFORM']
  254. when /darwin|macOS/
  255. $max_jobs = `sysctl -n hw.logicalcpu`.chomp unless $max_jobs
  256. ENV['GENERATOR'] = 'xcode' unless ENV['GENERATOR']
  257. ENV['PLATFORM'] = 'macOS' unless ENV['PLATFORM']
  258. when /win32|mingw|mswin|windows/
  259. unless $max_jobs
  260. require 'win32ole'
  261. WIN32OLE.connect('winmgmts://').ExecQuery("select NumberOfLogicalProcessors from Win32_ComputerSystem").each { |it|
  262. $max_jobs = it.NumberOfLogicalProcessors
  263. }
  264. end
  265. ENV['GENERATOR'] = 'vs' unless ENV['GENERATOR']
  266. ENV['PLATFORM'] = 'win' unless ENV['PLATFORM']
  267. else
  268. abort "Unsupported host system: #{build_host}"
  269. end
  270. # The 'ARCH' env-var, when set, has higher precedence than the 'URHO3D_64BIT' env-var
  271. ENV['URHO3D_64BIT'] = ENV['ARCH'] == '32' ? '0' : '1' if /32|64/ =~ ENV['ARCH']
  272. end
  273. task :gradle, [:task] do |_, args|
  274. system "#{ENV['OS'] ? 'gradlew.bat' : './gradlew'} #{args[:task]} #{ENV['CI'] ? '--console plain' : ''}" or abort
  275. end
  276. task :lint do
  277. lint_err = File.read(lint_err_file)
  278. puts lint_err
  279. # TODO: Tighten the check by failing the job later
  280. # abort 'Failed to pass linter checks' unless lint_err.empty?
  281. # puts 'Passed the linter checks'
  282. end
  283. task :style do
  284. system 'bash', '-c', %q{
  285. git diff --name-only HEAD~ -- Source \
  286. |grep -v ThirdParty \
  287. |grep -P '\.(?:c|cpp|h|hpp)' \
  288. |xargs clang-format -n -Werror 2>&1 \
  289. |tee build/clang-format.out \
  290. && exit ${PIPESTATUS[3]}
  291. } or abort 'Failed to pass style checks'
  292. puts 'Passed the style checks'
  293. end
  294. ### Internal methods ###
  295. def build_host
  296. ENV['HOST'] || RUBY_PLATFORM
  297. end
  298. def build_tree
  299. ENV['BUILD_TREE'] || "build/#{dockerized? ? 'dockerized-' : ''}#{ENV['PLATFORM'].downcase}"
  300. end
  301. def build_config
  302. /xcode|vs/ =~ ENV['GENERATOR'] ? "--config #{ENV.fetch('CONFIG', 'Release')}" : ''
  303. end
  304. def build_target(tgt, wrapper = '')
  305. %Q{#{wrapper} cmake --build "#{build_tree}" #{build_config} #{tgt ? "--target #{tgt}" : ''}}
  306. end
  307. def lint_err_file
  308. 'build/clang-tidy.out'
  309. end
  310. def verify_path(path)
  311. require 'pathname'
  312. begin
  313. Pathname.new(File.expand_path(path)).realdirpath.to_s
  314. rescue
  315. abort "The specified path '#{path}' is invalid!"
  316. end
  317. end
  318. def dockerized?
  319. File.exists?('/entrypoint.sh')
  320. end
  321. def first_match(regex, from)
  322. begin
  323. if from.instance_of?(Array)
  324. array = from
  325. else
  326. array = File.exists?(from) ? File.readlines(from) : from.split("\n")
  327. end
  328. array.grep(regex).first.match(regex).captures.first
  329. rescue
  330. nil
  331. end
  332. end
  333. def source_tree(name)
  334. <<-EOF
  335. bin/CoreData
  336. bin/Data/Materials/Mushroom.xml
  337. bin/Data/Models/Mushroom.mdl
  338. bin/Data/Music/Ninja Gods.ogg
  339. bin/Data/Textures/Mushroom.dds
  340. bin/Data/Textures/UrhoIcon.icns
  341. bin/Data/Textures/UrhoIcon.png
  342. cmake
  343. gradle
  344. script
  345. app/src/main/cpp/#{name}.cpp <- :urho_app_cpp
  346. app/src/main/cpp/#{name}.h <- :urho_app_h
  347. app/src/main/java/io/urho3d/#{name.downcase}/MainActivity.kt <- :main_activity_kt
  348. #{Dir.chdir('android/launcher-app') { Dir['src/main/res/{drawable,mipmap}*'].map { |it| "app/#{it} <- android/launcher-app/src/main/res" }.join("\n") }}
  349. app/src/main/res/values/strings.xml <- :strings_xml
  350. app/src/main/AndroidManifest.xml <- :android_manifest_xml
  351. app/build.gradle.kts <- :app_build_gradle_kts
  352. app/CMakeLists.txt <- :app_cmake_lists_txt
  353. app/proguard-rules.pro <- android/launcher-app
  354. build.gradle.kts <- :root_build_gradle_kts
  355. CMakeLists.txt <- :root_cmake_lists_txt
  356. gradle.properties
  357. gradlew
  358. gradlew.bat
  359. rakefile
  360. settings.gradle.kts <- :settings_gradle_kts
  361. .clang-format
  362. .clang-tidy
  363. .gitattributes <- :gitattributes
  364. .gitignore <- :gitignore
  365. EOF
  366. end
  367. def settings_gradle_kts(name)
  368. <<-EOF
  369. rootProject.name = "#{name}"
  370. include(":app")
  371. EOF
  372. end
  373. def app_build_gradle_kts(name)
  374. template = File.readlines('android/launcher-app/build.gradle.kts')
  375. sdk_version = first_match(/compileSdkVersion\((\d+)\)/, template)
  376. min_sdk_version = first_match(/minSdkVersion\((\d+)\)/, template)
  377. aar_version = ENV['CI'] && ENV['PLATFORM'] != 'android' ? 'unknown' : # Skip using gradle all together when on CI, unless for Android build
  378. first_match(/AAR version: (.+)/, `#{ENV['OS'] ? 'gradlew.bat' : './gradlew'} aarVersion 2>#{ENV['OS'] ? 'null' : '/dev/null'}`)
  379. type = ENV.fetch('URHO3D_LIB_TYPE', 'STATIC').downcase
  380. <<-EOF
  381. plugins {
  382. id("com.android.application")
  383. kotlin("android")
  384. kotlin("android.extensions")
  385. }
  386. val kotlinVersion: String by ext
  387. val ndkSideBySideVersion: String by ext
  388. val cmakeVersion: String by ext
  389. val buildStagingDir: String by ext
  390. android {
  391. ndkVersion = ndkSideBySideVersion
  392. compileSdkVersion(#{sdk_version})
  393. defaultConfig {
  394. minSdkVersion(#{min_sdk_version})
  395. targetSdkVersion(#{sdk_version})
  396. applicationId = "io.urho3d.#{name}"
  397. versionCode = 1
  398. versionName = "1.0"
  399. testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
  400. externalNativeBuild {
  401. cmake {
  402. arguments.apply {
  403. System.getenv("ANDROID_CCACHE")?.let { add("-D ANDROID_CCACHE=$it") }
  404. add("-D JNI_DIR=${project.file(buildStagingDir)}")
  405. // Pass along matching env-vars as CMake build options
  406. addAll(project.file("../script/.build-options")
  407. .readLines()
  408. .mapNotNull { variable -> System.getenv(variable)?.let { "-D $variable=$it" } }
  409. )
  410. }
  411. }
  412. }
  413. splits {
  414. abi {
  415. isEnable = project.hasProperty("ANDROID_ABI")
  416. reset()
  417. include(
  418. *(project.findProperty("ANDROID_ABI") as String? ?: "")
  419. .split(',')
  420. .toTypedArray()
  421. )
  422. }
  423. }
  424. }
  425. buildTypes {
  426. named("release") {
  427. isMinifyEnabled = false
  428. proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
  429. }
  430. }
  431. lintOptions {
  432. isAbortOnError = false
  433. }
  434. externalNativeBuild {
  435. cmake {
  436. version = cmakeVersion
  437. path = project.file("../CMakeLists.txt")
  438. setBuildStagingDirectory(buildStagingDir)
  439. }
  440. }
  441. sourceSets {
  442. named("main") {
  443. assets.srcDir(project.file("../bin"))
  444. }
  445. }
  446. }
  447. val urhoReleaseImpl by configurations.creating { isCanBeResolved = true }
  448. configurations.releaseImplementation.get().extendsFrom(urhoReleaseImpl)
  449. val urhoDebugImpl by configurations.creating { isCanBeResolved = true }
  450. configurations.debugImplementation.get().extendsFrom(urhoDebugImpl)
  451. dependencies {
  452. urhoReleaseImpl("io.urho3d:urho3d-lib-#{type}:#{aar_version}")
  453. urhoDebugImpl("io.urho3d:urho3d-lib-#{type}-debug:#{aar_version}")
  454. implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
  455. implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
  456. implementation("androidx.core:core-ktx:#{first_match(/"androidx.core:core-ktx:(.+)"/, template)}")
  457. implementation("androidx.appcompat:appcompat:#{first_match(/"androidx.appcompat:appcompat:(.+)"/, template)}")
  458. implementation("androidx.constraintlayout:constraintlayout:#{first_match(/"androidx.constraintlayout:constraintlayout:(.+)"/, template)}")
  459. testImplementation("junit:junit:#{first_match(/"junit:junit:(.+)"/, template)}")
  460. androidTestImplementation("androidx.test:runner:#{first_match(/"androidx.test:runner:(.+)"/, template)}")
  461. androidTestImplementation("androidx.test.espresso:espresso-core:#{first_match(/"androidx.test.espresso:espresso-core:(.+)"/, template)}")
  462. }
  463. afterEvaluate {
  464. android.buildTypes.forEach { buildType ->
  465. val config = buildType.name.capitalize()
  466. val unzipTaskName = "unzipJni$config"
  467. tasks {
  468. "generateJsonModel$config" {
  469. dependsOn(unzipTaskName)
  470. }
  471. register<Copy>(unzipTaskName) {
  472. val aar = configurations["urho${config}Impl"].resolve().first { it.name.startsWith("urho3d-lib") }
  473. from(zipTree(aar))
  474. include("urho3d/**")
  475. into(android.externalNativeBuild.cmake.buildStagingDirectory)
  476. }
  477. }
  478. }
  479. }
  480. tasks {
  481. register<Delete>("cleanAll") {
  482. dependsOn("clean")
  483. delete = setOf(android.externalNativeBuild.cmake.buildStagingDirectory)
  484. }
  485. }
  486. EOF
  487. end
  488. def root_build_gradle_kts(_)
  489. template = File.readlines('build.gradle.kts')
  490. <<-EOF
  491. buildscript {
  492. extra["kotlinVersion"] = "#{first_match(/extra\["kotlinVersion"\] = "(.+)"/, template)}"
  493. val kotlinVersion: String by extra
  494. repositories {
  495. google()
  496. jcenter()
  497. }
  498. dependencies {
  499. classpath("com.android.tools.build:gradle:#{first_match(/"com.android.tools.build:gradle:(.+)"/, template)}")
  500. classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
  501. }
  502. }
  503. val kotlinVersion: String by ext
  504. allprojects {
  505. repositories {
  506. google()
  507. jcenter()
  508. // Remove below two repos if you are only using released AAR from JCenter
  509. mavenLocal()
  510. if (System.getenv("GITHUB_ACTOR") != null && System.getenv("GITHUB_TOKEN") != null) {
  511. maven {
  512. name = "GitHubPackages"
  513. url = uri("https://maven.pkg.github.com/urho3d/Urho3D")
  514. credentials {
  515. username = System.getenv("GITHUB_ACTOR")
  516. password = System.getenv("GITHUB_TOKEN")
  517. }
  518. }
  519. }
  520. }
  521. buildscript {
  522. ext {
  523. set("kotlinVersion", kotlinVersion)
  524. set("ndkSideBySideVersion", "#{first_match(/set\("ndkSideBySideVersion", "(.+)"\)/, template)}")
  525. set("cmakeVersion", "#{first_match(/set\("cmakeVersion", "(.+)"\)/, template)}")
  526. set("buildStagingDir", "#{first_match(/set\("buildStagingDir", "(.+)"\)/, template)}")
  527. }
  528. }
  529. }
  530. tasks {
  531. wrapper {
  532. distributionType = Wrapper.DistributionType.ALL
  533. }
  534. "prepareKotlinBuildScriptModel" {
  535. listOf("Debug", "Release").forEach {
  536. dependsOn(":app:unzipJni$it")
  537. }
  538. }
  539. register<Delete>("clean") {
  540. // Clean the build artifacts generated by the Gradle build system only, but keep the buildDir
  541. rootProject.buildDir.listFiles { _, name -> name == "intermediates" || name == "kotlin" }?.let {
  542. delete = it.toSet()
  543. }
  544. }
  545. register<Delete>("cleanAll") {
  546. dependsOn("clean")
  547. }
  548. }
  549. EOF
  550. end
  551. def strings_xml(name)
  552. <<-EOF
  553. <resources>
  554. <string name="app_name">#{name}</string>
  555. </resources>
  556. EOF
  557. end
  558. def android_manifest_xml(name)
  559. <<-EOF
  560. <?xml version="1.0" encoding="utf-8"?>
  561. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  562. package="io.urho3d.#{name.downcase}">
  563. <application
  564. android:allowBackup="true"
  565. android:hardwareAccelerated="true"
  566. android:icon="@mipmap/ic_launcher"
  567. android:label="@string/app_name"
  568. android:roundIcon="@mipmap/ic_launcher_round"
  569. android:supportsRtl="true">
  570. <activity
  571. android:name=".MainActivity"
  572. android:configChanges="keyboardHidden|orientation|screenSize"
  573. android:screenOrientation="landscape"
  574. android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
  575. <intent-filter>
  576. <action android:name="android.intent.action.MAIN"/>
  577. <category android:name="android.intent.category.LAUNCHER"/>
  578. </intent-filter>
  579. </activity>
  580. </application>
  581. </manifest>
  582. EOF
  583. end
  584. def main_activity_kt(name)
  585. <<-EOF
  586. package io.urho3d.#{name.downcase}
  587. import io.urho3d.UrhoActivity
  588. class MainActivity : UrhoActivity()
  589. EOF
  590. end
  591. def app_cmake_lists_txt(name)
  592. <<-EOF
  593. set(TARGET_NAME #{name})
  594. define_source_files(GLOB_CPP_PATTERNS src/main/cpp/*.cpp GLOB_H_PATTERNS src/main/cpp/*.h RECURSE GROUP)
  595. setup_main_executable()
  596. setup_test()
  597. EOF
  598. end
  599. def root_cmake_lists_txt(name)
  600. <<-EOF
  601. cmake_minimum_required(VERSION #{first_match(/cmake_minimum_required\s*\(VERSION (.+)\)/, 'CMakeLists.txt')})
  602. project(#{name})
  603. set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
  604. include(UrhoCommon)
  605. add_subdirectory(app)
  606. EOF
  607. end
  608. def urho_app_h(name)
  609. <<-EOF
  610. #pragma once
  611. #include <Urho3D/Urho3DAll.h>
  612. class #{name} : public Application
  613. {
  614. URHO3D_OBJECT(#{name}, Application);
  615. public:
  616. explicit #{name}(Context* context);
  617. void Start() override;
  618. private:
  619. SharedPtr<Scene> scene_;
  620. };
  621. EOF
  622. end
  623. def urho_app_cpp(name)
  624. <<-EOF
  625. #include "#{name}.h"
  626. URHO3D_DEFINE_APPLICATION_MAIN(#{name});
  627. #{name}::#{name}(Context* context)
  628. : Application(context)
  629. {
  630. }
  631. void #{name}::Start()
  632. {
  633. auto* cache = GetSubsystem<ResourceCache>();
  634. auto* graphics = GetSubsystem<Graphics>();
  635. graphics->SetWindowIcon(cache->GetResource<Image>("Textures/UrhoIcon.png"));
  636. graphics->SetWindowTitle("#{name}");
  637. scene_ = new Scene(context_);
  638. scene_->CreateComponent<Octree>();
  639. Node* objectNode = scene_->CreateChild();
  640. auto* object = objectNode->CreateComponent<StaticModel>();
  641. object->SetModel(cache->GetResource<Model>("Models/Mushroom.mdl"));
  642. object->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml"));
  643. auto* sound = scene_->CreateComponent<SoundSource>();
  644. sound->SetSoundType(SOUND_MUSIC);
  645. auto* music = cache->GetResource<Sound>("Music/Ninja Gods.ogg");
  646. music->SetLooped(true);
  647. sound->Play(music);
  648. Node* lightNode = scene_->CreateChild();
  649. auto* light = lightNode->CreateComponent<Light>();
  650. light->SetLightType(LIGHT_DIRECTIONAL);
  651. lightNode->SetDirection(Vector3(0.6f, -1.f, 0.8f));
  652. Node* cameraNode = scene_->CreateChild();
  653. auto* camera = cameraNode->CreateComponent<Camera>();
  654. cameraNode->SetPosition(Vector3(0.f, 0.3f, -3.f));
  655. GetSubsystem<Renderer>()->SetViewport(0, new Viewport(context_, scene_, camera));
  656. SubscribeToEvent(E_KEYUP, [&](StringHash, VariantMap&) { engine_->Exit(); });
  657. SubscribeToEvent(E_UPDATE, [=](StringHash, VariantMap& eventData) {
  658. objectNode->Yaw(eventData[Update::P_TIMESTEP].GetFloat());
  659. });
  660. }
  661. EOF
  662. end
  663. def gitattributes(_)
  664. <<-EOF
  665. *.h linguist-language=C++
  666. EOF
  667. end
  668. def gitignore(_)
  669. <<-EOF
  670. # Code::Blocks project settings
  671. /*.cbp
  672. # Codelite project settings
  673. /*.project
  674. /*.workspace
  675. # Gradle project settings
  676. /local.properties
  677. .gradle/
  678. build/
  679. .cxx/
  680. # JetBrains IDE project settings
  681. /.idea/
  682. /cmake-build-*/
  683. *.iml
  684. # KDevelop project settings
  685. /*.kdev?
  686. # Qt Creator project settings
  687. /CMakeLists.txt.user
  688. # Visual Studio project settings
  689. /CMakeSettings.json
  690. /.vs/
  691. /out/
  692. # Misc.
  693. *~
  694. *.swp
  695. .DS_Store
  696. *.log
  697. *.bak
  698. Thumbs.db
  699. .directory
  700. EOF
  701. end
  702. # Load custom rake scripts
  703. Dir['.rake/*.rake'].each { |r| load r }
  704. # vi: set ts=2 sw=2 expandtab: