-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
packages.sh
executable file
·1075 lines (963 loc) · 36.6 KB
/
packages.sh
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
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# This file is part of The RetroPie Project
#
# The RetroPie Project is the legal property of its developers, whose names are
# too numerous to list here. Please refer to the COPYRIGHT.md file distributed with this source.
#
# See the LICENSE.md file at the top-level directory of this distribution and
# at https://raw.githubusercontent.com/RetroPie/RetroPie-Setup/master/LICENSE.md
#
declare -A __sections=(
[core]="core"
[main]="main"
[opt]="optional"
[exp]="experimental"
[driver]="driver"
[config]="configuration"
[depends]="dependency"
)
__NET_ERRMSG=""
function rp_listFunctions() {
local id
local desc
local mode
local func
local enabled
echo -e "ID: Description: List of available functions"
echo "-----------------------------------------------------------------------------------------------------------------------------------"
for id in ${__mod_id[@]}; do
if rp_isEnabled "$id"; then
printf "%-20s: %-42s :" "$id" "${__mod_info[$id/desc]}"
else
printf "*%-20s: %-42s : %s\n" "$id" "${__mod_info[$id/desc]}" "This module is not available for your platform"
continue
fi
while read mode; do
# skip private module functions (start with an underscore)
[[ "$mode" = _* ]] && continue
mode=${mode//_$id/}
echo -n " $mode"
done < <(compgen -A function -X \!*_$id)
fnExists "sources_${id}" && echo -n " clean"
fnExists "install_${id}" || fnExists "install_bin_${id}" && ! fnExists "remove_${id}" && echo -n " remove"
echo ""
done
echo "==================================================================================================================================="
}
function rp_printUsageinfo() {
echo -e "Usage:\n$0 <ID>\nThis will run the functions depends, sources, build, install, configure and clean automatically.\n"
echo -e "Alternatively, $0 can be called as\n$0 <ID> [function] where function is a supported module functions as listed below\n"
echo "Details of some common functions:"
echo "depends: install the dependencies for the module."
echo "sources: install the sources for the module"
echo "build: build/compile the module"
echo "install: install the compiled module"
echo "configure: configure the installed module (es_systems.cfg / launch parameters etc)"
echo "clean: remove the sources/build folder for the module"
echo "remove: remove/uninstall the module"
echo "help: get additional help on the module (available for all modules)"
echo -e "\nThis is a list of valid modules/packages and supported functions:\n"
rp_listFunctions
}
function rp_moduleVars() {
local id="$1"
# create variables that can be used in modules
local code
read -d "" -r code <<_EOF_
local md_desc="${__mod_info[$id/desc]}"
local md_help="${__mod_info[$id/help]}"
local md_type="${__mod_info[$id/type]}"
local md_flags="${__mod_info[$id/flags]}"
local md_path="${__mod_info[$id/path]}"
local md_licence="${__mod_info[$id/licence]}"
local md_repo_type="${__mod_info[$id/repo_type]}"
local md_repo_url="${__mod_info[$id/repo_url]}"
local md_repo_branch="${__mod_info[$id/repo_branch]}"
local md_repo_commit="${__mod_info[$id/repo_commit]}"
local md_build="$__builddir/$id"
local md_inst="$(rp_getInstallPath $id)"
# get module path folder + md_id for $md_data
local md_data="${__mod_info[$id/path]%/*}/$id"
_EOF_
echo "$code"
}
function rp_callModule() {
local md_id="$1"
local mode="$2"
# shift the function parameters left so $@ will contain any additional parameters which we can use in modules
shift 2
# check if module exists
if ! rp_hasModule "$md_id"; then
printMsgs "console" "No module '$md_id' found."
return 2
fi
# check if module is enabled for platform
if ! rp_isEnabled "$md_id"; then
printMsgs "console" "Module '$md_id' is not available for your system ($__platform)"
return 3
fi
# skip for modules 'builder' and 'setup' so that distcc settings do not propagate from them
if [[ "$md_id" != "builder" && "$md_id" != "setup" ]]; then
# if distcc is used and the module doesn't exclude it, add /usr/lib/distcc to PATH and MAKEFLAGS
if [[ -n "$DISTCC_HOSTS" ]] && ! hasFlag "${__mod_info[$md_id/flags]}" "nodistcc"; then
# use local variables so they are available to all child functions without changing the globals
local PATH="/usr/lib/distcc:$PATH"
local MAKEFLAGS="$MAKEFLAGS PATH=$PATH"
fi
fi
# parameters _auto_ _binary or _source_ (_source_ is used if no parameters are given for a module)
case "$mode" in
# install the module if not installed, and update if it is
_autoupdate_)
if rp_isInstalled "$md_id"; then
rp_callModule "$md_id" "_update_" || return 1
else
rp_callModule "$md_id" "_auto_" || return 1
fi
return 0
;;
# automatic modes used by rp_installModule to choose between binary/source based on pkg info
_auto_|_update_)
# if updating and a package isn't installed, return an error
if [[ "$mode" == "_update_" ]] && ! rp_isInstalled "$md_id"; then
__ERRMSGS+=("$md_id is not installed, so can't update")
return 1
fi
rp_loadPackageInfo "$md_id" "pkg_origin"
local pkg_origin="${__mod_info[$md_id/pkg_origin]}"
local has_binary=0
local has_net=0
isConnected && has_net=1
# for modules with nonet flag that don't need to download data, we force has_net to 1
hasFlag "${__mod_info[$md_id/flags]}" "nonet" && has_net=1
if [[ "$has_net" -eq 1 ]]; then
rp_hasBinary "$md_id"
local ret="$?"
[[ "$ret" -eq 0 ]] && has_binary=1
[[ "$ret" -eq 2 ]] && has_net=0
fi
# fail if we don't seem to be connected
if [[ "$has_net" -eq 0 ]]; then
__ERRMSGS+=("Can't install/update $md_id - $__NET_ERRMSG")
return 1
fi
local do_update=0
# if we are in _update_ mode we only update if there is a newer version of the binary or source
if [[ "$mode" == "_update_" ]]; then
printMsgs "heading" "Checking for updates for $md_id"
rp_hasNewerModule "$md_id" "$pkg_origin"
[[ "$?" -eq 0 || "$?" == 2 ]] && do_update=1
# if rp_hasNewerModule returns 3, then there was an error and we should abort
[[ "$?" -eq 3 ]] && return 1
else
do_update=1
fi
if [[ "$do_update" -eq 1 ]]; then
printMsgs "console" "Update is available - updating ..."
else
printMsgs "console" "No update was found."
fi
if [[ "$do_update" -eq 1 ]]; then
if [[ "$pkg_origin" != "source" && "$has_binary" -eq 1 ]]; then
rp_callModule "$md_id" _binary_ || return 1
else
rp_callModule "$md_id" _source_ || return 1
fi
fi
return 0
;;
_binary_)
for mode in depends install_bin configure; do
rp_callModule "$md_id" "$mode" || return 1
done
return 0
;;
# automatically build/install module from source if no _source_ or no parameters are given
""|_source_)
for mode in depends sources build install configure clean; do
rp_callModule "$md_id" "$mode" || return 1
done
return 0
;;
esac
# load our md_* variables
eval "$(rp_moduleVars $md_id)"
local md_mode="install"
# set md_conf_root to $configdir and to $configdir/ports for ports
# ports in libretrocores or systems (as ES sees them) in ports will need to change it manually with setConfigRoot
local md_conf_root
if [[ "$md_type" == "ports" ]]; then
setConfigRoot "ports"
else
setConfigRoot ""
fi
case "$mode" in
# remove sources
clean)
if [[ "$__persistent_repos" -eq 1 ]]; then
if [[ -d "$md_build/.git" ]]; then
git -C "$md_build" reset --hard
git -C "$md_build" clean -f -d
fi
else
rmDirExists "$md_build"
fi
return 0
;;
# create binary archive
create_bin)
rp_createBin || return 1
return 0
;;
# echo module help to console
help)
printMsgs "console" "$md_desc\n\n$md_help"
return 0;
;;
esac
# create function name
function="${mode}_${md_id}"
# automatically switch to install_bin if present while install is not, and handle cases where we have
# fallback functions when not present in modules - currently install_bin and remove
if ! fnExists "$function"; then
if [[ "$mode" == "install" ]] && fnExists "install_bin_${md_id}"; then
function="install_bin_${md_id}"
mode="install_bin"
elif [[ "$mode" != "install_bin" && "$mode" != "remove" ]]; then
return 0
fi
fi
# these can be returned by a module
local md_ret_require=()
local md_ret_files=()
local md_ret_errors=()
local md_ret_info=()
local action
local pushed=1
case "$mode" in
depends)
if [[ "$1" == "remove" ]]; then
md_mode="remove"
action="Removing"
else
action="Installing"
fi
action+=" dependencies for"
;;
sources)
action="Getting sources for"
mkdir -p "$md_build"
pushd "$md_build"
pushed=$?
;;
build)
action="Building"
pushd "$md_build" 2>/dev/null
pushed=$?
;;
install)
pushd "$md_build" 2>/dev/null
pushed=$?
;;
install_bin)
action="Installing (binary)"
;;
configure)
action="Configuring"
pushd "$md_inst" 2>/dev/null
pushed=$?
;;
remove)
action="Removing"
;;
_update_hook)
;;
*)
action="Running action '$mode' for"
;;
esac
# print an action and a description
if [[ -n "$action" ]]; then
printHeading "$action '$md_id' : $md_desc"
fi
case "$mode" in
remove)
fnExists "$function" && "$function" "$@"
md_mode="remove"
if fnExists "configure_${md_id}"; then
pushd "$md_inst" 2>/dev/null
pushed=$?
"configure_${md_id}"
fi
rm -rf "$md_inst"
rp_clearCachedInfo "$md_id"
printMsgs "console" "Removed directory $md_inst"
;;
install)
action="Installing"
# remove any previous install folder unless noinstclean flag is set
if ! hasFlag "${__mod_info[$md_id/flags]}" "noinstclean"; then
rmDirExists "$md_inst"
fi
mkdir -p "$md_inst"
"$function" "$@"
;;
install_bin)
if fnExists "install_bin_${md_id}"; then
mkdir -p "$md_inst"
if ! "$function" "$@"; then
md_ret_errors+=("Unable to install binary for $md_id")
fi
else
if rp_hasBinary "$md_id"; then
rp_installBin
else
md_ret_errors+=("Could not find a binary for $md_id")
fi
fi
;;
*)
# call the function with parameters
"$function" "$@"
;;
esac
# check if any required files are found
if [[ -n "$md_ret_require" ]]; then
for file in "${md_ret_require[@]}"; do
if [[ ! -e "$file" ]]; then
md_ret_errors+=("Could not successfully $mode $md_id - $md_desc ($file not found).")
break
fi
done
fi
if [[ "${#md_ret_errors}" -eq 0 && -n "$md_ret_files" ]]; then
# check for existence and copy any files/directories returned
local file
for file in "${md_ret_files[@]}"; do
if [[ ! -e "$md_build/$file" ]]; then
md_ret_errors+=("Could not successfully install $md_desc ($md_build/$file not found).")
break
fi
cp -Rvf "$md_build/$file" "$md_inst"
done
fi
# remove build folder if empty
[[ -d "$md_build" ]] && find "$md_build" -maxdepth 0 -empty -exec rmdir {} \;
local ret=0
# some errors were returned.
if [[ "${#md_ret_errors[@]}" -gt 0 ]]; then
__ERRMSGS+=("${md_ret_errors[@]}")
printMsgs "console" "${md_ret_errors[@]}" >&2
# if sources fails and we were called from the setup gui module clean sources
if [[ "$mode" == "sources" && "$__setup" -eq 1 ]]; then
rp_callModule "$md_id" clean
fi
# remove install folder if there is an error (and it is empty)
[[ -d "$md_inst" ]] && find "$md_inst" -maxdepth 0 -empty -exec rmdir {} \;
ret=1
else
[[ "$mode" == "install_bin" ]] && rp_setPackageInfo "$md_id" "binary"
[[ "$mode" == "install" ]] && rp_setPackageInfo "$md_id" "source"
# handle the case of a few drivers that don't have an install function and set the package info at build stage
if ! fnExists "install_${md_id}" && [[ "$mode" == "build" ]]; then
rp_setPackageInfo "$md_id" "source"
fi
fi
# some information messages were returned
if [[ "${#md_ret_info[@]}" -gt 0 ]]; then
__INFMSGS+=("${md_ret_info[@]}")
fi
[[ "$pushed" -eq 0 ]] && popd
return "$ret"
}
function rp_hasBinaries() {
[[ "$__has_binaries" -eq 1 ]] && return 0
return 1
}
function rp_getBinaryUrl() {
local id="$1"
local url=""
rp_hasBinaries && url="$__binary_url/${__mod_info[$id/type]}/$id.tar.gz"
if fnExists "install_bin_${id}"; then
if fnExists "__binary_url_${id}"; then
url="$(__binary_url_${id})"
else
url="notest"
fi
fi
echo "$url"
}
# returns 0 if file exists, 1 if it doesn't and 2 on other error
function rp_remoteFileExists() {
local url="$1"
local ret
# runCurl will cause stderr to be copied to output so we redirect both stdout/stderr to /dev/null.
# any errors will have been captured by runCurl
runCurl --location --max-time 10 --silent --show-error --fail --head "$url" &>/dev/null
ret="$?"
if [[ "$ret" -eq 0 ]]; then
return 0
elif [[ "$ret" -eq 22 ]]; then
return 1
else
return 2
fi
}
function rp_hasBinary() {
local id="$1"
# binary blacklist for armv7 Debian/OSMC due to GCC ABI incompatibility with
# threaded C++ apps on Raspbian (armv6 userland)
if [[ "$__os_id" != "Raspbian" ]] && ! isPlatform "armv6"; then
case "$id" in
emulationstation|lzdoom|lr-dinothawr|lr-ppsspp|ppsspp)
return 1
;;
esac
fi
local url="$(rp_getBinaryUrl $id)"
[[ "$url" == "notest" ]] && return 0
[[ -z "$url" ]] && return 1
[[ -n "${__mod_info[$id/has_binary]}" ]] && return "${__mod_info[$id/has_binary]}"
local ret=1
rp_remoteFileExists "$url"
ret="$?"
# if there wasn't an error, cache the result
[[ "$ret" -ne 2 ]] && __mod_info[$id/has_binary]="$ret"
return "$ret"
}
function rp_getFileDate() {
local url="$1"
[[ -z "$url" ]] && return 1
# get last-modified date stripping any CR in the output
local file_date=$(runCurl --location --silent --fail --head --no-styled-output "$url" | tr -d "\r" | grep -ioP "last-modified: \K.+")
# if there is a date set in last-modified header, then convert to iso-8601 format
if [[ -n "$file_date" ]]; then
file_date="$(date -Iseconds --date="$file_date")"
echo "$file_date"
return 0
fi
return 1
}
function rp_getBinaryDate() {
local id="$1"
local url="$(rp_getBinaryUrl $id)"
[[ -z "$url" || "$url" == "notest" ]] && return 1
local bin_date
if bin_date="$(rp_getFileDate "$url")"; then
echo "$bin_date"
return 0
fi
return 1
}
function rp_dateIsNewer() {
local date_a="$1"
local date_b="$2"
[[ -z "$date_a" || -z "$date_b" ]] && return 0
if date_a="$(date -d "$date_a" +%s 2>/dev/null)" && date_b="$(date -d "$date_b" +%s 2>/dev/null)"; then
[[ "$date_b" -gt "$date_a" ]] && return 0
else
return 0
fi
return 1
}
# resolve repository parameters - any parameters with a : will get the data from the following function name
function rp_resolveRepoParam() {
local param="$1"
if [[ "$param" == :* ]]; then
param="${param:1}"
if fnExists "$param"; then
echo "$($param)"
return
fi
fi
echo "$param"
}
# gets remote repository hash/revision - echos hash of remote repo and returns 0, or
# echos an error and returns 1 on failure
function rp_getRemoteRepoHash() {
local type="$1"
local url="$2"
local branch="$3"
local commit="$4"
local hash
local ret
local cmd=()
set -o pipefail
case "$type" in
git)
# when the remote repository uses an annotated git tag, the real commit is found by looking for the
# "tag^{}" reference, since the tag ref will point to the tag object itself, instead of the tagged
# commit. See gitrevisions(7).
cmd=(git ls-remote "$url" "$branch" "$branch^{}")
# grep to make sure we only return refs/heads/BRANCH and refs/tags/BRANCH in case there are additional
# references to the branch/tag eg refs/heads/SOMETHINGELSE/master which can be the case.
# we grab the last match reported by grep, as tags that also have a tag^{} reference
# will be displayed after.
hash=$("${cmd[@]}" 2>/dev/null | grep -P "\trefs/(heads|tags)/$branch" | tail -n1 | cut -f1)
;;
svn)
cmd=(svn info -r"$commit" "$url")
hash=$("${cmd[@]}" 2>/dev/null | grep -oP "Last Changed Rev: \K.*")
;;
esac
ret="$?"
set +o pipefail
if [[ "$ret" -ne 0 ]]; then
echo "${cmd[*]} failed with return code $ret - please check your network connection"
return 1
fi
echo "$hash"
return 0
}
# returns 0 if a module has a newer version, 1 if it doesn't, 2 if unknown (always update), or 3 if there is an error
function rp_hasNewerModule() {
local id="$1"
local type="$2"
[[ -n "${__mod_info[$id/has_newer]}" ]] && return "${__mod_info[$id/has_newer]}"
rp_loadPackageInfo "$id"
local pkg_origin="${__mod_info[$id/pkg_origin]}"
local pkg_date="${__mod_info[$id/pkg_date]}"
local pkg_repo_date="${__mod_info[$id/pkg_repo_date]}"
local pkg_repo_commit="${__mod_info[$id/pkg_repo_commit]}"
local ret=1
case "$type" in
binary)
ret=""
if [[ -n "$pkg_date" ]]; then
local bin_date="$(rp_getBinaryDate $id)"
if [[ -n "$bin_date" ]]; then
rp_dateIsNewer "$pkg_date" "$bin_date"
ret="$?"
fi
fi
[[ -z "$ret" ]] && ret=2
;;
source)
local repo_type="${__mod_info[$id/repo_type]}"
local repo_url="$(rp_resolveRepoParam "${__mod_info[$id/repo_url]}")"
case "$repo_type" in
file)
if [[ -n "$pkg_repo_date" ]]; then
local file_date="$(rp_getFileDate "$repo_url")"
rp_dateIsNewer "$pkg_repo_date" "$file_date"
ret="$?"
fi
;;
git|svn)
local repo_branch="$(rp_resolveRepoParam "${__mod_info[$id/repo_branch]}")"
local repo_commit="$(rp_resolveRepoParam "${__mod_info[$id/repo_commit]}")"
# if we are locked to a single commit, then we compare against the current module commit only
if [[ -n "$repo_commit" && "$repo_commit" != "HEAD" ]]; then
# if we are using git and the module has a commit hash, then adjust
# the package commit length for the comparison
if [[ "$repo_type" == "git" && "${#repo_commit}" -ge 4 ]]; then
pkg_repo_commit="${pkg_repo_commit::${#repo_commit}}"
fi
if [[ "$pkg_repo_commit" != "$repo_commit" ]]; then
ret=0
fi
else
local remote_commit
if remote_commit="$(rp_getRemoteRepoHash "$repo_type" "$repo_url" "$repo_branch" "$repo_commit")"; then
if [[ -n "$remote_commit" && "$pkg_repo_commit" != "$remote_commit" ]]; then
ret=0
fi
else
__ERRMSGS+=("$remote_commit")
ret=3
fi
fi
;;
:*)
local pkg_repo_extra="${__mod_info[$id/pkg_repo_extra]}"
# handle checking via module function - function should return 0 if there is a newer version
local function="${repo_type:1}"
if fnExists "$function"; then
"$function" newer
ret="$?"
fi
;;
*)
# fallback on forcing an update
ret=2
;;
esac
# if we are currently not going to update - check the last commit date of the module code
# if it's newer than the install date of the module we force an update, in case patches or build
# related options have been changed
if [[ "$ret" -eq 1 && "$__ignore_module_date" -ne 1 ]]; then
local vendor="${__mod_info[$id/vendor]}"
local repo_dir="$scriptdir"
[[ "$vendor" != "RetroPie" ]] && repo_dir+="/ext/$vendor"
local module_date="$(sudo -u "$__user" git -C "$repo_dir" log -1 --format=%cI -- "${__mod_info[$id/path]}")"
# just in case the module is not known to git, get the file last modified date
[[ -z "$module_date" ]] && module_date="$(date -Iseconds -r "${__mod_info[$id/path]}")"
if rp_dateIsNewer "$pkg_date" "$module_date"; then
ret=0
fi
fi
;;
*)
# for unknown or in the case of a blank pkg_origin assume there is an update
ret=2
;;
esac
# cache our return value
__mod_info[$id/has_newer]="$ret"
return "$ret"
}
function rp_getInstallPath() {
local id="$1"
echo "$rootdir/${__mod_info[$id/type]}/$id"
}
function rp_installBin() {
rp_hasBinaries || fatalError "There are no binary archives for platform $__platform"
local archive="$md_id.tar.gz";
local dest="$rootdir/$md_type"
local cmd_out
# create temporary folder
local tmp=$(mktemp -d)
if downloadAndVerify "$__binary_url/$md_type/$archive" "$tmp/$archive"; then
mkdir -p "$dest"
rm -rf "$dest/$md_id"
if tar -xvf "$tmp/$archive" -C "$dest"; then
rm -rf "$tmp"
return 0
else
md_ret_errors+=("Archive $archive failed to unpack correctly to $dest")
fi
fi
rm -rf "$tmp"
return 1
}
function rp_createBin() {
printHeading "Creating binary archive for $md_desc"
if [[ ! -d "$md_inst" ]]; then
printMsgs "console" "No install directory $md_inst - no archive created"
return 1
fi
if dirIsEmpty "$md_inst"; then
printMsgs "console" "Empty install directory $md_inst - no archive created"
return 1
fi
local archive="$md_id.tar.gz"
local dest="$__tmpdir/archives/$__binary_path/$md_type"
mkdir -p "$dest"
rm -f "$dest/$archive" "$dest/$archive.asc"
local ret=1
if tar cvzf "$dest/$archive" -C "$rootdir/$md_type" "$md_id"; then
if [[ -n "$__gpg_signing_key" ]]; then
if signFile "$dest/$archive"; then
chown "$__user":"$__group" "$dest/$archive" "$dest/$archive.asc"
ret=0
fi
else
ret=0
fi
fi
if [[ "$ret" -eq 0 ]]; then
cp "$md_inst/retropie.pkg" "$dest/$md_id.pkg"
else
rm -f "$dest/$archive"
fi
return "$ret"
}
function rp_hasModule() {
local id="$1"
[[ -n "${__mod_idx[$id]}" ]] && return 0
return 1
}
function rp_installModule() {
local id="$1"
local mode="$2"
[[ -z "$mode" ]] && mode="_auto_"
rp_callModule "$id" "$mode" || return 1
return 0
}
function rp_clearCachedInfo() {
local id="$1"
# clear cached data
# set pkg_info to 0 to force a reload when needed
__mod_info[$id/pkg_info]=0
__mod_info[$id/has_binary]=""
__mod_info[$id/has_newer]=""
}
# this is run after the install_ID function for a module - which by default would be from the
# folder set by md_build. However some modules install directly to md_inst which would mean unless
# they change directory also to md_inst in the install stage (which is common), it's possible this
# function could be run and not find the source. Therefore, we currently record the first directory used
# by gitPullOrClone and record it in __mod_info to be used here - but this needs further work to handle
# other cases.
function rp_setPackageInfo() {
local id="$1"
local install_path="$(rp_getInstallPath $id)"
[[ ! -d "$install_path" ]] && return 1
local pkg="$install_path/retropie.pkg"
local origin="$2"
rp_clearCachedInfo "$id"
iniConfig "=" '"' "$pkg"
iniSet "pkg_origin" "$origin"
local pkg_date
local pkg_repo_type
local pkg_repo_url
local pkg_repo_branch
local pkg_repo_commit
local pkg_repo_date
local pkg_repo_extra
if [[ "$origin" == "binary" ]]; then
pkg_date="$(rp_getBinaryDate $id)"
iniSet "pkg_date" "$pkg_date"
else
pkg_date="$(date -Iseconds)"
pkg_repo_type="${__mod_info[$id/repo_type]}"
pkg_repo_url="$(rp_resolveRepoParam "${__mod_info[$id/repo_url]}")"
pkg_repo_branch="$(rp_resolveRepoParam "${__mod_info[$id/repo_branch]}")"
case "$pkg_repo_type" in
git|svn)
if [[ "$pkg_repo_type" == "git" ]]; then
# if we have recorded a source install dir during gitPullOrClone use it, or default to md_build
local repo_dir="${__mod_info[$id/repo_dir]}"
[[ -z "$repo_dir" ]] && repo_dir="$md_build"
# date cannot understand the default date format of git
pkg_repo_date="$(git -C "$repo_dir" log -1 --format=%cI)"
pkg_repo_commit="$(git -C "$repo_dir" log -1 --format=%H)"
else
pkg_repo_date="$(svn info . | grep -oP "Last Changed Date: \K.*")"
pkg_repo_date="$(date -Iseconds -d "$pkg_repo_date")"
pkg_repo_commit="$(svn info . | grep -oP "Last Changed Rev: \K.*")"
fi
;;
file)
pkg_repo_date="$(rp_getFileDate "$pkg_repo_url")"
;;
:*)
# set data based on function hook - function should return code to define any pkg_* vars
# eg. local pkg_repo_date="something"
local function="${pkg_repo_type:1}"
fnExists "$function" && eval $("$function" get)
;;
esac
iniSet "pkg_date" "$pkg_date"
iniSet "pkg_repo_type" "$pkg_repo_type"
iniSet "pkg_repo_url" "$pkg_repo_url"
iniSet "pkg_repo_branch" "$pkg_repo_branch"
iniSet "pkg_repo_commit" "$pkg_repo_commit"
iniSet "pkg_repo_date" "$pkg_repo_date"
iniSet "pkg_repo_extra" "$pkg_repo_extra"
fi
}
# loads installed package information into __mod_info/pkg_* fields
# additional parameters are optional keys to load, but the data won't be cached if this is provided
function rp_loadPackageInfo() {
local id="$1"
# if we have cached the package information already, return
[[ "${__mod_info[$id/pkg_info]}" -eq 1 ]] && return
local keys
local cache=1
if [[ -z "$2" ]]; then
keys=(
pkg_origin
pkg_date
pkg_repo_type
pkg_repo_url
pkg_repo_branch
pkg_repo_commit
pkg_repo_date
pkg_repo_extra
)
else
# get user supplied keys but don't cache
shift
keys=("$@")
cache=0
fi
local load=0
local pkg_file="$(rp_getInstallPath $id)/retropie.pkg"
local builder_pkg_file="$__tmpdir/archives/$__binary_path/${__mod_info[$id/type]}/$id.pkg"
# fallback to using package info for built binaries so the binary builder code can update only changed modules
if [[ ! -f "$pkg_file" && -f "$builder_pkg_file" ]]; then
pkg_file="$builder_pkg_file"
fi
# if the pkg file is available, we will load the data in the next loop
[[ -f "$pkg_file" ]] && load=1
local key
local data
for key in "${keys[@]}"; do
# clear any previous data we have stored, but default "pkg_origin" to "unknown"
data=""
[[ "$key" == "pkg_origin" ]] && data="unknown"
__mod_info[$id/$key]="$data"
# if the package file is available and the field is set, override the default values
if [[ "$load" -eq 1 ]]; then
data="$(grep -oP "$key=\"\K[^\"]+" "$pkg_file")"
[[ -n "$data" ]] && __mod_info[$id/$key]="$data"
fi
done
# if loading all data set pkg_info to 1 so we avoid loading when not needed
[[ "$cache" -eq 1 ]] && __mod_info[$id/pkg_info]=1
}
function rp_registerModule() {
local path="$1"
local type="$2"
local vendor="$3"
# type is the last folder in the path as we now send a full path to rp_registerModule
type="${type##*/}"
local rp_module_id=""
local rp_module_desc=""
local rp_module_help=""
local rp_module_licence=""
local rp_module_section=""
local rp_module_flags=""
local rp_module_repo=""
local error=0
# for 3rd party modules, extract the module id and make sure it is unique
# as we don't want 3rd party repos overriding our built-in modules
if [[ "$vendor" != "RetroPie" ]]; then
rp_module_id=$(grep -oP "rp_module_id=\"\K([^\"]+)" "$path")
if [[ -n "${__mod_idx[$rp_module_id]}" ]]; then
__INFMSGS+=("Module $path was skipped as $rp_module_id is already used by ${__mod_info[$rp_module_id/path]}")
return
fi
fi
source "$path"
local var
for var in rp_module_id rp_module_desc; do
if [[ -z "${!var}" ]]; then
echo "Module $module_path is missing valid $var"
error=1
fi
done
[[ $error -eq 1 ]] && exit 1
local flags=($rp_module_flags)
local flag
local enabled=1
# flags are parsed in the order provided in the module - so the !all flag only makes sense first
# by default modules are enabled for all platforms
if [[ "$__ignore_flags" -ne 1 ]]; then
for flag in "${flags[@]}"; do
# !all excludes the module from all platforms
if [[ "$flag" == "!all" ]]; then
enabled=0
continue
fi
# flags without ! make the module valid for the platform
if isPlatform "$flag"; then
enabled=1
continue
fi
# flags with !flag will exclude the module for the platform
if [[ "$flag" =~ ^\!(.+) ]] && isPlatform "${BASH_REMATCH[1]}"; then
enabled=0
continue
fi
# enable or disable based on a comparison in the format :\$var:cmp:val or !:\$var:cmp:val
# eg. :\$__gcc_version:-lt:7 would be evaluated as [[ $__gcc_version -lt 7 ]]
# this would enable a module if the comparison was true
# !:\$__gcc_version:-lt:7 would disable if the comparison was true
# match and extract the parameters
if [[ "$flag" =~ ^(\!?):([^:]+):([^:]+):(.+)$ ]]; then
# enable or disable based on the first parameter (!)
local e=1
[[ ${BASH_REMATCH[1]} == "!" ]] && e=0
# evaluate the comparison
if eval "[[ ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} ${BASH_REMATCH[4]} ]]"; then
enabled=$e
fi
continue
fi
done
fi
local sections=($rp_module_section)
# get default section
rp_module_section="${sections[0]}"
# loop through any additional flag=section parameters
local flag section
for section in "${sections[@]:1}"; do
section=(${section/=/ })
flag="${section[0]}"
section="${section[1]}"
isPlatform "$flag" && rp_module_section="$section"
done
# create numerical index for each module id from nunber of added modules
__mod_idx["$rp_module_id"]="${#__mod_id[@]}"
__mod_id+=("$rp_module_id")
__mod_info["$rp_module_id/enabled"]="$enabled"
__mod_info["$rp_module_id/path"]="$path"
__mod_info["$rp_module_id/vendor"]="$vendor"
__mod_info["$rp_module_id/type"]="$type"
__mod_info["$rp_module_id/desc"]="$rp_module_desc"
__mod_info["$rp_module_id/help"]="$rp_module_help"
__mod_info["$rp_module_id/licence"]="$rp_module_licence"
__mod_info["$rp_module_id/section"]="$rp_module_section"
__mod_info["$rp_module_id/flags"]="$rp_module_flags"
# split module repo into type, url, branch and commit
if [[ -n "$rp_module_repo" ]]; then
local repo=($rp_module_repo)
__mod_info["$rp_module_id/repo_type"]="${repo[0]}"