[CI] Revise R tests. (#8430)
- Use the standard package check (check on the tarball instead of the source tree). - Run commands in parallel. - Cleanup dependencies installation. - Replace makefile. - Documentation. - Test using the image from rhub.
This commit is contained in:
parent
4449e30184
commit
a83748eb45
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -146,7 +146,7 @@ jobs:
|
|||||||
python -m pip install wheel setuptools cpplint pylint
|
python -m pip install wheel setuptools cpplint pylint
|
||||||
- name: Run lint
|
- name: Run lint
|
||||||
run: |
|
run: |
|
||||||
LINT_LANG=cpp make lint
|
python dmlc-core/scripts/lint.py xgboost cpp R-package/src
|
||||||
|
|
||||||
sphinx:
|
sphinx:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
19
.github/workflows/r_nold.yml
vendored
19
.github/workflows/r_nold.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
# Run R tests with noLD R. Only triggered by a pull request review
|
# Run expensive R tests with the help of rhub. Only triggered by a pull request review
|
||||||
# See discussion at https://github.com/dmlc/xgboost/pull/6378
|
# See discussion at https://github.com/dmlc/xgboost/pull/6378
|
||||||
|
|
||||||
name: XGBoost-R-noLD
|
name: XGBoost-R-noLD
|
||||||
@ -7,9 +7,6 @@ on:
|
|||||||
pull_request_review_comment:
|
pull_request_review_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
env:
|
|
||||||
R_PACKAGES: c('XML', 'igraph', 'data.table', 'ggplot2', 'DiagrammeR', 'Ckmeans.1d.dp', 'vcd', 'testthat', 'lintr', 'knitr', 'rmarkdown', 'e1071', 'cplm', 'devtools', 'float', 'titanic')
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # to fetch code (actions/checkout)
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
@ -18,26 +15,22 @@ jobs:
|
|||||||
if: github.event.comment.body == '/gha run r-nold-test' && contains('OWNER,MEMBER,COLLABORATOR', github.event.comment.author_association)
|
if: github.event.comment.body == '/gha run r-nold-test' && contains('OWNER,MEMBER,COLLABORATOR', github.event.comment.author_association)
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: rhub/debian-gcc-devel-nold
|
container:
|
||||||
|
image: rhub/debian-gcc-devel-nold
|
||||||
steps:
|
steps:
|
||||||
- name: Install git and system packages
|
- name: Install git and system packages
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
apt-get update && apt-get install -y git libcurl4-openssl-dev libssl-dev libssh2-1-dev libgit2-dev libxml2-dev
|
apt update && apt install libcurl4-openssl-dev libssl-dev libssh2-1-dev libgit2-dev libglpk-dev libxml2-dev libharfbuzz-dev libfribidi-dev git -y
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash -l {0}
|
||||||
run: |
|
run: |
|
||||||
cat > install_libs.R <<EOT
|
/tmp/R-devel/bin/Rscript -e "source('./R-package/tests/helper_scripts/install_deps.R')"
|
||||||
install.packages(${{ env.R_PACKAGES }},
|
|
||||||
repos = 'http://cloud.r-project.org',
|
|
||||||
dependencies = c('Depends', 'Imports', 'LinkingTo'))
|
|
||||||
EOT
|
|
||||||
/tmp/R-devel/bin/Rscript install_libs.R
|
|
||||||
|
|
||||||
- name: Run R tests
|
- name: Run R tests
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
129
.github/workflows/r_tests.yml
vendored
129
.github/workflows/r_tests.yml
vendored
@ -3,9 +3,7 @@ name: XGBoost-R-Tests
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
R_PACKAGES: c('XML', 'data.table', 'ggplot2', 'DiagrammeR', 'Ckmeans.1d.dp', 'vcd', 'testthat', 'lintr', 'knitr', 'rmarkdown', 'e1071', 'cplm', 'devtools', 'float', 'titanic')
|
|
||||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||||
_R_CHECK_EXAMPLE_TIMING_CPU_TO_ELAPSED_THRESHOLD_: 2.5
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # to fetch code (actions/checkout)
|
contents: read # to fetch code (actions/checkout)
|
||||||
@ -35,29 +33,22 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ${{ env.R_LIBS_USER }}
|
path: ${{ env.R_LIBS_USER }}
|
||||||
key: ${{ runner.os }}-r-${{ matrix.config.r }}-5-${{ hashFiles('R-package/DESCRIPTION') }}
|
key: ${{ runner.os }}-r-${{ matrix.config.r }}-6-${{ hashFiles('R-package/DESCRIPTION') }}
|
||||||
restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }}-5-${{ hashFiles('R-package/DESCRIPTION') }}
|
restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }}-6-${{ hashFiles('R-package/DESCRIPTION') }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: Rscript {0}
|
shell: Rscript {0}
|
||||||
run: |
|
run: |
|
||||||
install.packages(${{ env.R_PACKAGES }},
|
source("./R-package/tests/helper_scripts/install_deps.R")
|
||||||
repos = 'http://cloud.r-project.org',
|
|
||||||
dependencies = c('Depends', 'Imports', 'LinkingTo'))
|
|
||||||
- name: Install igraph on Windows
|
|
||||||
shell: Rscript {0}
|
|
||||||
if: matrix.config.os == 'windows-latest'
|
|
||||||
run: |
|
|
||||||
install.packages('igraph', type='binary')
|
|
||||||
|
|
||||||
- name: Run lintr
|
- name: Run lintr
|
||||||
run: |
|
run: |
|
||||||
cd R-package
|
cd R-package
|
||||||
MAKE="make -j$(nproc)" R CMD INSTALL .
|
MAKEFLAGS="-j$(nproc)" R CMD INSTALL .
|
||||||
# Disable lintr errors for now: https://github.com/dmlc/xgboost/issues/8012
|
# Disable lintr errors for now: https://github.com/dmlc/xgboost/issues/8012
|
||||||
Rscript tests/helper_scripts/run_lint.R || true
|
Rscript tests/helper_scripts/run_lint.R || true
|
||||||
|
|
||||||
test-with-R:
|
test-R-on-Windows:
|
||||||
runs-on: ${{ matrix.config.os }}
|
runs-on: ${{ matrix.config.os }}
|
||||||
name: Test R on OS ${{ matrix.config.os }}, R ${{ matrix.config.r }}, Compiler ${{ matrix.config.compiler }}, Build ${{ matrix.config.build }}
|
name: Test R on OS ${{ matrix.config.os }}, R ${{ matrix.config.r }}, Compiler ${{ matrix.config.compiler }}, Build ${{ matrix.config.build }}
|
||||||
strategy:
|
strategy:
|
||||||
@ -66,10 +57,8 @@ jobs:
|
|||||||
config:
|
config:
|
||||||
- {os: windows-latest, r: 'release', compiler: 'mingw', build: 'autotools'}
|
- {os: windows-latest, r: 'release', compiler: 'mingw', build: 'autotools'}
|
||||||
- {os: windows-latest, r: 'release', compiler: 'msvc', build: 'cmake'}
|
- {os: windows-latest, r: 'release', compiler: 'msvc', build: 'cmake'}
|
||||||
- {os: windows-latest, r: 'release', compiler: 'mingw', build: 'cmake'}
|
|
||||||
env:
|
env:
|
||||||
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
|
||||||
_R_CHECK_EXAMPLE_TIMING_CPU_TO_ELAPSED_THRESHOLD_: 2.5
|
|
||||||
RSPM: ${{ matrix.config.rspm }}
|
RSPM: ${{ matrix.config.rspm }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -85,80 +74,60 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ${{ env.R_LIBS_USER }}
|
path: ${{ env.R_LIBS_USER }}
|
||||||
key: ${{ runner.os }}-r-${{ matrix.config.r }}-5-${{ hashFiles('R-package/DESCRIPTION') }}
|
key: ${{ runner.os }}-r-${{ matrix.config.r }}-6-${{ hashFiles('R-package/DESCRIPTION') }}
|
||||||
restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }}-5-${{ hashFiles('R-package/DESCRIPTION') }}
|
restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }}-6-${{ hashFiles('R-package/DESCRIPTION') }}
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
shell: Rscript {0}
|
|
||||||
if: matrix.config.os != 'windows-latest'
|
|
||||||
run: |
|
|
||||||
install.packages(${{ env.R_PACKAGES }},
|
|
||||||
repos = 'http://cloud.r-project.org',
|
|
||||||
dependencies = c('Depends', 'Imports', 'LinkingTo'))
|
|
||||||
|
|
||||||
- name: Install binary dependencies
|
|
||||||
shell: Rscript {0}
|
|
||||||
if: matrix.config.os == 'windows-latest'
|
|
||||||
run: |
|
|
||||||
install.packages(${{ env.R_PACKAGES }},
|
|
||||||
type = 'binary',
|
|
||||||
repos = 'http://cloud.r-project.org',
|
|
||||||
dependencies = c('Depends', 'Imports', 'LinkingTo'))
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: "3.8"
|
python-version: "3.8"
|
||||||
architecture: 'x64'
|
architecture: 'x64'
|
||||||
|
|
||||||
- name: Test R
|
|
||||||
run: |
|
|
||||||
python tests/ci_build/test_r_package.py --compiler='${{ matrix.config.compiler }}' --build-tool='${{ matrix.config.build }}'
|
|
||||||
|
|
||||||
test-R-CRAN:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
config:
|
|
||||||
- {r: 'release'}
|
|
||||||
|
|
||||||
env:
|
|
||||||
_R_CHECK_EXAMPLE_TIMING_CPU_TO_ELAPSED_THRESHOLD_: 2.5
|
|
||||||
MAKE: "make -j$(nproc)"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
|
|
||||||
- uses: r-lib/actions/setup-r@v2
|
|
||||||
with:
|
|
||||||
r-version: ${{ matrix.config.r }}
|
|
||||||
|
|
||||||
- uses: r-lib/actions/setup-tinytex@v2
|
- uses: r-lib/actions/setup-tinytex@v2
|
||||||
|
|
||||||
- name: Install system packages
|
|
||||||
run: |
|
|
||||||
sudo apt-get update && sudo apt-get install libcurl4-openssl-dev libssl-dev libssh2-1-dev libgit2-dev pandoc pandoc-citeproc libglpk-dev
|
|
||||||
|
|
||||||
- name: Cache R packages
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ${{ env.R_LIBS_USER }}
|
|
||||||
key: ${{ runner.os }}-r-${{ matrix.config.r }}-5-${{ hashFiles('R-package/DESCRIPTION') }}
|
|
||||||
restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }}-5-${{ hashFiles('R-package/DESCRIPTION') }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
shell: Rscript {0}
|
shell: Rscript {0}
|
||||||
run: |
|
run: |
|
||||||
install.packages(${{ env.R_PACKAGES }},
|
source("./R-package/tests/helper_scripts/install_deps.R")
|
||||||
repos = 'http://cloud.r-project.org',
|
|
||||||
dependencies = c('Depends', 'Imports', 'LinkingTo'))
|
|
||||||
install.packages('igraph', repos = 'http://cloud.r-project.org', dependencies = c('Depends', 'Imports', 'LinkingTo'))
|
|
||||||
|
|
||||||
- name: Check R Package
|
- name: Test R
|
||||||
run: |
|
run: |
|
||||||
# Print stacktrace upon success of failure
|
python tests/ci_build/test_r_package.py --compiler='${{ matrix.config.compiler }}' --build-tool="${{ matrix.config.build }}" --task=check
|
||||||
make Rcheck || tests/ci_build/print_r_stacktrace.sh fail
|
|
||||||
tests/ci_build/print_r_stacktrace.sh success
|
test-R-on-Debian:
|
||||||
|
name: Test R package on Debian
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: rhub/debian-gcc-devel
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
# Must run before checkout to have the latest git installed.
|
||||||
|
# No need to add pandoc, the container has it figured out.
|
||||||
|
apt update && apt install libcurl4-openssl-dev libssl-dev libssh2-1-dev libgit2-dev libglpk-dev libxml2-dev libharfbuzz-dev libfribidi-dev git -y
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: bash -l {0}
|
||||||
|
run: |
|
||||||
|
/tmp/R-devel/bin/Rscript -e "source('./R-package/tests/helper_scripts/install_deps.R')"
|
||||||
|
|
||||||
|
- name: Test R
|
||||||
|
shell: bash -l {0}
|
||||||
|
run: |
|
||||||
|
python3 tests/ci_build/test_r_package.py --r=/tmp/R-devel/bin/R --build-tool=autotools --task=check
|
||||||
|
|
||||||
|
- uses: dorny/paths-filter@v2
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
r_package:
|
||||||
|
- 'R-package/**'
|
||||||
|
|
||||||
|
- name: Run document check
|
||||||
|
if: steps.changes.outputs.r_package == 'true'
|
||||||
|
run: |
|
||||||
|
python3 tests/ci_build/test_r_package.py --r=/tmp/R-devel/bin/R --task=doc
|
||||||
|
|||||||
145
Makefile
145
Makefile
@ -1,145 +0,0 @@
|
|||||||
ifndef DMLC_CORE
|
|
||||||
DMLC_CORE = dmlc-core
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifndef RABIT
|
|
||||||
RABIT = rabit
|
|
||||||
endif
|
|
||||||
|
|
||||||
ROOTDIR = $(CURDIR)
|
|
||||||
|
|
||||||
# workarounds for some buggy old make & msys2 versions seen in windows
|
|
||||||
ifeq (NA, $(shell test ! -d "$(ROOTDIR)" && echo NA ))
|
|
||||||
$(warning Attempting to fix non-existing ROOTDIR [$(ROOTDIR)])
|
|
||||||
ROOTDIR := $(shell pwd)
|
|
||||||
$(warning New ROOTDIR [$(ROOTDIR)] $(shell test -d "$(ROOTDIR)" && echo " is OK" ))
|
|
||||||
endif
|
|
||||||
MAKE_OK := $(shell "$(MAKE)" -v 2> /dev/null)
|
|
||||||
ifndef MAKE_OK
|
|
||||||
$(warning Attempting to recover non-functional MAKE [$(MAKE)])
|
|
||||||
MAKE := $(shell which make 2> /dev/null)
|
|
||||||
MAKE_OK := $(shell "$(MAKE)" -v 2> /dev/null)
|
|
||||||
endif
|
|
||||||
$(warning MAKE [$(MAKE)] - $(if $(MAKE_OK),checked OK,PROBLEM))
|
|
||||||
|
|
||||||
include $(DMLC_CORE)/make/dmlc.mk
|
|
||||||
|
|
||||||
# set compiler defaults for OSX versus *nix
|
|
||||||
# let people override either
|
|
||||||
OS := $(shell uname)
|
|
||||||
ifeq ($(OS), Darwin)
|
|
||||||
ifndef CC
|
|
||||||
export CC = $(if $(shell which clang), clang, gcc)
|
|
||||||
endif
|
|
||||||
ifndef CXX
|
|
||||||
export CXX = $(if $(shell which clang++), clang++, g++)
|
|
||||||
endif
|
|
||||||
else
|
|
||||||
# linux defaults
|
|
||||||
ifndef CC
|
|
||||||
export CC = gcc
|
|
||||||
endif
|
|
||||||
ifndef CXX
|
|
||||||
export CXX = g++
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
export CFLAGS= -DDMLC_LOG_CUSTOMIZE=1 -std=c++14 -Wall -Wno-unknown-pragmas -Iinclude $(ADD_CFLAGS)
|
|
||||||
CFLAGS += -I$(DMLC_CORE)/include -I$(RABIT)/include -I$(GTEST_PATH)/include
|
|
||||||
|
|
||||||
ifeq ($(TEST_COVER), 1)
|
|
||||||
CFLAGS += -g -O0 -fprofile-arcs -ftest-coverage
|
|
||||||
else
|
|
||||||
CFLAGS += -O3 -funroll-loops
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifndef LINT_LANG
|
|
||||||
LINT_LANG= "all"
|
|
||||||
endif
|
|
||||||
|
|
||||||
# specify tensor path
|
|
||||||
.PHONY: clean all lint clean_all doxygen rcpplint pypack Rpack Rbuild Rcheck
|
|
||||||
|
|
||||||
build/%.o: src/%.cc
|
|
||||||
@mkdir -p $(@D)
|
|
||||||
$(CXX) $(CFLAGS) -MM -MT build/$*.o $< >build/$*.d
|
|
||||||
$(CXX) -c $(CFLAGS) $< -o $@
|
|
||||||
|
|
||||||
# The should be equivalent to $(ALL_OBJ) except for build/cli_main.o
|
|
||||||
amalgamation/xgboost-all0.o: amalgamation/xgboost-all0.cc
|
|
||||||
$(CXX) -c $(CFLAGS) $< -o $@
|
|
||||||
|
|
||||||
rcpplint:
|
|
||||||
python3 dmlc-core/scripts/lint.py xgboost ${LINT_LANG} R-package/src
|
|
||||||
|
|
||||||
lint: rcpplint
|
|
||||||
python3 dmlc-core/scripts/lint.py --exclude_path python-package/xgboost/dmlc-core \
|
|
||||||
python-package/xgboost/include python-package/xgboost/lib \
|
|
||||||
python-package/xgboost/make python-package/xgboost/rabit \
|
|
||||||
python-package/xgboost/src --pylint-rc ${PWD}/python-package/.pylintrc xgboost \
|
|
||||||
${LINT_LANG} include src python-package
|
|
||||||
|
|
||||||
ifeq ($(TEST_COVER), 1)
|
|
||||||
cover: check
|
|
||||||
@- $(foreach COV_OBJ, $(COVER_OBJ), \
|
|
||||||
gcov -pbcul -o $(shell dirname $(COV_OBJ)) $(COV_OBJ) > gcov.log || cat gcov.log; \
|
|
||||||
)
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) -rf build lib bin *~ */*~ */*/*~ */*/*/*~ */*.o */*/*.o */*/*/*.o #xgboost
|
|
||||||
$(RM) -rf build_tests *.gcov tests/cpp/xgboost_test
|
|
||||||
if [ -d "R-package/src" ]; then \
|
|
||||||
cd R-package/src; \
|
|
||||||
$(RM) -rf rabit src include dmlc-core amalgamation *.so *.dll; \
|
|
||||||
cd $(ROOTDIR); \
|
|
||||||
fi
|
|
||||||
|
|
||||||
clean_all: clean
|
|
||||||
cd $(DMLC_CORE); "$(MAKE)" clean; cd $(ROOTDIR)
|
|
||||||
cd $(RABIT); "$(MAKE)" clean; cd $(ROOTDIR)
|
|
||||||
|
|
||||||
# create pip source dist (sdist) pack for PyPI
|
|
||||||
pippack: clean_all
|
|
||||||
cd python-package; python setup.py sdist; mv dist/*.tar.gz ..; cd ..
|
|
||||||
|
|
||||||
# Script to make a clean installable R package.
|
|
||||||
Rpack: clean_all
|
|
||||||
rm -rf xgboost xgboost*.tar.gz
|
|
||||||
cp -r R-package xgboost
|
|
||||||
rm -rf xgboost/src/*.o xgboost/src/*.so xgboost/src/*.dll
|
|
||||||
rm -rf xgboost/src/*/*.o
|
|
||||||
rm -rf xgboost/demo/*.model xgboost/demo/*.buffer xgboost/demo/*.txt
|
|
||||||
rm -rf xgboost/demo/runall.R
|
|
||||||
cp -r src xgboost/src/src
|
|
||||||
cp -r include xgboost/src/include
|
|
||||||
cp -r amalgamation xgboost/src/amalgamation
|
|
||||||
mkdir -p xgboost/src/rabit
|
|
||||||
cp -r rabit/include xgboost/src/rabit/include
|
|
||||||
cp -r rabit/src xgboost/src/rabit/src
|
|
||||||
rm -rf xgboost/src/rabit/src/*.o
|
|
||||||
mkdir -p xgboost/src/dmlc-core
|
|
||||||
cp -r dmlc-core/include xgboost/src/dmlc-core/include
|
|
||||||
cp -r dmlc-core/src xgboost/src/dmlc-core/src
|
|
||||||
cp ./LICENSE xgboost
|
|
||||||
cat R-package/src/Makevars.in|sed '2s/.*/PKGROOT=./' > xgboost/src/Makevars.in
|
|
||||||
cat R-package/src/Makevars.win|sed '2s/.*/PKGROOT=./' > xgboost/src/Makevars.win
|
|
||||||
rm -f xgboost/src/Makevars.win-e # OSX sed create this extra file; remove it
|
|
||||||
bash R-package/remove_warning_suppression_pragma.sh
|
|
||||||
bash xgboost/remove_warning_suppression_pragma.sh
|
|
||||||
rm xgboost/remove_warning_suppression_pragma.sh
|
|
||||||
rm xgboost/CMakeLists.txt
|
|
||||||
rm -rfv xgboost/tests/helper_scripts/
|
|
||||||
|
|
||||||
R ?= R
|
|
||||||
|
|
||||||
Rbuild: Rpack
|
|
||||||
$(R) CMD build xgboost
|
|
||||||
rm -rf xgboost
|
|
||||||
|
|
||||||
Rcheck: Rbuild
|
|
||||||
$(R) CMD check --as-cran xgboost*.tar.gz
|
|
||||||
|
|
||||||
-include build/*.d
|
|
||||||
-include build/*/*.d
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# running all scripts in demo folder
|
# running all scripts in demo folder, removed during packaging.
|
||||||
demo(basic_walkthrough, package = 'xgboost')
|
demo(basic_walkthrough, package = 'xgboost')
|
||||||
demo(custom_objective, package = 'xgboost')
|
demo(custom_objective, package = 'xgboost')
|
||||||
demo(boost_from_prediction, package = 'xgboost')
|
demo(boost_from_prediction, package = 'xgboost')
|
||||||
|
|||||||
50
R-package/tests/helper_scripts/install_deps.R
Normal file
50
R-package/tests/helper_scripts/install_deps.R
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
## Install dependencies of R package for testing. The list might not be
|
||||||
|
## up-to-date, check DESCRIPTION for the latest list and update this one if
|
||||||
|
## inconsistent is found.
|
||||||
|
pkgs <- c(
|
||||||
|
## CI
|
||||||
|
"devtools",
|
||||||
|
"XML",
|
||||||
|
"cplm",
|
||||||
|
"e1071",
|
||||||
|
## suggests
|
||||||
|
"knitr",
|
||||||
|
"rmarkdown",
|
||||||
|
"ggplot2",
|
||||||
|
"DiagrammeR",
|
||||||
|
"Ckmeans.1d.dp",
|
||||||
|
"vcd",
|
||||||
|
"testthat",
|
||||||
|
"lintr",
|
||||||
|
"igraph",
|
||||||
|
"float",
|
||||||
|
"crayon",
|
||||||
|
"titanic",
|
||||||
|
## imports
|
||||||
|
"Matrix",
|
||||||
|
"methods",
|
||||||
|
"data.table",
|
||||||
|
"jsonlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
ncpus <- parallel::detectCores()
|
||||||
|
print(paste0("Using ", ncpus, " cores to install dependencies."))
|
||||||
|
|
||||||
|
if (.Platform$OS.type == "unix") {
|
||||||
|
print("Installing source packages on unix.")
|
||||||
|
install.packages(
|
||||||
|
pkgs,
|
||||||
|
repo = "https://cloud.r-project.org",
|
||||||
|
dependencies = c("Depends", "Imports", "LinkingTo"),
|
||||||
|
Ncpus = parallel::detectCores()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
print("Installing binary packages on Windows.")
|
||||||
|
install.packages(
|
||||||
|
pkgs,
|
||||||
|
repo = "https://cloud.r-project.org",
|
||||||
|
dependencies = c("Depends", "Imports", "LinkingTo"),
|
||||||
|
Ncpus = parallel::detectCores(),
|
||||||
|
type = "binary"
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -503,10 +503,3 @@ XGBoost uses `Sphinx <https://www.sphinx-doc.org/en/stable/>`_ for documentation
|
|||||||
Checkout the ``requirements.txt`` file under ``doc/``
|
Checkout the ``requirements.txt`` file under ``doc/``
|
||||||
|
|
||||||
Under ``xgboost/doc`` directory, run ``make <format>`` with ``<format>`` replaced by the format you want. For a list of supported formats, run ``make help`` under the same directory.
|
Under ``xgboost/doc`` directory, run ``make <format>`` with ``<format>`` replaced by the format you want. For a list of supported formats, run ``make help`` under the same directory.
|
||||||
|
|
||||||
*********
|
|
||||||
Makefiles
|
|
||||||
*********
|
|
||||||
|
|
||||||
It's only used for creating shorthands for running linters, performing packaging tasks
|
|
||||||
etc. So the remaining makefiles are legacy.
|
|
||||||
|
|||||||
@ -39,12 +39,6 @@ Code Style
|
|||||||
- This is mainly to be consistent with the rest of the project.
|
- This is mainly to be consistent with the rest of the project.
|
||||||
- Another reason is we will be able to check style automatically with a linter.
|
- Another reason is we will be able to check style automatically with a linter.
|
||||||
|
|
||||||
- You can check the style of the code by typing the following command at root folder.
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
make rcpplint
|
|
||||||
|
|
||||||
- When needed, you can disable the linter warning of certain line with ``// NOLINT(*)`` comments.
|
- When needed, you can disable the linter warning of certain line with ``// NOLINT(*)`` comments.
|
||||||
- We use `roxygen <https://cran.r-project.org/web/packages/roxygen2/vignettes/roxygen2.html>`_ for documenting the R package.
|
- We use `roxygen <https://cran.r-project.org/web/packages/roxygen2/vignettes/roxygen2.html>`_ for documenting the R package.
|
||||||
|
|
||||||
@ -79,6 +73,7 @@ The following steps are followed to add a new Rmarkdown vignettes:
|
|||||||
|
|
||||||
The reason we do this is to avoid exploded repo size due to generated images.
|
The reason we do this is to avoid exploded repo size due to generated images.
|
||||||
|
|
||||||
|
|
||||||
R package versioning
|
R package versioning
|
||||||
====================
|
====================
|
||||||
See :ref:`release`.
|
See :ref:`release`.
|
||||||
@ -89,6 +84,11 @@ According to `R extension manual <https://cran.r-project.org/doc/manuals/r-relea
|
|||||||
it is good practice to register native routines and to disable symbol search. When any changes or additions are made to the
|
it is good practice to register native routines and to disable symbol search. When any changes or additions are made to the
|
||||||
C++ interface of the R package, please make corresponding changes in ``src/init.c`` as well.
|
C++ interface of the R package, please make corresponding changes in ``src/init.c`` as well.
|
||||||
|
|
||||||
|
Generating the Package and Running Tests
|
||||||
|
========================================
|
||||||
|
|
||||||
|
The source layout of XGBoost is a bit unusual to normal R packages as XGBoost is primarily written in C++ with multiple language bindings in mind. As a result, some special cares need to be taken to generate a standard R tarball. Most of the tests are being run on CI, and as a result, the best way to see how things work is by looking at the CI configuration files (GitHub action, at the time of writing). There are helper scripts in ``tests/ci_build`` and ``R-package/tests/helper_scripts`` for running various checks including linter and making the standard tarball.
|
||||||
|
|
||||||
*********************************
|
*********************************
|
||||||
Running Formatting Checks Locally
|
Running Formatting Checks Locally
|
||||||
*********************************
|
*********************************
|
||||||
|
|||||||
@ -6,12 +6,13 @@ from multiprocessing import Pool, cpu_count
|
|||||||
from typing import Dict, Tuple
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
from pylint import epylint
|
from pylint import epylint
|
||||||
from test_utils import DirectoryExcursion
|
from test_utils import DirectoryExcursion, print_time, record_time
|
||||||
|
|
||||||
CURDIR = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
|
CURDIR = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
|
||||||
PROJECT_ROOT = os.path.normpath(os.path.join(CURDIR, os.path.pardir, os.path.pardir))
|
PROJECT_ROOT = os.path.normpath(os.path.join(CURDIR, os.path.pardir, os.path.pardir))
|
||||||
|
|
||||||
|
|
||||||
|
@record_time
|
||||||
def run_black(rel_path: str) -> bool:
|
def run_black(rel_path: str) -> bool:
|
||||||
cmd = ["black", "-q", "--check", rel_path]
|
cmd = ["black", "-q", "--check", rel_path]
|
||||||
ret = subprocess.run(cmd).returncode
|
ret = subprocess.run(cmd).returncode
|
||||||
@ -27,10 +28,12 @@ Please run the following command on your machine to address the formatting error
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@record_time
|
||||||
def run_isort(rel_path: str) -> bool:
|
def run_isort(rel_path: str) -> bool:
|
||||||
cmd = ["isort", "--check", "--profile=black", rel_path]
|
cmd = ["isort", "--check", "--profile=black", rel_path]
|
||||||
ret = subprocess.run(cmd).returncode
|
ret = subprocess.run(cmd).returncode
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
|
subprocess.run(["isort", "--version"])
|
||||||
msg = """
|
msg = """
|
||||||
Please run the following command on your machine to address the formatting error:
|
Please run the following command on your machine to address the formatting error:
|
||||||
|
|
||||||
@ -41,6 +44,7 @@ Please run the following command on your machine to address the formatting error
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@record_time
|
||||||
def run_mypy(rel_path: str) -> bool:
|
def run_mypy(rel_path: str) -> bool:
|
||||||
with DirectoryExcursion(os.path.join(PROJECT_ROOT, "python-package")):
|
with DirectoryExcursion(os.path.join(PROJECT_ROOT, "python-package")):
|
||||||
path = os.path.join(PROJECT_ROOT, rel_path)
|
path = os.path.join(PROJECT_ROOT, rel_path)
|
||||||
@ -117,17 +121,13 @@ class PyLint:
|
|||||||
return nerr == 0
|
return nerr == 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
@record_time
|
||||||
parser = argparse.ArgumentParser(
|
def run_pylint() -> bool:
|
||||||
description=(
|
return PyLint()()
|
||||||
"Run static checkers for XGBoost, see `python_lint.yml' "
|
|
||||||
"conda env file for a list of dependencies."
|
|
||||||
)
|
@record_time
|
||||||
)
|
def main(args: argparse.Namespace) -> None:
|
||||||
parser.add_argument("--format", type=int, choices=[0, 1], default=1)
|
|
||||||
parser.add_argument("--type-check", type=int, choices=[0, 1], default=1)
|
|
||||||
parser.add_argument("--pylint", type=int, choices=[0, 1], default=1)
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.format == 1:
|
if args.format == 1:
|
||||||
black_results = [
|
black_results = [
|
||||||
run_black(path)
|
run_black(path)
|
||||||
@ -148,6 +148,8 @@ if __name__ == "__main__":
|
|||||||
"tests/python/test_quantile_dmatrix.py",
|
"tests/python/test_quantile_dmatrix.py",
|
||||||
"tests/python-gpu/test_gpu_data_iterator.py",
|
"tests/python-gpu/test_gpu_data_iterator.py",
|
||||||
"tests/ci_build/lint_python.py",
|
"tests/ci_build/lint_python.py",
|
||||||
|
"tests/ci_build/test_r_package.py",
|
||||||
|
"tests/ci_build/test_utils.py",
|
||||||
"tests/test_distributed/test_with_spark/",
|
"tests/test_distributed/test_with_spark/",
|
||||||
"tests/test_distributed/test_gpu_with_spark/",
|
"tests/test_distributed/test_gpu_with_spark/",
|
||||||
# demo
|
# demo
|
||||||
@ -177,7 +179,7 @@ if __name__ == "__main__":
|
|||||||
"doc/",
|
"doc/",
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
if not all(black_results):
|
if not all(isort_results):
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
if args.type_check == 1:
|
if args.type_check == 1:
|
||||||
@ -194,6 +196,8 @@ if __name__ == "__main__":
|
|||||||
"tests/python/test_data_iterator.py",
|
"tests/python/test_data_iterator.py",
|
||||||
"tests/python-gpu/test_gpu_data_iterator.py",
|
"tests/python-gpu/test_gpu_data_iterator.py",
|
||||||
"tests/ci_build/lint_python.py",
|
"tests/ci_build/lint_python.py",
|
||||||
|
"tests/ci_build/test_r_package.py",
|
||||||
|
"tests/ci_build/test_utils.py",
|
||||||
"tests/test_distributed/test_with_spark/test_data.py",
|
"tests/test_distributed/test_with_spark/test_data.py",
|
||||||
"tests/test_distributed/test_gpu_with_spark/test_data.py",
|
"tests/test_distributed/test_gpu_with_spark/test_data.py",
|
||||||
"tests/test_distributed/test_gpu_with_dask/test_gpu_with_dask.py",
|
"tests/test_distributed/test_gpu_with_dask/test_gpu_with_dask.py",
|
||||||
@ -202,5 +206,22 @@ if __name__ == "__main__":
|
|||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
if args.pylint == 1:
|
if args.pylint == 1:
|
||||||
if not PyLint()():
|
if not run_pylint():
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=(
|
||||||
|
"Run static checkers for XGBoost, see `python_lint.yml' "
|
||||||
|
"conda env file for a list of dependencies."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
parser.add_argument("--format", type=int, choices=[0, 1], default=1)
|
||||||
|
parser.add_argument("--type-check", type=int, choices=[0, 1], default=1)
|
||||||
|
parser.add_argument("--pylint", type=int, choices=[0, 1], default=1)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
main(args)
|
||||||
|
finally:
|
||||||
|
print_time()
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# To be called when R package tests have failed
|
|
||||||
|
|
||||||
set -e
|
|
||||||
set -x
|
|
||||||
|
|
||||||
flag="$1"
|
|
||||||
|
|
||||||
if [ -f "xgboost.Rcheck/00install.out" ]; then
|
|
||||||
echo "===== xgboost.Rcheck/00install.out ===="
|
|
||||||
cat xgboost.Rcheck/00install.out
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -f "xgboost.Rcheck/00check.log" ]; then
|
|
||||||
printf "\n\n===== xgboost.Rcheck/00check.log ====\n"
|
|
||||||
cat xgboost.Rcheck/00check.log
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$flag" == "fail" ]]
|
|
||||||
then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@ -1,95 +1,315 @@
|
|||||||
|
"""Utilities for packaging R code and running tests."""
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
from time import time
|
from pathlib import Path
|
||||||
|
from platform import system
|
||||||
|
|
||||||
from test_utils import DirectoryExcursion
|
from test_utils import DirectoryExcursion, cd, print_time, record_time
|
||||||
|
|
||||||
ROOT = os.path.normpath(
|
ROOT = os.path.normpath(
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir,
|
os.path.join(
|
||||||
os.path.pardir))
|
os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir
|
||||||
r_package = os.path.join(ROOT, 'R-package')
|
)
|
||||||
|
)
|
||||||
|
r_package = os.path.join(ROOT, "R-package")
|
||||||
|
|
||||||
|
|
||||||
def get_mingw_bin():
|
def get_mingw_bin() -> str:
|
||||||
return os.path.join('c:/rtools40/mingw64/', 'bin')
|
return os.path.join("c:/rtools40/mingw64/", "bin")
|
||||||
|
|
||||||
|
|
||||||
def test_with_autotools(args):
|
@cd(ROOT)
|
||||||
with DirectoryExcursion(r_package):
|
@record_time
|
||||||
|
def pack_rpackage() -> Path:
|
||||||
|
"""Compose the directory used for creating R package tar ball."""
|
||||||
|
dest = Path("xgboost")
|
||||||
|
|
||||||
|
def pkgroot(path: str) -> None:
|
||||||
|
"""Change makefiles according to the package layout."""
|
||||||
|
with open(Path("R-package") / "src" / path, "r") as fd:
|
||||||
|
makefile = fd.read()
|
||||||
|
makefile = makefile.replace("PKGROOT=../../", "PKGROOT=.", 1)
|
||||||
|
with open(dest / "src" / path, "w") as fd:
|
||||||
|
fd.write(makefile)
|
||||||
|
|
||||||
|
output = subprocess.run(["git", "clean", "-xdf", "--dry-run"], capture_output=True)
|
||||||
|
if output.returncode != 0:
|
||||||
|
raise ValueError("Failed to check git repository status.", output)
|
||||||
|
would_remove = output.stdout.decode("utf-8").strip().split("\n")
|
||||||
|
|
||||||
|
if would_remove and not all(f.find("tests/ci_build") != -1 for f in would_remove):
|
||||||
|
raise ValueError(
|
||||||
|
"\n".join(would_remove) + "\nPlease cleanup the working git repository."
|
||||||
|
)
|
||||||
|
|
||||||
|
shutil.copytree("R-package", dest)
|
||||||
|
os.remove(dest / "demo" / "runall.R")
|
||||||
|
# core
|
||||||
|
shutil.copytree("src", dest / "src" / "src")
|
||||||
|
shutil.copytree("include", dest / "src" / "include")
|
||||||
|
shutil.copytree("amalgamation", dest / "src" / "amalgamation")
|
||||||
|
# rabit
|
||||||
|
rabit = Path("rabit")
|
||||||
|
os.mkdir(dest / "src" / rabit)
|
||||||
|
shutil.copytree(rabit / "src", dest / "src" / "rabit" / "src")
|
||||||
|
shutil.copytree(rabit / "include", dest / "src" / "rabit" / "include")
|
||||||
|
# dmlc-core
|
||||||
|
dmlc_core = Path("dmlc-core")
|
||||||
|
os.mkdir(dest / "src" / dmlc_core)
|
||||||
|
shutil.copytree(dmlc_core / "include", dest / "src" / "dmlc-core" / "include")
|
||||||
|
shutil.copytree(dmlc_core / "src", dest / "src" / "dmlc-core" / "src")
|
||||||
|
# makefile & license
|
||||||
|
shutil.copyfile("LICENSE", dest / "LICENSE")
|
||||||
|
osxmakef = dest / "src" / "Makevars.win-e"
|
||||||
|
if os.path.exists(osxmakef):
|
||||||
|
os.remove(osxmakef)
|
||||||
|
pkgroot("Makevars.in")
|
||||||
|
pkgroot("Makevars.win")
|
||||||
|
# misc
|
||||||
|
rwsp = Path("R-package") / "remove_warning_suppression_pragma.sh"
|
||||||
|
if system() != "Windows":
|
||||||
|
subprocess.check_call(rwsp)
|
||||||
|
rwsp = dest / "remove_warning_suppression_pragma.sh"
|
||||||
|
if system() != "Windows":
|
||||||
|
subprocess.check_call(rwsp)
|
||||||
|
os.remove(rwsp)
|
||||||
|
os.remove(dest / "CMakeLists.txt")
|
||||||
|
shutil.rmtree(dest / "tests" / "helper_scripts")
|
||||||
|
return dest
|
||||||
|
|
||||||
|
|
||||||
|
@cd(ROOT)
|
||||||
|
@record_time
|
||||||
|
def build_rpackage(path: str) -> str:
|
||||||
|
def find_tarball() -> str:
|
||||||
|
found = []
|
||||||
|
for root, subdir, files in os.walk("."):
|
||||||
|
for f in files:
|
||||||
|
if f.endswith(".tar.gz") and f.startswith("xgboost"):
|
||||||
|
found.append(os.path.join(root, f))
|
||||||
|
if not found:
|
||||||
|
raise ValueError("Failed to find output tar ball.")
|
||||||
|
if len(found) > 1:
|
||||||
|
raise ValueError("Found more than one packages:", found)
|
||||||
|
return found[0]
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
print("Ncpus:", f"{os.cpu_count()}")
|
||||||
|
env.update({"MAKEFLAGS": f"-j{os.cpu_count()}"})
|
||||||
|
subprocess.check_call([R, "CMD", "build", path], env=env)
|
||||||
|
|
||||||
|
tarball = find_tarball()
|
||||||
|
return tarball
|
||||||
|
|
||||||
|
|
||||||
|
@cd(ROOT)
|
||||||
|
@record_time
|
||||||
|
def check_rpackage(path: str) -> None:
|
||||||
|
env = os.environ.copy()
|
||||||
|
print("Ncpus:", f"{os.cpu_count()}")
|
||||||
|
env.update(
|
||||||
|
{
|
||||||
|
"MAKEFLAGS": f"-j{os.cpu_count()}",
|
||||||
|
# cran specific environment variables
|
||||||
|
"_R_CHECK_EXAMPLE_TIMING_CPU_TO_ELAPSED_THRESHOLD_": str(2.5),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Actually we don't run this check on windows due to dependency issue.
|
||||||
|
if system() == "Windows":
|
||||||
|
# make sure compiler from rtools is used.
|
||||||
mingw_bin = get_mingw_bin()
|
mingw_bin = get_mingw_bin()
|
||||||
CXX = os.path.join(mingw_bin, 'g++.exe')
|
CXX = os.path.join(mingw_bin, "g++.exe")
|
||||||
CC = os.path.join(mingw_bin, 'gcc.exe')
|
CC = os.path.join(mingw_bin, "gcc.exe")
|
||||||
cmd = ['R.exe', 'CMD', 'INSTALL', str(os.path.curdir)]
|
env.update({"CC": CC, "CXX": CXX})
|
||||||
env = os.environ.copy()
|
|
||||||
env.update({'CC': CC, 'CXX': CXX, "MAKE": "make -j$(nproc)"})
|
status = subprocess.run([R, "CMD", "check", "--as-cran", path], env=env)
|
||||||
subprocess.check_call(cmd, env=env)
|
with open(Path("xgboost.Rcheck") / "00check.log", "r") as fd:
|
||||||
subprocess.check_call([
|
check_log = fd.read()
|
||||||
'R.exe', '-q', '-e',
|
|
||||||
"library(testthat); setwd('tests'); source('testthat.R')"
|
with open(Path("xgboost.Rcheck") / "00install.out", "r") as fd:
|
||||||
])
|
install_log = fd.read()
|
||||||
subprocess.check_call([
|
|
||||||
'R.exe', '-q', '-e',
|
msg = f"""
|
||||||
"demo(runall, package = 'xgboost')"
|
----------------------- Install ----------------------
|
||||||
])
|
{install_log}
|
||||||
|
|
||||||
|
----------------------- Check -----------------------
|
||||||
|
{check_log}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if status.returncode != 0:
|
||||||
|
print(msg)
|
||||||
|
raise ValueError("Failed r package check.")
|
||||||
|
|
||||||
|
if check_log.find("WARNING") != -1:
|
||||||
|
print(msg)
|
||||||
|
raise ValueError("Has unresolved warnings.")
|
||||||
|
if check_log.find("Examples with CPU time") != -1:
|
||||||
|
print(msg)
|
||||||
|
raise ValueError("Suspicious NOTE.")
|
||||||
|
|
||||||
|
|
||||||
def test_with_cmake(args):
|
@cd(r_package)
|
||||||
os.mkdir('build')
|
@record_time
|
||||||
with DirectoryExcursion('build'):
|
def check_rmarkdown() -> None:
|
||||||
if args.compiler == 'mingw':
|
assert system() != "Windows", "Document test doesn't support Windows."
|
||||||
|
env = os.environ.copy()
|
||||||
|
env.update({"MAKEFLAGS": f"-j{os.cpu_count()}"})
|
||||||
|
print("Checking R document with devtools.")
|
||||||
|
bin_dir = os.path.dirname(R)
|
||||||
|
rscript = os.path.join(bin_dir, "Rscript")
|
||||||
|
subprocess.check_call([rscript, "-e", "devtools::document()"], env=env)
|
||||||
|
output = subprocess.run(["git", "diff", "--name-only"], capture_output=True)
|
||||||
|
if len(output.stdout.decode("utf-8").strip()) != 0:
|
||||||
|
raise ValueError("Please run `devtools::document()`.")
|
||||||
|
|
||||||
|
|
||||||
|
@cd(r_package)
|
||||||
|
@record_time
|
||||||
|
def test_with_autotools() -> None:
|
||||||
|
"""Windows only test. No `--as-cran` check, only unittests. We don't want to manage
|
||||||
|
the dependencies on Windows machine.
|
||||||
|
|
||||||
|
"""
|
||||||
|
assert system() == "Windows"
|
||||||
|
mingw_bin = get_mingw_bin()
|
||||||
|
CXX = os.path.join(mingw_bin, "g++.exe")
|
||||||
|
CC = os.path.join(mingw_bin, "gcc.exe")
|
||||||
|
cmd = [R, "CMD", "INSTALL", str(os.path.curdir)]
|
||||||
|
env = os.environ.copy()
|
||||||
|
env.update({"CC": CC, "CXX": CXX, "MAKEFLAGS": f"-j{os.cpu_count()}"})
|
||||||
|
subprocess.check_call(cmd, env=env)
|
||||||
|
subprocess.check_call(
|
||||||
|
["R.exe", "-q", "-e", "library(testthat); setwd('tests'); source('testthat.R')"]
|
||||||
|
)
|
||||||
|
subprocess.check_call(["R.exe", "-q", "-e", "demo(runall, package = 'xgboost')"])
|
||||||
|
|
||||||
|
|
||||||
|
@record_time
|
||||||
|
def test_with_cmake(args: argparse.Namespace) -> None:
|
||||||
|
os.mkdir("build")
|
||||||
|
with DirectoryExcursion("build"):
|
||||||
|
if args.compiler == "mingw":
|
||||||
mingw_bin = get_mingw_bin()
|
mingw_bin = get_mingw_bin()
|
||||||
CXX = os.path.join(mingw_bin, 'g++.exe')
|
CXX = os.path.join(mingw_bin, "g++.exe")
|
||||||
CC = os.path.join(mingw_bin, 'gcc.exe')
|
CC = os.path.join(mingw_bin, "gcc.exe")
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env.update({'CC': CC, 'CXX': CXX})
|
env.update({"CC": CC, "CXX": CXX})
|
||||||
subprocess.check_call([
|
subprocess.check_call(
|
||||||
'cmake', os.path.pardir, '-DUSE_OPENMP=ON', '-DR_LIB=ON',
|
[
|
||||||
'-DCMAKE_CONFIGURATION_TYPES=Release', '-G', 'Unix Makefiles',
|
"cmake",
|
||||||
],
|
os.path.pardir,
|
||||||
env=env)
|
"-DUSE_OPENMP=ON",
|
||||||
subprocess.check_call(['make', '-j', 'install'])
|
"-DR_LIB=ON",
|
||||||
elif args.compiler == 'msvc':
|
"-DCMAKE_CONFIGURATION_TYPES=Release",
|
||||||
subprocess.check_call([
|
"-G",
|
||||||
'cmake', os.path.pardir, '-DUSE_OPENMP=ON', '-DR_LIB=ON',
|
"Unix Makefiles",
|
||||||
'-DCMAKE_CONFIGURATION_TYPES=Release', '-A', 'x64'
|
],
|
||||||
])
|
env=env,
|
||||||
subprocess.check_call([
|
)
|
||||||
'cmake', '--build', os.path.curdir, '--target', 'install',
|
subprocess.check_call(["make", "-j", "install"])
|
||||||
'--config', 'Release'
|
elif args.compiler == "msvc":
|
||||||
])
|
subprocess.check_call(
|
||||||
|
[
|
||||||
|
"cmake",
|
||||||
|
os.path.pardir,
|
||||||
|
"-DUSE_OPENMP=ON",
|
||||||
|
"-DR_LIB=ON",
|
||||||
|
"-DCMAKE_CONFIGURATION_TYPES=Release",
|
||||||
|
"-A",
|
||||||
|
"x64",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
subprocess.check_call(
|
||||||
|
[
|
||||||
|
"cmake",
|
||||||
|
"--build",
|
||||||
|
os.path.curdir,
|
||||||
|
"--target",
|
||||||
|
"install",
|
||||||
|
"--config",
|
||||||
|
"Release",
|
||||||
|
]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError('Wrong compiler')
|
raise ValueError("Wrong compiler")
|
||||||
with DirectoryExcursion(r_package):
|
with DirectoryExcursion(r_package):
|
||||||
subprocess.check_call([
|
subprocess.check_call(
|
||||||
'R.exe', '-q', '-e',
|
[
|
||||||
"library(testthat); setwd('tests'); source('testthat.R')"
|
R,
|
||||||
])
|
"-q",
|
||||||
subprocess.check_call([
|
"-e",
|
||||||
'R.exe', '-q', '-e',
|
"library(testthat); setwd('tests'); source('testthat.R')",
|
||||||
"demo(runall, package = 'xgboost')"
|
]
|
||||||
])
|
)
|
||||||
|
subprocess.check_call([R, "-q", "-e", "demo(runall, package = 'xgboost')"])
|
||||||
|
|
||||||
|
|
||||||
|
@record_time
|
||||||
def main(args: argparse.Namespace) -> None:
|
def main(args: argparse.Namespace) -> None:
|
||||||
start = time()
|
if args.task == "build":
|
||||||
if args.build_tool == 'autotools':
|
src_dir = pack_rpackage()
|
||||||
test_with_autotools(args)
|
build_rpackage(src_dir)
|
||||||
|
elif args.task == "doc":
|
||||||
|
check_rmarkdown()
|
||||||
|
elif args.task == "check":
|
||||||
|
if args.build_tool == "autotools" and system() != "Windows":
|
||||||
|
src_dir = pack_rpackage()
|
||||||
|
tarball = build_rpackage(src_dir)
|
||||||
|
check_rpackage(tarball)
|
||||||
|
elif args.build_tool == "autotools":
|
||||||
|
test_with_autotools()
|
||||||
|
else:
|
||||||
|
test_with_cmake(args)
|
||||||
else:
|
else:
|
||||||
test_with_cmake(args)
|
raise ValueError("Unexpected task.")
|
||||||
print("Duration:", time() - start)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser(
|
||||||
parser.add_argument('--compiler',
|
description=(
|
||||||
type=str,
|
"Helper script for making R package and running R tests on CI. There are"
|
||||||
choices=['mingw', 'msvc'],
|
" also other helper scripts in the R tests directory for installing"
|
||||||
help='Compiler used for compiling CXX code.')
|
" dependencies and running linter."
|
||||||
|
)
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--build-tool',
|
"--task",
|
||||||
type=str,
|
type=str,
|
||||||
choices=['cmake', 'autotools'],
|
choices=["build", "check", "doc"],
|
||||||
help='Build tool for compiling CXX code and install R package.')
|
default="check",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--compiler",
|
||||||
|
type=str,
|
||||||
|
choices=["mingw", "msvc"],
|
||||||
|
help="Compiler used for compiling CXX code. Only relevant for windows build",
|
||||||
|
default="mingw",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--build-tool",
|
||||||
|
type=str,
|
||||||
|
choices=["cmake", "autotools"],
|
||||||
|
help="Build tool for compiling CXX code and install R package.",
|
||||||
|
default="autotools",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--r",
|
||||||
|
type=str,
|
||||||
|
default="R" if system() != "Windows" else "R.exe",
|
||||||
|
help="Path to the R executable.",
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
main(args)
|
R = args.r
|
||||||
|
|
||||||
|
try:
|
||||||
|
main(args)
|
||||||
|
finally:
|
||||||
|
print_time()
|
||||||
|
|||||||
@ -1,14 +1,72 @@
|
|||||||
|
"""Utilities for the CI."""
|
||||||
import os
|
import os
|
||||||
from typing import Union
|
from datetime import datetime, timedelta
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Any, Callable, Dict, TypedDict, TypeVar, Union
|
||||||
|
|
||||||
|
|
||||||
class DirectoryExcursion:
|
class DirectoryExcursion:
|
||||||
def __init__(self, path: Union[os.PathLike, str]):
|
def __init__(self, path: Union[os.PathLike, str]) -> None:
|
||||||
self.path = path
|
self.path = path
|
||||||
self.curdir = os.path.normpath(os.path.abspath(os.path.curdir))
|
self.curdir = os.path.normpath(os.path.abspath(os.path.curdir))
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self) -> None:
|
||||||
os.chdir(self.path)
|
os.chdir(self.path)
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args: Any) -> None:
|
||||||
os.chdir(self.curdir)
|
os.chdir(self.curdir)
|
||||||
|
|
||||||
|
|
||||||
|
R = TypeVar("R")
|
||||||
|
|
||||||
|
|
||||||
|
def cd(path: Union[os.PathLike, str]) -> Callable:
|
||||||
|
"""Decorator for changing directory temporarily."""
|
||||||
|
|
||||||
|
def chdir(func: Callable[..., R]) -> Callable[..., R]:
|
||||||
|
@wraps(func)
|
||||||
|
def inner(*args: Any, **kwargs: Any) -> R:
|
||||||
|
with DirectoryExcursion(path):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
return chdir
|
||||||
|
|
||||||
|
|
||||||
|
Record = TypedDict("Record", {"count": int, "total": timedelta})
|
||||||
|
timer: Dict[str, Record] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def record_time(func: Callable[..., R]) -> Callable[..., R]:
|
||||||
|
"""Decorator for recording function runtime."""
|
||||||
|
global timer
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def inner(*args: Any, **kwargs: Any) -> R:
|
||||||
|
if func.__name__ not in timer:
|
||||||
|
timer[func.__name__] = {"count": 0, "total": timedelta(0)}
|
||||||
|
s = datetime.now()
|
||||||
|
try:
|
||||||
|
r = func(*args, **kwargs)
|
||||||
|
finally:
|
||||||
|
e = datetime.now()
|
||||||
|
timer[func.__name__]["count"] += 1
|
||||||
|
timer[func.__name__]["total"] += e - s
|
||||||
|
return r
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
def print_time() -> None:
|
||||||
|
"""Print all recorded items by :py:func:`record_time`."""
|
||||||
|
global timer
|
||||||
|
for k, v in timer.items():
|
||||||
|
print(
|
||||||
|
"Name:",
|
||||||
|
k,
|
||||||
|
"Called:",
|
||||||
|
v["count"],
|
||||||
|
"Elapsed:",
|
||||||
|
f"{v['total'].seconds} secs",
|
||||||
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user