-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·1382 lines (1187 loc) · 44 KB
/
build.sh
File metadata and controls
executable file
·1382 lines (1187 loc) · 44 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
#!/usr/bin/env bash
#==============================================================================
# pgbalancer Build Script
#
# A modular, OS-aware build script that automatically detects dependencies,
# configures the build environment, and compiles pgbalancer for your platform.
#
# Supported platforms:
# - macOS (Apple Silicon & Intel)
# - Ubuntu/Debian
# - Rocky Linux/RHEL/CentOS/Fedora
#
# Copyright (c) 2024-2025, pgElephant, Inc.
#==============================================================================
set -e # Exit on error
set -o pipefail # Catch errors in pipes
#==============================================================================
# Configuration & Global Variables
#==============================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_LOG="${SCRIPT_DIR}/build.log"
VERBOSE=${VERBOSE:-0}
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Build configuration
CONFIGURE_OPTS=""
MAKE_JOBS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
#==============================================================================
# Logging Functions
#==============================================================================
log_info() {
echo -e "${BLUE}ℹ${NC} $*" | tee -a "$BUILD_LOG"
}
log_success() {
echo -e "${GREEN}✓${NC} $*" | tee -a "$BUILD_LOG"
}
log_warn() {
echo -e "${YELLOW}⚠${NC} $*" | tee -a "$BUILD_LOG"
}
log_error() {
echo -e "${RED}✗${NC} $*" | tee -a "$BUILD_LOG"
}
fatal_error() {
log_error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log_error "BUILD FAILED!"
log_error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log_error ""
log_error "$@"
log_error ""
log_error "Build log saved to: $BUILD_LOG"
log_error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 1
}
log_step() {
echo -e "\n${GREEN}▶${NC} $*" | tee -a "$BUILD_LOG"
}
#==============================================================================
# OS Detection
#==============================================================================
detect_os() {
log_step "Detecting operating system..."
if [[ "$OSTYPE" == "darwin"* ]]; then
OS="macos"
if [[ $(uname -m) == "arm64" ]]; then
ARCH="arm64"
HOMEBREW_PREFIX="/opt/homebrew"
else
ARCH="x86_64"
HOMEBREW_PREFIX="/usr/local"
fi
log_info "Detected: macOS ($ARCH)"
elif [[ -f /etc/os-release ]]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
OS="ubuntu"
ARCH=$(uname -m)
log_info "Detected: Ubuntu/Debian ($ARCH)"
;;
rhel|centos|rocky|almalinux|fedora)
OS="rhel"
ARCH=$(uname -m)
log_info "Detected: RHEL/Rocky/CentOS ($ARCH)"
;;
*)
log_error "Unsupported Linux distribution: $ID"
exit 1
;;
esac
else
log_error "Unable to detect operating system"
exit 1
fi
export OS ARCH
}
#==============================================================================
# Dependency Detection & Installation - macOS
#==============================================================================
check_homebrew() {
if ! command -v brew &> /dev/null; then
fatal_error \
"Homebrew is not installed!" \
"" \
"pgbalancer build on macOS requires Homebrew package manager." \
"" \
"Install Homebrew:" \
" /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" \
"" \
"Or visit: https://brew.sh"
fi
log_success "Homebrew found: $(brew --version | head -1)"
return 0
}
find_macos_dependencies() {
log_step "Checking macOS dependencies..."
local missing_deps=()
local required_deps=(
"postgresql@17:PostgreSQL 17"
"libyaml:libyaml"
"openssl@3:OpenSSL 3"
"json-c:JSON-C"
"curl:cURL"
"bison:GNU Bison"
"gawk:GNU Awk"
)
for dep_spec in "${required_deps[@]}"; do
IFS=':' read -r dep_name dep_desc <<< "$dep_spec"
if brew list "$dep_name" &>/dev/null; then
log_success "$dep_desc is installed"
else
log_warn "$dep_desc is missing"
missing_deps+=("$dep_name")
fi
done
if [[ ${#missing_deps[@]} -gt 0 ]]; then
log_warn "Missing dependencies: ${missing_deps[*]}"
read -p "Install missing dependencies? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
for dep in "${missing_deps[@]}"; do
log_info "Installing $dep..."
brew install "$dep"
done
else
log_error "Cannot proceed without required dependencies"
exit 1
fi
fi
# Find PostgreSQL installation - NO DEFAULTS, must be explicitly found
log_info "Searching for PostgreSQL installation..."
# Priority 1: Check if user specified PG_PREFIX environment variable
if [[ -n "$PG_PREFIX" ]]; then
if [[ -x "$PG_PREFIX/bin/pg_config" ]]; then
PG_CONFIG="$PG_PREFIX/bin/pg_config"
log_success "Using user-specified PostgreSQL: $PG_PREFIX"
else
log_error "PG_PREFIX is set to '$PG_PREFIX' but pg_config not found at $PG_PREFIX/bin/pg_config"
exit 1
fi
fi
# Priority 2: Try Homebrew installations (only if PG_PREFIX not set)
if [[ -z "$PG_PREFIX" ]]; then
for pg_version in 17 16 15 14 13; do
local pg_brew_path=$(brew --prefix postgresql@${pg_version} 2>/dev/null)
if [[ -n "$pg_brew_path" && -x "$pg_brew_path/bin/pg_config" ]]; then
PG_PREFIX="$pg_brew_path"
PG_CONFIG="$pg_brew_path/bin/pg_config"
log_success "Found PostgreSQL ${pg_version} via Homebrew: $PG_PREFIX"
break
fi
done
fi
# Priority 3: Try generic postgresql package
if [[ -z "$PG_PREFIX" ]]; then
local pg_brew_path=$(brew --prefix postgresql 2>/dev/null)
if [[ -n "$pg_brew_path" && -x "$pg_brew_path/bin/pg_config" ]]; then
PG_PREFIX="$pg_brew_path"
PG_CONFIG="$pg_brew_path/bin/pg_config"
log_success "Found PostgreSQL via Homebrew: $PG_PREFIX"
fi
fi
# Priority 4: If not found via Homebrew, search system installations
if [[ -z "$PG_PREFIX" ]]; then
log_info "Not found in Homebrew, searching system installations..."
# Build list of potential PostgreSQL installation directories dynamically
local search_paths=()
# Common installation prefixes (no hard-coded versions)
for prefix in /usr/local /opt /usr; do
# Look for any versioned installations dynamically
if [[ -d "$prefix" ]]; then
# Find directories matching PostgreSQL patterns
for pg_dir in "$prefix"/pgsql* "$prefix"/postgresql*; do
[[ -d "$pg_dir" ]] && search_paths+=("$pg_dir")
done
fi
done
# macOS specific paths (only if on macOS)
if [[ "$OS" == "macos" ]]; then
# Search /Library/PostgreSQL for any version
if [[ -d "/Library/PostgreSQL" ]]; then
for pg_dir in /Library/PostgreSQL/*; do
[[ -d "$pg_dir" ]] && search_paths+=("$pg_dir")
done
fi
# Search Postgres.app installations
if [[ -d "/Applications/Postgres.app/Contents/Versions" ]]; then
for pg_dir in /Applications/Postgres.app/Contents/Versions/*; do
[[ -d "$pg_dir" ]] && search_paths+=("$pg_dir")
done
fi
fi
# Search all discovered paths for pg_config
for pg_path in "${search_paths[@]}"; do
if [[ -x "$pg_path/bin/pg_config" ]]; then
PG_PREFIX="$pg_path"
PG_CONFIG="$pg_path/bin/pg_config"
log_success "Found PostgreSQL at: $PG_PREFIX"
break
fi
done
fi
# Final check
if [[ -z "$PG_PREFIX" || ! -x "$PG_CONFIG" ]]; then
fatal_error \
"PostgreSQL NOT FOUND!" \
"" \
"pgbalancer requires PostgreSQL 13 or later." \
"" \
"Installation instructions:" \
" brew install postgresql@17" \
"" \
"Or specify custom PostgreSQL installation:" \
" export PG_PREFIX=/path/to/postgresql" \
" ./build.sh" \
"" \
"To use PostgreSQL from /usr/local/pgsql.17:" \
" export PG_PREFIX=/usr/local/pgsql.17" \
" ./build.sh"
fi
# Verify pg_config works
if ! "$PG_CONFIG" --version &>/dev/null; then
fatal_error \
"Found pg_config at $PG_CONFIG but it doesn't work!" \
"" \
"The pg_config binary exists but failed to execute." \
"This usually means:" \
" • PostgreSQL is not properly installed" \
" • Missing shared libraries" \
" • Architecture mismatch (x86_64 vs arm64)" \
"" \
"Try reinstalling PostgreSQL:" \
" brew reinstall postgresql@17"
fi
# Get actual paths from pg_config
PG_VERSION=$($PG_CONFIG --version | awk '{print $2}')
PG_INCLUDE=$($PG_CONFIG --includedir)
PG_LIB=$($PG_CONFIG --libdir)
log_info "PostgreSQL version: $PG_VERSION"
log_info "PostgreSQL include: $PG_INCLUDE"
log_info "PostgreSQL lib: $PG_LIB"
# Verify libpq exists
if [[ ! -f "$PG_LIB/libpq.dylib" && ! -f "$PG_LIB/libpq.a" ]]; then
fatal_error \
"libpq NOT FOUND in $PG_LIB" \
"" \
"PostgreSQL client libraries are missing." \
"" \
"Solutions:" \
" • Reinstall PostgreSQL: brew reinstall postgresql@17" \
" • Check library path: ls -la $PG_LIB/libpq*" \
" • Verify PostgreSQL installation: pg_config --libdir"
fi
log_success "Verified libpq at: $PG_LIB/libpq.dylib"
export PG_PREFIX PG_CONFIG PG_INCLUDE PG_LIB PG_VERSION
}
setup_macos_build_env() {
log_step "Setting up macOS build environment..."
# Add PostgreSQL and Homebrew to PATH (using detected paths)
export PATH="${PG_PREFIX}/bin:${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/opt/bison/bin:$PATH"
# Dynamically find library prefixes
local libyaml_prefix=$(brew --prefix libyaml 2>/dev/null || echo "${HOMEBREW_PREFIX}")
local openssl_prefix=$(brew --prefix openssl@3 2>/dev/null || echo "${HOMEBREW_PREFIX}")
local jsonc_prefix=$(brew --prefix json-c 2>/dev/null || echo "${HOMEBREW_PREFIX}")
# Build include and library paths using detected PostgreSQL paths
export CPPFLAGS="-I${PG_INCLUDE} -I${PG_INCLUDE}/server -I${libyaml_prefix}/include -I${openssl_prefix}/include -I${jsonc_prefix}/include"
export LDFLAGS="-L${PG_LIB} -L${libyaml_prefix}/lib -L${openssl_prefix}/lib -L${jsonc_prefix}/lib"
# Use pg_config directly for configure
CONFIGURE_OPTS="--with-pgsql=${PG_PREFIX} --with-openssl"
log_info "Build configuration:"
log_info " PATH: $PATH"
log_info " CPPFLAGS: $CPPFLAGS"
log_info " LDFLAGS: $LDFLAGS"
log_info " PG_CONFIG: $PG_CONFIG"
log_info " Configure: $CONFIGURE_OPTS"
}
#==============================================================================
# Dependency Detection & Installation - Ubuntu/Debian
#==============================================================================
check_apt() {
if ! command -v apt-get &> /dev/null; then
fatal_error \
"apt-get not found!" \
"" \
"This script requires apt-get package manager for Ubuntu/Debian." \
"Your system appears to be Ubuntu/Debian but apt-get is not available."
fi
return 0
}
find_ubuntu_dependencies() {
log_step "Checking Ubuntu/Debian dependencies..."
local missing_deps=()
local required_packages=(
"build-essential:Build tools"
"libpq-dev:PostgreSQL development headers"
"libyaml-dev:libyaml development headers"
"libssl-dev:OpenSSL development headers"
"libjson-c-dev:JSON-C development headers"
"libcurl4-openssl-dev:cURL development headers"
"bison:GNU Bison"
"flex:Flex lexical analyzer"
"gawk:GNU Awk"
)
for pkg_spec in "${required_packages[@]}"; do
IFS=':' read -r pkg_name pkg_desc <<< "$pkg_spec"
if dpkg -l "$pkg_name" 2>/dev/null | grep -q "^ii"; then
log_success "$pkg_desc is installed"
else
log_warn "$pkg_desc is missing"
missing_deps+=("$pkg_name")
fi
done
if [[ ${#missing_deps[@]} -gt 0 ]]; then
log_warn "Missing dependencies: ${missing_deps[*]}"
read -p "Install missing dependencies? (requires sudo) (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
sudo apt-get update
sudo apt-get install -y "${missing_deps[@]}"
else
log_error "Cannot proceed without required dependencies"
exit 1
fi
fi
# Find PostgreSQL - require explicit detection, no defaults
log_info "Searching for PostgreSQL installation..."
# Check if user specified PG_PREFIX
if [[ -n "$PG_PREFIX" && -x "$PG_PREFIX/bin/pg_config" ]]; then
PG_CONFIG="$PG_PREFIX/bin/pg_config"
log_success "Using user-specified PostgreSQL: $PG_PREFIX"
else
# Try to find pg_config in PATH
PG_CONFIG=$(command -v pg_config 2>/dev/null || true)
if [[ -x "$PG_CONFIG" ]]; then
PG_PREFIX=$(dirname $(dirname "$PG_CONFIG"))
log_success "Found PostgreSQL in PATH: $PG_PREFIX"
else
# Search common installation paths
local found=0
for pg_path in /usr/lib/postgresql/*/bin/pg_config /usr/pgsql-*/bin/pg_config; do
if [[ -x "$pg_path" ]]; then
PG_CONFIG="$pg_path"
PG_PREFIX=$(dirname $(dirname "$pg_path"))
log_success "Found PostgreSQL: $PG_PREFIX"
found=1
break
fi
done
if [[ $found -eq 0 ]]; then
fatal_error \
"PostgreSQL NOT FOUND on Ubuntu/Debian!" \
"" \
"pg_config not found in PATH or common locations." \
"" \
"Install PostgreSQL development package:" \
" sudo apt-get update" \
" sudo apt-get install postgresql-server-dev-all" \
"" \
"Or for a specific version:" \
" sudo apt-get install postgresql-server-dev-17" \
"" \
"Or specify custom installation:" \
" export PG_PREFIX=/path/to/postgresql" \
" ./build.sh"
fi
fi
fi
# Get actual paths from pg_config
PG_VERSION=$($PG_CONFIG --version | awk '{print $2}')
PG_INCLUDE=$($PG_CONFIG --includedir)
PG_LIB=$($PG_CONFIG --libdir)
log_info "PostgreSQL version: $PG_VERSION"
log_info "PostgreSQL include: $PG_INCLUDE"
log_info "PostgreSQL lib: $PG_LIB"
export PG_PREFIX PG_CONFIG PG_INCLUDE PG_LIB PG_VERSION
}
setup_ubuntu_build_env() {
log_step "Setting up Ubuntu/Debian build environment..."
CONFIGURE_OPTS="--with-pgsql=${PG_PREFIX} --with-openssl"
log_info "Configure options: $CONFIGURE_OPTS"
}
#==============================================================================
# Dependency Detection & Installation - RHEL/Rocky/CentOS
#==============================================================================
check_yum() {
if command -v dnf &> /dev/null; then
PKG_MANAGER="dnf"
elif command -v yum &> /dev/null; then
PKG_MANAGER="yum"
else
fatal_error \
"Package manager not found!" \
"" \
"This script requires dnf or yum package manager for RHEL/Rocky." \
"Your system appears to be RHEL-based but no package manager is available."
fi
log_info "Package manager: $PKG_MANAGER"
return 0
}
find_rhel_dependencies() {
log_step "Checking RHEL/Rocky dependencies..."
local missing_deps=()
local required_packages=(
"gcc:GCC compiler"
"make:GNU Make"
"postgresql-devel:PostgreSQL development headers"
"libyaml-devel:libyaml development headers"
"openssl-devel:OpenSSL development headers"
"json-c-devel:JSON-C development headers"
"libcurl-devel:cURL development headers"
"bison:GNU Bison"
"flex:Flex lexical analyzer"
"gawk:GNU Awk"
)
for pkg_spec in "${required_packages[@]}"; do
IFS=':' read -r pkg_name pkg_desc <<< "$pkg_spec"
if rpm -q "$pkg_name" &>/dev/null; then
log_success "$pkg_desc is installed"
else
log_warn "$pkg_desc is missing"
missing_deps+=("$pkg_name")
fi
done
if [[ ${#missing_deps[@]} -gt 0 ]]; then
log_warn "Missing dependencies: ${missing_deps[*]}"
read -p "Install missing dependencies? (requires sudo) (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
sudo $PKG_MANAGER install -y "${missing_deps[@]}"
else
log_error "Cannot proceed without required dependencies"
exit 1
fi
fi
# Find PostgreSQL - require explicit detection, no defaults
log_info "Searching for PostgreSQL installation..."
# Check if user specified PG_PREFIX
if [[ -n "$PG_PREFIX" && -x "$PG_PREFIX/bin/pg_config" ]]; then
PG_CONFIG="$PG_PREFIX/bin/pg_config"
log_success "Using user-specified PostgreSQL: $PG_PREFIX"
else
# Try to find pg_config in PATH
PG_CONFIG=$(command -v pg_config 2>/dev/null || true)
if [[ -x "$PG_CONFIG" ]]; then
PG_PREFIX=$(dirname $(dirname "$PG_CONFIG"))
log_success "Found PostgreSQL in PATH: $PG_PREFIX"
else
# Search common RHEL/Rocky paths
local found=0
for pg_path in /usr/pgsql-*/bin/pg_config /usr/lib64/pgsql/bin/pg_config; do
if [[ -x "$pg_path" ]]; then
PG_CONFIG="$pg_path"
PG_PREFIX=$(dirname $(dirname "$pg_path"))
log_success "Found PostgreSQL: $PG_PREFIX"
found=1
break
fi
done
if [[ $found -eq 0 ]]; then
fatal_error \
"PostgreSQL NOT FOUND on RHEL/Rocky!" \
"" \
"pg_config not found in PATH or common locations." \
"" \
"Install PostgreSQL development package:" \
" sudo $PKG_MANAGER install postgresql-devel" \
"" \
"For PostgreSQL official repository:" \
" sudo $PKG_MANAGER install postgresql17-devel" \
"" \
"Or specify custom installation:" \
" export PG_PREFIX=/path/to/postgresql" \
" ./build.sh"
fi
fi
fi
# Get actual paths from pg_config
PG_VERSION=$($PG_CONFIG --version | awk '{print $2}')
PG_INCLUDE=$($PG_CONFIG --includedir)
PG_LIB=$($PG_CONFIG --libdir)
log_info "PostgreSQL version: $PG_VERSION"
log_info "PostgreSQL include: $PG_INCLUDE"
log_info "PostgreSQL lib: $PG_LIB"
export PG_PREFIX PG_CONFIG PG_INCLUDE PG_LIB PG_VERSION
}
setup_rhel_build_env() {
log_step "Setting up RHEL/Rocky build environment..."
CONFIGURE_OPTS="--with-pgsql=${PG_PREFIX} --with-openssl"
log_info "Configure options: $CONFIGURE_OPTS"
}
#==============================================================================
# Build Functions
#==============================================================================
run_configure() {
log_step "Running configure..."
cd "$SCRIPT_DIR"
log_info "Command: ./configure $CONFIGURE_OPTS"
if [[ $VERBOSE -eq 1 ]]; then
if ! ./configure $CONFIGURE_OPTS 2>&1 | tee -a "$BUILD_LOG"; then
configure_failed
fi
else
if ! ./configure $CONFIGURE_OPTS >> "$BUILD_LOG" 2>&1; then
configure_failed
fi
fi
log_success "Configure completed successfully"
}
configure_failed() {
local error_hints=""
# Check for common configure errors
if grep -q "libpq is not installed" "$BUILD_LOG" 2>/dev/null; then
error_hints="libpq (PostgreSQL client library) not found"
elif grep -q "openssl/ssl.h" "$BUILD_LOG" 2>/dev/null; then
error_hints="OpenSSL headers not found"
elif grep -q "yaml.h" "$BUILD_LOG" 2>/dev/null; then
error_hints="libyaml headers not found"
elif grep -q "json-c" "$BUILD_LOG" 2>/dev/null; then
error_hints="json-c library not found"
fi
case "$OS" in
macos)
fatal_error \
"Configure failed!" \
"" \
"${error_hints:+Issue: $error_hints}" \
"" \
"Install missing dependencies:" \
" brew install postgresql@17 libyaml openssl@3 json-c curl" \
"" \
"Check the build log for details:" \
" tail -50 $BUILD_LOG"
;;
ubuntu)
fatal_error \
"Configure failed!" \
"" \
"${error_hints:+Issue: $error_hints}" \
"" \
"Install missing dependencies:" \
" sudo apt-get install libpq-dev libyaml-dev libssl-dev libjson-c-dev libcurl4-openssl-dev" \
"" \
"Check the build log for details:" \
" tail -50 $BUILD_LOG"
;;
rhel)
fatal_error \
"Configure failed!" \
"" \
"${error_hints:+Issue: $error_hints}" \
"" \
"Install missing dependencies:" \
" sudo $PKG_MANAGER install postgresql-devel libyaml-devel openssl-devel json-c-devel libcurl-devel" \
"" \
"Check the build log for details:" \
" tail -50 $BUILD_LOG"
;;
esac
}
run_make_clean() {
log_step "Cleaning previous build..."
cd "$SCRIPT_DIR"
if [[ -f Makefile ]]; then
make clean >> "$BUILD_LOG" 2>&1 || true
fi
log_success "Clean completed"
}
run_make() {
log_step "Building pgbalancer (using $MAKE_JOBS parallel jobs)..."
cd "$SCRIPT_DIR"
if [[ $VERBOSE -eq 1 ]]; then
if ! make -j"$MAKE_JOBS" 2>&1 | tee -a "$BUILD_LOG"; then
build_failed
fi
else
if ! make -j"$MAKE_JOBS" >> "$BUILD_LOG" 2>&1; then
build_failed
fi
fi
log_success "Build completed successfully"
}
build_failed() {
fatal_error \
"Compilation failed!" \
"" \
"The build process encountered errors." \
"" \
"Common issues:" \
" • Missing compiler (gcc/clang)" \
" • Missing headers or libraries" \
" • Incompatible compiler version" \
"" \
"Check the last 50 lines of the build log:" \
" tail -50 $BUILD_LOG" \
"" \
"For verbose output, try:" \
" ./build.sh -v --clean"
}
build_bctl() {
log_step "Building bctl CLI tool..."
cd "$SCRIPT_DIR/bctl"
# Fix macOS-specific issues in bctl Makefile
if [[ "$OS" == "macos" ]]; then
# Remove -Wno-stringop-truncation (not supported on macOS clang)
if grep -q "Wno-stringop-truncation" Makefile 2>/dev/null; then
sed -i '' 's/-Wno-stringop-truncation//g' Makefile
fi
# Remove -lcrypt (not needed on macOS)
if grep -q "\-lcrypt" Makefile 2>/dev/null; then
sed -i '' 's/-lcrypt//g' Makefile
fi
fi
make clean >> "$BUILD_LOG" 2>&1 || true
if [[ $VERBOSE -eq 1 ]]; then
make 2>&1 | tee -a "$BUILD_LOG"
else
make >> "$BUILD_LOG" 2>&1
fi
cd "$SCRIPT_DIR"
log_success "bctl build completed"
}
build_pgbalancer_config() {
log_step "Building pgbalancer_config utility..."
local config_dir="$SCRIPT_DIR/src/tools/pgbalancer_config"
if [[ ! -d "$config_dir" ]]; then
log_warn "pgbalancer_config directory not found, skipping"
return 0
fi
cd "$config_dir"
# Build with detected paths
if [[ $VERBOSE -eq 1 ]]; then
make clean >> "$BUILD_LOG" 2>&1 || true
make PGSQL_BIN_DIR="${PG_PREFIX}/bin" PGSQL_LIB_DIR="${PG_LIB}" 2>&1 | tee -a "$BUILD_LOG"
else
make clean >> "$BUILD_LOG" 2>&1 || true
make PGSQL_BIN_DIR="${PG_PREFIX}/bin" PGSQL_LIB_DIR="${PG_LIB}" >> "$BUILD_LOG" 2>&1
fi
cd "$SCRIPT_DIR"
log_success "pgbalancer_config build completed"
}
verify_build() {
log_step "Verifying build artifacts..."
local errors=0
# Check main binary
if [[ -x "$SCRIPT_DIR/src/pgbalancer" ]]; then
local version=$("$SCRIPT_DIR/src/pgbalancer" --version 2>&1 | head -1)
log_success "pgbalancer binary: $version"
else
log_error "pgbalancer binary not found or not executable"
errors=$((errors + 1))
fi
# Check bctl
if [[ -x "$SCRIPT_DIR/bctl/bctl" ]]; then
log_success "bctl binary found"
else
log_warn "bctl binary not found (optional)"
fi
# Check tools
for tool in src/tools/pgmd5/pg_md5 src/tools/pgenc/pg_enc; do
if [[ -x "$SCRIPT_DIR/$tool" ]]; then
log_success "Tool found: $tool"
else
log_warn "Tool not found: $tool (optional)"
fi
done
return $errors
}
create_build_structure() {
log_step "Creating organized build directory structure..."
local build_dir="$SCRIPT_DIR/build"
# Create directory structure
mkdir -p "$build_dir"/{bin,lib,etc,share/doc}
log_success "Created build directories:"
log_info " $build_dir/bin - Binaries"
log_info " $build_dir/lib - Libraries"
log_info " $build_dir/etc - Configuration files"
log_info " $build_dir/share/doc - Documentation"
# Copy binaries
log_step "Copying binaries to build/bin..."
if [[ -x "$SCRIPT_DIR/src/pgbalancer" ]]; then
cp -v "$SCRIPT_DIR/src/pgbalancer" "$build_dir/bin/" | tee -a "$BUILD_LOG"
log_success "Copied pgbalancer"
fi
if [[ -x "$SCRIPT_DIR/bctl/bctl" ]]; then
cp -v "$SCRIPT_DIR/bctl/bctl" "$build_dir/bin/" | tee -a "$BUILD_LOG"
log_success "Copied bctl"
fi
if [[ -x "$SCRIPT_DIR/src/tools/pgbalancer_config/pgbalancer_config" ]]; then
cp -v "$SCRIPT_DIR/src/tools/pgbalancer_config/pgbalancer_config" "$build_dir/bin/" | tee -a "$BUILD_LOG"
log_success "Copied pgbalancer_config"
fi
# Copy tools
for tool in pg_md5 pg_enc; do
for tool_path in "$SCRIPT_DIR"/src/tools/*/"$tool"; do
if [[ -x "$tool_path" ]]; then
cp -v "$tool_path" "$build_dir/bin/" | tee -a "$BUILD_LOG"
log_success "Copied $tool"
fi
done
done
# Copy watchdog CLI if exists
if [[ -x "$SCRIPT_DIR/src/tools/watchdog/wd_cli" ]]; then
cp -v "$SCRIPT_DIR/src/tools/watchdog/wd_cli" "$build_dir/bin/" | tee -a "$BUILD_LOG"
log_success "Copied wd_cli"
fi
# Copy libraries
log_step "Copying libraries to build/lib..."
find "$SCRIPT_DIR/src" -name "*.a" -exec cp -v {} "$build_dir/lib/" \; 2>/dev/null | tee -a "$BUILD_LOG" || true
# Copy configuration examples
log_step "Copying configuration files to build/etc..."
# Determine default paths based on detected PostgreSQL installation
local default_data_dir="${PG_PREFIX%/*}/data"
local default_log_dir="${SCRIPT_DIR}/logs"
local default_socket_dir="\${TMPDIR:-/tmp}"
# Create sample configuration (using dynamic paths)
cat > "$build_dir/etc/pgbalancer.conf.sample" << EOF
# pgbalancer Sample Configuration
# Generated on $(date)
# Copy this file to pgbalancer.conf and customize for your environment
# Server Settings
listen_addresses = '*'
port = 5432
socket_dir = '${default_socket_dir}'
pcp_listen_addresses = '*'
pcp_port = 9898
# Connection Pool Settings
num_init_children = 32
max_pool = 4
child_life_time = 300
child_max_connections = 0
# Backend Database Servers
# Customize these paths for your PostgreSQL instances
backend_hostname0 = 'localhost'
backend_port0 = 5433
backend_weight0 = 1
backend_data_directory0 = '${default_data_dir}1'
backend_hostname1 = 'localhost'
backend_port1 = 5434
backend_weight1 = 1
backend_data_directory1 = '${default_data_dir}2'
# Health Check
health_check_period = 30
health_check_timeout = 20
health_check_user = 'postgres'
health_check_password = ''
health_check_database = 'postgres'
# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = '${default_log_dir}'
log_filename = 'pgbalancer-%Y-%m-%d_%H%M%S.log'
log_line_prefix = '%t: pid %p: '
log_connections = on
log_hostname = on
log_statement = off
# Load Balancing
load_balance_mode = on
ignore_leading_white_space = on
# Replication
master_slave_mode = on
master_slave_sub_mode = 'stream'
# Online Recovery
recovery_user = 'postgres'
recovery_password = ''
# Paths detected during build:
# PostgreSQL: ${PG_PREFIX}
# Build Date: $(date)
# OS: ${OS} (${ARCH})
EOF
# Copy the official YAML sample if it exists
if [[ -f "$SCRIPT_DIR/src/sample/pgbalancer.yaml.sample" ]]; then
# Simply copy the sample file as-is (no modifications)
cp "$SCRIPT_DIR/src/sample/pgbalancer.yaml.sample" "$build_dir/etc/pgbalancer.yaml"
log_success "Copied pgbalancer.yaml from sample (unmodified)"
else
# Generate YAML with proper nested structure
cat > "$build_dir/etc/pgbalancer.yaml.sample" << EOF
---
# pgbalancer YAML Configuration Sample
# Generated on $(date)
# Build Info: PostgreSQL ${PG_VERSION} at ${PG_PREFIX}
# OS: ${OS} (${ARCH})
# Clustering Configuration
clustering:
mode: streaming_replication
# Network Configuration
network:
listen_addresses: "*"
port: 5432
socket_dir: ${default_socket_dir}
unix_socket_permissions: 0777
# Connection Pool Settings
connection_pool:
num_init_children: 32
max_pool: 4
child_life_time: 300
child_max_connections: 0
connection_cache: on
reset_query_list: 'ABORT; DISCARD ALL'
# Authentication
authentication:
enable_pool_hba: off
authentication_timeout: 60
allow_clear_text_frontend_auth: off
ssl: off
# Logging Configuration
logging:
destination: stderr
line_prefix: '%t: pid %p: '
connections: on
hostname: on
statement: off
per_node_statement: off
pid_file_name: ${default_socket_dir}/pgbalancer.pid
logdir: ${default_log_dir}
# Load Balancing
load_balancing:
mode: on
ignore_leading_white_space: on
disable_load_balance_on_write: transaction
statement_level_load_balance: off
black_function_list: 'currval,lastval,nextval,setval'
# Replication Settings
replication:
mode: on
sub_mode: stream
check_period: 10
check_user: postgres
check_password: ''
check_database: postgres
delay_threshold: 0
prefer_lower_delay_standby: off
# Health Check