-
Notifications
You must be signed in to change notification settings - Fork 132
Expand file tree
/
Copy pathqbt-nox-static.bash
More file actions
executable file
·4288 lines (3874 loc) · 233 KB
/
qbt-nox-static.bash
File metadata and controls
executable file
·4288 lines (3874 loc) · 233 KB
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
#!/bin/bash
#
# cSpell:includeRegExp #.*
#
# Copyright 2020 by userdocs and contributors
#
# SPDX-License-Identifier: Apache-2.0
#
# @author - userdocs
#
# @contributors IceCodeNew Stanislas boredazfcuk AdvenT. guillaumedsde inochisa angristan xNihil0 Jercik voidtao
#
# https://github.com/userdocs/qbittorrent-nox-static/graphs/contributors
#
# @credits - https://gist.github.com/notsure2 https://github.com/c0re100/qBittorrent-Enhanced-Edition
#
# Script Formatting - https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format
#
#################################################################################################################################################
# Script version = Major minor patch
#################################################################################################################################################
script_version="2.2.4"
#################################################################################################################################################
# Set some script features - https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
#################################################################################################################################################
# Note: Removed global 'set -a' for security - variables are now explicitly exported when needed
#################################################################################################################################################
# Unset some variables to set defaults.
#################################################################################################################################################
unset qbt_skip_delete qbt_git_proxy qbt_curl_proxy qbt_install_dir qbt_working_dir qbt_modules_test qbt_python_version
unset qbt_cflags qbt_cxxflags qbt_cppflags qbt_ldflags qbt_cflags_consumed qbt_cxxflags_consumed qbt_cppflags_consumed qbt_ldflags_consumed
#################################################################################################################################################
# Declare our associative arrays
#################################################################################################################################################
# Associative arrays
declare -gA multi_arch_options qbt_test_tools qbt_core_deps qbt_deps_delete
declare -gA qbt_modules_delete skip_modules qbt_modules_install qbt_privileges_required
declare -gA github_url github_tag app_version source_archive_url qbt_workflow_archive_url
declare -gA qbt_workflow_override source_default qbt_activated_modules
# Indexed arrays
declare -ga qbt_modules_order qbt_modules_install_processed qbt_modules_selected_compare
#################################################################################################################################################
# Color me up Scotty - define some color values to use as variables in the scripts.
#################################################################################################################################################
color_red="\e[31m" color_red_light="\e[91m"
color_green="\e[32m" color_green_light="\e[92m"
color_yellow="\e[33m" color_yellow_light="\e[93m"
color_blue="\e[34m" color_blue_light="\e[94m"
color_magenta="\e[35m" color_magenta_light="\e[95m"
color_cyan="\e[36m" color_cyan_light="\e[96m"
text_bold="\e[1m" text_dim="\e[2m" text_underlined="\e[4m" text_blink="\e[5m" text_newline="\n"
unicode_red_circle="\e[31m\U2B24\e[0m" unicode_red_light_circle="\e[91m\U2B24\e[0m"
unicode_green_circle="\e[32m\U2B24\e[0m" unicode_green_light_circle="\e[92m\U2B24\e[0m"
unicode_yellow_circle="\e[33m\U2B24\e[0m" unicode_yellow_light_circle="\e[93m\U2B24\e[0m"
unicode_blue_circle="\e[34m\U2B24\e[0m" unicode_blue_light_circle="\e[94m\U2B24\e[0m"
unicode_magenta_circle="\e[35m\U2B24\e[0m" unicode_magenta_light_circle="\e[95m\U2B24\e[0m"
unicode_cyan_circle="\e[36m\U2B24\e[0m" unicode_cyan_light_circle="\e[96m\U2B24\e[0m"
unicode_grey_circle="\e[37m\U2B24\e[0m" unicode_grey_light_circle="\e[97m\U2B24\e[0m"
color_end="\e[0m"
#################################################################################################################################################
# Function to test color and show outputs in the terminal
#################################################################################################################################################
_color_test() {
# Check if the terminal supports color output
if [[ -t 1 ]]; then
color_array=("${color_red}red" "${color_red_light}light red" "${color_green}green" "${color_green_light}light green" "${color_yellow}yellow" "${color_yellow_light}light yellow" "${color_blue}blue" "${color_blue_light}light blue" "${color_magenta}magenta" "${color_magenta_light}light magenta" "${color_cyan}cyan" "${color_cyan_light}light cyan")
formatting_array=("${text_bold}Text Bold" "${text_dim}Text Dim" "${text_underlined}Text Underline" "${text_newline}New line" "${text_blink}Text Blink")
unicode_array=("${unicode_red_circle}" "${unicode_red_light_circle}" "${unicode_green_circle}" "${unicode_green_light_circle}" "${unicode_yellow_circle}" "${unicode_yellow_light_circle}" "${unicode_blue_circle}" "${unicode_blue_light_circle}" "${unicode_magenta_circle}" "${unicode_magenta_light_circle}" "${unicode_cyan_circle}" "${unicode_cyan_light_circle}" "${unicode_grey_circle}" "${unicode_grey_light_circle}")
printf '\n'
for colors in "${color_array[@]}" "${formatting_array[@]}" "${unicode_array[@]}"; do
printf '%b\n' "${colors}${color_end}"
done
printf '\n'
exit 0
else
printf '%s\n' "The terminal does not support color output."
exit 1
fi
}
[[ ${1} == "ctest" ]] && _color_test # ./scriptname.sh ctest
#######################################################################################################################################################
# Get script basename and full path
#######################################################################################################################################################
script_full_path="$(readlink -f "${BASH_SOURCE[0]}")"
script_parent_path="${script_full_path%/*}"
script_basename="${script_full_path##*/}"
#######################################################################################################################################################
# Function to source /etc/os-release and get info from it on demand.
#######################################################################################################################################################
get_os_info() {
# shellcheck source=/dev/null
if source /etc/os-release &> /dev/null; then
printf "%s" "${!1%_*}" # the expansion part is specific to the Alpine VERSION_ID format 1.2.3_alpha but won't break anything in Debian based format e.g. 12/24.04
else
printf "%s" "unknown" # This will make the script exit on the version check and provide useful reason.
fi
}
#######################################################################################################################################################
# Checks to see if we are on a supported OS and release.
#######################################################################################################################################################
os_id="$(get_os_info ID)" # Get the ID for this OS.
os_version_codename="$(get_os_info VERSION_CODENAME)" # Get the codename for this OS. Note, Alpine does not have a unique codename.
os_version_id="$(get_os_info VERSION_ID)" # Get the version number for this codename, for example: 10, 20.04, 3.12.4
[[ ${os_id} =~ ^(alpine)$ ]] && os_version_codename="alpine" # If alpine, set the codename to alpine. We check for min v3.18 later with codenames.
if [[ ${os_id} =~ ^(debian|ubuntu)$ ]]; then
# dpkg --print-architecture give amd64/arm64 and arch gives x86_64/aarch64
os_arch="$(dpkg --print-architecture 2> /dev/null)"
elif [[ ${os_id} =~ ^(alpine)$ ]]; then
# apk --print-arch gives x86_64/aarch64
os_arch="$(apk --print-arch 2> /dev/null)"
else
os_arch="unknown"
fi
# Check against allowed codenames or if the codename is alpine version greater than 3.18
if [[ ! ${os_version_codename} =~ ^(alpine|trixie|noble)$ ]] || [[ ${os_version_codename} =~ ^(alpine)$ && "$(apk version -t "${os_version_id}" "3.18")" == "<" ]]; then
printf '\n%b\n\n' " ${unicode_red_circle} ${color_yellow} This is not a supported OS. There is no reason to continue.${color_end}"
printf '%b\n\n' " id: ${text_dim}${color_yellow_light}${os_id}${color_end} codename: ${text_dim}${color_yellow_light}${os_version_codename}${color_end} version: ${text_dim}${color_red_light}${os_version_id}${color_end}"
printf '%b\n\n' " ${unicode_yellow_circle} ${text_dim}These are the supported platforms${color_end}"
printf '%b\n' " ${color_magenta_light}Debian${color_end} - ${color_blue_light}trixie${color_end}"
printf '%b\n' " ${color_magenta_light}Ubuntu${color_end} - ${color_blue_light}noble${color_end}"
printf '%b\n\n' " ${color_magenta_light}Alpine${color_end} - ${color_blue_light}3.18${color_end} ${text_dim}or greater${color_end}"
exit 1
fi
#######################################################################################################################################################
# Source env vars from a file if it exists but it will be overridden by switches and flags passed to the script
#######################################################################################################################################################
if [[ -f "${script_parent_path}/.qbt_env" ]]; then
printf '\n%b\n' " ${unicode_magenta_circle} Sourcing ${color_blue_light}.qbt_env${color_end} file - ${color_red_light}This will override your settings!${color_end}"
# shellcheck source=/dev/null
. "${script_parent_path}/.qbt_env"
fi
#######################################################################################################################################################
# Multi arch stuff - Define all available multi arches we use from here https://github.com/userdocs/qbt-musl-cross-make#readme
#######################################################################################################################################################
multi_arch_options["default"]="default"
multi_arch_options["aarch64"]="aarch64"
multi_arch_options["armel"]="armel"
multi_arch_options["armhf"]="armhf"
multi_arch_options["armv7"]="armv7"
multi_arch_options["x86"]="x86"
multi_arch_options["x86_64"]="x86_64"
multi_arch_options["mips"]="mips"
multi_arch_options["mipsel"]="mipsel"
multi_arch_options["mips64"]="mips64"
multi_arch_options["mips64el"]="mips64el"
multi_arch_options["powerpc"]="powerpc"
multi_arch_options["ppc64el"]="ppc64el"
multi_arch_options["s390x"]="s390x"
multi_arch_options["riscv64"]="riscv64"
multi_arch_options["loongarch64"]="loongarch64"
#######################################################################################################################################################
# This function sets some default values we use but whose values can be overridden by certain flags or exported as variables before running the script
#######################################################################################################################################################
_set_default_values() {
# For debian based docker deploys to not get prompted to set the timezone.
if [[ ${os_id} =~ ^(debian|ubuntu)$ ]]; then
export LANG="C.UTF-8"
export LANGUAGE="C.UTF-8"
export LC_ALL="C.UTF-8"
export DEBIAN_FRONTEND="noninteractive"
export TZ="Europe/London"
fi
# If set to yes the script behaves like qbittorrent-nox-static.sh and automatically install core deps if it has the privileges and any are required.
qbt_legacy_mode="${qbt_legacy_mode:-no}"
# hide certain options by default to not confuse on first run. Lots of options are not really needed for a basic build.
qbt_advanced_view="${qbt_advanced_view:-yes}"
# qbt mcm docker images - these env settings tell the script that the host env is the cross build containers
# Otherwise the script thinks the cross build container host is native and applies the wrong settings.
QBT_MCM_DOCKER="${QBT_MCM_DOCKER:-}"
QBT_MCM_TARGET="${QBT_MCM_TARGET:-}"
QBT_CROSS_NAME="${QBT_CROSS_NAME:-}"
# Set the working dir to our current location and all things well be relative to this location.
qbt_working_dir="$(pwd)"
# Used with printf. Use the qbt_working_dir variable but the ${HOME} path is replaced with a literal ~
qbt_working_dir_short="${qbt_working_dir/${HOME}/\~}"
qbt_build_dir="${qbt_build_dir:-qbt-build}" # Build directory
# Install relative to the script location.
qbt_install_dir="${qbt_working_dir}/${qbt_build_dir}"
# Used with printf. Use the qbt_install_dir variable but the ${HOME} path is replaced with a literal ~
qbt_install_dir_short="${qbt_install_dir/${HOME}/\~}"
# Default to empty to use host native build tools. This way we can build on native arch on a supported OS and skip cross build toolchains
# This will override .qbt_env qbt_cross_name setting.
if [[ -n ${QBT_CROSS_NAME} ]]; then
qbt_cross_name="${QBT_CROSS_NAME}"
else
qbt_cross_name="${qbt_cross_name:-default}"
fi
qbt_with_qemu="${qbt_with_qemu:-yes}"
# module management - We can choose to install normal zlib (motley) or zlib-ng. For now defaulting to zlib due to a arch detection bug in zlib-ng
qbt_zlib_type=${qbt_zlib_type:-zlib}
# The default build configuration is qmake + qt5, qbt_build_tool=cmake or -c will make qt6 and cmake default
qbt_build_tool="${qbt_build_tool:-cmake}"
# Default to host - we are not really using this for anything other than what it defaults to so no need to set it.
qbt_cross_target="${qbt_cross_target:-${os_id}}"
# yes to create debug build to use with gdb - disables stripping - for some reason libtorrent b2 builds are 200MB or larger. qbt_build_debug=yes or -d
qbt_build_debug="${qbt_build_debug:-no}"
# github actions workflows - use https://github.com/userdocs/qbt-workflow-files/releases/latest instead of direct downloads from various source locations.
qbt_workflow_files="${qbt_workflow_files:-no}"
# Provide a git username and repo in this format - username/repo
# In this repo the structure needs to be like this /patches/libtorrent/1.2.11/patch and/or /patches/qbittorrent/4.3.1/patch
# your patch file will be automatically fetched and loaded for those matching tags.
qbt_patches_url="${qbt_patches_url:-userdocs/qbittorrent-nox-static}"
# testing = easy way to switch to test qbt-musl-cross-make-test builds via an env in the workflow.
qbt_mcm_url="${qbt_mcm_url:-userdocs/qbt-musl-cross-make}"
# provide the github tag and it will use that to download instead of the latest release.
qbt_mcm_tag="${qbt_mcm_tag:-}"
# Default to this version of libtorrent is no tag or branch is specified. qbt_libtorrent_version=1.2 or 2.0 or 2.1 or -lt v1.2.18
qbt_libtorrent_version="${qbt_libtorrent_version:-2.0}"
# Use release Jamfile unless we need a specific fix from the relevant RC branch.
# Using this can also break builds when non backported changes are present which will require a custom jamfile
qbt_libtorrent_master_jamfile="${qbt_libtorrent_master_jamfile:-no}"
# Strip symbols by default as we need full debug builds to be useful gdb to backtrace so stripping is a sensible default optimization.
qbt_optimise_strip="${qbt_optimise_strip:-yes}"
# Github actions specific - Build revisions - The workflow will set this dynamically so that the urls are not hardcoded to a single repo
qbt_revision_url="${qbt_revision_url:-userdocs/qbittorrent-nox-static}"
# Provide a path to check for cached local git repos and use those instead. Priority over workflow files.
qbt_cache_dir="${qbt_cache_dir%/}"
# Env setting for the icu tag
qbt_skip_icu="${qbt_skip_icu:-yes}"
# Default to expecting qemu to be present for cross builds.
# yes will use the _qbt_host_deps function to pull in this prebuilt dependency package https://github.com/userdocs/qbt-host-deps
qbt_host_deps="${qbt_host_deps:-no}"
# Where are the deps installed to relative to qbt_install_dir
qbt_host_deps_path="${qbt_install_dir}/host_deps"
# Which repo is hosting them.
qbt_host_deps_repo="${qbt_host_deps_repo:-userdocs/qbt-host-deps}"
if [[ ${qbt_with_qemu} == "yes" ]] || [[ ${qbt_with_qemu} == "no" && ${qbt_host_deps} == "yes" ]]; then
qbt_modules_delete["icu_host_deps"]="true"
qbt_modules_delete["qtbase_host_deps"]="true"
qbt_modules_delete["qttools_host_deps"]="true"
fi
if [[ ${qbt_with_qemu} == "no" ]]; then
if [[ ${qbt_skip_icu} == "yes" ]]; then
qbt_modules_delete["icu_host_deps"]="true"
fi
fi
# dependency version management - Env setting for the boost tag
if [[ ${qbt_libtorrent_version} == "1.2" || ${qbt_libtorrent_tag} =~ ^(v1\.2\.|RC_1_2) ]]; then
qbt_boost_tag="${qbt_boost_tag:-boost-1.86.0}"
else
qbt_boost_tag="${qbt_boost_tag:-}"
fi
# module management - libtorrent v1.2 is the module that requires iconv otherwise it's not needed.
_libtorrent_v2_iconv_check
# Env setting for the libtorrent tag
qbt_libtorrent_tag="${qbt_libtorrent_tag:-}"
# Env setting for the Qt tag
qbt_qt_tag="${qbt_qt_tag:-}"
# Env setting for the qbittorrent tag
qbt_qbittorrent_tag="${qbt_qbittorrent_tag:-}"
# We are only using python3 but it's easier to just change this if we need to for some reason.
qbt_python_version="3"
# provide gcc flags for the build - this is not used by default but can be set to provide custom flags for the build.
qbt_optimise="${qbt_optimise:-no}"
# The baseline cxx standard is 17. This is dynamically resolved by _set_cxx_standard based on app versions
qbt_standard="${qbt_standard:-17}"
# Get the local users $PATH before we isolate the script by setting HOME to the install dir in the _set_build_directory function.
qbt_local_paths="$PATH"
# The Alpine repository we use for package sources
CDN_URL="http://dl-cdn.alpinelinux.org/alpine/edge/main" # for alpine
# Native Alpine linux configuration. Does not apply when cross building using qbt-mcm
qbt_use_lto="${qbt_use_lto:-yes}"
# Use mold as the linker - available from qbt-mcm 2614
qbt_linker_mold="${qbt_linker_mold:-no}"
# Part 1: a series of # Dynamic tests to change settings based on the use of qmake,cmake,strip and debug
# Part 2, the compiler options are located in the _custom_flags function.
if [[ ${qbt_build_debug} == "yes" ]]; then
qbt_optimise_strip="no"
qbt_cmake_debug='ON'
qbt_libtorrent_debug='debug-symbols=on'
qbt_qbittorrent_debug='--enable-debug'
qbt_cmake_build_type="Debug"
qbt_openssl_build_type="--debug"
qbt_use_lto="no"
else
qbt_cmake_debug='OFF'
qbt_cmake_build_type="Release"
qbt_openssl_build_type="--release"
fi
# module management - staticish builds - remove glibc module so it links to system glibc
if [[ ${qbt_static_ish:=no} == "yes" ]]; then
if [[ ${os_id} =~ ^(debian|ubuntu)$ ]]; then qbt_modules_delete["glibc"]="true"; fi
if [[ ${qbt_cross_name} != "default" ]]; then
printf '\n%b\n' " ${unicode_red_light_circle} You cannot use the ${color_magenta_light}staticish${color_end} with cross compilation${color_end}"
printf '\n%b\n\n' " ${unicode_yellow_light_circle} Provided by ${color_yellow_light}qbt_static_ish=\"${color_end}${color_green_light}yes${color_end}${color_yellow_light}\"${color_end} or ${color_cyan_light}-si${color_end}${color_end}"
exit 1
fi
fi
# Dynamic tests to change settings based on the use of qmake,cmake,strip and debug
case "${qbt_qt_version}" in
5)
if [[ ${qbt_build_tool} != 'cmake' ]]; then
qbt_build_tool="qmake"
qbt_use_qt6="OFF"
fi
;;&
6)
qbt_build_tool="cmake"
qbt_use_qt6="ON"
;;&
"")
if [[ ${qbt_build_tool} == 'cmake' ]]; then
qbt_qt_version="6"
else
qbt_qt_version="5"
qbt_use_qt6="OFF"
fi
;;&
*)
[[ ! ${qbt_qt_version} =~ ^(5|6)$ ]] && qbt_workflow_files="no"
[[ ${qbt_build_tool} == 'qmake' && ${qbt_qt_version} =~ ^6 ]] && qbt_build_tool="cmake"
[[ ${qbt_build_tool} == 'cmake' && ${qbt_qt_version} =~ ^5 ]] && qbt_build_tool="cmake" qbt_qt_version="6"
[[ ${qbt_build_tool} == 'cmake' && ${qbt_qt_version} =~ ^6 ]] && qbt_use_qt6="ON"
;;
esac
# If we are cross building then bootstrap the cross build tools we need for the target arch else set native arch and remove the debian cross build tools
if [[ ${multi_arch_options[${qbt_cross_name}]} == "${qbt_cross_name}" && ${qbt_cross_name} != "default" ]]; then
_multi_arch info_bootstrap
else
cross_arch="$(uname -m)"
qbt_deps_delete["crossbuild-essential-${cross_arch}"]="true"
fi
#######################################################################################################################################################
# Create some associative arrays to use with checks to establish, modules, dependencies, privileges and required tools for the script to run.
#######################################################################################################################################################
# Define our list of available modules in a associative array for checks and a indexed array for order.
qbt_modules_install["all"]="true" && qbt_modules_order=("all")
qbt_modules_install["install"]="true" && qbt_modules_order+=("install")
qbt_modules_install["glibc"]="true" && qbt_modules_order+=("glibc")
qbt_modules_install["zlib"]="true" && qbt_modules_order+=("zlib")
qbt_modules_install["iconv"]="true" && qbt_modules_order+=("iconv")
qbt_modules_install["icu_host_deps"]="true" && qbt_modules_order+=("icu_host_deps")
qbt_modules_install["icu"]="true" && qbt_modules_order+=("icu")
qbt_modules_install["openssl"]="true" && qbt_modules_order+=("openssl")
qbt_modules_install["boost"]="true" && qbt_modules_order+=("boost")
qbt_modules_install["libtorrent"]="true" && qbt_modules_order+=("libtorrent")
# qbt_modules_install["double_conversion"]="true" && qbt_modules_order+=("double_conversion")
qbt_modules_install["qtbase_host_deps"]="true" && qbt_modules_order+=("qtbase_host_deps")
qbt_modules_install["qtbase"]="true" && qbt_modules_order+=("qtbase")
qbt_modules_install["qttools_host_deps"]="true" && qbt_modules_order+=("qttools_host_deps")
qbt_modules_install["qttools"]="true" && qbt_modules_order+=("qttools")
qbt_modules_install["qbittorrent"]="true" && qbt_modules_order+=("qbittorrent")
# Define our list of privilege checks
qbt_privileges_required["root"]="false"
qbt_privileges_required["sudo"]="false"
# Define our list of required test tools to performs basic script functions
qbt_test_tools["curl"]="false"
qbt_test_tools["bash"]="false"
qbt_test_tools["git"]="false"
# Define our list of required dependencies per supported OS for the script to run
if [[ ${os_id} =~ ^(alpine)$ ]]; then # Alpine specific dependencies
qbt_modules_delete["glibc"]="true"
if [[ -z ${qbt_cache_dir} ]]; then
qbt_deps_delete["coreutils"]="true"
qbt_deps_delete["gpg"]="true"
fi
qbt_core_deps["autoconf"]="false"
qbt_core_deps["build-base"]="false"
qbt_core_deps["coreutils"]="false"
qbt_core_deps["cmake"]="false"
qbt_core_deps["ninja-build"]="false"
qbt_core_deps["ninja-is-really-ninja"]="false"
qbt_core_deps["gpg"]="false"
qbt_core_deps["linux-headers"]="false"
qbt_core_deps["pkgconf"]="false"
# qbt_core_deps["py${qbt_python_version}-numpy"]="false"
# qbt_core_deps["py${qbt_python_version}-numpy-dev"]="false"
qbt_core_deps["ttf-freefont"]="false"
qbt_core_deps["xz"]="false"
# qbt_core_deps["musl-dbg"]="false"
# qbt_core_deps["linux-headers"]="false"
if [[ ${qbt_host_deps} == "yes" ]] || [[ ${qbt_with_qemu} == "yes" && ${qbt_cross_name} != "default" ]]; then
qbt_core_deps["file"]="false"
qbt_core_deps["fortify-headers"]="false"
qbt_core_deps["libc-dev"]="false"
qbt_core_deps["make"]="false"
qbt_core_deps["patch"]="false"
qbt_deps_delete["build-base"]="true"
fi
fi
if [[ ${os_id} =~ ^(debian|ubuntu)$ ]]; then # Debian specific dependencies
if [[ -z ${qbt_cache_dir} ]]; then
qbt_deps_delete["autopoint"]="true"
qbt_deps_delete["gperf"]="true"
fi
qbt_core_deps["autopoint"]="false"
qbt_core_deps["bison"]="false"
qbt_core_deps["build-essential"]="false"
qbt_core_deps["cmake"]="false"
qbt_core_deps["crossbuild-essential-${cross_arch}"]="false"
qbt_core_deps["gawk"]="false"
qbt_core_deps["gettext"]="false"
qbt_core_deps["gperf"]="false"
qbt_core_deps["ninja-build"]="false"
qbt_core_deps["openssl"]="false"
qbt_core_deps["pkg-config"]="false"
# qbt_core_deps["python${qbt_python_version}-numpy"]="false"
qbt_core_deps["texinfo"]="false"
qbt_core_deps["unzip"]="false"
qbt_core_deps["xz-utils"]="false"
if [[ ${qbt_host_deps} == "yes" ]] || [[ ${qbt_with_qemu} == "yes" && ${qbt_cross_name} != "default" ]]; then
qbt_core_deps["libc6-dev"]="false"
qbt_core_deps["make"]="false"
qbt_core_deps["patch"]="false"
qbt_deps_delete["build-essential"]="true"
fi
fi
# Deps with same package name across Alpine and Debian based systems
qbt_core_deps["automake"]="false"
qbt_core_deps["bash"]="false"
qbt_core_deps["curl"]="false"
qbt_core_deps["git"]="false"
qbt_core_deps["graphviz"]="false"
qbt_core_deps["libtool"]="false"
qbt_core_deps["perl"]="false"
qbt_core_deps["python${qbt_python_version}"]="false"
qbt_core_deps["python${qbt_python_version}-dev"]="false"
qbt_core_deps["re2c"]="false"
# remove this module by default unless provided as a first argument to the script.
if [[ ${1} != 'install' ]]; then
qbt_modules_delete["install"]="true"
fi
# Don't remove the icu module if it was provided as a positional parameter.
if [[ ${*} =~ ([[:space:]]|^)"icu"([[:space:]]|$) ]]; then
qbt_skip_icu="no"
fi
# Skip icu by default unless the -i flag is provided.
if [[ ${qbt_skip_icu} != "no" ]]; then
qbt_modules_delete["icu"]="true"
fi
# Configure default dependencies and modules if cmake is not specified
if [[ ${qbt_build_tool} != 'cmake' ]]; then
qbt_deps_delete["cmake"]="true"
qbt_deps_delete["graphviz"]="true"
qbt_deps_delete["ninja-build"]="true"
qbt_deps_delete["ninja-is-really-ninja"]="true"
qbt_deps_delete["re2c"]="true"
qbt_deps_delete["ttf-freefont"]="true"
qbt_deps_delete["unzip"]="true"
qbt_modules_delete["double_conversion"]="true"
else
if [[ ${qbt_skip_icu} != "no" ]]; then
qbt_modules_delete["icu"]="true"
fi
fi
}
# Version-to-standard thresholds: cxx_version_map["app:threshold_version"]=standard
# These use "Round Up" logic: find the first threshold where version <= threshold_version
declare -A cxx_version_map=(
["libtorrent:1.1.99"]=14
["libtorrent:1.2.18"]=17
["libtorrent:1.2.99"]=20
["libtorrent:2.0.9"]=17
["libtorrent:2.0.99"]=20
["libtorrent:999.999.999.999"]=23
["qbittorrent:4.3.2"]=14
["qbittorrent:4.3.99"]=17
["qbittorrent:4.5.99"]=17
["qbittorrent:5.1.99"]=20
["qbittorrent:999.999.999.999"]=23
)
# Qt version standard caps: cxx_qt_cap["qt_version"]=max_allowed_standard
declare -A cxx_qt_cap=(
["5"]=17
["6"]=23
)
# OS compiler capability: cxx_os_cap["os_codename"]=max_supported_standard
# Only listed OS versions support c++20 and above. Unlisted OS versions are capped at 17.
declare -A cxx_os_cap=(
["alpine"]=23
["trixie"]=23
["noble"]=23
)
#######################################################################################################################################################
# These functions set some build conditions dynamically based on the libtorrent versions, qt version and qbittorrent combinations
#######################################################################################################################################################
# Resolve the cxx standard requirement for a given app based on its branch or version
_resolve_app_cxx_std() {
local app="$1"
local tag="${github_tag[$app]}"
local version="${app_version[$app]}"
# Determine the highest version parsed from either the tag or the version string
local v_tag v_app v_target
v_tag="$(_semantic_version "${tag}")"
v_app="$(_semantic_version "${version}")"
v_target="${v_tag}"
((v_app > v_tag)) && v_target="${v_app}"
# Check version thresholds using "Round Up" (Ceiling) logic
local threshold_key threshold_version v_threshold
# Iterate sorted keys for the specific app
while IFS=':' read -r _ threshold_version; do
threshold_key="${app}:${threshold_version}"
v_threshold="$(_semantic_version "${threshold_version}")"
if ((v_target <= v_threshold)); then
printf '%s' "${cxx_version_map[$threshold_key]}"
return
fi
done < <(printf '%s\n' "${!cxx_version_map[@]}" | grep "^${app}:" | sort -V)
printf '%s' "17"
}
## Determine the cxx standard by resolving requirements from libtorrent and qbittorrent
# Flow: Max(Requirements) -> Apply System Caps (Min) -> Validation
_set_cxx_standard() {
local resolved_std=23
# Resolve each app's standard and take the LOWEST capable standard to build both
local app app_std qbt_app_std=17 qbt_libtorrent_std=14
for app in libtorrent qbittorrent; do
app_std="$(_resolve_app_cxx_std "${app}")"
((app_std < resolved_std)) && resolved_std="${app_std}"
[[ ${app} == "libtorrent" ]] && qbt_libtorrent_std="${app_std}"
[[ ${app} == "qbittorrent" ]] && qbt_app_std="${app_std}"
done
# Validate: Prevent building incompatible ABIs like c++14 with c++17+ or c++17 with c++20+
if (((qbt_libtorrent_std == 14 && qbt_app_std >= 17) || (qbt_app_std == 14 && qbt_libtorrent_std >= 17))); then
printf '\n%b\n\n' " ${text_blink}${unicode_red_light_circle}${color_end} ${color_yellow}ABI Mismatch: libtorrent (c++${qbt_libtorrent_std}) and qBittorrent (c++${qbt_app_std}) cannot be built together as c++14 is incompatible with c++17+.${color_end}"
if [[ -n ${GITHUB_REPOSITORY} ]]; then touch disable-qt5; fi
if [[ -d ${release_info_dir} ]]; then touch "${release_info_dir}/disable-qt5"; fi
exit 1
elif (((qbt_libtorrent_std == 17 && qbt_app_std >= 20) || (qbt_app_std == 17 && qbt_libtorrent_std >= 20))); then
printf '\n%b\n\n' " ${text_blink}${unicode_red_light_circle}${color_end} ${color_yellow}ABI Mismatch: libtorrent (c++${qbt_libtorrent_std}) and qBittorrent (c++${qbt_app_std}) cannot be built together as c++17 is incompatible with c++20+.${color_end}"
if [[ -n ${GITHUB_REPOSITORY} ]]; then touch disable-qt5; fi
if [[ -d ${release_info_dir} ]]; then touch "${release_info_dir}/disable-qt5"; fi
exit 1
elif (((qbt_libtorrent_std == 20 && qbt_app_std >= 23) || (qbt_app_std == 20 && qbt_libtorrent_std >= 23))); then
printf '\n%b\n\n' " ${text_blink}${unicode_red_light_circle}${color_end} ${color_yellow}ABI Mismatch: libtorrent (c++${qbt_libtorrent_std}) and qBittorrent (c++${qbt_app_std}) cannot be built together as c++20 is incompatible with c++23+.${color_end}"
if [[ -n ${GITHUB_REPOSITORY} ]]; then touch disable-qt5; fi
if [[ -d ${release_info_dir} ]]; then touch "${release_info_dir}/disable-qt5"; fi
exit 1
fi
# Apply Qt version cap (Ceiling)
local qt_cap="${cxx_qt_cap[${qbt_qt_version}]:-23}"
# Apply OS compiler capability cap (Ceiling)
local os_cap="${cxx_os_cap[${os_version_codename}]:-23}"
# Validate: qBittorrent v5+ (qbt needs >= 20) requires Qt6
if ((qbt_app_std >= 20 && qt_cap <= 17)); then
printf '\n%b\n\n' " ${text_blink}${unicode_red_light_circle}${color_end} ${color_yellow}qBittorrent ${color_magenta}${github_tag[qbittorrent]}${color_yellow} does not support ${color_red}Qt5${color_yellow}. Please use ${color_green}Qt6${color_yellow} or a qBittorrent ${color_green}v4${color_yellow} tag.${color_end}"
if [[ -n ${GITHUB_REPOSITORY} ]]; then touch disable-qt5; fi
if [[ -d ${release_info_dir} ]]; then touch "${release_info_dir}/disable-qt5"; fi
exit 1
fi
# Validate: qBittorrent v5+ (qbt needs >= 20) requires a modern OS compiler
if ((qbt_app_std >= 20 && os_cap < 20)); then
printf '\n%b\n\n' " ${text_blink}${unicode_red_light_circle}${color_end} ${color_yellow}qBittorrent ${color_magenta}${github_tag[qbittorrent]}${color_yellow} does not support less than ${color_red}c++20${color_yellow}. Please use an OS with a more modern compiler for v5${color_end}"
if [[ -n ${GITHUB_REPOSITORY} ]]; then touch disable-qt5; fi
if [[ -d ${release_info_dir} ]]; then touch "${release_info_dir}/disable-qt5"; fi
exit 1
fi
# Apply the caps to the resolved requirement (Taking the MINIMUM of floors and ceilings)
((resolved_std > qt_cap)) && resolved_std="${qt_cap}"
((resolved_std > os_cap)) && resolved_std="${os_cap}"
qbt_standard="${resolved_std}"
}
_libtorrent_v2_iconv_check() {
# iconv is only needed for libtorrent v1.1/v1.2 - delete for everything else
qbt_modules_delete["iconv"]="true"
if [[ ${qbt_libtorrent_version} =~ ^1\.[12]$ || ${github_tag[libtorrent]} =~ ^(v1\.[12]\.|RC_1_[12]) ]]; then
unset 'qbt_modules_delete[iconv]'
fi
}
#######################################################################################################################################################
# _print_env
#######################################################################################################################################################
_print_env() {
printf '\n%b\n\n' " ${unicode_yellow_circle} Default env settings${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_zlib_type=\"${color_green_light}${qbt_zlib_type}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_skip_icu=\"${color_green_light}${qbt_skip_icu}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_boost_tag=\"${color_green_light}${github_tag[boost]}${color_yellow_light}\"${color_end}"
printf '%b\n' " ${color_yellow_light} qbt_libtorrent_version=\"${color_green_light}${qbt_libtorrent_version}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_libtorrent_tag=\"${color_green_light}${github_tag[libtorrent]}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_libtorrent_master_jamfile=\"${color_green_light}${qbt_libtorrent_master_jamfile}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_qt_version=\"${color_green_light}${qbt_qt_version}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_qt_tag=\"${color_green_light}${github_tag[qtbase]}${color_yellow_light}\"${color_end}"
printf '%b\n' " ${color_yellow_light} qbt_qbittorrent_tag=\"${color_green_light}${github_tag[qbittorrent]}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_build_dir=\"${color_green_light}${qbt_build_dir}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_build_tool=\"${color_green_light}${qbt_build_tool}${color_yellow_light}\"${color_end}"
printf '%b\n' " ${color_yellow_light} qbt_cross_name=\"${color_green_light}${qbt_cross_name}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_mcm_url=\"${color_green_light}${qbt_mcm_url}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_patches_url=\"${color_green_light}${qbt_patches_url}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_workflow_files=\"${color_green_light}${qbt_workflow_files}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_cache_dir=\"${color_green_light}${qbt_cache_dir}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_optimise_strip=\"${color_green_light}${qbt_optimise_strip}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_build_debug=\"${color_green_light}${qbt_build_debug}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_standard=\"${color_green_light}${qbt_standard}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_static_ish=\"${color_green_light}${qbt_static_ish}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_optimise=\"${color_green_light}${qbt_optimise}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_with_qemu=\"${color_green_light}${qbt_with_qemu}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_host_deps=\"${color_green_light}${qbt_host_deps}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_host_deps_repo=\"${color_green_light}${qbt_host_deps_repo}${color_yellow_light}\"${color_end}"
[[ $qbt_advanced_view == "yes" ]] && printf '%b\n' " ${color_yellow_light} qbt_legacy_mode=\"${color_green_light}${qbt_legacy_mode}${color_yellow_light}\"${color_end}"
printf '%b\n' " ${color_yellow_light} qbt_advanced_view=\"${color_green_light}${qbt_advanced_view}${color_yellow_light}\"${color_end}"
printf '\n'
}
#######################################################################################################################################################
# This function converts a version string to a number for comparison purposes.
#######################################################################################################################################################
_semantic_version() {
local version_string="${1#v}" # Strip leading v
local base tag tag_num
local major=0 minor=0 patch=0 build=0 prerelease=999
# Handle explicitly "master", "main", or "latest" to evaluate to a maximum version
if [[ ${version_string,,} =~ ^(master|main|latest)$ ]]; then
printf "10%d%03d%03d%03d%03d" 999 999 999 999 999
return
fi
# Extract version base and pre-release tag
if [[ ${version_string} =~ ^([0-9\.]+)[-\.]?([a-zA-Z]+)[-\.]?([0-9]+)?(.*)$ ]]; then
base="${BASH_REMATCH[1]}"
tag="${BASH_REMATCH[2]}"
tag_num="${BASH_REMATCH[3]}"
else
base="${version_string}"
tag=""
tag_num=""
fi
# Convert base safely
local -a base_array
read -ra base_array < <(printf "%s" "${base//./ }" | sed 's/[^0-9 ]//g')
major="$((10#${base_array[0]:-0}))"
minor="$((10#${base_array[1]:-0}))"
patch="$((10#${base_array[2]:-0}))"
build="$((10#${base_array[3]:-0}))"
# Weigh prerelease tags to ensure they resolve as mathematically lower than final versions
if [[ -n ${tag} ]]; then
case "${tag,,}" in
a | alpha) prerelease=$((100 + 10#${tag_num:-0})) ;;
b | beta) prerelease=$((200 + 10#${tag_num:-0})) ;;
r | rc) prerelease=$((300 + 10#${tag_num:-0})) ;;
*) prerelease=999 ;;
esac
fi
# Use 10# prefix for values to prevent octal interpretation of zero-padded numbers like 08, 09
# Prepend 10 to the final string to prevent bash `[[ -lt ]]` evaluation from parsing it as octal if major is 0
printf "10%d%03d%03d%03d%03d" "${major}" "${minor}" "${patch}" "${build}" "${prerelease}"
}
#######################################################################################################################################################
# Script Version check
#######################################################################################################################################################
_script_version() {
# Fetch remote script content once
local remote_content
remote_content="$(
_curl -sL "${script_url}"
printf x
)"
remote_content="${remote_content%x}"
if [[ -z ${remote_content} ]]; then
printf '\n%b\n' " ${unicode_yellow_circle} Could not fetch remote script for version/integrity check. Skipping."
return
fi
local script_version_remote
script_version_remote="$(printf '%s' "${remote_content}" | sed -rn 's|^script_version="(.*)"$|\1|p')"
# SHA256 integrity check
local local_sha256 remote_sha256 is_modified=false
if command -v sha256sum &> /dev/null; then
read -r local_sha256 _ < <(sha256sum "${script_full_path}" 2> /dev/null)
read -r remote_sha256 _ < <(printf '%s' "${remote_content}" | sha256sum)
if [[ -n ${local_sha256} && -n ${remote_sha256} && ${local_sha256} != "${remote_sha256}" ]]; then
is_modified=true
fi
fi
if [[ "$(_semantic_version "${script_version}")" -lt "$(_semantic_version "${script_version_remote}")" ]]; then
printf '\n%b\n' " ${text_blink}${unicode_red_circle}${color_end} Script update available! Versions - ${color_yellow_light}local:${color_red_light}${script_version}${color_end} ${color_yellow_light}remote:${color_green_light}${script_version_remote}${color_end}"
if [[ ${script_basename} == "qbittorrent-nox-static.sh" ]]; then
printf '\n%b\n' " ${unicode_green_circle} curl -sLo ${BASH_SOURCE[0]} ${script_url}${color_end}"
else
printf '\n%b\n' " ${unicode_green_circle} curl -sLo ${BASH_SOURCE[0]} usrdx.github.io/s/qbt.bash${color_end}"
fi
elif [[ "$(_semantic_version "${script_version}")" -gt "$(_semantic_version "${script_version_remote}")" ]]; then
printf '\n%b\n' " ${unicode_green_circle} Script version: ${color_red_light}${script_version}-dev${color_end}"
if [[ ${is_modified} == true ]]; then
printf '\n%b\n' " ${unicode_yellow_circle} Warning: Local development script has been modified.${color_end}"
fi
else
printf '\n%b\n' " ${unicode_green_circle} Script version: ${color_green_light}${script_version}${color_end}"
if [[ ${is_modified} == true ]]; then
printf '\n%b\n' " ${unicode_yellow_circle} Warning: Local script has been modified and differs from the remote version.${color_end}"
[[ -n ${local_sha256} ]] && printf '\n%b\n' " ${text_dim}Local SHA256: ${local_sha256}${color_end}"
[[ -n ${remote_sha256} ]] && printf '%b\n' " ${text_dim}Remote SHA256: ${remote_sha256}${color_end}"
fi
fi
}
#######################################################################################################################################################
# This function will check for a list of defined dependencies from the qbt_core_deps array. Apps like python3-dev are dynamically set
#######################################################################################################################################################
_check_dependencies() {
local pkgman=()
local command_test_tool=()
local command_install_deps=()
local command_update_upgrade_os=()
local install_simulation=()
local filtered_params=()
_privilege_check() {
printf '\n%b\n' " ${unicode_blue_light_circle} ${text_bold}Checking: ${color_red_light}available privileges${color_end}"
if [[ "$(id -un 2> /dev/null)" == 'root' ]]; then
printf '\n%b\n' " $unicode_green_circle ${color_red_light}root${color_end}"
qbt_privileges_required["root"]="true"
command_privilege=()
else
printf '\n%b\n' " $unicode_red_circle ${color_red_light}root${color_end}"
fi
if sudo -n true &> /dev/null; then
printf '%b\n' " $unicode_green_circle ${color_red_light}sudo${color_end}"
qbt_privileges_required["sudo"]="true"
[[ ${qbt_privileges_required["root"]} != "true" ]] && command_privilege=("sudo" "-E")
else
printf '%b\n' " $unicode_red_circle ${color_red_light}sudo${color_end}"
fi
if [[ $os_id =~ (debian|ubuntu) ]]; then
pkgman+=("${command_privilege[@]}" "dpkg" "-s")
command_test_tool+=("${command_privilege[@]}" "dpkg" "-s")
command_install_deps+=("${command_privilege[@]}" "apt-get" "install" "-y")
command_update_upgrade_os+=("${command_privilege[@]}" "bash" "-c" "apt-get update && apt-get upgrade -y && apt-get autoremove -y")
install_simulation+=("${command_privilege[@]}" "apt" "install" "--simulate")
elif [[ $os_id == "alpine" ]]; then
pkgman+=("${command_privilege[@]}" "apk" "info" "-e" "--no-cache")
command_test_tool+=("${command_privilege[@]}" "apk" "info" "-e" "--no-cache")
command_install_deps+=("${command_privilege[@]}" "apk" "add" "-u" "--no-cache" "--repository=${CDN_URL}")
command_update_upgrade_os+=("${command_privilege[@]}" "bash" "-c" "apk update --no-cache && apk upgrade --no-cache --repository=${CDN_URL} && apk fix")
install_simulation+=("${command_privilege[@]}" "apk" "add" "--simulate" "--no-cache")
fi
}
_check_tools_work() {
local tool="${1}"
local tool_type="${2}"
local run_type="${3}"
_command_test() {
if [[ $tool_type == "test_tools" ]]; then
command -v "${tool}"
elif [[ $tool_type == "build_tools" ]]; then
"${command_test_tool[@]}" "${tool}"
fi
}
if _command_test &> /dev/null; then
if [[ ${run_type} != "silent" ]]; then
printf "%b\n" " $unicode_green_circle ${color_yellow}${1}${color_end}"
fi
return 0
else
if [[ ${run_type} != "silent" ]]; then
printf "%b\n" " $unicode_red_circle ${color_yellow}${1}${color_end}"
fi
return 1
fi
}
_check_dependency_status() {
local silent="${1:-}"
filtered_params=()
for pparam in "$@"; do
if [[ $pparam != "silent" ]]; then
filtered_params+=("$pparam")
fi
done && unset pparam
[[ ${silent} != 'silent' ]] && printf '\n%b\n\n' " ${unicode_blue_light_circle} ${text_bold}Checking: ${color_yellow}test${color_end}"
while IFS= read -r qbt_tt; do
if _check_tools_work "${qbt_tt}" "test_tools" "${silent}"; then
qbt_test_tools["${qbt_tt}"]="true"
fi
done < <(printf '%s\n' "${!qbt_test_tools[@]}" | sort)
# remove packages in the qbt_deps_delete arrays from the qbt_core_deps array
for qbt_dd in "${!qbt_deps_delete[@]}"; do
unset "qbt_core_deps[${qbt_dd}]"
done && unset qbt_dd
# remove test_tools packages in the qbt_test_tools array from the qbt_core_deps array if available via command -v
for qbt_tt in "${!qbt_test_tools[@]}"; do
if [[ ${qbt_test_tools[$qbt_tt]} == "true" ]]; then
unset "qbt_core_deps[$qbt_tt]"
fi
done && unset qbt_tt
[[ ${silent} != 'silent' ]] && printf '\n%b\n\n' " ${unicode_blue_light_circle} ${text_bold}Checking: ${color_magenta}core${color_end}"
qbt_core_deps_sorted=()
# This checks over the qbt_core_deps array for the OS specified dependencies to see if they are installed
while IFS= read -r pkg; do
_run_pkgman() { "${pkgman[@]}" "${pkg}"; }
if _run_pkgman > /dev/null 2>&1; then
[[ ${silent} != 'silent' ]] && printf '%b\n' " ${unicode_green_circle} ${color_magenta}${pkg}${color_end}"
qbt_core_deps["${pkg}"]="true"
else
if [[ -n ${pkg} ]]; then
[[ ${silent} != 'silent' ]] && printf '%b\n' " ${unicode_red_circle} ${color_magenta}${pkg}${color_end}"
qbt_core_deps_sorted+=("$pkg")
qbt_core_deps["${pkg}"]="false"
fi
fi
done < <(printf '%s\n' "${!qbt_core_deps[@]}" | sort)
}
_privilege_check
_check_dependency_status "${@}"
if [[ ${qbt_privileges_required["root"]} == "true" || ${qbt_privileges_required["sudo"]} == "true" ]]; then
if [[ ${qbt_core_deps[*]} =~ "false" ]]; then
printf '\n%b\n\n' " $unicode_blue_circle ${color_blue}Info:${color_end} $script_full_path"
if ! "${install_simulation[@]}" "${!qbt_test_tools[@]}" &> /dev/null; then
printf '%b\n' " $unicode_blue_circle ${color_blue_light}$script_basename${color_end} ${color_magenta}update${color_end} ------------ update host - package simulation failed"
fi
if [[ ${qbt_test_tools[*]} =~ "false" ]]; then
printf '%b\n' " $unicode_blue_circle ${color_blue_light}$script_basename${color_end} ${color_yellow}install_test${color_end} ------ installs minimum required tools to run tests"
fi
printf '%b\n' " $unicode_blue_circle ${color_blue_light}$script_basename${color_end} ${color_magenta}install_core${color_end} ------ installs required build tools to use script"
if ! "${install_simulation[@]}" "${!qbt_test_tools[@]}" &> /dev/null; then
printf '%b\n' " $unicode_blue_circle ${color_blue_light}$script_basename${color_end} ${color_green_light}bootstrap_deps${color_end} ---- ${color_magenta}update${color_end} + ${color_yellow}install_test${color_end} + ${color_magenta}install_core${color_end}"
else
printf '%b\n' " $unicode_blue_circle ${color_blue_light}$script_basename${color_end} ${color_green_light}bootstrap_deps${color_end} ---- ${color_yellow}install_test${color_end} + ${color_magenta}install_core${color_end}"
fi
fi
else
printf '\n%b\n\n' " $unicode_red_circle ${color_yellow}Warning${color_end}: No root or sudo privileges detected. Nothing to do"
printf '%b\n' " $unicode_red_circle ${color_yellow}Warning${color_end}: ${color_magenta}test_tools${color_end} are required to access basic features of the script.${color_end}"
fi
if [[ ${qbt_test_tools[*]} =~ "false" ]]; then
printf '\n%b\n' " $unicode_red_circle ${color_yellow}Warning:${color_end} Missing required ${color_magenta}test_tools${color_end}"
fi
if [[ ${qbt_core_deps[*]} =~ "false" ]]; then
printf '\n%b\n' " $unicode_red_circle ${color_yellow}Warning:${color_end} Missing required components of ${color_magenta}install_core${color_end}"
fi
# Check if user is able to install the dependencies, if yes then do so, if no then exit.
if [[ ${qbt_privileges_required["root"]} == "true" || ${qbt_privileges_required["sudo"]} == "true" ]]; then
_update_os() {
printf '\n%b\n\n' " ${unicode_blue_light_circle} ${color_green}Updating${color_end}"
"${command_update_upgrade_os[@]}"
}
_install_tools() {
# We don't want to run update every time. Only if the the installation command cannot work without an update being run first
if ! "${install_simulation[@]}" "${!qbt_test_tools[@]}" &> /dev/null; then _update_os; fi
if [[ ${1} == "test" ]]; then
printf '\n%b\n\n' " ${unicode_blue_light_circle}${color_green} Installing test dependencies${color_end}"
elif [[ ${1} == "core" ]]; then
printf '\n%b\n\n' " ${unicode_blue_light_circle}${color_green} Installing core dependencies${color_end}"
fi
if [[ ${1} == "test" ]]; then
for qbt_tt in "${!qbt_test_tools[@]}"; do
if [[ $qbt_tt != "root" && $qbt_tt != "sudo" ]]; then
"${command_install_deps[@]}" "$qbt_tt"
fi
done && unset qbt_tt
fi
if [[ ${1} == "core" ]]; then
"${command_install_deps[@]}" "${qbt_core_deps_sorted[@]}"
fi
}
if [[ $* =~ ([[:space:]]|^)(update)([[:space:]]|$) ]]; then
_update_os
fi
if [[ $* =~ ([[:space:]]|^)(install_test)([[:space:]]|$) ]]; then
_install_tools test
fi
if [[ $* =~ ([[:space:]]|^)(install_core)([[:space:]]|$) ]]; then
_install_tools core
fi
# qbt_legacy_mode = qbittorrent-nox-static.sh emulation and will make this script behave like qbittorrent-nox-static.sh where it does not
# require user interaction to attempt to install dependencies if it has the required privileges
if [[ ${qbt_legacy_mode} == "yes" && ${qbt_core_deps[*]} =~ "false" ]] || [[ $* =~ ([[:space:]]|^)(bootstrap_deps)([[:space:]]|$) ]]; then
_update_os
_install_tools core
fi
_check_dependency_status silent "${@}"
else
printf '\n%b\n' " ${text_bold}Please request or install the missing core dependencies before using this script${color_end}"
if [[ ${os_id} =~ ^(alpine)$ ]]; then
printf '\n%b\n\n' " ${color_red_light}apk add${color_end} ${qbt_core_deps_sorted[*]}"
elif [[ ${os_id} =~ ^(debian|ubuntu)$ ]]; then
printf '\n%b\n\n' " ${color_red_light}apt-get install -y${color_end} ${qbt_core_deps_sorted[*]}"
fi
exit 1
fi
if [[ ${qbt_test_tools[*]} =~ "false" ]]; then
printf '\n'