Rakefile 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. #
  2. # Copyright (c) 2008-2019 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. require 'pathname'
  23. require 'json'
  24. require 'yaml'
  25. ### Tasks for general users ###
  26. # Usage: rake scaffolding dir=/path/to/new/project/root [project=Scaffolding] [target=Main]
  27. desc 'Create a new project using Urho3D as external library'
  28. task :scaffolding do
  29. abort 'Usage: rake scaffolding dir=/path/to/new/project/root [project=Scaffolding] [target=Main]' unless ENV['dir']
  30. project = ENV['project'] || 'Scaffolding'
  31. target = ENV['target'] || 'Main'
  32. abs_path = scaffolding ENV['dir'], project, target
  33. puts "\nNew project created in #{abs_path}.\n\n"
  34. puts "In order to configure and generate your project build tree you may need to first set"
  35. puts "'URHO3D_HOME' environment variable or use 'URHO3D_HOME' build option to point to the"
  36. puts "Urho3D project build tree or custom Urho3D SDK installation location.\n\n"
  37. puts "Please see https://urho3d.github.io/documentation/HEAD/_using_library.html for more detail.\nFor example:\n\n"
  38. puts "$ cd #{abs_path}\n$ rake cmake URHO3D_HOME=/path/to/Urho3D/build-tree\n$ rake make\n\n"
  39. end
  40. # Usage: rake cmake [<generator>] [<platform>] [<option>=<value> [<option>=<value>]] [[<platform>_]build_tree=/path/to/build-tree] [fix_scm]
  41. # e.g.: rake cmake clean android; or rake cmake android URHO3D_LIB_TYPE=SHARED; or rake cmake ios URHO3D_LUA=1 build_tree=~/ios-Build
  42. #
  43. # To avoid repeating the customized build tree locations, you can set and export them as environment variables.
  44. # e.g.: export native_build_tree=~/custom-native-Build android_build_tree=~/custom-android-Build mingw_build_tree=~/custom-mingw-Build rpi_build_tree=~/custom-rpi-Build
  45. # rake cmake rpi URHO3D_LUAJIT=1 URHO3D_LUAJIT_AMALG=1 && rake make rpi
  46. # The RPI build tree will be generated in the ~/custom-rpi-Build and then build from there
  47. desc 'Invoke one of the build scripts with the build tree location predetermined based on the target platform'
  48. task :cmake do
  49. script = 'cmake_generic'
  50. platform = 'native'
  51. build_options = ''
  52. File.readlines('script/.build-options').each { |var|
  53. var.chomp!
  54. ARGV << "#{var}=\"#{ENV[var]}\"" if ENV[var] && !ARGV.find { |arg| /#{var}=/ =~ arg }
  55. }
  56. ARGV.each { |option|
  57. task option.to_sym do ; end; Rake::Task[option].clear # No-op hack
  58. case option
  59. when 'cmake', 'generic'
  60. # do nothing
  61. when 'clean', 'codeblocks', 'codelite', 'eclipse', 'ninja', 'vs2015', 'vs2017', 'vs2019', 'xcode'
  62. script = "cmake_#{option}" unless script == 'cmake_clean'
  63. when 'android', 'arm', 'ios', 'tvos', 'mingw', 'rpi', 'web'
  64. platform = option
  65. build_options = "#{build_options} -D#{option.upcase}=1" unless script == 'cmake_clean'
  66. script = 'cmake_xcode' if /(?:i|tv)os/ =~ option && script != 'cmake_clean'
  67. script = 'cmake_mingw' if option == 'mingw' && ENV['OS'] && script != 'cmake_clean'
  68. when 'fix_scm'
  69. build_options = "#{build_options} --fix-scm" if script == 'cmake_eclipse'
  70. else
  71. build_options = "#{build_options} -D#{option}" unless /build_tree=.*/ =~ option || script == 'cmake_clean'
  72. end
  73. }
  74. build_tree = ENV["#{platform}_build_tree"] || ENV['build_tree'] || "build/#{platform}"
  75. if ENV['OS']
  76. # CMake claims mingw32-make does not build correctly with MSYS shell in the PATH env-var and prevents build tree generation if so
  77. # Our CI on Windows host requires MSYS shell, so we cannot just simply remove it from the PATH globally
  78. # Instead, we modify the PATH env-var locally here just before invoking the CMake generator
  79. ENV['PATH'] = ENV['PATH'].gsub /Git\\usr\\bin/, 'GoAway'
  80. else
  81. ccache_envvar = ENV['CCACHE_SLOPPINESS'] ? '' : 'CCACHE_SLOPPINESS=pch_defines,time_macros' # Only attempt to do the right thing when user hasn't done it
  82. ccache_envvar = "#{ccache_envvar} CCACHE_COMPRESS=1" unless ENV['CCACHE_COMPRESS']
  83. end
  84. system "#{ccache_envvar} script/#{script}#{ENV['OS'] ? '.bat' : '.sh'} \"#{build_tree}\" #{build_options}" or abort
  85. end
  86. # Usage: rake make [<platform>] [<option>=<value> [<option>=<value>]] [[<platform>_]build_tree=/path/to/build-tree] [numjobs=n] [clean_first] [unfilter]
  87. # e.g.: rake make android; or rake make android doc; or rake make ios config=Debug sdk=iphonesimulator build_tree=~/ios-Build
  88. desc 'Build the generated project in its corresponding build tree'
  89. task :make do
  90. numjobs = ENV['numjobs'] || ''
  91. platform = 'native'
  92. cmake_build_options = ''
  93. build_options = ''
  94. unfilter = false
  95. ['config', 'target', 'sdk', 'ARCHS', 'ARGS', 'unfilter', 'verbosity'].each { |var|
  96. ARGV << "#{var}=\"#{ENV[var]}\"" if ENV[var] && !ARGV.find { |arg| /#{var}=/ =~ arg }
  97. }
  98. ARGV.each { |option|
  99. task option.to_sym do ; end; Rake::Task[option].clear # No-op hack
  100. case option
  101. when 'codeblocks', 'codelite', 'eclipse', 'generic', 'make', 'ninja', 'vs2015', 'vs2017', 'vs2019', 'xcode'
  102. # do nothing
  103. when 'android', 'arm', 'ios', 'tvos', 'mingw', 'rpi', 'web'
  104. platform = option
  105. when 'clean_first'
  106. cmake_build_options = "#{cmake_build_options} --clean-first"
  107. when 'unfilter'
  108. unfilter = true
  109. else
  110. if /(?:config|target)=.*/ =~ option
  111. cmake_build_options = "#{cmake_build_options} --#{option.gsub(/=/, ' ')}"
  112. elsif /(?:ARCHS|ARGS)=.*/ =~ option
  113. # The ARCHS option is only applicable for xcodebuild, useful to specify a non-default arch to build when in Debug build configuration where ONLY_ACTIVE_ARCH is set to YES
  114. # The ARGS option is only applicable for make, useful to pass extra arguments while building a specific target, e.g. ARGS=-VV when the target is 'test' to turn on extra verbose mode
  115. build_options = "#{build_options} #{option}"
  116. elsif /unfilter=\W*?(?<unfilter_value>\w+)/ =~ option
  117. unfilter = !(/(?:true|yes|1)/i =~ unfilter_value).nil?
  118. elsif /verbosity=.*/ =~ option
  119. # The verbosity option is only applicable for msbuild when building RUN_TESTS, useful to specify the verbosity of the test output
  120. if ARGV.include?('target=RUN_TESTS')
  121. build_options = "#{build_options} /#{option.gsub(/=/, ':')}"
  122. unfilter = true
  123. end
  124. elsif /(?:build_tree|numjobs)=.*/ !~ option
  125. build_options = "#{build_options} #{/=/ =~ option ? '-' + option.gsub(/=/, ' ') : option}"
  126. end
  127. end
  128. }
  129. build_tree = ENV["#{platform}_build_tree"] || ENV['build_tree'] || "build/#{platform}"
  130. if ENV['OS']
  131. # While calling mingw-32-make itself does not require the PATH to be altered (as long as it is not inside an MSYS shell),
  132. # we have to do it again here because our build system invokes CMake internally to generate things on-the-fly as part of the build process
  133. ENV['PATH'] = ENV['PATH'].gsub /Git\\usr\\bin/, 'GoAway'
  134. else
  135. ccache_envvar = ENV['CCACHE_SLOPPINESS'] ? '' : 'CCACHE_SLOPPINESS=pch_defines,time_macros' # Only attempt to do the right thing when user hasn't done it
  136. ccache_envvar = "#{ccache_envvar} CCACHE_COMPRESS=1" unless ENV['CCACHE_COMPRESS']
  137. end
  138. if !Dir.glob("#{build_tree}/*.xcodeproj").empty?
  139. # xcodebuild
  140. if !numjobs.empty?
  141. build_options = "-jobs #{numjobs}#{build_options}"
  142. end
  143. filter = !unfilter && !ARGV.include?('target=RUN_TESTS') && system('xcpretty -v >/dev/null 2>&1') ? '|xcpretty -c && exit ${PIPESTATUS[0]}' : ''
  144. elsif !Dir.glob("#{build_tree}\\*.sln".gsub(/\\/, '/')).empty?
  145. # msbuild
  146. numjobs = ":#{numjobs}" unless numjobs.empty?
  147. build_options = "/maxcpucount#{numjobs}#{build_options}"
  148. filter = unfilter ? '' : '/nologo /verbosity:minimal'
  149. filter = filter + ' /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"' if ENV['APPVEYOR']
  150. elsif !Dir.glob("#{build_tree}/*.ninja").empty?
  151. # ninja
  152. if !numjobs.empty?
  153. build_options = "-j#{numjobs}#{build_options}"
  154. end
  155. filter = ''
  156. else
  157. # make
  158. if numjobs.empty?
  159. case RUBY_PLATFORM
  160. when /linux/
  161. numjobs = (platform == 'web' ? `grep 'core id' /proc/cpuinfo |sort |uniq |wc -l` : `grep -c processor /proc/cpuinfo`).chomp
  162. when /darwin/
  163. numjobs = `sysctl -n hw.#{platform == 'web' ? 'physical' : 'logical'}cpu`.chomp
  164. when /win32|mingw|mswin/
  165. require 'win32ole'
  166. WIN32OLE.connect('winmgmts://').ExecQuery("select NumberOf#{platform == 'web' ? '' : 'Logical'}Processors from Win32_ComputerSystem").each { |out| numjobs = platform == 'web' ? out.NumberOfProcessors : out.NumberOfLogicalProcessors }
  167. else
  168. numjobs = 1
  169. end
  170. end
  171. build_options = "-j#{numjobs}#{build_options}"
  172. filter = ''
  173. end
  174. system "cd \"#{build_tree}\" && #{ccache_envvar} cmake --build . #{cmake_build_options} -- #{build_options} #{filter}" or abort
  175. end
  176. ### Tasks for Urho3D maintainers ###
  177. # Usage: rake git remote_add|sync|subtree
  178. desc 'Collections of convenience git commands, multiple git commands may be executed in one rake command'
  179. task :git do
  180. success = true
  181. consumed = false
  182. ARGV.each_with_index { |command, index|
  183. task command.to_sym do ; end; Rake::Task[command].clear # No-op hack
  184. next if consumed
  185. case command
  186. when 'remote_add', 'sync', 'subtree'
  187. success = system "rake git_#{ARGV[index, ARGV.length - index].delete_if { |arg| /=/ =~ arg }.join ' '}"
  188. consumed = true
  189. else
  190. abort 'Usage: rake git remote_add|sync|subtree' unless command == 'git' && ARGV.length > 1
  191. end
  192. }
  193. abort unless success
  194. end
  195. # Usage: rake git remote_add [remote=<local-name>] url=<remote-url>'
  196. desc 'Add a new remote and configure it so that its tags will be fetched into a unique namespace'
  197. task :git_remote_add do
  198. abort 'Usage: rake git remote_add [remote=<name>] url=<remote-url>' unless ENV['url']
  199. remote = ENV['remote'] || /\/(.*?)\.git/.match(ENV['url'])[1]
  200. system "git remote add #{remote} #{ENV['url']} && git config --add remote.#{remote}.fetch +refs/tags/*:refs/tags/#{remote}/* && git config remote.#{remote}.tagopt --no-tags && git fetch #{remote}" or abort
  201. end
  202. # Usage: rake git sync [master=master] [upstream=upstream]
  203. desc "Fetch and merge an upstream's remote branch to a fork's local branch then pushing the local branch to the fork's corresponding remote branch"
  204. task :git_sync do
  205. master = ENV['master'] || 'master'
  206. upstream = ENV['upstream'] || 'upstream'
  207. system "git fetch #{upstream} && git checkout #{master} && git merge -m 'Sync at #{Time.now.localtime}.' #{upstream}/#{master} && git push && git checkout -" or abort
  208. end
  209. # Usage: rake git subtree split|rebase|add|push|pull
  210. desc 'Misc. sub-commands for git subtree operations'
  211. task :git_subtree do
  212. ARGV.each { |subcommand|
  213. task subcommand.to_sym do ; end; Rake::Task[subcommand].clear # No-op hack
  214. case subcommand
  215. when 'split'
  216. abort 'Usage: rake git subtree split subdir=</path/to/subdir/to/be/split> [split_branch=<name>]' unless ENV['subdir']
  217. ENV['split_branch'] = "#{Pathname.new(ENV['subdir']).basename}-split" unless ENV['split_branch']
  218. system "git subtree split --prefix #{ENV['subdir']} -b #{ENV['split_branch']}" or abort
  219. when 'rebase'
  220. abort 'Usage: rake git subtree rebase baseline=<commit|branch|tag> split_branch=<name>' unless ENV['baseline'] && ENV['split_branch']
  221. ENV['rebased_branch'] = "#{Pathname.new(ENV['baseline']).basename}-#{ENV['rebased_branch_suffix'] || 'modified-for-urho3d'}"
  222. head = `git log --pretty=format:'%H' #{ENV['split_branch']} |head -1`.chomp
  223. tail = `git log --reverse --pretty=format:'%H' #{ENV['split_branch']} |head -1`.chomp
  224. system "git rebase --onto #{ENV['baseline']} #{tail} #{head} && git checkout -b #{ENV['rebased_branch']}" or abort "After resolving all the conflicts, issue this command manually:\ngit checkout -b #{ENV['rebased_branch']}"
  225. when 'add'
  226. abort 'Usage: rake git subtree add subdir=</path/to/subdir/to/be/split> remote=<name> baseline=<commit|branch|tag>' unless ENV['subdir'] && ENV['remote'] && ENV['baseline']
  227. ENV['rebased_branch'] = "#{Pathname.new(ENV['baseline']).basename}-#{ENV['rebased_branch_suffix'] || 'modified-for-urho3d'}"
  228. system "git push -u #{ENV['remote']} #{ENV['rebased_branch']} && git rm -r #{ENV['subdir']} && git commit -qm 'Replace #{ENV['subdir']} subdirectory with subtree.' && git subtree add --prefix #{ENV['subdir']} #{ENV['remote']} #{ENV['rebased_branch']} --squash" or abort
  229. when 'push'
  230. abort 'Usage: rake git subtree push subdir=</path/to/subdir/to/be/split> remote=<name> baseline=<commit|branch|tag>' unless ENV['subdir'] && ENV['remote'] && ENV['baseline']
  231. ENV['rebased_branch'] = "#{Pathname.new(ENV['baseline']).basename}-#{ENV['rebased_branch_suffix'] || 'modified-for-urho3d'}"
  232. system "git subtree push --prefix #{ENV['subdir']} #{ENV['remote']} #{ENV['rebased_branch']}" or abort
  233. when 'pull'
  234. abort 'Usage: rake git subtree pull subdir=</path/to/subdir/to/be/split> remote=<name> baseline=<commit|branch|tag>' unless ENV['subdir'] && ENV['remote'] && ENV['baseline']
  235. ENV['rebased_branch'] = "#{Pathname.new(ENV['baseline']).basename}-#{ENV['rebased_branch_suffix'] || 'modified-for-urho3d'}"
  236. system "git subtree pull --prefix #{ENV['subdir']} #{ENV['remote']} #{ENV['rebased_branch']} --squash" or abort
  237. else
  238. abort 'Usage: rake git subtree split|rebase|add|push|pull' unless subcommand == 'git_subtree' && ARGV.length > 1
  239. end
  240. }
  241. end
  242. ### Tasks for CI builds and tests ###
  243. # Usage: NOT intended to be used manually
  244. desc 'Build and run the Annotate tool (temporary)'
  245. task :ci_annotate do
  246. system 'rake cmake URHO3D_CLANG_TOOLS=1 && rake make annotate' or abort 'Failed to annotate'
  247. system "git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/$TRAVIS_REPO_SLUG.git && if git fetch origin clang-tools:clang-tools 2>/dev/null; then git push -qf origin --delete clang-tools; fi && git checkout -B clang-tools && git stash -q && git reset --hard HEAD~ && git stash pop -q && sed -i \"s/SF_DEFAULT/URHO3D_PCH=0 URHO3D_BINDINGS=1 SF_DEFAULT/g\" .travis.yml && git add -A .travis.yml Source/Urho3D && if git commit -qm 'Result of Annotator tool. [skip appveyor] [ci only: clang-tools]'; then git push -q -u origin clang-tools >/dev/null 2>&1; fi" or abort 'Failed to push clang-tools branch'
  248. end
  249. # Usage: NOT intended to be used manually
  250. desc 'Push the generated binding source files to clang-tools branch (temporary)'
  251. task :ci_push_bindings do
  252. abort "Skipped pushing to #{ENV['TRAVIS_BRANCH']} branch due to moving HEAD" unless `git fetch -qf origin #{ENV['TRAVIS_PULL_REQUEST'] == 'false' ? ENV['TRAVIS_BRANCH'] : %Q{+refs/pull/#{ENV['TRAVIS_PULL_REQUEST']}/head'}}; git log -1 --pretty=format:'%H' FETCH_HEAD` == ENV['TRAVIS_COMMIT']
  253. system "rm -rf fastcomp-clang && git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/$TRAVIS_REPO_SLUG.git && git add -A Source/Urho3D && if git commit -qm 'Result of AutoBinder tool. [ci skip]'; then git push -q origin HEAD:#{ENV['TRAVIS_BRANCH']} >/dev/null 2>&1; fi" or abort "Failed to push #{ENV['TRAVIS_BRANCH']} branch"
  254. end
  255. # Usage: NOT intended to be used manually
  256. desc 'Configure, build, and test Urho3D project'
  257. task :ci do
  258. next if timeup # Measure the VM overhead
  259. # Skip if only performing CI for selected branches and the current branch is not in the list
  260. unless ENV['RELEASE_TAG']
  261. next if ENV['TRAVIS'] && /\[skip travis\]/ =~ ENV['COMMIT_MESSAGE'] # For feature parity with AppVeyor's [skip appveyor]
  262. matched = /\[ci only:(.*?)\]/.match(ENV['COMMIT_MESSAGE'])
  263. next if matched && !matched[1].split(/[ ,]/).reject!(&:empty?).map { |i| /#{i}/ =~ (ENV['TRAVIS_BRANCH'] || ENV['APPVEYOR_REPO_BRANCH']) }.any?
  264. end
  265. # Clear ccache on demand
  266. if ENV['USE_CCACHE'] then
  267. clear = /\[cache clear\]/ =~ ENV['COMMIT_MESSAGE']
  268. system "ccache -z #{clear ? '-C' : ''}"
  269. puts; $stdout.flush
  270. end
  271. # Obtain our custom data, if any
  272. if ENV['APPVEYOR']
  273. # AppVeyor does not provide job number environment variable in the same semantics as TRAVIS_JOB_NUMBER nor it supports custom data in its .appveyor.yml document
  274. if ENV['included_sample'] || ENV['excluded_sample'] # Inclusion has higher precedence
  275. pairs = (ENV['included_sample'] || ENV['excluded_sample']).split
  276. samples = pairs.pop.split ','
  277. matched = true
  278. pairs.each { |pair|
  279. kv = pair.split '='
  280. matched = false if ENV[kv.first] != kv.last
  281. }
  282. samples.each { |name| ENV["#{ENV['included_sample'] ? 'INCLUDED' : 'EXCLUDED'}_SAMPLE_#{name}"] = '1' } if matched
  283. end
  284. else
  285. data = YAML::load(File.open('.travis.yml'))['data']
  286. data['excluded_sample']["##{ENV['TRAVIS_JOB_NUMBER'].split('.').last}"].each { |name| ENV["EXCLUDED_SAMPLE_#{name}"] = '1' } if data && data['excluded_sample'] && data['excluded_sample']["##{ENV['TRAVIS_JOB_NUMBER'].split('.').last}"]
  287. end
  288. # Unshallow the clone's history when necessary
  289. if ENV['PACKAGE_UPLOAD'] && !ENV['RELEASE_TAG']
  290. system 'git fetch --tags --unshallow' or abort 'Failed to unshallow cloned repository'
  291. puts; $stdout.flush
  292. end
  293. # Show the compiler toolchain version because CMake/Emscripten toolchain file does not show this info and also because the build tree on Travis CI is cached and just being reconfigured each time
  294. if ENV['WEB']
  295. system 'clang --version && emcc --version' or abort 'Failed to find Emscripten compiler toolchain'
  296. end
  297. # Show CMake version
  298. system 'cmake --version' or abort 'Failed to find CMake'
  299. puts; $stdout.flush
  300. # Use out-of-source build tree
  301. ENV['build_tree'] = 'build/ci'
  302. # Currently we don't have the infra to test run all the platforms; also skip when doing packaging build due to time constraint
  303. ENV['URHO3D_TESTING'] = '1' if ((ENV['LINUX'] && !ENV['URHO3D_64BIT']) || (ENV['OSX'] && !ENV['IOS'] && !ENV['TVOS']) || ENV['APPVEYOR']) && !ENV['PACKAGE_UPLOAD']
  304. # When not explicitly specified then use generic generator
  305. generator = ENV['XCODE'] ? 'xcode' : (ENV['APPVEYOR'] ? (ENV['MINGW'] ? 'mingw' : 'vs2019') : '')
  306. # Cache the initial build tree for next run on platform that is slow to generate the build tree
  307. system "mkdir -p #{ENV['build_tree']} && cp -rp #{ENV['HOME']}/initial-build-tree/* #{ENV['build_tree']} && git diff $(cat #{ENV['HOME']}/initial-build-tree/.sha1) $TRAVIS_COMMIT --name-only 2>/dev/null |grep -i cmake |xargs touch 2>/dev/null" if (ENV['OSX'] || ENV['WEB']) && ENV['CI'] && File.exist?("#{ENV['HOME']}/initial-build-tree/.sha1")
  308. system "rake cmake #{generator} URHO3D_DATABASE_SQLITE=1 URHO3D_EXTRAS=1" or abort 'Failed to configure Urho3D library build'
  309. system "bash -c 'cp -rp #{ENV['build_tree']}/* #{ENV['HOME']}/initial-build-tree 2>/dev/null && rm -rf #{ENV['HOME']}/initial-build-tree/{bin,include} 2>/dev/null && echo $TRAVIS_COMMIT >#{ENV['HOME']}/initial-build-tree/.sha1'" if (ENV['OSX'] || ENV['WEB']) && ENV['CI']
  310. next if timeup # Measure the CMake configuration overhead
  311. # Temporarily put the logic here for clang-tools migration until everything else are in their places
  312. if ENV['URHO3D_BINDINGS']
  313. system 'rake make' or abort 'Failed to build or test Urho3D library with annotated source files'
  314. system 'rake ci_push_bindings' or abort
  315. next
  316. end
  317. redirect = '2>/tmp/lint.err' if ENV['URHO3D_LINT']
  318. if !wait_for_block { Thread.current[:subcommand_to_kill] = 'xcodebuild'; system "rake make #{redirect}" }
  319. already_timeup = File.exists?('already_timeup.log')
  320. success = false
  321. if ENV['TRAVIS'] && !ENV['XCODE'] && !already_timeup && !timeup(true, 10)
  322. # The build cache could be corrupted, so clear the cache and retry one more time
  323. system "cd #{ENV['build_tree']}/Source/Urho3D/tolua++-prefix/src/tolua++-build && make clean >/dev/null 2>&1"
  324. system "cd #{ENV['build_tree']}/Source/ThirdParty/LuaJIT/buildvm-prefix/src/buildvm-build && make clean >/dev/null 2>&1"
  325. success = system "ccache -Cz && rake make clean_first #{redirect}"
  326. end
  327. unless success
  328. abort 'Failed to build Urho3D library' unless already_timeup
  329. $stderr.puts "Skipped the rest of the CI processes due to insufficient time"
  330. next
  331. end
  332. end
  333. if ENV['URHO3D_LINT']
  334. lint_err = File.read('/tmp/lint.err')
  335. puts "\nLinter result:\n\n#{lint_err}\n"; $stdout.flush
  336. # Exclude ThirdParty and generated code
  337. filtered_lint_err = lint_err.scan(/(.+:\d+:\d+:.+\[.+\])/).flatten.select { |it| it =~ /\[\w+-.+\]/ }.reject { |it| it =~ /ThirdParty|generated|HashMap\.h.+?clang-analyzer-core.CallAndMessag/ }
  338. unless filtered_lint_err.empty?
  339. puts "New linter error(s) found:\n\n"
  340. filtered_lint_err.each { |it| puts it }
  341. puts; $stdout.flush
  342. abort 'Failed to pass linter checks'
  343. end
  344. else
  345. if ENV['URHO3D_TESTING'] && !timeup
  346. # Multi-config CMake generators use different test target name than single-config ones for no good reason
  347. test = "#{ENV['OSX'] || ENV['APPVEYOR'] ? '' : 'xvfb-run'} rake make target=#{(ENV['OS'] && !ENV['MINGW']) || ENV['XCODE'] ? 'RUN_TESTS' : 'test'}"
  348. system "#{test}" or abort 'Failed to test Urho3D library'
  349. test = "&& echo#{ENV['OS'] ? '.' : ''} && #{test}"
  350. else
  351. test = ''
  352. end
  353. # Skip scaffolding test when time up or packaging for platform with slow build environment, or when the build config may run out of disk space
  354. unless ENV['CI'] && ((ENV['IOS'] || ENV['TVOS'] || ENV['WEB'] || ENV['OS']) && ENV['PACKAGE_UPLOAD'] || (ENV['CMAKE_BUILD_TYPE'] == 'Debug' && ENV['URHO3D_LIB_TYPE'] == 'STATIC')) || timeup
  355. # Staged-install Urho3D SDK when on Travis-CI; normal install when on AppVeyor
  356. ENV['DESTDIR'] = ENV['HOME'] unless ENV['APPVEYOR']
  357. if !wait_for_block("Installing Urho3D SDK to #{ENV['DESTDIR'] ? "#{ENV['DESTDIR']}/usr/local" : 'default system-wide location'}...") { Thread.current[:subcommand_to_kill] = 'xcodebuild'; system "rake make target=install >#{ENV['OS'] ? 'nul' : '/dev/null'}" }
  358. abort 'Failed to install Urho3D SDK' unless File.exists?('already_timeup.log')
  359. $stderr.puts "Skipped the rest of the CI processes due to insufficient time"
  360. next
  361. end
  362. urho3d_home = "#{Dir.pwd}/#{ENV['build_tree']}"
  363. # Use non out-of-source build tree for scaffolding test
  364. ENV['build_tree'] = '.'
  365. # Ensure the following variables are auto-discovered during scaffolding test
  366. ENV['URHO3D_64BIT'] = nil unless ENV['APPVEYOR'] # AppVeyor uses VS generator which always requires URHO3D_64BIT as input variable
  367. ['URHO3D_LIB_TYPE', 'URHO3D_STATIC_RUNTIME', 'URHO3D_OPENGL', 'URHO3D_D3D11', 'URHO3D_SSE', 'URHO3D_DATABASE_ODBC', 'URHO3D_DATABASE_SQLITE', 'URHO3D_LUAJIT', 'URHO3D_TESTING'].each { |var| ENV[var] = nil }
  368. # First test - create a new project on the fly that uses newly installed Urho3D SDK
  369. Dir.chdir scaffolding 'UsingSDK' do
  370. puts "\nConfiguring downstream project using Urho3D SDK...\n\n"; $stdout.flush
  371. # SDK installation to a system-wide location does not need URHO3D_HOME to be defined, staged-installation does
  372. system "#{ENV['DESTDIR'] ? 'URHO3D_HOME=~/usr/local' : ''} rake cmake #{generator} && rake make #{test}" or abort 'Failed to configure/build/test temporary downstream project using Urho3D as external library'
  373. end
  374. next if timeup
  375. # Second test - create a new project on the fly that uses newly built Urho3D library in the build tree
  376. Dir.chdir scaffolding 'UsingBuildTree' do
  377. puts "Configuring downstream project using Urho3D library in its build tree...\n\n"; $stdout.flush
  378. system "rake cmake #{generator} URHO3D_HOME=#{urho3d_home} && rake make #{test}" or abort 'Failed to configure/build/test temporary downstream project using Urho3D as external library'
  379. end
  380. # Clean up so that these test dirs do not show up in the CI mirror branches
  381. require 'fileutils'
  382. FileUtils.rm_rf(['UsingSDK', 'UsingBuildTree'])
  383. end
  384. end
  385. system 'ccache -s' if ENV['USE_CCACHE']
  386. end
  387. # Usage: NOT intended to be used manually
  388. desc 'Setup build cache'
  389. task :ci_setup_cache do
  390. puts 'Setting up build cache using docker volume...'
  391. # This is a hack as it relies on docker volume internal directory structure
  392. system 'docker volume create $(id -u).urho3d_home_dir && sudo rm -rf /var/lib/docker/volumes/$(id -u).urho3d_home_dir/_data && sudo ln -s $HOME/urho3d_home_dir /var/lib/docker/volumes/$(id -u).urho3d_home_dir/_data' or abort 'Failed to setup build cache'
  393. # Ensure '.build-options' and '.env-file' are up-to-date
  394. 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 && cat script/.build-options <(perl -ne 'while (/(\w+)=.+?/g) {print "$1\n"}' .travis.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 .build-options and .env-file'
  395. end
  396. # Usage: NOT intended to be used manually
  397. desc 'Update site on GitHub Pages (and source tree on GitHub while we are at it)'
  398. task :ci_site_update do
  399. # Skip when :ci rake task was skipped
  400. build_tree = 'build/ci'
  401. next unless File.exist?("#{build_tree}/CMakeCache.txt")
  402. next if timeup
  403. puts "Updating site...\n\n"
  404. system 'git clone --depth 1 -q https://github.com/urho3d/urho3d.github.io.git ~/urho3d.github.io' or abort 'Failed to clone urho3d/urho3d.github.io'
  405. # Update credits from README.md to about.yml
  406. system "ruby -lne 'BEGIN { credits = false }; puts $_ if credits; credits = true if /bugfixes by:/; credits = false if /^$/' README.md |ruby -i -le 'credits = STDIN.read; puts ARGF.read.gsub(/(?<=contributors:\n).*?\n\n/m, credits)' ~/urho3d.github.io/_data/about.yml" or abort 'Failed to update credits'
  407. # Setup doxygen to use minimal theme
  408. system "ruby -i -pe 'BEGIN { a = {%q{HTML_HEADER} => %q{minimal-header.html}, %q{HTML_FOOTER} => %q{minimal-footer.html}, %q{HTML_STYLESHEET} => %q{minimal-doxygen.css}, %q{HTML_COLORSTYLE_HUE} => 200, %q{HTML_COLORSTYLE_SAT} => 0, %q{HTML_COLORSTYLE_GAMMA} => 40, %q{DOT_IMAGE_FORMAT} => %q{svg}, %q{INTERACTIVE_SVG} => %q{YES}, %q{COLS_IN_ALPHA_INDEX} => 3} }; a.each {|k, v| gsub(/\#{k}\s*?=.*?\n/, %Q{\#{k} = \#{v}\n}) }' #{build_tree}/Docs/generated/Doxyfile" or abort 'Failed to setup doxygen configuration file'
  409. system "cp ~/urho3d.github.io/_includes/Doxygen/minimal-* #{build_tree}/Docs" or abort 'Failed to copy minimal-themed template'
  410. release = ENV['RELEASE_TAG'] || 'HEAD'
  411. unless release == 'HEAD'
  412. system "mkdir -p ~/urho3d.github.io/documentation/#{release}" or abort 'Failed to create directory for new document version'
  413. system "ruby -i -pe 'gsub(/HEAD/, %q{#{release}})' #{build_tree}/Docs/minimal-header.html" or abort 'Failed to update document version in YAML Front Matter block'
  414. append_new_release release or abort 'Failed to add new release to document data file'
  415. end
  416. # Generate and sync doxygen pages
  417. system "cd #{build_tree} && make -j$numjobs doc >/dev/null 2>&1 && ruby -i -pe 'gsub(/(<\\/?h)3([^>]*?>)/, %q{\\14\\2}); gsub(/(<\\/?h)2([^>]*?>)/, %q{\\13\\2}); gsub(/(<\\/?h)1([^>]*?>)/, %q{\\12\\2})' Docs/html/_*.html && rsync -a --delete Docs/html/ ~/urho3d.github.io/documentation/#{release}" or abort 'Failed to generate/rsync doxygen pages'
  418. # TODO: remove below workaround after upgrading to 1.8.14 or greater
  419. system "cp ~/urho3d.github.io/documentation/1.7/dynsections.js ~/urho3d.github.io/documentation/#{release}" or abort 'Failed to workaround Doxygen 1.8.13 bug'
  420. # Supply GIT credentials to push site documentation changes to urho3d/urho3d.github.io.git
  421. system "cd ~/urho3d.github.io && git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/urho3d/urho3d.github.io.git && git add -A . && if git commit -qm \"Travis CI: site documentation update at #{Time.now.utc}.\n\nCommit: https://github.com/$TRAVIS_REPO_SLUG/commit/$TRAVIS_COMMIT\n\nMessage: $COMMIT_MESSAGE\"; then git push -q >/dev/null 2>&1 && echo Site updated successfully; fi" or abort 'Failed to update site'
  422. next if timeup
  423. # Skip detecting source tree changes when HEAD has moved or it is too late already as a release tag has just been pushed
  424. unless ENV['RELEASE_TAG'] || `git fetch -qf origin #{ENV['TRAVIS_BRANCH']}; git log -1 --pretty=format:'%H' FETCH_HEAD` != ENV['TRAVIS_COMMIT']
  425. puts "Updating source tree...\n\n"
  426. # Supply GIT credentials to push source tree changes to urho3d/Urho3D.git
  427. system 'git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/$TRAVIS_REPO_SLUG.git'
  428. system "git add script Source && git commit -qm 'Travis CI: source tree update at #{Time.now.utc}.' >/dev/null 2>&1" # Use extra quiet mode as there could be no changes at all
  429. if /2008-([0-9]{4}) the Urho3D project/.match(File.read('Rakefile'))[1].to_i != Time.now.year
  430. # Automatically bump copyright when crossing a new year
  431. system "git add #{bump_copyright_year.join ' '} && if git commit -qm 'Travis CI: bump copyright to #{Time.now.year}.'; then git push origin HEAD:#{ENV['TRAVIS_BRANCH']} -q >/dev/null 2>&1 && echo Bumped copyright - Happy New Year!; fi" or abort "Failed to push copyright update for #{ENV['TRAVIS_BRANCH']}"
  432. ['urho3d.github.io master', 'android-ndk ndk-update-trigger', 'armhf-sysroot sysroot-update-trigger', 'arm64-sysroot sysroot-update-trigger', 'rpi-sysroot sysroot-update-trigger', 'emscripten-sdk sdk-update-trigger', 'dockerized master', 'dockerized native', 'dockerized mingw', 'dockerized android', 'dockerized rpi', 'dockerized arm', 'dockerized web'].each { |var| pair = var.split; system "if [ ! -d ~/#{pair.first} ]; then git clone -q --depth 1 --branch #{pair.last} https://github.com/urho3d/#{pair.first} ~/#{pair.first}; fi" or abort "Failed to clone urho3d/#{pair.first}"; system "cd ~/#{pair.first} && git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/urho3d/#{pair.first} && git add #{bump_copyright_year("~/#{pair.first}").join ' '} 2>/dev/null && git add #{bump_copyright_year("~/#{pair.first}", '2014-[0-9]{4} Yao').join ' '} 2>/dev/null && if git commit -qm 'Travis CI: bump copyright to #{Time.now.year}.\n[ci skip]'; then git push -q >/dev/null 2>&1; fi" or abort "Failed to push copyright update for urho3d/#{pair.first}"; }
  433. elsif system("git add Docs/*API* && git commit -qm 'Test commit to detect API documentation changes'")
  434. # Automatically give instruction to do packaging when API has changed, unless the instruction is already given in this commit
  435. bump_soversion 'Source/Urho3D/.soversion' or abort 'Failed to bump soversion'
  436. system "git add Source/Urho3D/.soversion && git commit --amend -qm \"Travis CI: API documentation update at #{Time.now.utc}.\n#{ENV['PACKAGE_UPLOAD'] ? '' : '[ci package]'}\n\nCommit: https://github.com/$TRAVIS_REPO_SLUG/commit/$TRAVIS_COMMIT\n\nMessage: #{ENV['COMMIT_MESSAGE'].gsub(/\[.*\]/, '')}\" && echo Source tree updated successfully" or abort 'Failed to commit API documentation'
  437. end
  438. else
  439. puts 'Skipped detecting source tree changes due to moving HEAD' unless ENV['RELEASE_TAG']
  440. end
  441. end
  442. # Usage: NOT intended to be used manually
  443. desc 'Update web samples to GitHub Pages'
  444. task :ci_emscripten_samples_update do
  445. next if timeup
  446. build_tree = 'build/ci'
  447. puts 'Updating Web samples in main website...'
  448. system 'git clone --depth 1 -q https://github.com/urho3d/urho3d.github.io.git ~/urho3d.github.io' or abort 'Failed to clone urho3d/urho3d.github.io'
  449. system "rsync -a --delete --exclude tool --exclude *.pak --exclude index.md #{build_tree}/bin/ ~/urho3d.github.io/samples" or abort 'Failed to rsync Web samples'
  450. Dir.chdir('~/urho3d.github.io/samples') {
  451. next unless system 'git diff --quiet Urho3D.js.data'
  452. uuid = `git diff --color=never --word-diff-regex='\\w+' --word-diff=porcelain Urho3D.js`.split.grep(/^[+-]\w+-/).map { |it| it[0] = ''; it }
  453. system %Q(ruby -i.bak -pe "gsub '#{uuid.last}', '#{uuid.first}'" Urho3D.js)
  454. if system 'git diff --quiet Urho3D.js'
  455. File.unlink 'Urho3D.js.bak'
  456. Dir['*.js'].each { |file| system %Q(ruby -i -pe "gsub '#{uuid.last}', '#{uuid.first}'" #{file}) }
  457. else
  458. File.rename 'Urho3D.js.bak', 'Urho3D.js'
  459. end
  460. }
  461. update_web_samples_data or abort 'Failed to update Web json data file'
  462. root_commit, _ = get_root_commit_and_recipients
  463. system "cd ~/urho3d.github.io && git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/urho3d/urho3d.github.io.git && git add -A . && ( git commit -qm \"Travis CI: Web samples update at #{Time.now.utc}.\n\nCommit: https://github.com/$TRAVIS_REPO_SLUG/commit/#{root_commit}\n\nMessage: #{`git log --format=%B -n 1 #{root_commit}`}\" || true) && git push -q >/dev/null 2>&1" or abort 'Failed to update Web samples'
  464. end
  465. # Usage: NOT intended to be used manually
  466. desc 'Create all CI mirror branches'
  467. task :ci_create_mirrors do
  468. # Skip all CI branches creation if there are more commits externally
  469. abort 'Skipped creating mirror branches due to moving remote HEAD' unless `git fetch -qf origin #{ENV['TRAVIS_PULL_REQUEST'] == 'false' ? ENV['TRAVIS_BRANCH'] : %Q{+refs/pull/#{ENV['TRAVIS_PULL_REQUEST']}/head'}}; git log -1 --pretty=format:'%H' FETCH_HEAD` == ENV['TRAVIS_COMMIT'] # This HEAD movement detection logic is more complex than usual as the original intention was to allow mirror creation on PR, however, we have scaled it back for now
  470. # Skip non-mandatory branches if there are pending commits internally
  471. head = `git log -1 --pretty=format:'%H' HEAD`
  472. head_moved = head != ENV['TRAVIS_COMMIT'] # Local head may be moved because of API documentation update
  473. # Reset the head to the original commit position for mirror creation
  474. system 'git checkout -qf $TRAVIS_COMMIT' if head_moved
  475. system 'git config user.name $GIT_NAME && git config user.email $GIT_EMAIL && git remote set-url --push origin https://[email protected]/$TRAVIS_REPO_SLUG.git' or abort 'Failed to re-checkout commit'
  476. # Limit the scanning to only master branch
  477. scan = ENV['TRAVIS_BRANCH'] == 'master'
  478. # Check if it is time to generate annotation
  479. #annotate = ENV['TRAVIS_BRANCH'] == 'master' && (ENV['PACKAGE_UPLOAD'] || /\[ci annotate\]/ =~ ENV['COMMIT_MESSAGE']) && /\[ci only:.*?\]/ !~ ENV['COMMIT_MESSAGE']
  480. annotate = false
  481. # Determine which CI mirror branches to be auto created
  482. unless ENV['RELEASE_TAG']
  483. skip_travis = /\[skip travis\]/ =~ ENV['COMMIT_MESSAGE'] # For feature parity with AppVeyor's [skip appveyor]
  484. matched = /\[ci only:(.*?)\]/.match(ENV['COMMIT_MESSAGE'])
  485. ci_only = matched ? matched[1].split(/[ ,]/).reject!(&:empty?) : nil
  486. else
  487. ci_only = nil
  488. end
  489. # Escape double quotes in the commit message so they do not interfere with the string interpolation below
  490. escaped_commit_message = ENV['COMMIT_MESSAGE'].gsub(/"/, '\"')
  491. # Obtain the whole stream and process the rest of documents except the first one since travis-build does not do that at the moment
  492. stream = YAML::load_stream(File.open('.travis.yml'))
  493. notifications = stream[0]['notifications']
  494. notifications['email']['recipients'] = get_root_commit_and_recipients().last unless notifications['email']['recipients']
  495. preset = stream[0]['data']['stages'] || {}
  496. stage = stream[0]['stage'] || 'test'
  497. # Install Travis CLI Ruby gem to interface with Travis
  498. system 'gem install travis >/dev/null 2>&1'
  499. stream.drop(1).each { |doc| branch = doc.delete('branch'); ci = branch['name']; ci_branch = ENV['RELEASE_TAG'] || (ENV['TRAVIS_BRANCH'] == 'master' && ENV['TRAVIS_PULL_REQUEST'] == 'false') ? ci : (ENV['TRAVIS_PULL_REQUEST'] == 'false' ? "#{ENV['TRAVIS_BRANCH']}-#{ci}" : "PR ##{ENV['TRAVIS_PULL_REQUEST']}-#{ci}"); is_appveyor_ci = branch['appveyor']; next if skip_travis && !is_appveyor_ci; unless (branch['mandatory'] || !head_moved) && ((ci_only && ci_only.map { |i| /#{i}/ =~ ci }.any?) || (!ci_only && (branch['active'] || (scan && /Scan/ =~ ci) || (annotate && /Annotate/ =~ ci)))); system "if git fetch origin #{ci_branch}:#{ci_branch} 2>/dev/null; then git push -qf origin --delete #{ci_branch}; fi"; puts "Skipped creating #{ci_branch} mirror branch due to moving HEAD" if !ci_only && branch['active'] && head_moved; next; end; unless is_appveyor_ci; doc['notifications'] = notifications unless doc['notifications']; doc['matrix']['include'].delete_if { |build| build['condition'] && !ENV[build['condition']] } && doc['matrix']['include'].each_with_index { |build, index| stage = build['stage'] || stage; build['env'] = build['env'].join(' ') if build['env'] && build['env'].kind_of?(Array); build['before_script'].flatten! if build['before_script']; doc['matrix']['include'][index].merge! preset[stage] if preset[stage] } if doc['matrix'] && doc['matrix']['include']; doc_name = '.travis.yml'; else doc_name = '.appveyor.yml'; end; File.open("#{doc_name}.new", 'w') { |file| file.write doc.to_yaml }; puts "Creating #{ci_branch} mirror branch..."; alt = system("travis branches --org --no-interactive -r #{ENV['TRAVIS_REPO_SLUG']} |grep ^#{ci_branch}: |grep -cqP 'started|created'") ? '-alt' : nil; system "git checkout -qB #{ci_branch} && rm .appveyor.yml .travis.yml && mv #{doc_name}.new #{doc_name} && git add -A . && git commit -qm \"#{escaped_commit_message}\" && git push -qf -u origin #{ci_branch}:#{ci_branch}#{alt} >/dev/null 2>&1 && git checkout -q - && sleep 5" or abort "Failed to create #{ci_branch} mirror branch" }
  500. # Push pending commits if any
  501. system "git push origin #{head}:#{ENV['TRAVIS_BRANCH']} -q >/dev/null 2>&1" or abort "Failed to push pending commits to #{ENV['TRAVIS_BRANCH']}" if head_moved
  502. end
  503. # Usage: NOT intended to be used manually
  504. desc 'Delete CI mirror branch'
  505. task :ci_delete_mirror do
  506. # Skip when we are performing a release (in case we need to rerun the job to recreate the package)
  507. if ENV['RELEASE_TAG']
  508. # Do not use "abort" here because AppVeyor, unlike Travis, also handles the exit status of the processes invoked in the "on_finish" section of the .appveyor.yml
  509. # Using "abort" may incorrectly (or correctly, depends on your POV) report the whole CI as failed when the CI mirror branch deletion is being skipped
  510. $stderr.puts "Skipped deleting #{ENV['TRAVIS_BRANCH'] || ENV['APPVEYOR_REPO_BRANCH']} mirror branch"
  511. next
  512. end
  513. system "bash -c 'git config user.name #{ENV['GIT_NAME']} && git config user.email #{ENV['GIT_EMAIL']} && git remote set-url --push origin https://#{ENV['GH_TOKEN']}@github.com/#{ENV['TRAVIS_REPO_SLUG'] || ENV['APPVEYOR_REPO_NAME']}.git'"
  514. system "bash -c 'git push -qf origin --delete #{ENV['TRAVIS_BRANCH'] || ENV['APPVEYOR_REPO_BRANCH']} >/dev/null 2>&1'" or abort "Failed to delete #{ENV['TRAVIS_BRANCH'] || ENV['APPVEYOR_REPO_BRANCH']} mirror branch"
  515. end
  516. # Usage: NOT intended to be used manually
  517. desc 'Make binary package and upload it to a designated central hosting server'
  518. task :ci_package_upload do
  519. # Use out-of-source build tree
  520. ENV['build_tree'] = 'build/ci'
  521. # Skip when :ci rake task was skipped
  522. next unless File.exist?("#{ENV['build_tree']}/CMakeCache.txt")
  523. next if timeup
  524. # Generate the documentation if necessary
  525. if ENV['SITE_UPDATE']
  526. if File.exist?('.site_updated')
  527. # Skip if site is already updated before
  528. ENV['SITE_UPDATE'] = nil
  529. end
  530. elsif !File.exists?("#{ENV['build_tree']}/Docs/html/index.html")
  531. puts "Generating documentation...\n"; $stdout.flush
  532. # Ignore the exit status from 'make doc' on Windows host system because Doxygen may not return exit status correctly on Windows
  533. system "rake make target=doc >#{ENV['OS'] ? 'nul' : '/dev/null'}" or ENV['OS'] or abort 'Failed to generate documentation'
  534. next if timeup
  535. end
  536. # Make the package
  537. puts "Packaging artifacts...\n\n"; $stdout.flush
  538. if ENV['IOS'] || ENV['TVOS']
  539. # TODO: There is a bug in CMake/CPack that causes the 'package' target failed to build for iOS and tvOS platforms, workaround by calling cpack directly; CMake 3.4 runs the target successfully, however, the result tarball is incomplete (somehow it misses packaging the library itself, another bug?)
  540. system "cd #{ENV['build_tree']} && cpack -G TGZ 2>/dev/null" or abort 'Failed to make binary package'
  541. else
  542. if ENV['URHO3D_USE_LIB64_RPM']
  543. system 'rake cmake' or abort 'Failed to reconfigure to generate 64-bit RPM package'
  544. system "rm #{ENV['build_tree']}/Urho3D-*" or abort 'Failed to remove previously generated artifacts' # This task can be invoked more than one time
  545. end
  546. system "#{!ENV['OS'] && (ENV['URHO3D_64BIT'] || ENV['RPI'] || ENV['ARM']) ? 'setarch i686' : ''} rake make target=package" or abort 'Failed to make binary package'
  547. end
  548. # Determine the upload location
  549. puts "\nUploading artifacts...\n\n"; $stdout.flush
  550. setup_digital_keys
  551. repo = ENV[ENV['TRAVIS'] ? 'TRAVIS_REPO_SLUG' : 'APPVEYOR_REPO_NAME']
  552. unless ENV['RELEASE_TAG']
  553. upload_dir = "/home/frs/project/#{repo}/Snapshots"
  554. if ENV['SITE_UPDATE']
  555. # Download source packages from GitHub
  556. system "export SNAPSHOT_VER=$(git describe $TRAVIS_COMMIT |ruby -pe 'gsub(/-(?!g)/, %q{.})'); wget -q https://github.com/$TRAVIS_REPO_SLUG/tarball/$TRAVIS_COMMIT -O Urho3D-$SNAPSHOT_VER-Source-snapshot.tar.gz && wget -q https://github.com/$TRAVIS_REPO_SLUG/zipball/$TRAVIS_COMMIT -O Urho3D-$SNAPSHOT_VER-Source-snapshot.zip" or abort 'Failed to get source packages'
  557. # Only keep the snapshots from the last 10 revisions
  558. retry_block { system "for v in $(sftp [email protected] <<EOF |tr ' ' '\n' |grep Urho3D- |cut -d '-' -f1,2 |uniq |tail -n +11
  559. cd #{upload_dir}
  560. ls -1t
  561. bye
  562. EOF
  563. ); do echo rm #{upload_dir}/${v}-*; done |sftp -b - [email protected] >/dev/null 2>&1" } or warn 'Failed to housekeep snapshots'
  564. end
  565. else
  566. upload_dir = "/home/frs/project/#{repo}/#{ENV['RELEASE_TAG']}"
  567. if ENV['SITE_UPDATE']
  568. # Download source packages from GitHub
  569. system 'wget -q https://github.com/$TRAVIS_REPO_SLUG/archive/$RELEASE_TAG.tar.gz -O Urho3D-$RELEASE_TAG-Source.tar.gz && wget -q https://github.com/$TRAVIS_REPO_SLUG/archive/$RELEASE_TAG.zip -O Urho3D-$RELEASE_TAG-Source.zip' or abort 'Failed to get source packages'
  570. end
  571. # Make sure the release directory exists remotely, do this in all the build jobs as we don't know which one would start uploading first
  572. retry_block { system "bash -c 'sftp [email protected] <<EOF >/dev/null 2>&1
  573. mkdir #{upload_dir}
  574. bye
  575. EOF'" } or abort 'Failed to create release directory remotely'
  576. end
  577. if ENV['SITE_UPDATE']
  578. # Upload the source package
  579. retry_block { system "scp Urho3D-* [email protected]:#{upload_dir}" } or abort 'Failed to upload source package'
  580. if ENV['RELEASE_TAG']
  581. # Mark the source tarball as default download for host systems other than Windows/Mac/Linux
  582. retry_block { system "curl -H 'Accept: application/json' -X PUT -d 'default=bsd&default=solaris&default=others' -d \"api_key=$SF_API\" https://sourceforge.net/projects/%s/files/%s/#{ENV['RELEASE_TAG']}/Urho3D-#{ENV['RELEASE_TAG']}-Source.tar.gz" % ENV['TRAVIS_REPO_SLUG'].split('/') } or abort 'Failed to set source tarball as default download'
  583. end
  584. # Sync readme and license files, just in case they are updated in the repo
  585. retry_block { system 'for f in README.md LICENSE; do mtime=$(git log --format=%ai -n1 $f); touch -d "$mtime" $f; done' } or abort 'Failed to acquire file modified time'
  586. retry_block { system 'rsync -e ssh -az README.md LICENSE [email protected]:/home/frs/project/$TRAVIS_REPO_SLUG' } or abort 'Failed to sync readme and license files'
  587. # Mark that the site has been updated
  588. File.open('.site_updated', 'w') {}
  589. end
  590. # Upload the binary package
  591. retry_block { wait_for_block('', 55) { system "bash -c 'scp #{ENV['build_tree']}/Urho3D-* [email protected]:#{upload_dir}'" } } or abort 'Failed to upload binary package'
  592. if ENV['RELEASE_TAG'] && ENV['SF_DEFAULT']
  593. # Mark the corresponding binary package as default download for each Windows/Mac/Linux host systems
  594. retry_block { system "bash -c \"curl -H 'Accept: application/json' -X PUT -d 'default=%s' -d \"api_key=$SF_API\" https://sourceforge.net/projects/%s/files/%s/#{ENV['RELEASE_TAG']}/Urho3D-#{ENV['RELEASE_TAG']}-%s\"" % ENV['SF_DEFAULT'].split(':').insert(1, repo.split('/')).flatten } or abort 'Failed to set binary tarball/zip as default download'
  595. end
  596. end
  597. # Usage: NOT intended to be used manually
  598. desc 'Start/stop the timer'
  599. task :ci_timer do
  600. timeup
  601. end
  602. # Always call this function last in the multiple conditional check so that the checkpoint message does not being echoed unnecessarily
  603. def timeup quiet = false, cutoff_time = ENV['RELEASE_TAG'] ? 60.0 : 45.0
  604. unless File.exists?('start_time.log')
  605. system 'touch start_time.log split_time.log' if ENV['CI']
  606. return nil
  607. end
  608. current_time = Time.now
  609. elapsed_time = (current_time - File.atime('start_time.log')) / 60.0
  610. unless quiet
  611. lap_time = (current_time - File.atime('split_time.log')) / 60.0
  612. system 'touch split_time.log'
  613. puts "\n=== elapsed time: #{elapsed_time.to_i} minutes #{((elapsed_time - elapsed_time.to_i) * 60.0).round} seconds, lap time: #{lap_time.to_i} minutes #{((lap_time - lap_time.to_i) * 60.0).round} seconds ===\n\n" unless File.exists?('already_timeup.log'); $stdout.flush
  614. end
  615. return system('touch already_timeup.log') if elapsed_time > cutoff_time
  616. end
  617. def scaffolding dir, project = 'Scaffolding', target = 'Main'
  618. begin
  619. dir = Pathname.new(dir).realdirpath.to_s
  620. rescue
  621. abort "Failed to scaffolding due to invalid parent directory in '#{dir}'"
  622. end
  623. dir.gsub!(/\//, '\\') if ENV['OS']
  624. build_script = <<EOF
  625. # Set CMake minimum version and CMake policy required by UrhoCommon module
  626. cmake_minimum_required (VERSION 3.2.3)
  627. if (COMMAND cmake_policy)
  628. # Libraries linked via full path no longer produce linker search paths
  629. cmake_policy (SET CMP0003 NEW)
  630. # INTERFACE_LINK_LIBRARIES defines the link interface
  631. cmake_policy (SET CMP0022 NEW)
  632. # Disallow use of the LOCATION target property - so we set to OLD as we still need it
  633. cmake_policy (SET CMP0026 OLD)
  634. # MACOSX_RPATH is enabled by default
  635. cmake_policy (SET CMP0042 NEW)
  636. # Honor the visibility properties for SHARED target types only
  637. cmake_policy (SET CMP0063 OLD)
  638. endif ()
  639. # Set project name
  640. project (#{project})
  641. # Set CMake modules search path
  642. set (CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake/Modules)
  643. # Include UrhoCommon.cmake module after setting project name
  644. include (UrhoCommon)
  645. # Define target name
  646. set (TARGET_NAME #{target})
  647. # Define source files
  648. define_source_files ()
  649. # Setup target with resource copying
  650. setup_main_executable ()
  651. # Setup test cases
  652. if (URHO3D_ANGELSCRIPT)
  653. setup_test (NAME ExternalLibAS OPTIONS Scripts/12_PhysicsStressTest.as -w)
  654. endif ()
  655. if (URHO3D_LUA)
  656. setup_test (NAME ExternalLibLua OPTIONS LuaScripts/12_PhysicsStressTest.lua -w)
  657. endif ()
  658. EOF
  659. # TODO: Rewrite in pure Ruby when it supports symlink creation on Windows platform and avoid forward/backward slash conversion
  660. if ENV['OS']
  661. system("@echo off && mkdir \"#{dir}\"\\bin && copy Source\\Tools\\Urho3DPlayer\\Urho3DPlayer.* \"#{dir}\" >nul && (for %f in (script CMake) do mklink /D \"#{dir}\"\\%f %cd%\\%f >nul) && mklink \"#{dir}\"\\Rakefile %cd%\\Rakefile && (for %d in (Autoload,CoreData,Data) do mklink /D \"#{dir}\"\\bin\\%d %cd%\\bin\\%d >nul)") && File.write("#{dir}/CMakeLists.txt", build_script) or abort 'Failed to scaffolding'
  662. else
  663. system("bash -c \"mkdir -p '#{dir}'/bin && cp Source/Tools/Urho3DPlayer/Urho3DPlayer.* '#{dir}' && for f in script Rakefile CMake; do ln -snf `pwd`/\\$f '#{dir}'; done && ln -snf `pwd`/bin/{Autoload,CoreData,Data} '#{dir}'/bin\"") && File.write("#{dir}/CMakeLists.txt", build_script) or abort 'Failed to scaffolding'
  664. end
  665. return dir
  666. end
  667. def get_root_commit_and_recipients
  668. # Root commit is a commit submitted by human
  669. root_commit = `git show -s --format='%H' #{ENV['TRAVIS_COMMIT']}`.rstrip
  670. recipients = `git show -s --format='%ae %ce' #{root_commit}`.chomp.split.uniq
  671. if recipients.include? '[email protected]'
  672. matched = /Commit:.*commit\/(.*?)\n/.match(ENV['COMMIT_MESSAGE'])
  673. if (matched)
  674. root_commit = matched[1]
  675. recipients = `git show -s --format='%ae %ce' #{root_commit}`.chomp.split.uniq
  676. end
  677. end
  678. return root_commit, recipients
  679. end
  680. # Usage: wait_for_block('This is a long function call...') { call_a_func } or abort
  681. # wait_for_block('This is a long system call...') { system 'do_something' } or abort
  682. def wait_for_block comment = '', cutoff_time = ENV['RELEASE_TAG'] ? 60.0 : 45.0, retries = -1, retry_interval = 60
  683. return nil if timeup(true, cutoff_time)
  684. # Wait until the code block is completed or it is killed externally by user via Ctrl+C or when it exceeds the number of retries (if the retries parameter is provided)
  685. thread = Thread.new { rc = yield; Thread.main.wakeup; rc }
  686. thread.priority = 1 # Make the worker thread has higher priority than the main thread
  687. str = comment
  688. retries = retries * 60 / retry_interval unless retries == -1
  689. until thread.status == false
  690. if retries == 0 || timeup(true, cutoff_time)
  691. thread.kill
  692. # Also kill the child subproceses spawned by the worker thread if specified
  693. system "killall #{thread[:subcommand_to_kill]}" if thread[:subcommand_to_kill]
  694. sleep 5
  695. break
  696. end
  697. print str; str = '.'; $stdout.flush # Flush the standard output stream in case it is buffered to prevent Travis-CI into thinking that the build/test has stalled
  698. retries -= 1 if retries > 0
  699. sleep retry_interval
  700. end
  701. puts "\n" if str == '.'; $stdout.flush
  702. thread.join
  703. return thread.value
  704. end
  705. # Usage: retry_block { code-block } or abort
  706. def retry_block retries = 10, retry_interval = 1
  707. until yield
  708. retries -= 1
  709. return nil if retries == 0
  710. sleep retry_interval
  711. end
  712. 0
  713. end
  714. def append_new_release release, filename = '~/urho3d.github.io/_data/urho3d.json'
  715. begin
  716. urho3d_hash = JSON.parse File.read filename
  717. unless urho3d_hash['releases'].last == release
  718. urho3d_hash['releases'] << release
  719. end
  720. File.open(filename, 'w') { |file| file.puts urho3d_hash.to_json }
  721. return 0
  722. rescue
  723. nil
  724. end
  725. end
  726. def update_web_samples_data dir = '~/urho3d.github.io/samples', filename = '~/urho3d.github.io/_data/web.json'
  727. begin
  728. web = { 'samples' => {} }
  729. Dir.chdir(dir) { web['samples']['Native'] = Dir['*.html'].sort }
  730. web['player'] = web['samples']['Native'].pop # Assume the last sample after sorting is the Urho3DPlayer.html
  731. {'AngelScript' => 'Scripts', 'Lua' => 'LuaScripts'}.each { |lang, subdir|
  732. Dir.chdir("bin/Data/#{subdir}") {
  733. script_samples = Dir['[0-9]*'].sort
  734. deleted_samples = [] # Delete samples that do not have their native counterpart
  735. script_samples.each { |sample| deleted_samples.push sample unless web['samples']['Native'].include? "#{sample.split('.').first}.html" }
  736. web['samples'][lang] = (script_samples - deleted_samples).map { |sample| "#{subdir}/#{sample}" }
  737. }
  738. }
  739. File.open(filename, 'w') { |file| file.puts web.to_json }
  740. return 0
  741. rescue
  742. nil
  743. end
  744. end
  745. def bump_copyright_year dir='.', regex='2008-[0-9]{4} the Urho3D project'
  746. begin
  747. Dir.chdir dir do
  748. copyrighted = `git grep -El '#{regex}'`.split
  749. copyrighted.each { |filename|
  750. replaced_content = File.read(filename).gsub(/#{regex}/, regex.gsub('[0-9]{4}', Time.now.year.to_s))
  751. File.open(filename, 'w') { |file| file.puts replaced_content }
  752. }
  753. return copyrighted
  754. end
  755. rescue
  756. abort 'Failed to bump copyright year'
  757. end
  758. end
  759. def bump_soversion filename
  760. begin
  761. version = File.read(filename).split '.'
  762. bump_version version, 2
  763. File.open(filename, 'w') { |file| file.puts version.join '.' }
  764. return 0
  765. rescue
  766. nil
  767. end
  768. end
  769. def bump_version version, index
  770. if index > 0 && version[index].to_i == 255
  771. version[index] = 0
  772. bump_version version, index - 1
  773. else
  774. version[index] = version[index].to_i + 1
  775. end
  776. end
  777. def setup_digital_keys
  778. system "bash -c 'mkdir -p ~/.ssh && chmod 700 ~/.ssh'" or abort 'Failed to create ~/.ssh directory'
  779. system "bash -c 'ssh-keyscan frs.sourceforge.net >>~/.ssh/known_hosts 2>/dev/null'" or abort 'Failed to append frs.sourceforge.net server public key to known_hosts'
  780. # Workaround travis encryption key size limitation. Rather than using the solution in their FAQ (using AES to encrypt/decrypt the file and check in the encrypted file into repo), our solution is more pragmatic. The private key below is incomplete. Only the missing portion is encrypted. Much less secure than the original 2048-bit RSA has to offer but good enough for our case.
  781. system "bash -c 'cat <<EOF >~/.ssh/id_rsa
  782. -----BEGIN RSA PRIVATE KEY-----
  783. MIIEpQIBAAKCAQEAnZGzFEypdXKY3KDT0Q3NLY4Bv74yKgJ4LIgbXothx8w4CfM0
  784. VeWBL/AE2iRISEWGB07LruM9y+U/wt58WlCVu001GuJuvXwWenlljsvH8qQlErYi
  785. oXlCwAeVVeanILGL8CPS7QlyzOwwnVF6NdcmfDJjTthBVFbvHrWGo5if86zcZyMR
  786. 2BB5QVEr5fU0yOPFp0+2p7J3cA6HQSKwjUiDtJ+lM62UQp7InCCT3qeh5KYHQcYb
  787. KVJTyj5iycVuBujHDwNAivLq82ojG7LcKjP+Ia8fblardCOQyFk6pSDM79NJJ2Dg
  788. 3ZbYIJeUmqSqFhRW/13Bro7Z1aNGrdh/XZkkHwIDAQABAoIBACHcBFJxYtzVIloO
  789. yVWcFKIcaO3OLjNu0monWVJIu1tW3BfvRijLJ6aoejJyJ4I4RmPdn9FWDZp6CeiT
  790. LL+vn21fWvELBWb8ekwZOCSmT7IpaboKn4h5aUmgl4udA/73iC2zVQkQxbWZb5zu
  791. vEdDk4aOwV5ZBDjecYX01hjjnEOdZHGJlF/H/Xs0hYX6WDG3/r9QCJJ0nfd1/Fk2
  792. zdbZRtAbyRz6ZHiYKnFQ441qRRaEbzunkvTBEwu9iqzlE0s/g49LJL0mKEp7rt/J
  793. 4iS3LZTQbJNx5J0ti8ZJKHhvoWb5RJxNimwKvVHC0XBZKTiLMrhnADmcpjLz53F8
  794. #{ENV['SF_KEY']}
  795. sx27yCaeBeKXV0tFOeZmgK664VM9EgesjIX4sVOJ5mA3xBJBOtz9n66LjoIlIM58
  796. dvsAnJt7MUBdclL/RBHEjbUxgGBDcazfWSuJe0sGczhnXMN94ox4MSECgYEAx5cv
  797. cs/2KurjtWPanDGSz71LyGNdL/xQrAud0gi49H0tyYr0XmzNoe2CbZ/T5xGSZB92
  798. PBcz4rnHQ/oujo/qwjNpDD0xVLEU70Uy/XiY5/v2111TFC4clfE/syZPywKAztt3
  799. y2l5z+QdsNigRPDhKw+7CFYaAnYBEISxR6nabT8CgYEAqHrM8fdn2wsCNE6XvkZQ
  800. O7ZANHNIKVnaRqW/8HW7EFAWQrlQTgzFbtR4uNBIqAtPsvwSx8Pk652+OR1VKfSv
  801. ya3dtqY3rY/ErXWyX0nfPQEbYj/oh8LbS6zPw75yIorP3ACIwMw3GRNWIvkdAGTn
  802. BMUgpWHUDLWWpWRrSzNi90ECgYEAkxxzQ6vW5OFGv17/NdswO+BpqCTc/c5646SY
  803. ScRWFxbhFclOvv5xPqYiWYzRkmIYRaYO7tGnU7jdD9SqVjfrsAJWrke4QZVYOdgG
  804. cl9eTLchxLGr15b5SOeNrQ1TCO4qZM3M6Wgv+bRI0h2JW+c0ABpTIBzehOvXcwZq
  805. 6MhgD98CgYEAtOPqc4aoIRUy+1oijpWs+wU7vAc8fe4sBHv5fsv7naHuPqZgyQYY
  806. 32a54xZxlsBw8T5P4BDy40OR7fu+6miUfL+WxUdII4fD3grlIPw6bpNE0bCDykv5
  807. RLq28S11hDrKf/ZetXNuIprfTlhl6ISBy+oWQibhXmFZSxEiXNV6hCQ=
  808. -----END RSA PRIVATE KEY-----
  809. EOF'" or abort 'Failed to create user private key to id_rsa'
  810. system "bash -c 'chmod 600 ~/.ssh/id_rsa'" or abort 'Failed to change id_rsa file permission'
  811. end
  812. # Load custom rake scripts
  813. Dir['.rake/*.rake'].each { |r| load r }
  814. # vi: set ts=2 sw=2 expandtab: