-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfigure.win
More file actions
executable file
·370 lines (348 loc) · 15.1 KB
/
Copy pathconfigure.win
File metadata and controls
executable file
·370 lines (348 loc) · 15.1 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
#!/bin/sh
# =====================================================================
# cppDE configure.win: Windows install-time detection of optional
# system libraries (SUNDIALS, SuiteSparse/KLU).
#
# Detection order (each step skipped if already populated):
# 1. Explicit env vars (CPPDE_CVODE_*) override everything: for
# users who have built or installed SUNDIALS / SuiteSparse in a
# custom location and want to point the package at it directly.
# 2. pkg-config probe: only useful when pkg-config is on PATH AND
# the library shipped a .pc file. KLU does (uppercase `KLU.pc`),
# but the MSYS2 mingw-w64-ucrt-x86_64-sundials package as of
# v7.5 ships no sundials*.pc, so this step typically misses
# SUNDIALS on Windows.
# 3. Rtools-ucrt64 compile probe: the realistic Windows path.
# Rtools 4.4 / 4.5 expose their install root via
# RTOOLS{44,45}_HOME env var. The ucrt64 sub-prefix
# (`<rtools>/ucrt64/`) is the standard MSYS2 layout: `pacman -S
# mingw-w64-ucrt-x86_64-{sundials,suitesparse}` populates
# `<rtools>/ucrt64/{include,lib,bin}`. Rtools' R does NOT add
# this to LOCAL_SOFT (which points at the static.posix sysroot
# instead), so we have to inject `-I<rtools>/ucrt64/include` and
# `-L<rtools>/ucrt64/lib` ourselves. The compile probe verifies
# ABI compatibility before committing the flags. The bin path is
# recorded as `runtime_dll_path` so R/zzz.R can prepend it to
# PATH at package load time, otherwise dyn.load() of the
# compiled solver would fail to resolve libsundials_*.dll.
#
# If none of the routes find the libraries, an empty stub config is
# written and Windows-specific install instructions are printed; the
# package still installs, but cvode() and the sparse Jacobian path
# error at runtime with the same hints.
#
# Minimal library set (what the sources actually reference):
# CPPDE_CVODE_LIBS = -lsundials_cvodes -lsundials_nvecserial \
# -lsundials_sunmatrixdense \
# -lsundials_sunlinsoldense \
# -lsundials_core
# CPPDE_CVODE_KLU_LIBS = -lsundials_sunmatrixsparse \
# -lsundials_sunlinsolklu -lklu
# (Windows KLU builds may additionally need -lamd -lcolamd -lbtf
# -lsuitesparseconfig if KLU is linked statically.)
#
# The detected flags are written to inst/cvodeConfig.dcf, which is
# read at package load time (R/zzz.R) into an internal `cvodeConfig`
# environment consumed by R/cvode.R and R/tools.R at runtime.
# =====================================================================
PKG_CONFIG="${PKG_CONFIG:-pkg-config}"
log() { echo "configure.win: $*"; }
# Normalise a Windows path to forward slashes (bash-safe expansion).
fwd_slash() { echo "$1" | sed 's|\\|/|g'; }
# --- Toolchain handles for compile probes ---
: "${R_HOME:=$(R RHOME 2>/dev/null || true)}"
R_HOME=$(fwd_slash "$R_HOME")
RBIN=""
CXX=""
if [ -n "$R_HOME" ] && [ -x "${R_HOME}/bin/R" ]; then
RBIN="${R_HOME}/bin/R"
elif command -v R >/dev/null 2>&1; then
RBIN="$(command -v R)"
fi
if [ -n "$RBIN" ]; then
CXX=$("${RBIN}" CMD config CXX 2>/dev/null || true)
CXXFLAGS=$("${RBIN}" CMD config CXXFLAGS 2>/dev/null || true)
CPPFLAGS=$("${RBIN}" CMD config CPPFLAGS 2>/dev/null || true)
fi
PROBE_LAST_CMD=""
PROBE_LAST_ERR=""
probe_compile() {
# $1: source snippet; $2: extra cflags; $3: extra libs
# Captures cmd + stderr in PROBE_LAST_{CMD,ERR} on every call so the
# caller can surface failures via append_probe_err / dump_probe_errs.
[ -z "$CXX" ] && { PROBE_LAST_ERR="(no C++ compiler: R CMD config CXX returned empty)"; return 1; }
snippet="$1"; extra_cflags="$2"; extra_libs="$3"
tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t conftest)
printf '%s\n' "$snippet" > "$tmpdir/conftest.cpp"
PROBE_LAST_CMD="$CXX $CPPFLAGS $CXXFLAGS $extra_cflags -o conftest.exe conftest.cpp $extra_libs"
PROBE_LAST_ERR=$($CXX $CPPFLAGS $CXXFLAGS $extra_cflags -o "$tmpdir/conftest.exe" \
"$tmpdir/conftest.cpp" $extra_libs 2>&1)
rc=$?
rm -rf "$tmpdir"
return $rc
}
append_probe_err() {
# $1: name var holding accumulated errors; $2: tag
eval "prev=\$$1"
new=$(printf '%s\n --- %s ---\n $ %s\n%s' \
"$prev" "$2" "$PROBE_LAST_CMD" \
"$(printf '%s' "$PROBE_LAST_ERR" | sed 's/^/ /')")
eval "$1=\$new"
}
dump_probe_errs() {
# $1: var holding accumulated errors
eval "errs=\$$1"
[ -z "$errs" ] && return 0
log "Probe diagnostics (set CPPDE_CVODE_LIBS / CPPDE_CVODE_KLU_LIBS to bypass detection):"
printf '%s\n' "$errs" | while IFS= read -r line; do log "$line"; done
}
# --- Discover Rtools ucrt64 sysroot (where pacman drops mingw-w64-ucrt-* pkgs) ---
ucrt_root=""
for v in 44 45 46 47; do
eval "rh=\${RTOOLS${v}_HOME:-}"
rh=$(fwd_slash "$rh")
if [ -n "$rh" ] && [ -d "${rh}/ucrt64/include" ] && [ -d "${rh}/ucrt64/lib" ]; then
ucrt_root="${rh}/ucrt64"
log "Rtools ucrt64 sysroot: $ucrt_root (from RTOOLS${v}_HOME)"
break
fi
done
# Fallback: probe standard install paths if the env vars weren't set.
if [ -z "$ucrt_root" ]; then
for cand in "C:/rtools47/ucrt64" "C:/rtools46/ucrt64" \
"C:/rtools45/ucrt64" "C:/rtools44/ucrt64"; do
if [ -d "${cand}/include" ] && [ -d "${cand}/lib" ]; then
ucrt_root="$cand"
log "Rtools ucrt64 sysroot: $ucrt_root (probed default path)"
break
fi
done
fi
# --- Start with env-var overrides (always win if set) ---
cflags="${CPPDE_CVODE_CFLAGS:-}"
libs="${CPPDE_CVODE_LIBS:-}"
klu_cflags="${CPPDE_CVODE_KLU_CFLAGS:-}"
klu_libs="${CPPDE_CVODE_KLU_LIBS:-}"
if [ -n "$libs" ]; then
log "SUNDIALS configured via CPPDE_CVODE_LIBS env var"
fi
if [ -n "$klu_libs" ]; then
log "KLU configured via CPPDE_CVODE_KLU_LIBS env var"
fi
# --- SUNDIALS via pkg-config (only if env var didn't set it) ---
if [ -z "$libs" ] && command -v "$PKG_CONFIG" >/dev/null 2>&1 \
&& "$PKG_CONFIG" --exists sundials-cvodes 2>/dev/null; then
cflags=$("$PKG_CONFIG" --cflags \
sundials-cvodes sundials-nvecserial \
sundials-sunmatrixdense sundials-sunlinsoldense 2>/dev/null || true)
libs=$("$PKG_CONFIG" --libs \
sundials-cvodes sundials-nvecserial \
sundials-sunmatrixdense sundials-sunlinsoldense 2>/dev/null || true)
if [ -n "$libs" ]; then
log "SUNDIALS detected via pkg-config"
fi
fi
# --- SUNDIALS via compile probe against Rtools ucrt64 sysroot ---
sundials_probe_errs=""
if [ -z "$libs" ] && [ -n "$ucrt_root" ] && [ -d "$ucrt_root/include/sundials" ]; then
snippet='
#include <cvodes/cvodes.h>
#include <nvector/nvector_serial.h>
#include <sunmatrix/sunmatrix_dense.h>
#include <sunlinsol/sunlinsol_dense.h>
int main() {
SUNContext ctx = 0;
SUNContext_Create(0, &ctx);
if (ctx) SUNContext_Free(&ctx);
return 0;
}
'
# SUNDIALS 7+ split out libsundials_core; SUNDIALS 6.x doesn't ship
# it at all (the linker errors with "cannot find -lsundials_core"
# rather than dropping the missing lib). Try the 6.x-style link
# first, then add -lsundials_core for 7+.
trial_cflags="-I${ucrt_root}/include"
base_libs="-L${ucrt_root}/lib -lsundials_cvodes -lsundials_nvecserial -lsundials_sunmatrixdense -lsundials_sunlinsoldense"
for trial_libs in "$base_libs" "$base_libs -lsundials_core"; do
if probe_compile "$snippet" "$trial_cflags" "$trial_libs"; then
cflags="$trial_cflags"
libs="$trial_libs"
log "SUNDIALS detected via compile probe ($ucrt_root, libs: $trial_libs)"
break
fi
append_probe_err sundials_probe_errs "ucrt64 probe ($ucrt_root, libs: $trial_libs)"
done
fi
# --- KLU via pkg-config (only if env var didn't set it) ---
if [ -z "$klu_libs" ] && command -v "$PKG_CONFIG" >/dev/null 2>&1; then
for name in KLU klu; do
if "$PKG_CONFIG" --exists "$name" 2>/dev/null; then
pc_cflags=$("$PKG_CONFIG" --cflags "$name" 2>/dev/null || true)
pc_libs=$("$PKG_CONFIG" --libs "$name" 2>/dev/null || true)
if [ -n "$pc_libs" ]; then
klu_cflags="$pc_cflags"
klu_libs="$pc_libs"
if [ -n "$libs" ]; then
klu_libs="-lsundials_sunmatrixsparse -lsundials_sunlinsolklu $klu_libs"
fi
log "KLU detected via pkg-config ($name)"
break
fi
fi
done
fi
# --- KLU via compile probe against Rtools ucrt64 sysroot ---
# MSYS2 ships klu.h in <ucrt_root>/include/suitesparse/, matching KLU.pc's
# prefix layout.
klu_probe_errs=""
if [ -z "$klu_libs" ] && [ -n "$ucrt_root" ] && [ -f "$ucrt_root/include/suitesparse/klu.h" ]; then
klu_snippet="#include <klu.h>
int main() { klu_common c; klu_defaults(&c); return 0; }"
trial_klu_cflags="-I${ucrt_root}/include/suitesparse"
for trial_klu_libs in \
"-L${ucrt_root}/lib -lklu" \
"-L${ucrt_root}/lib -lklu -lamd -lcolamd -lbtf -lsuitesparseconfig"; do
if probe_compile "$klu_snippet" "$trial_klu_cflags" "$trial_klu_libs"; then
klu_cflags="$trial_klu_cflags"
klu_libs="$trial_klu_libs"
if [ -n "$libs" ]; then
klu_libs="-lsundials_sunmatrixsparse -lsundials_sunlinsolklu $klu_libs"
fi
log "KLU detected via compile probe ($ucrt_root, libs: $trial_klu_libs)"
break
fi
append_probe_err klu_probe_errs "ucrt64 klu probe ($ucrt_root, libs: $trial_klu_libs)"
done
fi
# --- Runtime DLL path (only set if we used the Rtools ucrt64 sysroot) ---
runtime_dll_path=""
if [ -n "$ucrt_root" ] && { [ -n "$libs" ] || [ -n "$klu_libs" ]; }; then
# Only expose the bin path if we actually picked up libs from this
# sysroot. If the user supplied custom env vars, they're responsible
# for runtime DLL discoverability themselves.
case "${cflags}${libs}${klu_cflags}${klu_libs}" in
*"$ucrt_root"*) runtime_dll_path="${ucrt_root}/bin" ;;
esac
fi
available=FALSE
klu_available=FALSE
[ -n "$libs" ] && available=TRUE
[ -n "$klu_libs" ] && klu_available=TRUE
# --- Print Windows-specific install hints if missing ---
if [ "$available" = "FALSE" ] || [ "$klu_available" = "FALSE" ]; then
log "----------------------------------------------------------------"
if [ "$available" = "FALSE" ] && [ "$klu_available" = "FALSE" ]; then
log "SUNDIALS and SuiteSparse/KLU not found - cvode() backend and"
log " sparse Jacobian path will both be DISABLED."
elif [ "$available" = "FALSE" ]; then
log "SUNDIALS not found - cvode() backend will be DISABLED."
else
log "SuiteSparse/KLU not found - sparse Jacobian path will be"
log " DISABLED for both cppODE() and cvode() (sparse=TRUE errors;"
log " sparse=NULL falls back to dense)."
fi
log ""
log " Recommended install path on Windows (Rtools 4.4 / 4.5):"
log ""
log " 1. From any shell (PowerShell, cmd, Git Bash), call Rtools'"
log " pacman by full path, substitute your installed version"
log " for <ver> (e.g. 44 or 45):"
log ""
log " C:\\rtools<ver>\\usr\\bin\\pacman.exe -Sy --noconfirm \\"
if [ "$available" = "FALSE" ]; then
log " mingw-w64-ucrt-x86_64-sundials \\"
fi
if [ "$klu_available" = "FALSE" ]; then
log " mingw-w64-ucrt-x86_64-suitesparse"
fi
log ""
log " (Don't just type 'pacman' in Git Bash, that bash"
log " doesn't see Rtools' /usr/bin and will say 'command not"
log " found'. Calling pacman.exe by full path avoids the"
log " wrong-shell trap.)"
log ""
log " 2. Re-run R CMD INSTALL <path/to/cppDE> from PowerShell."
log " configure.win compile-probes <RTOOLS_HOME>/ucrt64/ and"
log " picks up the libs automatically; the runtime DLL path"
log " is recorded so R/zzz.R can put it on PATH at load time."
log ""
log " Advanced: if you have a custom SUNDIALS / SuiteSparse build"
log " (CMake from source, vcpkg, standalone MSYS2, etc.), point the"
log " package at it via env vars before R CMD INSTALL. In PowerShell:"
if [ "$available" = "FALSE" ]; then
log " \$env:CPPDE_CVODE_CFLAGS = '-IC:/path/to/sundials/include'"
log " \$env:CPPDE_CVODE_LIBS = '-LC:/path/to/sundials/lib -lsundials_cvodes -lsundials_nvecserial -lsundials_sunmatrixdense -lsundials_sunlinsoldense -lsundials_core'"
fi
if [ "$klu_available" = "FALSE" ]; then
log " \$env:CPPDE_CVODE_KLU_CFLAGS = '-IC:/path/to/suitesparse/include'"
log " \$env:CPPDE_CVODE_KLU_LIBS = '-LC:/path/to/suitesparse/lib -lsundials_sunmatrixsparse -lsundials_sunlinsolklu -lklu -lamd -lcolamd -lbtf -lsuitesparseconfig'"
fi
log " In that case you must also ensure the matching DLL bin/ dir"
log " is on PATH at R session start, otherwise dyn.load() will"
log " fail to resolve the runtime libraries."
log "----------------------------------------------------------------"
[ "$available" = "FALSE" ] && dump_probe_errs sundials_probe_errs
[ "$klu_available" = "FALSE" ] && dump_probe_errs klu_probe_errs
fi
# ---------------------------------------------------------------------
# OpenMP. Optional: without it solveODEBatch() runs serially.
# ---------------------------------------------------------------------
openmp_available=0
openmp_cxxflags=""
openmp_libs=""
omp_flag=$(sed -n 's/^[[:space:]]*SHLIB_OPENMP_CXXFLAGS[[:space:]]*=[[:space:]]*//p' \
"${R_HOME}/etc/Makeconf" 2>/dev/null | head -n1)
if [ -n "$omp_flag" ]; then
omp_snippet='#include <omp.h>
int main(){ int n = 0;
#pragma omp parallel
{ n = omp_get_num_threads(); }
return n > 0 ? 0 : 1; }'
if probe_compile "$omp_snippet" "$omp_flag" "$omp_flag"; then
openmp_available=1
openmp_cxxflags="$omp_flag"
openmp_libs="$omp_flag"
log "OpenMP: enabled ($omp_flag)"
else
log "OpenMP: flag '$omp_flag' present but does not link; disabled"
fi
else
log "OpenMP: no SHLIB_OPENMP_CXXFLAGS in Makeconf; disabled"
fi
mkdir -p inst
cat > inst/cvodeConfig.dcf <<EOF
available: $available
cflags: $cflags
libs: $libs
klu_available: $klu_available
klu_cflags: $klu_cflags
klu_libs: $klu_libs
runtime_dll_path: $runtime_dll_path
openmp_available: $([ "$openmp_available" = "1" ] && echo TRUE || echo FALSE)
openmp_cxxflags: $openmp_cxxflags
openmp_libs: $openmp_libs
EOF
log "Summary: CVODE=$([ "$available" = TRUE ] && echo ON || echo off), KLU=$([ "$klu_available" = TRUE ] && echo ON || echo off)"
if [ -n "$runtime_dll_path" ]; then
log "Runtime DLL path recorded: $runtime_dll_path"
fi
# Always exit 0: missing optional libs are not a configure error;
# the package installs in a degraded mode (CVODE / sparse path raise
# at runtime). A non-zero exit here would make R CMD INSTALL abort
# with "configuration failed" instead of just emitting the warnings
# above. (This was the bug that broke Windows CI: the trailing
# `[ -n "$runtime_dll_path" ] && log ...` returned 1 when the var
# was empty, and that became the script's exit code.)
# ---------------------------------------------------------------------------
# Vignette: carry it into an install that did not go through a full build.
#
# Only R CMD build moves the PDF into inst/doc, so a source tree and the
# tarball install_github makes would both install without the vignette. The
# guard is the PDF itself: a CRAN tarball has only the .asis left here.
# Revisit this block for a CRAN submission.
# ---------------------------------------------------------------------------
if [ -f vignettes/Methods.pdf ]; then
mkdir -p inst/doc
cp -f vignettes/Methods.pdf vignettes/Methods.pdf.asis inst/doc/
fi
exit 0