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