forked from serkor1/ta-lib-R
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfigure
More file actions
executable file
·535 lines (479 loc) · 18.4 KB
/
configure
File metadata and controls
executable file
·535 lines (479 loc) · 18.4 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
#!/bin/sh
## configure: UNIX
##
## details:
##
## configure will *always* check for a pre-installed
## TA-Lib using default locations using #include <ta-lib/ta_libc.h>
## whether it builds, or not, is left to mercy of your installed compiler (gcc).
## If the routine can't be built (and the reason does not matter) configure
## will use the vendored TA-Lib located in src/ta-lib
## This behaviour can be overridden by passing --force-vendor
##
set -eu
## {pkg} information
RPKGNAME=$(grep "^Package:" DESCRIPTION | sed "s/Package: //")
RPKGVERSION=$(grep "^Version:" DESCRIPTION | sed "s/Version: //")
echo
echo Configure for {$RPKGNAME} v$RPKGVERSION
echo
## utility functions
##
## details:
## these functions serves absolutely no *real*
## purpose other than coloring simplifying the
## messages sent to the host.
##
## NOTE: defined before the argument parser so the
## helpers are available if an unknown flag
## triggers an error message.
error() {
printf '\e[0;31m%s\033[0m' "Build-error:"
}
url() {
printf '\e[0;34m%s\033[0m' $1
}
var() {
printf '\e[0;34m%s\033[0m' $1
}
info() {
printf '\e[0;34m%s\033[0m' "Build-step:"
}
success() {
printf '[\e[0;32m%s\033[0m]' "OK"
}
warning() {
printf '[\e[0;33m%s\033[0m]' "Failed"
}
## details:
## this function generates a temporary
## directory for building the libraries
## and is deleted after configure is done
temporary_directory() {
dir="$(mktemp -d)" || {
echo
printf '%s\n' "$(error) could not create temporary directory"
exit 1
}
printf '%s' "$dir"
}
## configure args and vars
##
## details:
## args are passed as: --configure-args="--foo-bar"
## vars are passed as: --configure-vars="FOO='DOTHIS'"
##
## args:
## --force-vendor: if passed it will use the vendored
## TA-Lib via CMake
##
## any other argument is forwarded verbatim as a C/C++
## compiler flag to both the vendored TA-Lib (CMake) build
## and the R wrapper compile step (Makevars). Example:
##
## R CMD INSTALL . --configure-args="-O3 -march=native"
##
## NOTE: binaries built with -march=native are tied to the
## build host's CPU features and are NOT portable.
##
## vars:
##
FORCE_VENDOR=0
OPTFLAGS=""
for arg in "$@"; do
case "$arg" in
--force-vendor)
FORCE_VENDOR=1
;;
## autotools-style arguments injected by `emconfigure` (and by other
## cross-compile harnesses) — dropped so they don't leak into
## CMAKE_C_FLAGS and get handed to the compiler as unknown flags.
--build=*|--host=*|--target=*|ac_cv_*=*)
;;
*)
OPTFLAGS="${OPTFLAGS} ${arg}"
;;
esac
done
if [ -n "$OPTFLAGS" ]; then
printf '%s Forwarding compiler flags:%s\n' "$(info)" "$OPTFLAGS"
fi
## sanity checks
##
## details:
## taken from the 'Writing R Extensions' these steps
## should always be included.
printf '%s Checking for %s' "$(info)" "$(var R_HOME)"
## details:
## check R_HOME
: ${R_HOME=`R RHOME`}
if test -z "${R_HOME}"; then
printf ' %s\n'"$(warning)"
printf '%s could not determine %s\n'"$(error)" "$(var R_HOME)"
printf '\n'
exit 1
else
printf ' %s\n' "$(success)"
R_BIN="${R_HOME}/bin/R"
fi
## R compiler configuration
##
## details:
## CMake's compiler probe walks $PATH and on macOS picks up
## /usr/bin/clang from Xcode CLT instead of the R toolchain at
## /opt/R/<arch>/bin/clang. The resulting libta-lib.a then has
## a different SDK / deployment target than the .so being
## linked by R, which manifests on the CRAN macOS builder as
## an early install failure with no useful surface log.
##
## WRE Section 1.2.6 ("Using cmake") instructs to propagate CC/CXX/
## CFLAGS/CXXFLAGS/CPPFLAGS/LDFLAGS from R's config to cmake.
## We capture them once here and reuse them below — both for
## the system TA-Lib link probe (so it tests the *same*
## compiler R will use) and for the vendored CMake build.
R_CC_FULL=$("$R_BIN" CMD config CC)
R_CXX_FULL=$("$R_BIN" CMD config CXX)
R_CFLAGS=$("$R_BIN" CMD config CFLAGS)
R_CXXFLAGS=$("$R_BIN" CMD config CXXFLAGS)
R_CPPFLAGS=$("$R_BIN" CMD config CPPFLAGS)
R_LDFLAGS=$("$R_BIN" CMD config LDFLAGS)
## Detect a compiler launcher (ccache/sccache/distcc) baked into
## R's CC/CXX by ~/.R/Makevars and hand it to CMake via
## CMAKE_<LANG>_COMPILER_LAUNCHER. Without this CMake invokes
## "ccache -Dfoo ..." directly and ccache misreads -D as its own
## debug-level flag (#57).
LAUNCHER_ARGS=""
case "$(basename "${R_CC_FULL%% *}")" in
ccache|sccache|distcc)
LAUNCHER_ARGS="$LAUNCHER_ARGS -DCMAKE_C_COMPILER_LAUNCHER=${R_CC_FULL%% *}"
R_CC_FULL="${R_CC_FULL#* }"
;;
esac
case "$(basename "${R_CXX_FULL%% *}")" in
ccache|sccache|distcc)
LAUNCHER_ARGS="$LAUNCHER_ARGS -DCMAKE_CXX_COMPILER_LAUNCHER=${R_CXX_FULL%% *}"
R_CXX_FULL="${R_CXX_FULL#* }"
;;
esac
## details:
## R CMD config returns the compiler with leading flags baked in
## (e.g. "clang -arch x86_64" or "/opt/R/x86_64/bin/clang -arch
## arm64"). CMAKE_C_COMPILER expects a bare executable path, so
## split the first token off as the compiler binary and fold the
## remainder into the flags.
R_CC=$(echo "$R_CC_FULL" | awk '{print $1}')
R_CC_EXTRA=$(echo "$R_CC_FULL" | cut -s -d' ' -f2-)
R_CXX=$(echo "$R_CXX_FULL" | awk '{print $1}')
R_CXX_EXTRA=$(echo "$R_CXX_FULL" | cut -s -d' ' -f2-)
## details:
## compile a small C-routine with R CMD SHLIB
## as a belt and buckles test
printf '%s Compiling C-program with R CMD SHLIB' "$(info)"
SHLIB_TEST=$(temporary_directory)
cat > "${SHLIB_TEST}/conftest.c" <<'EOF'
void foo(void) {}
EOF
if ! ( cd "$SHLIB_TEST" && "$R_BIN" CMD SHLIB conftest.c >/dev/null 2>&1 ); then
printf ' %s\n' "$(warning)"
echo $(error) R CMD SHLIB test compilation failed. Build-tools may be missing or misconfigured.
exit 1
fi
printf ' %s\n' $(success)
rm -rf $SHLIB_TEST
## TA-Lib availability
##
## details:
## CRAN requires compiled packages with external dependencies to
## prefer the user's installation. We probe via pkg-config — bare
## `$CC` only sees default include paths, so a Homebrew install
## under /opt/homebrew or a MacPorts install under /opt/local would
## be missed and the package would silently fall back to vendor.
## pkg-config supplies the right -I and -L; we then compile AND link
## a test program that calls a real TA-Lib symbol, so a stale header
## without a resolvable library can't masquerade as a working install.
##
## Any failure (pkg-config missing, ta-lib.pc absent, link fails)
## falls through to the vendored build. --force-vendor short-circuits
## detection entirely so PREINSTALLED stays 1 and the Makevars step
## picks the vendored static library.
PREINSTALLED=1
TALIB_CFLAGS=""
TALIB_LIBS=""
if [ "$FORCE_VENDOR" -eq 0 ]; then
printf '%s Probing for system TA-Lib via pkg-config' "$(info)"
PKG_CONFIG_MISSING=0
if ! command -v pkg-config >/dev/null 2>&1; then
PKG_CONFIG_MISSING=1
elif pkg-config --exists ta-lib; then
TALIB_CFLAGS="$(pkg-config --cflags ta-lib)"
TALIB_LIBS="$(pkg-config --libs ta-lib)"
TALIB_SYSTEM=$(temporary_directory)
cat > "${TALIB_SYSTEM}/conftest.c" <<'EOF'
#include <ta-lib/ta_libc.h>
int main(void){ TA_Initialize(); TA_Shutdown(); return 0; }
EOF
## details:
## $TALIB_CFLAGS / $TALIB_LIBS are intentionally unquoted —
## pkg-config returns space-separated flag lists that must
## word-split to reach the compiler as separate arguments.
: "${CC:=$R_CC}"
if ( cd "$TALIB_SYSTEM" && $CC $TALIB_CFLAGS conftest.c $TALIB_LIBS -o conftest >/dev/null 2>&1 ); then
PREINSTALLED=0
fi
rm -rf "$TALIB_SYSTEM"
fi
if [ "$PREINSTALLED" -eq 0 ]; then
printf ' %s\n' "$(success)"
else
printf ' %s\n' "$(warning)"
## details:
## surface the most likely user-fixable cause of fallback —
## a missing system TA-Lib is normal on CRAN builders, but
## a missing pkg-config on a machine that *does* have ta-lib
## installed is silent and confusing without a hint here.
if [ "$PKG_CONFIG_MISSING" -eq 1 ]; then
printf '%s pkg-config not found on PATH — falling back to vendored TA-Lib. Install pkg-config to link against a system install instead.\n' "$(info)"
fi
fi
fi
## conditional building using CMake
## for vendored TA-Lib
##
## details:
## if --force-vendor is passed FORCE_VENDOR
## is set to 1, 0 otherwise - so everything
## is based on the above compilation
if [ "$(( PREINSTALLED + FORCE_VENDOR ))" -ne 0 ]; then
TALIB="src/ta-lib" # vendor location
## vendor check start
## it's NOT enough to check for $TALIB as
## the folder gets cloned, but its empty,
## and therefore never enters the branch.
## - instead of checking for the folder
## we check for CMakeLists.txt inside the
## folder, which is safe because if its not
## there - then {talib} can't build anyways.
if [ ! -f "$TALIB/CMakeLists.txt" ]; then
if ! command -v git > /dev/null 2>&1; then
printf ' %s\n' "$(warning)"
echo
echo $(error) Git is not found on default PATH.
echo
echo "=========================================================================="
echo "Install Git:"
echo " Linux:"
echo " Debian/Ubuntu: sudo apt-get update && sudo apt-get install -y git"
echo " Fedora/RHEL/CentOS: sudo dnf install -y git"
echo " Arch: sudo pacman -S --needed git"
echo " openSUSE: sudo zypper install -y git"
echo
echo " macOS:"
echo " Homebrew: brew install git"
echo " MacPorts: sudo port install git"
echo " Xcode CLT (also installs Git): xcode-select --install"
echo
echo "After installation, ensure 'git' is on PATH and restart the shell/terminal."
echo
echo "=========================================================================="
echo "Submit bug-reports here: $(url https://github.com/serkor1/ta-lib-R)"
echo
exit 1
fi
if git -C . status > /dev/null 2>&1; then
printf ' %s\n' "$(warning)"
printf "Build-error: $TALIB not found. Either make a new clone, or initialize the submodule.\n"
printf "Clone:\t\tgit clone --recursive $(url https://github.com/serkor1/ta-lib-R.git)\n"
printf "Initialize:\tgit submodule update --init --recursive\n"
exit 1
## if its NOT a git repository it is safe to assume that the development
## version is being installed via {pak} or other tools which does not
## include submodules by default
else
printf ' %s\n' "$(warning)"
echo
echo "TA-Lib (core) not found. Cloning $(url https://github.com/TA-Lib/ta-lib.git)"
echo
git clone https://github.com/TA-Lib/ta-lib.git ${TALIB} || {
echo $(error) Could not clone $(url https://github.com/TA-Lib/ta-lib.git).
echo Check your internet connection, or submit a bug-report.
exit 1
}
fi
fi
## vendor check end
TARGET="$(cd "$TALIB" 2>/dev/null && pwd)/local"
## find CMake on PATH before proceeding
## with the build - this approach is necessary
## for MacOS which in (some) cases are located
## in a different PATH than otherwise.
## - This approach is a part 'Writing R Extensions' and
## is also recommended by Dirk Eddelbuettel.
## details:
## `|| CMAKE=""` is load-bearing. Without it, `which` returning
## non-zero propagates through `var=$(...)` to the assignment's
## exit status; `set -e` then kills configure silently before the
## explicit "CMake not found on PATH" branch below can print.
if test -z "${CMAKE:-}"; then
# Look for a cmake binary in the current path
CMAKE=`which cmake 2>/dev/null` || CMAKE=""
fi
if test -z "${CMAKE:-}"; then
# Check for a MacOS specific path
CMAKE=`which /Applications/CMake.app/Contents/bin/cmake 2>/dev/null` || CMAKE=""
fi
if test -z "${CMAKE:-}"; then
## NOTE: after many attempts I can't make
## configure "work" using libtools. It either complains about
## missing libraries, or links.
##
## CMake just works well here, end of story.
printf ' %s\n' "$(error)"
printf 'CMake not found on PATH\n'
echo "=========================================================================="
echo "Install CMake:"
echo " Linux:"
echo " Debian/Ubuntu: sudo apt-get update && sudo apt-get install -y cmake"
echo " Fedora/RHEL/CentOS: sudo dnf install -y cmake"
echo " Arch: sudo pacman -S --needed cmake"
echo " openSUSE: sudo zypper install -y cmake"
echo
echo " macOS:"
echo " Homebrew: brew install cmake"
echo " MacPorts: sudo port install cmake"
echo
echo "Alternatively download the installer: https://cmake.org/download/"
echo "After installation, restart the shell or add CMake to PATH"
echo "=========================================================================="
echo "Submit bug-reports here: $(url https://github.com/serkor1/ta-lib-R)"
echo
exit 1
fi
## NOTE: this builds a temporary
## directory to build TA-Lib
## without this step .Rbuildignore will riot.
## $TARGET must NOT be cleaned here — R CMD INSTALL
## links against libta-lib.a inside it after configure
## completes.
BUILDLOCATION=$(temporary_directory)
trap 'rm -rf "$BUILDLOCATION"' EXIT
## macOS-specific CMake settings
##
## details:
## CRAN's macOS builders target a fixed deployment SDK (e.g.
## -mmacosx-version-min=11.0 baked into R's CFLAGS) but run on
## a newer host (e.g. Ventura 13.x). Without an explicit
## CMAKE_OSX_DEPLOYMENT_TARGET, CMake uses the host version,
## producing objects whose LC_BUILD_VERSION exceeds the .so's
## target and breaking the link. CMAKE_OSX_ARCHITECTURES is
## set from `uname -m` so the static lib matches the running R
## arch (x86_64 or arm64).
##
## The block is no-op on Linux/other Unixes.
OSX_CMAKE_ARGS=""
case "$(uname -s)" in
Darwin)
OSX_ARCH=$(uname -m)
OSX_DEPLOY=$(printf '%s' "$R_CFLAGS $R_CXXFLAGS" \
| sed -n 's/.*-mmacosx-version-min=\([0-9.]*\).*/\1/p' \
| head -n1)
OSX_CMAKE_ARGS="-DCMAKE_OSX_ARCHITECTURES=$OSX_ARCH"
if [ -n "$OSX_DEPLOY" ]; then
OSX_CMAKE_ARGS="$OSX_CMAKE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET=$OSX_DEPLOY"
fi
;;
esac
## Portable parallel-job count
##
## details:
## `nproc` is GNU coreutils and absent on macOS by default.
## Without inherit_errexit the failed substitution silently
## produced an empty `-j` argument, which Make tolerates but
## Ninja rejects. Use getconf (POSIX) which is present on
## both Linux and macOS, with a safe fallback.
if command -v getconf >/dev/null 2>&1; then
NJOBS=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)
elif command -v nproc >/dev/null 2>&1; then
NJOBS=$(nproc 2>/dev/null || echo 2)
else
NJOBS=2
fi
if [ "${NJOBS:-2}" -gt 2 ]; then
NJOBS=$((NJOBS - 2))
else
NJOBS=1
fi
if command -v "${CMAKE}" >/dev/null 2>&1; then
printf '%s Configure and generate TA-Lib\n' "$(info)"
${CMAKE} -S "$TALIB" -B "${BUILDLOCATION}" \
-Wno-dev --log-level=NOTICE \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$TARGET" \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCMAKE_C_COMPILER="$R_CC" \
-DCMAKE_CXX_COMPILER="$R_CXX" \
-DCMAKE_C_FLAGS="$R_CC_EXTRA $R_CFLAGS $R_CPPFLAGS -w -fPIC ${OPTFLAGS}" \
-DCMAKE_CXX_FLAGS="$R_CXX_EXTRA $R_CXXFLAGS $R_CPPFLAGS -w -fPIC ${OPTFLAGS}" \
-DCMAKE_EXE_LINKER_FLAGS="$R_LDFLAGS" \
-DCMAKE_SHARED_LINKER_FLAGS="$R_LDFLAGS" \
$OSX_CMAKE_ARGS \
$LAUNCHER_ARGS \
-DBUILD_DEV_TOOLS=OFF
printf '%s\n' "$(success)"
printf '%s Build and install TA-Lib\n' "$(info)"
${CMAKE} --build "${BUILDLOCATION}" --target install -- -j"$NJOBS"
printf '%s\n' "$(success)"
fi
fi
## Makevars
##
## details:
## this is probably the most important step, if done
## incorrectly the uptream build wont be linked to R
printf '%s Constructing %s and %s' "$(info)" "$(var PKG_CFLAGS)" "$(var PKG_LIBS)"
CFLAGS=""
if [ $PREINSTALLED -eq 0 ]; then
## details:
## the pkg-config probe above already captured the cflags and
## libs and proved they link. Re-running pkg-config here would
## duplicate that work and could disagree with what we tested.
CFLAGS="$TALIB_CFLAGS"
PKG_LIBS="$TALIB_LIBS"
else
CFLAGS="-I${TARGET##*/src/}/include -I${TARGET##*/src/}/include/ta-lib"
SEARCH_DIR="$TALIB/local/lib/"
## details
## find the first *.a file to be linked
## to R
PKG_LIBS=$( find "$SEARCH_DIR" -maxdepth 1 -type f -name '*.a' | head -n 1 )
if [ -z "$PKG_LIBS" ]; then
printf ' %s\n' "$(warning)"
echo $(error) Could not find static libraries in $SEARCH_DIR.
echo This is a bug, please submit a issue here: $(url https://github.com/serkor1/ta-lib-R)
exit 1
fi
PKG_LIBS="${PKG_LIBS##*src/} -lm"
fi
## Opt-in strict warnings for the R wrapper compile step.
## Triggered by 'make check' via TALIB_STRICT_WARNINGS=1.
## Flags are appended to PKG_CFLAGS only, NOT to the CMake
## build of vendored ta-lib (which keeps -w) — this prevents
## ta-lib's own code from drowning the log.
if [ "${TALIB_STRICT_WARNINGS:-0}" = "1" ]; then
WARN_FLAGS="-Wall -Wpedantic -Wextra -Wno-unused-parameter -Wno-cast-function-type"
printf '%s Strict warnings enabled: %s\n' "$(info)" "$WARN_FLAGS"
else
WARN_FLAGS=""
fi
CFLAGS="$CFLAGS $OPTFLAGS $WARN_FLAGS"
printf ' %s\n' "$(success)"
printf '%s Constructing %s' "$(info)" "$(var Makevars)"
cat > src/Makevars <<EOF
# autogenerated by configure
PKG_CFLAGS = $CFLAGS
PKG_LIBS = $PKG_LIBS
EOF
printf ' %s\n' "$(success)"
echo
echo Configure status $(success)
echo