rakefile 28 KB

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