From cc781770a7f80447718f7618e567711d048341fb Mon Sep 17 00:00:00 2001 From: Ziv Yaniv Date: Wed, 31 Dec 2025 14:17:41 -0500 Subject: [PATCH] Add support for Windows Add support for building and testing on Windows OS in a consistent manner to Linux and macOS. On Windows, this uses RTools to provide the necessary build tools. Testing is performed in CircleCI as it offers a more powerful build machine under the free plan. Co-authored-by: Artur-man --- .circleci/config.yml | 44 +++++++++++------ README.md | 9 ++-- configure.win | 110 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 17 deletions(-) create mode 100755 configure.win diff --git a/.circleci/config.yml b/.circleci/config.yml index 508703e..ce5c2b5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,6 +18,12 @@ executors: environment: RUNNER_OS: macos resource_class: m4pro.medium + windows: + machine: + image: windows-server-2022-gui:current + environment: + RUNNER_OS: windows + resource_class: windows.large jobs: r-build: @@ -32,8 +38,12 @@ jobs: steps: - checkout - run: - name: Set environment + name: Set library for default R package installation command: | + # All packages will be installed into the directory + # referenced by the R_LIBS environment variable. + mkdir -p $HOME/Rlibs + ls -la ${R_LIBS} echo "export R_LIBS=$HOME/Rlibs" >> $BASH_ENV - run: name: Install R << parameters.r-version >> @@ -44,18 +54,18 @@ jobs: brew tap r-lib/rig brew install --cask rig rig add $R_VERSION - # on macOS the R package is installed into a MAJOR.MINOR directory - # the PATCH is ignored (only one patch version supported) the - # architecture is added as a suffix (arm64, x86_64) to allow for - # installations to coexist - rig default "${R_VERSION%.*}-arm64" elif [ "$RUNNER_OS" == "linux" ]; then `which sudo` curl -L https://rig.r-pkg.org/deb/rig.gpg -o /etc/apt/trusted.gpg.d/rig.gpg `which sudo` sh -c 'echo "deb http://rig.r-pkg.org/deb rig main" > /etc/apt/sources.list.d/rig.list' `which sudo` apt update `which sudo` apt install r-rig sudo rig add $R_VERSION - sudo rig default $R_VERSION + elif [ "$RUNNER_OS" == "windows" ]; then + choco install rig -y + export PATH="$PATH:/c/Program Files/rig" + rig add $R_VERSION + echo "export PATH=\"/c/Program Files/R/R-$R_VERSION/bin:\${PATH}\"" >> $BASH_ENV + echo "export R_HOME=\"/c/Program Files/R/R-$R_VERSION/\"" >> $BASH_ENV fi - run: name: System Dependencies @@ -66,25 +76,31 @@ jobs: sudo rm -rf /var/lib/apt/lists/* elif [ "$RUNNER_OS" == "macos" ]; then brew install cmake + elif [ "$RUNNER_OS" == "windows" ]; then + choco install rtools -y + echo "RTOOLS_HOME=$(ls -d /c/rtools* | head -n 1)" >> $BASH_ENV fi - run: name: Configuration Information command: | - mkdir -p ${R_LIBS} - c++ --version + if [ "$RUNNER_OS" == "windows" ]; then + export PATH="${RTOOLS_HOME}/usr/bin:${RTOOLS_HOME}/x86_64-w64-mingw32.static.posix/bin:${R_HOME}/bin:${PATH}" + fi + echo "R_LIBS is: ${R_LIBS}" + c++ --version || gcc --version || echo "no c++ compiler found" cmake --version || echo "cmake not found" - which R - R --version + make --version || echo "make not found" + R --version || echo "R not found" - run: name: Install R packages command: | - R -e "install.packages(c('remotes'), lib=c('${R_LIBS}'), repo='https://cloud.r-project.org/')" + R -e "install.packages(c('remotes'), repo='https://cloud.r-project.org/')" - run: name: Build and test no_output_timeout: 30m command: | set -x - R -e "remotes::install_git(c('.'), lib=c('${R_LIBS}'), configure.vars=c('MAKEJ=3'))" + R -e "remotes::install_git(c('.'), configure.vars=c('MAKEJ=3'))" workflows: r-build-test: @@ -94,7 +110,7 @@ workflows: matrix: parameters: r-version: ['4.3.1', '4.4.1'] - os: ["macos-arm"] + os: ["macos-arm", "windows"] filters: branches: only: diff --git a/README.md b/README.md index c5a3357..97fcade 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,13 @@ Use multicore compilation and build additional modules not included in the defau remotes::install_github("SimpleITK/SimpleITKRInstaller", configure.vars=c("MAKEJ=6", "ADDITIONAL_SITK_MODULES=-DSimpleITK_USE_ELASTIX=ON\\ -DModule_ITKIODCMTK:BOOL=ON")) ``` +Note: +On Linux and Mac requires [CMake](https://cmake.org/) and [git](https://git-scm.com/) in the path. -Requires _cmake_ and _git_ in the path. - -Tested on Linux and Mac. +On Windows requires [rtools](https://cran.r-project.org/bin/windows/Rtools/) installation and setting the `RTOOLS_HOME` environment variable. For example: +```R +Sys.setenv(RTOOLS_HOME = "C:/rtools45") +``` # How to Cite diff --git a/configure.win b/configure.win new file mode 100755 index 0000000..5f69601 --- /dev/null +++ b/configure.win @@ -0,0 +1,110 @@ +#!/usr/bin/env sh +set -e + +# configure script that will fetch and build SimpleITK, then move the results +# to somewhere that R can install. It takes a long time and uses +# a substantial amount of disk space +# +# Requires rtools (https://cran.r-project.org/bin/windows/Rtools/) +# + +# --- Settings --- +# Load shared configuration for all OS (SimpleITK repository URL and tag) +export SimpleITKGit=$(Rscript -e "d <- desc::desc(file='DESCRIPTION'); urls <- strsplit(d\$get_field('URL'), ',')[[1]]; cat(trimws(urls[1]))") +export SITKTAG=v$(Rscript -e "cat(desc::desc_get_field('Version', file='DESCRIPTION'))") + +PKGBASED=$(pwd) +echo "$PKGBASED" + +# --- Check R_HOME and RTOOLS_HOME--- +if [ -z "$R_HOME" ]; then + echo "Environment variable \"R_HOME\" is not set!" 1>&2 + exit 1 +fi + +if [ -z "$RTOOLS_HOME" ]; then + echo "Environment variable \"RTOOLS_HOME\" is not set!" 1>&2 + exit 1 +fi + +# Update PATH to include Rtools binaries +export PATH="${RTOOLS_HOME}/usr/bin:${RTOOLS_HOME}/x86_64-w64-mingw32.static.posix/bin:${PATH}" + +# Use Rscript on Windows +RCALL="${R_HOME}/bin/Rscript.exe" +export RCALL + +echo "R_LIBS is: ${R_LIBS}" +c++ --version || gcc --version || echo "no c++ compiler found" +cmake --version || echo "cmake not found" +make --version || echo "make not found" +$RCALL --version || echo "R not found" + + +# --- Build location: short path to avoid ITK limit --- +BUILDDIR="C:/bld" +echo "Using short build directory: $BUILDDIR" +mkdir -p "$BUILDDIR" + +# Pull compiler flags from R (optional but kept for parity) +CC="$("${R_HOME}/bin/R.exe" CMD config CC)" +CXX="$("${R_HOME}/bin/R.exe" CMD config CXX)" +CFLAGS="$("${R_HOME}/bin/R.exe" CMD config CFLAGS)" +CXXFLAGS="$("${R_HOME}/bin/R.exe" CMD config CXX11FLAGS)" +CPPFLAGS="$("${R_HOME}/bin/R.exe" CMD config CPPFLAGS)" +export CC CXX CFLAGS CXXFLAGS CPPFLAGS + +# Parallelism (default 1 if not provided) +: "${MAKEJ:=1}" +export MAKEJ + +# --- Build in SITK directory --- +( + cd "$BUILDDIR" + + if [ ! -d SimpleITK ]; then + git clone "$SimpleITKGit" + cd SimpleITK + git checkout "$SITKTAG" + else + cd SimpleITK + git fetch --tags + git checkout "$SITKTAG" + fi + + SITK_SRC="$(pwd)" + + mkdir -p ../Build + cd ../Build + + echo "Path:" + echo $PWD + + # Configure with Unix Makefiles (Rtools toolchain) + cmake -G "Unix Makefiles" \ + -DWRAP_DEFAULT=OFF \ + -DWRAP_R=ON \ + -DSimpleITK_BUILD_DISTRIBUTE=ON \ + -DBUILD_EXAMPLES=OFF \ + -DBUILD_TESTING=OFF \ + -DITK_SKIP_PATH_LENGTH_CHECK=ON \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DITK_USE_BUILD_DIR:BOOL=ON \ + -DCMAKE_C_FLAGS="-DHAVE_POSIX_MEMALIGN=0" \ + -DCMAKE_CXX_FLAGS="-DHAVE_POSIX_MEMALIGN=0" \ + ${ADDITIONAL_SITK_MODULES} \ + "${BUILDDIR}/SimpleITK/SuperBuild/" + + echo "Parallel build using -j${MAKEJ}" + echo $PWD + # Build the specific target with parallel jobs + cmake --build . --target SimpleITK-build -- -j"${MAKEJ}" + + # Remove ITK-build to save space (if present) + [ -d ITK-build ] && rm -rf ITK-build + + # Move wrapped R package into the package root using R + "${RCALL}" -f "${PKGBASED}/sitkmove.R" --args \ + "SimpleITK-build/Wrapping/R/Packaging/SimpleITK" \ + "${PKGBASED}" +) \ No newline at end of file