Update CI with testing framework from eigen_ci_cross_testing.

Refactors the testing scripts and adds more coverage.
diff --git a/.gitignore b/.gitignore
index 19dfac9..369ae25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,7 @@
 core.*
 *.bak
 *~
-*build*
+*.build*
 *.moc.*
 *.moc
 ui_*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e5a3c00..6096b67 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,7 @@
 # This file is part of Eigen, a lightweight C++ template library
 # for linear algebra.
 #
-# Copyright (C) 2020 Arm Ltd. and Contributors
+# Copyright (C) 2023, The Eigen Authors
 #
 # This Source Code Form is subject to the terms of the Mozilla
 # Public License v. 2.0. If a copy of the MPL was not distributed
@@ -14,10 +14,21 @@
   - test
 
 variables:
-  BUILDDIR: builddir
-  EIGEN_CI_CMAKE_GENEATOR: "Ninja"
+  # Regular expression for controlling which jobs are run.
+  EIGEN_CI_JOB_REGEX: ""
+  # CMake build directory.
+  EIGEN_CI_BUILDDIR: .build
+  # Specify the CMake build target.
+  EIGEN_CI_BUILD_TARGET: ""
+  # If a test regex is specified, that will be selected.
+  # Otherwise, we will try a label if specified.
+  EIGEN_CI_TEST_REGEX: ""
+  EIGEN_CI_TEST_LABEL: ""
 
 include:
-  - "/ci/smoketests.gitlab-ci.yml"
-  - "/ci/build.gitlab-ci.yml"
-  - "/ci/test.gitlab-ci.yml"
+  - "/ci/common.gitlab-ci.yml"
+  - "/ci/build.linux.gitlab-ci.yml"
+  - "/ci/build.windows.gitlab-ci.yml"
+  - "/ci/test.linux.gitlab-ci.yml"
+  - "/ci/test.windows.gitlab-ci.yml"
+  - "/ci/smoketests.linux.gitlab-ci.yml"
diff --git a/ci/build.gitlab-ci.yml b/ci/build.gitlab-ci.yml
deleted file mode 100644
index 073212a..0000000
--- a/ci/build.gitlab-ci.yml
+++ /dev/null
@@ -1,141 +0,0 @@
-.build:linux:base:
-  stage: build
-  image: ubuntu:18.04
-  before_script:
-    - apt-get update -y
-    - apt-get install -y --no-install-recommends software-properties-common
-    - add-apt-repository -y  ppa:ubuntu-toolchain-r/test
-    - apt-get update
-    - apt-get install --no-install-recommends -y ${EIGEN_CI_CXX_COMPILER}
-      ${EIGEN_CI_CC_COMPILER} cmake ninja-build
-  script:
-    - mkdir -p ${BUILDDIR} && cd ${BUILDDIR}
-    - CXX=${EIGEN_CI_CXX_COMPILER} CC=${EIGEN_CI_CC_COMPILER} cmake -G
-      ${EIGEN_CI_CMAKE_GENEATOR} -DEIGEN_TEST_CXX11=${EIGEN_TEST_CXX11}
-      ${EIGEN_CI_ADDITIONAL_ARGS} ..
-    - cmake --build . --target buildtests
-  artifacts:
-    name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
-    paths:
-      - ${BUILDDIR}/
-    expire_in: 5 days
-  only:
-    - schedules
-
-######## x86-64 ################################################################
-# # GCC-4.8 (the oldest compiler we support)
-# build:x86-64:linux:gcc-4.8:cxx11-on:
-#   extends: .build:linux:base
-#   variables:
-#     EIGEN_CI_CXX_COMPILER: "g++-4.8"
-#     EIGEN_CI_CC_COMPILER: "gcc-4.8"
-#     EIGEN_TEST_CXX11: "on"
-#   tags:
-#     - eigen-runner
-#     - linux
-#     - x86-64
-
-# GCC-9
-build:x86-64:linux:gcc-9:cxx11-on:
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "g++-9"
-    EIGEN_CI_CC_COMPILER: "gcc-9"
-    EIGEN_TEST_CXX11: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-
-# GCC-10
-build:x86-64:linux:gcc-10:cxx11-on:
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "g++-10"
-    EIGEN_CI_CC_COMPILER: "gcc-10"
-    EIGEN_TEST_CXX11: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-
-# Clang-10
-build:x86-64:linux:clang-10:cxx11-on:
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "clang++-10"
-    EIGEN_CI_CC_COMPILER: "clang-10"
-    EIGEN_TEST_CXX11: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-
-# Clang-10, AVX512
-build:x86-64:linux:clang-10:cxx11-on:avx512:
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "clang++-10"
-    EIGEN_CI_CC_COMPILER: "clang-10"
-    EIGEN_TEST_CXX11: "on"
-    EIGEN_TEST_AVX512DQ: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-    - avx512
-
-######## AArch64 ###############################################################
-# GCC-10
-build:aarch64:linux:gcc-10:cxx11-on:
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "g++-10"
-    EIGEN_CI_CC_COMPILER: "gcc-10"
-    EIGEN_TEST_CXX11: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - aarch64
-
-# # Clang-10
-build:aarch64:linux:clang-10:cxx11-on:
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "clang++-10"
-    EIGEN_CI_CC_COMPILER: "clang-10"
-    EIGEN_TEST_CXX11: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - aarch64
-
-######## ppc64le ###############################################################
-# Currently all ppc64le jobs are allowed to fail
-
-# GCC-10
-build:ppc64le:linux:gcc-10:cxx11-on:
-  allow_failure: true
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "g++-10"
-    EIGEN_CI_CC_COMPILER: "gcc-10"
-    EIGEN_TEST_CXX11: "on"
-    EIGEN_CI_ADDITIONAL_ARGS: "-DCMAKE_CXX_FLAGS='-DEIGEN_ALTIVEC_DISABLE_MMA'"
-  tags:
-    - eigen-runner
-    - linux
-    - ppc64le
-
-# Clang-10
-build:ppc64le:linux:clang-10:cxx11-on:
-  allow_failure: true
-  extends: .build:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "clang++-10"
-    EIGEN_CI_CC_COMPILER: "clang-10"
-    EIGEN_TEST_CXX11: "on"
-  tags:
-    - eigen-runner
-    - linux
-    - ppc64le
diff --git a/ci/build.linux.gitlab-ci.yml b/ci/build.linux.gitlab-ci.yml
new file mode 100644
index 0000000..49fcdbf
--- /dev/null
+++ b/ci/build.linux.gitlab-ci.yml
@@ -0,0 +1,306 @@
+# Base configuration for linux cross-compilation.
+.build:linux:cross:
+  extends: .common:linux:cross
+  stage: build
+  variables:
+    EIGEN_CI_BUILD_TARGET: buildtests
+  script:
+    - . ci/scripts/build.linux.script.sh
+  tags:
+    - linux
+    - eigen-runner
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+    - if: $CI_PIPELINE_SOURCE == "web"
+  cache:
+    key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG-BUILD"
+    paths:
+      - ${EIGEN_CI_BUILDDIR}/
+
+######## x86-64 ################################################################
+
+.build:linux:cross:x86-64:
+  extends: .build:linux:cross
+  variables:
+    EIGEN_CI_TARGET_ARCH: x86_64
+    EIGEN_CI_CROSS_TARGET_TRIPLE: x86_64-linux-gnu
+
+# GCC-6 (minimum on Ubuntu 18.04)
+build:linux:cross:x86-64:gcc-6:default:
+  extends: .build:linux:cross:x86-64
+  image: ubuntu:18.04
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-6
+    EIGEN_CI_CXX_COMPILER: g++-6
+    EIGEN_CI_CROSS_INSTALL: g++-6-x86-64-linux-gnu
+    EIGEN_CI_CROSS_C_COMPILER: x86_64-linux-gnu-gcc-6
+    EIGEN_CI_CROSS_CXX_COMPILER: x86_64-linux-gnu-g++-6
+
+# GCC-10 (stable recent version)
+build:linux:cross:x86-64:gcc-10:default:
+  extends: .build:linux:cross:x86-64
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-10
+    EIGEN_CI_CXX_COMPILER: g++-10
+    EIGEN_CI_CROSS_INSTALL: g++-10-x86-64-linux-gnu
+    EIGEN_CI_CROSS_C_COMPILER: x86_64-linux-gnu-gcc-10
+    EIGEN_CI_CROSS_CXX_COMPILER: x86_64-linux-gnu-g++-10
+
+build:linux:cross:x86-64:gcc-10:avx2:
+  extends: build:linux:cross:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_TEST_AVX2=on"
+    
+build:linux:cross:x86-64:gcc-10:avx512dq:
+  extends: build:linux:cross:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_TEST_AVX512DQ=on"
+
+# Clang-6 (minimum on Ubuntu 20.04)
+build:linux:cross:x86-64:clang-6:default:
+  extends: .build:linux:cross:x86-64
+  image: ubuntu:20.04
+  variables:
+    EIGEN_CI_INSTALL: clang-6.0 lld-6.0
+    EIGEN_CI_C_COMPILER: clang-6.0
+    EIGEN_CI_CXX_COMPILER: clang++-6.0
+    EIGEN_CI_CROSS_INSTALL: g++-8-x86-64-linux-gnu clang-6.0 lld-6.0
+    EIGEN_CI_ADDITIONAL_ARGS: -DCMAKE_EXE_LINKER_FLAGS=-fuse-ld=lld-6.0
+
+# Clang-12 (stable recent version)
+build:linux:cross:x86-64:clang-12:default:
+  extends: .build:linux:cross:x86-64
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+    EIGEN_CI_C_COMPILER: clang-12
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_CROSS_INSTALL: g++-10-x86-64-linux-gnu clang-12
+
+build:linux:cross:x86-64:clang-12:avx2:
+  extends: build:linux:cross:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_TEST_AVX2=on"
+    
+build:linux:cross:x86-64:clang-12:avx512dq:
+  extends: build:linux:cross:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_TEST_AVX512DQ=on"
+    
+# # Sanitizers (Disabled because ASAN hangs and MSAN requires instrumented libc++)
+# build:linux:cross:x86-64:clang-12:default:asan:
+#   extends: build:linux:cross:x86-64:clang-12:default
+#   variables:
+#     EIGEN_CI_ADDITIONAL_ARGS: 
+#       -DEIGEN_TEST_CUSTOM_CXX_FLAGS=-fsanitize=address,undefined
+#       -DEIGEN_TEST_CUSTOM_LINKER_FLAGS=-fsanitize=address,undefined
+
+# build:linux:cross:x86-64:clang-12:default:msan:
+#   extends: build:linux:cross:x86-64:clang-12:default
+#   variables:
+#     EIGEN_CI_ADDITIONAL_ARGS: 
+#       -DEIGEN_TEST_CUSTOM_CXX_FLAGS=-fsanitize=memory
+#       -DEIGEN_TEST_CUSTOM_LINKER_FLAGS=-fsanitize=memory
+
+######## CUDA ##################################################################
+
+.build:linux:cuda:
+  extends: .build:linux:cross:x86-64
+  variables:
+    # Addtional flags passed to the cuda compiler.
+    EIGEN_CI_CUDA_CXX_FLAGS: ""
+    # Compute architectures present in the GitLab CI runners.
+    EIGEN_CI_CUDA_COMPUTE_ARCH: "50;75"
+    EIGEN_CI_BUILD_TARGET: buildtests_gpu
+    EIGEN_CI_TEST_CUDA_CLANG: "off"
+    EIGEN_CI_ADDITIONAL_ARGS:
+      -DEIGEN_TEST_CUDA=on 
+      -DEIGEN_CUDA_CXX_FLAGS=${EIGEN_CI_CUDA_CXX_FLAGS}
+      -DEIGEN_CUDA_COMPUTE_ARCH=${EIGEN_CI_CUDA_COMPUTE_ARCH}
+      -DEIGEN_TEST_CUDA_CLANG=${EIGEN_CI_TEST_CUDA_CLANG}
+  tags:
+    - eigen-runner
+    - linux
+    # Requires intel runner for cuda docker image support.
+    - x86-64
+
+# NVidia no longer provides docker images < CUDA 11.0.3.
+# # GCC-7, CUDA-9.2
+# build:linux:cuda-9.2:gcc-7:
+#   extends: .build:linux:cuda
+#   image: nvidia/cuda:9.2-devel-ubuntu18.04
+#   variables:
+#     # cuda 9.2 doesn't support sm_75, so lower to 70.
+#     EIGEN_CI_CUDA_COMPUTE_ARCH: "50;70"
+#     EIGEN_CI_C_COMPILER: gcc-7
+#     EIGEN_CI_CXX_COMPILER: g++-7
+
+# # Clang-10, CUDA-9.2
+# build:linux:cuda-9.2:clang-10:
+#   extends: build:linux:cuda-9.2:gcc-7
+#   variables:
+#     EIGEN_CI_C_COMPILER: clang-10
+#     EIGEN_CI_CXX_COMPILER: clang++-10
+#     EIGEN_CI_TEST_CUDA_CLANG: "on"
+
+# # GCC-8, CUDA-10.2
+# build:linux:cuda-10.2:gcc-8:
+#   extends: .build:linux:cuda
+#   image: nvidia/cuda:10.2-devel-ubuntu18.04
+#   variables:
+#     EIGEN_CI_C_COMPILER: gcc-8
+#     EIGEN_CI_CXX_COMPILER: g++-8
+
+# # Clang-10, CUDA-10.2
+# build:linux:cuda-10.2:clang-10:
+#   extends: build:linux:cuda-10.2:gcc-8
+#   variables:
+#     EIGEN_CI_C_COMPILER: clang-10
+#     EIGEN_CI_CXX_COMPILER: clang++-10
+#     EIGEN_CI_TEST_CUDA_CLANG: "on"
+    
+# GCC-10, CUDA-11.4
+build:linux:cuda-11.4:gcc-10:
+  extends: .build:linux:cuda
+  image: nvidia/cuda:11.4.3-devel-ubuntu20.04
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-10
+    EIGEN_CI_CXX_COMPILER: g++-10
+
+# Clang-12, CUDA-11.4
+build:linux:cuda-11.4:clang-12:
+  extends: build:linux:cuda-11.4:gcc-10
+  variables:
+    EIGEN_CI_C_COMPILER: clang-12
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_TEST_CUDA_CLANG: "on"
+
+# GCC-10, CUDA-12.2
+build:linux:cuda-12.2:gcc-10:
+  extends: .build:linux:cuda
+  image: nvidia/cuda:12.2.0-devel-ubuntu20.04
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-10
+    EIGEN_CI_CXX_COMPILER: g++-10
+
+# Clang-12, CUDA-12.2
+build:linux:cuda-12.2:clang-12:
+  extends: build:linux:cuda-12.2:gcc-10
+  variables:
+    EIGEN_CI_C_COMPILER: clang-12
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_TEST_CUDA_CLANG: "on"
+
+# ######## HIP ###################################################################
+# Note: these are currently build-only, until we get an AMD-supported runner.
+
+# ROCm HIP
+build:linux:rocm-latest:gcc-10:
+  extends: .build:linux:cross
+  image: rocm/dev-ubuntu-18.04:latest
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-10
+    EIGEN_CI_CXX_COMPILER: g++-10
+    EIGEN_CI_BUILD_TARGET: buildtests_gpu
+    EIGEN_CI_ADDITIONAL_ARGS: -DEIGEN_TEST_HIP=on
+  tags:
+    - eigen-runner
+    - linux
+    - x86-64
+
+######## arm ###################################################################
+    
+.build:linux:cross:arm:
+  extends: .build:linux:cross
+  variables:
+    EIGEN_CI_TARGET_ARCH: arm
+    EIGEN_CI_CROSS_TARGET_TRIPLE: arm-linux-gnueabihf
+    EIGEN_CI_ADDITIONAL_ARGS: >
+      -DEIGEN_TEST_CUSTOM_CXX_FLAGS=-march=armv7-a;-mfpu=neon-vfpv4
+
+build:linux:cross:arm:gcc-10:default:
+  extends: .build:linux:cross:arm
+  variables:
+    EIGEN_CI_CROSS_INSTALL: g++-10-arm-linux-gnueabihf
+    EIGEN_CI_CROSS_C_COMPILER: arm-linux-gnueabihf-gcc-10
+    EIGEN_CI_CROSS_CXX_COMPILER: arm-linux-gnueabihf-g++-10
+
+build:linux:cross:arm:clang-12:default:
+  extends: .build:linux:cross:arm
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+    EIGEN_CI_C_COMPILER: clang-12
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_CROSS_INSTALL: g++-10-arm-linux-gnueabihf clang-12
+
+######## aarch64 ###############################################################
+    
+.build:linux:cross:aarch64:
+  extends: .build:linux:cross
+  variables:
+    EIGEN_CI_TARGET_ARCH: aarch64
+    EIGEN_CI_CROSS_TARGET_TRIPLE: aarch64-linux-gnu
+    EIGEN_CI_ADDITIONAL_ARGS: -DEIGEN_TEST_CUSTOM_CXX_FLAGS=-march=armv8.2-a+fp16
+
+build:linux:cross:aarch64:gcc-10:default:
+  extends: .build:linux:cross:aarch64
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-10
+    EIGEN_CI_CXX_COMPILER: g++-10
+    EIGEN_CI_CROSS_INSTALL: g++-10-aarch64-linux-gnu
+    EIGEN_CI_CROSS_C_COMPILER: aarch64-linux-gnu-gcc-10
+    EIGEN_CI_CROSS_CXX_COMPILER: aarch64-linux-gnu-g++-10
+
+build:linux:cross:aarch64:clang-12:default:
+  extends: .build:linux:cross:aarch64
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+    EIGEN_CI_C_COMPILER: clang-12
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_CROSS_INSTALL: g++-10-aarch64-linux-gnu clang-12
+
+######## ppc64le ###############################################################
+
+.build:linux:cross:ppc64le:
+  extends: .build:linux:cross
+  variables:
+    EIGEN_CI_TARGET_ARCH: ppc64le
+    EIGEN_CI_CROSS_TARGET_TRIPLE: powerpc64le-linux-gnu
+
+build:linux:cross:ppc64le:gcc-10:default:
+  extends: .build:linux:cross:ppc64le
+  variables:
+    EIGEN_CI_C_COMPILER: gcc-10
+    EIGEN_CI_CXX_COMPILER: g++-10
+    EIGEN_CI_CROSS_INSTALL: g++-10-powerpc64le-linux-gnu
+    EIGEN_CI_CROSS_C_COMPILER: powerpc64le-linux-gnu-gcc-10
+    EIGEN_CI_CROSS_CXX_COMPILER: powerpc64le-linux-gnu-g++-10
+    # Temporarily disable MMA until #2457 is resolved.
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_ALTIVEC_DISABLE_MMA=1"
+
+build:linux:cross:ppc64le:clang-12:default:
+  extends: .build:linux:cross:ppc64le
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+    EIGEN_CI_C_COMPILER: clang-12
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_CROSS_INSTALL: g++-10-powerpc64le-linux-gnu clang-12
+
+######## MR Smoke Tests ########################################################
+
+build:linux:cross:x86-64:gcc-10:default:smoketest:
+  extends: build:linux:cross:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_BUILD_TARGET: buildsmoketests
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+  # Clear tags so it can run on any GitLab shared runner.  
+  tags: []
+
+build:linux:cross:x86-64:clang-12:default:smoketest:
+  extends: build:linux:cross:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_BUILD_TARGET: buildsmoketests
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+  # Clear tags so it can run on any GitLab shared runner.  
+  tags: []
diff --git a/ci/build.windows.gitlab-ci.yml b/ci/build.windows.gitlab-ci.yml
new file mode 100644
index 0000000..3eb0a67
--- /dev/null
+++ b/ci/build.windows.gitlab-ci.yml
@@ -0,0 +1,92 @@
+# Base configuration for windows builds.
+.build:windows:
+  extends: .common:windows
+  stage: build
+  variables:
+    EIGEN_CI_BUILD_TARGET: buildtests
+    # Reduce overall build size and compile time. 
+    # Note: /d2ReducedOptimizeHugeFunctions is only available in VS 2019.
+    EIGEN_CI_TEST_CUSTOM_CXX_FLAGS: "/d2ReducedOptimizeHugeFunctions /DEIGEN_STRONG_INLINE=inline /Os"
+  script:
+     - ./ci/scripts/build.windows.script.ps1
+  tags:
+    - eigen-runner
+    - windows
+    - x86-64
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+    - if: $CI_PIPELINE_SOURCE == "web"
+  cache:
+    key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG-BUILD"
+    paths:
+      - ${EIGEN_CI_BUILDDIR}/
+
+######### MSVC #################################################################
+
+# MSVC 14.16 (VS 2017)
+build:windows:x86-64:msvc-14.16:default:
+  extends: .build:windows
+  variables:
+    EIGEN_CI_MSVC_VER: "14.16"
+    # Override to remove unsupported /d2ReducedOptimizeHugeFunctions.
+    EIGEN_CI_TEST_CUSTOM_CXX_FLAGS: "/DEIGEN_STRONG_INLINE=inline /Os"
+
+# MSVC 14.29 (VS 2019)
+build:windows:x86-64:msvc-14.29:default:
+  extends: .build:windows
+  variables:
+    EIGEN_CI_MSVC_VER: "14.29"
+    
+build:windows:x86-64:msvc-14.29:avx2:
+  extends: build:windows:x86-64:msvc-14.29:default
+  variables:
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_TEST_AVX2=on"
+
+build:windows:x86-64:msvc-14.29:avx512dq:
+  extends: build:windows:x86-64:msvc-14.29:default
+  variables:
+    EIGEN_CI_ADDITIONAL_ARGS: "-DEIGEN_TEST_AVX512DQ=on"
+
+######### MSVC + CUDA ##########################################################
+.build:windows:cuda:
+  extends: .build:windows
+  variables:
+    # Addtional flags passed to the cuda compiler.
+    EIGEN_CI_CUDA_CXX_FLAGS: ""
+    # Compute architectures present in the GitLab CI runners.
+    EIGEN_CI_CUDA_COMPUTE_ARCH: "50;75"
+    EIGEN_CI_BUILD_TARGET: buildtests_gpu
+    EIGEN_CI_ADDITIONAL_ARGS:
+      -DEIGEN_TEST_CUDA=on 
+      -DEIGEN_CUDA_CXX_FLAGS="${EIGEN_CI_CUDA_CXX_FLAGS}"
+      -DEIGEN_CUDA_COMPUTE_ARCH=${EIGEN_CI_CUDA_COMPUTE_ARCH}
+  tags:
+    - eigen-runner
+    - windows
+    - x86-64
+    - cuda
+    
+# MSVC 14.16 + CUDA 9.2
+build:windows:x86-64:cuda-9.2:msvc-14.16:
+  extends: .build:windows:cuda
+  variables:
+    # CUDA 9.2 doesn't support sm_75.
+    EIGEN_CI_CUDA_COMPUTE_ARCH: "50;70"
+    # CUDA 9.2 only supports up to VS 2017.
+    EIGEN_CI_MSVC_VER: "14.16"
+    EIGEN_CI_TEST_CUSTOM_CXX_FLAGS: "/DEIGEN_STRONG_INLINE=inline /Os"
+    EIGEN_CI_BEFORE_SCRIPT: $$env:CUDA_PATH=$$env:CUDA_PATH_V9_2
+
+# MSVC 14.29 + CUDA 10.2
+build:windows:x86-64:cuda-10.2:msvc-14.29:
+  extends: .build:windows:cuda
+  variables:
+    EIGEN_CI_MSVC_VER: "14.29"
+    EIGEN_CI_BEFORE_SCRIPT: $$env:CUDA_PATH=$$env:CUDA_PATH_V10_2
+
+# MSVC 14.29 + CUDA 11.4
+build:windows:x86-64:cuda-11.4:msvc-14.29:
+  extends: .build:windows:cuda
+  variables:
+    EIGEN_CI_MSVC_VER: "14.29"
+    EIGEN_CI_BEFORE_SCRIPT: $$env:CUDA_PATH=$$env:CUDA_PATH_V11_4
diff --git a/ci/common.gitlab-ci.yml b/ci/common.gitlab-ci.yml
new file mode 100644
index 0000000..cf6d783
--- /dev/null
+++ b/ci/common.gitlab-ci.yml
@@ -0,0 +1,38 @@
+# Base configuration for linux builds and tests.
+.common:linux:cross:
+  image: ubuntu:20.04
+  variables:
+    EIGEN_CI_TARGET_ARCH: ""
+    EIGEN_CI_ADDITIONAL_ARGS: ""
+    # If host matches target, use the following:
+    EIGEN_CI_C_COMPILER: ""
+    EIGEN_CI_CXX_COMPILER: ""
+    EIGEN_CI_INSTALL: "${EIGEN_CI_C_COMPILER} ${EIGEN_CI_CXX_COMPILER}"
+    # If host does not match the target, use the following:
+    EIGEN_CI_CROSS_TARGET_TRIPLE: ""
+    EIGEN_CI_CROSS_C_COMPILER: ${EIGEN_CI_C_COMPILER}
+    EIGEN_CI_CROSS_CXX_COMPILER: ${EIGEN_CI_CXX_COMPILER}
+    EIGEN_CI_CROSS_INSTALL: "${EIGEN_CI_CROSS_C_COMPILER} ${EIGEN_CI_CROSS_CXX_COMPILER}"
+  before_script:
+    # Call script in current shell - it sets up some environment variables.
+    - . ci/scripts/common.linux.before_script.sh
+  artifacts:
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+    paths:
+      - ${EIGEN_CI_BUILDDIR}/
+    expire_in: 5 days
+
+# Base configuration for Windows builds and tests.
+.common:windows:
+  variables:
+    EIGEN_CI_MSVC_ARCH: x64
+    EIGEN_CI_MSVC_VER: "14.29"
+    EIGEN_CI_ADDITIONAL_ARGS: ""
+    EIGEN_CI_BEFORE_SCRIPT: ""
+  before_script:
+     - . ci/scripts/common.windows.before_script.ps1
+  artifacts:
+    name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
+    paths:
+      - ${EIGEN_CI_BUILDDIR}/
+    expire_in: 5 days
diff --git a/ci/scripts/build.linux.script.sh b/ci/scripts/build.linux.script.sh
new file mode 100644
index 0000000..d00edbf
--- /dev/null
+++ b/ci/scripts/build.linux.script.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+set -x
+
+# Create and enter build directory.
+rootdir=`pwd`
+mkdir -p ${EIGEN_CI_BUILDDIR}
+cd ${EIGEN_CI_BUILDDIR}
+
+# Configure build.
+cmake -G Ninja                                                   \
+  -DCMAKE_CXX_COMPILER=${EIGEN_CI_CXX_COMPILER}                  \
+  -DCMAKE_C_COMPILER=${EIGEN_CI_C_COMPILER}                      \
+  -DCMAKE_CXX_COMPILER_TARGET=${EIGEN_CI_CXX_COMPILER_TARGET}    \
+  ${EIGEN_CI_ADDITIONAL_ARGS} ${rootdir}
+
+target=""
+if [[ ${EIGEN_CI_BUILD_TARGET} ]]; then
+  target="--target ${EIGEN_CI_BUILD_TARGET}"
+fi
+
+# Builds (particularly gcc) sometimes get killed, potentially when running
+# out of resources.  In that case, keep trying to build the remaining
+# targets (k0), then try to build again with a single thread (j1) to minimize
+# resource use.
+cmake --build . ${target} -- -k0 || cmake --build . ${target} -- -k0 -j1
+
+# Return to root directory.
+cd ${rootdir}
+
+set +x
diff --git a/ci/scripts/build.windows.script.ps1 b/ci/scripts/build.windows.script.ps1
new file mode 100644
index 0000000..155da3a
--- /dev/null
+++ b/ci/scripts/build.windows.script.ps1
@@ -0,0 +1,42 @@
+# Find Visual Studio installation directory.
+$VS_INSTALL_DIR = &"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath
+
+# Run VCVarsAll.bat initialization script and extract environment variables.
+# http://allen-mack.blogspot.com/2008/03/replace-visual-studio-command-prompt.html
+cmd.exe /c "`"${VS_INSTALL_DIR}\VC\Auxiliary\Build\vcvarsall.bat`" $EIGEN_CI_MSVC_ARCH -vcvars_ver=$EIGEN_CI_MSVC_VER & set" |
+  foreach {
+    if ($_ -match "=") {
+      $v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])"
+    }
+  }
+
+# Create and enter build directory.
+$rootdir = Get-Location
+mkdir $EIGEN_CI_BUILDDIR
+cd $EIGEN_CI_BUILDDIR
+
+# We need to split EIGEN_CI_ADDITIONAL_ARGS, otherwise they are interpretted
+# as a single argument.  Split by space, unless double-quoted.
+$split_args = [regex]::Split(${EIGEN_CI_ADDITIONAL_ARGS}, ' (?=(?:[^"]|"[^"]*")*$)' )
+
+# Configure build.
+cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel `
+      -DEIGEN_TEST_CUSTOM_CXX_FLAGS="${EIGEN_CI_TEST_CUSTOM_CXX_FLAGS}" `
+      ${split_args} "${rootdir}"
+
+$target = ""
+if (${EIGEN_CI_BUILD_TARGET}) {
+  $target = "--target ${EIGEN_CI_BUILD_TARGET}"
+}
+
+# Windows builds sometimes fail due heap errors. In that case, try
+# building the rest, then try to build again with a single thread.
+cmake --build . ${target} -- -k0 || cmake --build . ${target} -- -k0 -j1
+
+$success = $LASTEXITCODE
+
+# Return to root directory.
+cd ${rootdir}
+
+# Explicitly propagate exit code to indicate pass/failure of build command.
+if($success -ne 0) { Exit $success }
diff --git a/ci/scripts/common.linux.before_script.sh b/ci/scripts/common.linux.before_script.sh
new file mode 100644
index 0000000..9222a84
--- /dev/null
+++ b/ci/scripts/common.linux.before_script.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+set -x
+
+echo "Running ${CI_JOB_NAME}"
+
+# Get architecture and display CI configuration.
+export ARCH=`uname -m`
+export NPROC=`nproc`
+echo "arch=$ARCH, target=${EIGEN_CI_TARGET_ARCH}"
+echo "Processors: ${NPROC}"
+echo "CI Variables:"
+export | grep EIGEN
+
+# Set noninteractive, otherwise tzdata may be installed and prompt for a
+# geographical region.
+export DEBIAN_FRONTEND=noninteractive
+apt-get update -y > /dev/null
+apt-get install -y --no-install-recommends software-properties-common ninja-build cmake git > /dev/null
+add-apt-repository -y ppa:ubuntu-toolchain-r/test > /dev/null
+apt-get update -y > /dev/null
+
+# Install required dependencies and set up compilers.
+# These are required even for testing to ensure that dynamic runtime libraries
+# are available.
+if [[ "$ARCH" == "${EIGEN_CI_TARGET_ARCH}" ]]; then
+  apt-get install -y --no-install-recommends ${EIGEN_CI_INSTALL} > /dev/null;
+  export EIGEN_CI_CXX_IMPLICIT_INCLUDE_DIRECTORIES="";
+  export EIGEN_CI_CXX_COMPILER_TARGET="";
+else
+  apt-get install -y --no-install-recommends ${EIGEN_CI_CROSS_INSTALL} > /dev/null;
+  export EIGEN_CI_C_COMPILER=${EIGEN_CI_CROSS_C_COMPILER};
+  export EIGEN_CI_CXX_COMPILER=${EIGEN_CI_CROSS_CXX_COMPILER};
+  export EIGEN_CI_CXX_COMPILER_TARGET=${EIGEN_CI_CROSS_TARGET_TRIPLE};
+  # Tell the compiler where to find headers and libraries if using clang.
+  # NOTE: this breaks GCC since it messes with include path order
+  #       (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70129)
+  if [[ "${EIGEN_CI_CROSS_CXX_COMPILER}" == *"clang"* ]]; then
+    export CPLUS_INCLUDE_PATH="/usr/${EIGEN_CI_CROSS_TARGET_TRIPLE}/include";
+    export LIBRARY_PATH="/usr/${EIGEN_CI_CROSS_TARGET_TRIPLE}/lib64";
+  fi
+fi
+
+echo "Compilers: ${EIGEN_CI_C_COMPILER} ${EIGEN_CI_CXX_COMPILER}"
+
+if [ -n "$EIGEN_CI_BEFORE_SCRIPT" ]; then eval "$EIGEN_CI_BEFORE_SCRIPT"; fi
+
+set +x
diff --git a/ci/scripts/common.windows.before_script.ps1 b/ci/scripts/common.windows.before_script.ps1
new file mode 100644
index 0000000..291edde
--- /dev/null
+++ b/ci/scripts/common.windows.before_script.ps1
@@ -0,0 +1,7 @@
+echo "Running ${CI_JOB_NAME}"
+
+# Print configuration variables.
+Get-Variable | findstr EIGEN
+
+# Run a custom before-script command.
+if ("${EIGEN_CI_BEFORE_SCRIPT}") { Invoke-Expression -Command "${EIGEN_CI_BEFORE_SCRIPT}" }
diff --git a/ci/scripts/test.linux.after_script.sh b/ci/scripts/test.linux.after_script.sh
new file mode 100644
index 0000000..3bc0c2f
--- /dev/null
+++ b/ci/scripts/test.linux.after_script.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -x
+
+# Enter build directory.
+rootdir=`pwd`
+cd ${EIGEN_CI_BUILDDIR}
+
+# Install xml processor.
+apt-get update -y
+apt-get install --no-install-recommends -y xsltproc
+
+# Generate test results.
+xsltproc ${rootdir}/ci/CTest2JUnit.xsl Testing/`head -n 1 < Testing/TAG`/Test.xml > "JUnitTestResults_$CI_JOB_ID.xml"
+
+# Return to root directory.
+cd ${rootdir}
+
+set +x
diff --git a/ci/scripts/test.linux.script.sh b/ci/scripts/test.linux.script.sh
new file mode 100644
index 0000000..7ce892e
--- /dev/null
+++ b/ci/scripts/test.linux.script.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -x
+
+# Enter build directory.
+rootdir=`pwd`
+cd ${EIGEN_CI_BUILDDIR}
+
+target=""
+if [[ ${EIGEN_CI_TEST_REGEX} ]]; then
+  target="-R ${EIGEN_CI_TEST_REGEX}"
+elif [[ ${EIGEN_CI_TEST_LABEL} ]]; then
+  target="-L ${EIGEN_CI_TEST_LABEL}"
+fi
+
+# Repeat tests up to three times to ignore flakes.  Do not re-run with -T test,
+# otherwise we lose test results for those that passed.
+# Note: starting with CMake 3.17, we can use --repeat until-pass:3, but we have
+#       no way of easily installing this on ppc64le.
+ctest --parallel ${NPROC} --output-on-failure --no-compress-output  \
+      --build-no-clean -T test ${target} || \
+  ctest -j${NPROC} --output-on-failure --no-compress-output --rerun-failed || \
+  ctest -j${NPROC} --output-on-failure --no-compress-output --rerun-failed
+
+# Return to root directory.
+cd ${rootdir}
+
+set +x
diff --git a/ci/scripts/test.windows.after_script.ps1 b/ci/scripts/test.windows.after_script.ps1
new file mode 100644
index 0000000..59cd5a9
--- /dev/null
+++ b/ci/scripts/test.windows.after_script.ps1
@@ -0,0 +1,17 @@
+# Change to build directory.
+$rootdir = Get-Location
+cd ${EIGEN_CI_BUILDDIR}
+
+# Determine the appropriate test tag and results file.
+$TEST_TAG = Get-Content Testing\TAG | select -first 1
+
+# PowerShell equivalent to xsltproc:
+$XSL_FILE = Resolve-Path "..\ci\CTest2JUnit.xsl"
+$INPUT_FILE = Resolve-Path Testing\$TEST_TAG\Test.xml
+$OUTPUT_FILE = Join-Path -Path $pwd -ChildPath JUnitTestResults_$CI_JOB_ID.xml
+$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
+$xslt.Load($XSL_FILE)
+$xslt.Transform($INPUT_FILE,$OUTPUT_FILE)
+
+# Return to root directory.
+cd ${rootdir}
diff --git a/ci/scripts/test.windows.script.ps1 b/ci/scripts/test.windows.script.ps1
new file mode 100644
index 0000000..f1b0c69
--- /dev/null
+++ b/ci/scripts/test.windows.script.ps1
@@ -0,0 +1,30 @@
+# Change to build directory.
+$rootdir = Get-Location
+cd $EIGEN_CI_BUILDDIR
+
+# Determine number of processors for parallel tests.
+$NPROC=${Env:NUMBER_OF_PROCESSORS}
+
+# Set target based on regex or label.
+$target = ""
+if (${EIGEN_CI_TEST_REGEX}) {
+  $target = "-R","${EIGEN_CI_TEST_REGEX}"
+} elseif (${EIGEN_CI_TEST_LABEL}) {
+  $target = "-L","${EIGEN_CI_TEST_LABEL}"
+}
+
+# Repeat tests up to three times to ignore flakes.  Do not re-run with -T test,
+# otherwise we lose test results for those that passed.
+# Note: starting with CMake 3.17, we can use --repeat until-pass:3, but we have
+#       no way of easily installing this on ppc64le.
+ctest -j$NPROC --output-on-failure --no-compress-output --build-no-clean -T test $target || `
+  ctest -j$NPROC --output-on-failure --no-compress-output --rerun-failed || `
+  ctest -j$NPROC --output-on-failure --no-compress-output --rerun-failed
+
+$success = $LASTEXITCODE
+
+# Return to root directory.
+cd ${rootdir}
+
+# Explicitly propagate exit code to indicate pass/failure of test command.
+if($success -ne 0) { Exit $success }
diff --git a/ci/scripts/vars.linux.sh b/ci/scripts/vars.linux.sh
new file mode 100644
index 0000000..f772555
--- /dev/null
+++ b/ci/scripts/vars.linux.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# Initialize default variables used by the CI.
+export EIGEN_CI_ADDITIONAL_ARGS=""
+export EIGEN_CI_BEFORE_SCRIPT=""
+export EIGEN_CI_BUILD_TARGET="buildtests"
+export EIGEN_CI_BUILDDIR=".build"
+export EIGEN_CI_C_COMPILER="clang"
+export EIGEN_CI_CXX_COMPILER="clang++"
+export EIGEN_CI_TEST_CUSTOM_CXX_FLAGS=""
+export EIGEN_CI_TEST_LABEL="Official"
+export EIGEN_CI_TEST_REGEX=""
diff --git a/ci/scripts/vars.windows.ps1 b/ci/scripts/vars.windows.ps1
new file mode 100644
index 0000000..0a1c587
--- /dev/null
+++ b/ci/scripts/vars.windows.ps1
@@ -0,0 +1,10 @@
+# Initialize default variables used by the CI.
+$EIGEN_CI_ADDITIONAL_ARGS       = ""
+$EIGEN_CI_BEFORE_SCRIPT         = ""
+$EIGEN_CI_BUILD_TARGET          = "buildtests"
+$EIGEN_CI_BUILDDIR              = ".build"
+$EIGEN_CI_MSVC_ARCH             = "x64"
+$EIGEN_CI_MSVC_VER              = "14.29"
+$EIGEN_CI_TEST_CUSTOM_CXX_FLAGS = "/d2ReducedOptimizeHugeFunctions /DEIGEN_STRONG_INLINE=inline /Os"
+$EIGEN_CI_TEST_LABEL            = "Official"
+$EIGEN_CI_TEST_REGEX            = ""
diff --git a/ci/smoketests.gitlab-ci.yml b/ci/smoketests.gitlab-ci.yml
deleted file mode 100644
index c69d392..0000000
--- a/ci/smoketests.gitlab-ci.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-.buildsmoketests:linux:base:
-  stage: buildsmoketests
-  image: ubuntu:18.04
-  before_script:
-    - apt-get update -y
-    - apt-get install -y --no-install-recommends software-properties-common
-    - add-apt-repository -y  ppa:ubuntu-toolchain-r/test
-    - apt-get update
-    - apt-get install --no-install-recommends -y ${EIGEN_CI_CXX_COMPILER}
-      ${EIGEN_CI_CC_COMPILER} cmake ninja-build
-  script:
-    - mkdir -p ${BUILDDIR} && cd ${BUILDDIR}
-    - CXX=${EIGEN_CI_CXX_COMPILER} CC=${EIGEN_CI_CC_COMPILER} cmake -G
-      ${EIGEN_CI_CMAKE_GENEATOR} -DEIGEN_TEST_CXX11=${EIGEN_TEST_CXX11}
-      ${EIGEN_CI_ADDITIONAL_ARGS} ..
-    - cmake --build . --target buildsmoketests
-  artifacts:
-    name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
-    paths:
-      - ${BUILDDIR}/
-    expire_in: 5 days
-  only:
-    - merge_requests
-
-buildsmoketests:x86-64:linux:gcc-10:cxx11-on:
-  extends: .buildsmoketests:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "g++-10"
-    EIGEN_CI_CC_COMPILER: "gcc-10"
-    EIGEN_TEST_CXX11: "on"
-
-buildsmoketests:x86-64:linux:clang-10:cxx11-on:
-  extends: .buildsmoketests:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: "clang++-10"
-    EIGEN_CI_CC_COMPILER: "clang-10"
-    EIGEN_TEST_CXX11: "on"
-
-.smoketests:linux:base:
-  stage: smoketests
-  image: ubuntu:18.04
-  before_script:
-    - apt-get update -y
-    - apt-get install -y --no-install-recommends software-properties-common
-    - add-apt-repository -y ppa:ubuntu-toolchain-r/test
-    - apt-get update
-    - apt-get install --no-install-recommends -y ${EIGEN_CI_CXX_COMPILER}
-      ${EIGEN_CI_CC_COMPILER} cmake ninja-build xsltproc
-  script:
-    - export NPROC=`nproc`
-    - echo ${NPROC}
-    - export CXX=${EIGEN_CI_CXX_COMPILER}
-    - export CC=${EIGEN_CI_CC_COMPILER}
-    - cd ${BUILDDIR} && ctest -j${NPROC} --output-on-failure --no-compress-output
-      --build-no-clean -T test -L smoketest
-  after_script:
-    - apt-get update -y
-    - apt-get install --no-install-recommends -y xsltproc
-    - cd ${BUILDDIR}
-    - xsltproc ../ci/CTest2JUnit.xsl Testing/`head -n 1 < Testing/TAG`/Test.xml > "JUnitTestResults_$CI_JOB_ID.xml"
-  artifacts:
-    reports:
-      junit:
-        - ${BUILDDIR}/JUnitTestResults_$CI_JOB_ID.xml
-    expire_in: 5 days
-  only:
-    - merge_requests
-
-smoketests:x86-64:linux:gcc-10:cxx11-on:
-  extends: .smoketests:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: g++-10
-    EIGEN_CI_CC_COMPILER: gcc-10
-  needs: [ "buildsmoketests:x86-64:linux:gcc-10:cxx11-on" ]
-
-smoketests:x86-64:linux:clang-10:cxx11-on:
-  extends: .smoketests:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: clang++-10
-    EIGEN_CI_CC_COMPILER: clang-10
-  needs: [ "buildsmoketests:x86-64:linux:clang-10:cxx11-on" ]
diff --git a/ci/smoketests.linux.gitlab-ci.yml b/ci/smoketests.linux.gitlab-ci.yml
new file mode 100644
index 0000000..02d45bc
--- /dev/null
+++ b/ci/smoketests.linux.gitlab-ci.yml
@@ -0,0 +1,33 @@
+smoketests:build:linux:cross:x86-64:gcc-10:default:
+  stage: buildsmoketests
+  extends: build:linux:cross:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_BUILD_TARGET: "buildsmoketests"
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+smoketests:build:linux:cross:x86-64:clang-12:default:
+  stage: buildsmoketests
+  extends: build:linux:cross:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_BUILD_TARGET: "buildsmoketests"
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+smoketests:test:linux:x86-64:gcc-10:default:
+  stage: smoketests
+  extends: .test:linux:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: smoketests
+  needs: [ "smoketests:build:linux:cross:x86-64:gcc-10:default" ]
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+
+smoketests:test:linux:x86-64:clang-12:default:
+  stage: smoketests
+  extends: .test:linux:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: smoketests
+  needs: [ "smoketests:build:linux:cross:x86-64:clang-12:default" ]
+  rules:
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
diff --git a/ci/test.gitlab-ci.yml b/ci/test.gitlab-ci.yml
deleted file mode 100644
index 63012bd..0000000
--- a/ci/test.gitlab-ci.yml
+++ /dev/null
@@ -1,238 +0,0 @@
-.test:linux:base:
-  stage: test
-  image: ubuntu:18.04
-  retry: 2
-  before_script:
-    - apt-get update -y
-    - apt-get install -y --no-install-recommends software-properties-common
-    - add-apt-repository -y ppa:ubuntu-toolchain-r/test
-    - apt-get update
-    - apt-get install --no-install-recommends -y ${EIGEN_CI_CXX_COMPILER}
-      ${EIGEN_CI_CC_COMPILER} cmake ninja-build xsltproc
-  script:
-    - export NPROC=`nproc`
-    - echo ${NPROC}
-    - export CXX=${EIGEN_CI_CXX_COMPILER}
-    - export CC=${EIGEN_CI_CC_COMPILER}
-    - cd ${BUILDDIR} && ctest -j${NPROC} --output-on-failure --no-compress-output
-      --build-no-clean -T test -L ${EIGEN_CI_TEST_LABEL}
-  after_script:
-    - apt-get update -y
-    - apt-get install --no-install-recommends -y xsltproc
-    - cd ${BUILDDIR}
-    - xsltproc ../ci/CTest2JUnit.xsl Testing/`head -n 1 < Testing/TAG`/Test.xml > "JUnitTestResults_$CI_JOB_ID.xml"
-  artifacts:
-    reports:
-      junit:
-        - ${BUILDDIR}/JUnitTestResults_$CI_JOB_ID.xml
-    expire_in: 5 days
-  only:
-    - schedules
-
-##### x86-64 ###################################################################
-# # GCC-4.8
-# .test:x86-64:linux:gcc-4.8:cxx11-on:
-#   extends: .test:linux:base
-#   variables:
-#     EIGEN_CI_CXX_COMPILER: g++-4.8
-#     EIGEN_CI_CC_COMPILER: gcc-4.8
-#   needs: [ "build:x86-64:linux:gcc-4.8:cxx11-on" ]
-#   tags:
-#     - eigen-runner
-#     - linux
-#     - x86-64
-
-# test:x86-64:linux:gcc-4.8:cxx11-on:official:
-#   extends: .test:x86-64:linux:gcc-4.8:cxx11-on
-#   variables:
-#     EIGEN_CI_TEST_LABEL: "Official"
-
-# test:x86-64:linux:gcc-4.8:cxx11-on:unsupported:
-#   extends: .test:x86-64:linux:gcc-4.8:cxx11-on
-#   variables:
-#     EIGEN_CI_TEST_LABEL: "Unsupported"
-
-# GCC-9
-.test:x86-64:linux:gcc-9:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: g++-9
-    EIGEN_CI_CC_COMPILER: gcc-9
-  needs: [ "build:x86-64:linux:gcc-9:cxx11-on" ]
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-
-test:x86-64:linux:gcc-9:cxx11-on:official:
-  extends: .test:x86-64:linux:gcc-9:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:x86-64:linux:gcc-9:cxx11-on:unsupported:
-  extends: .test:x86-64:linux:gcc-9:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-# GCC-10
-.test:x86-64:linux:gcc-10:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: g++-10
-    EIGEN_CI_CC_COMPILER: gcc-10
-  needs: [ "build:x86-64:linux:gcc-10:cxx11-on" ]
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-
-test:x86-64:linux:gcc-10:cxx11-on:official:
-  extends: .test:x86-64:linux:gcc-10:cxx11-on
-  allow_failure: true
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:x86-64:linux:gcc-10:cxx11-on:unsupported:
-  extends: .test:x86-64:linux:gcc-10:cxx11-on
-  allow_failure: true
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-# Clang 10
-.test:x86-64:linux:clang-10:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: clang++-10
-    EIGEN_CI_CC_COMPILER: clang-10
-  needs: [ "build:x86-64:linux:clang-10:cxx11-on" ]
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-
-test:x86-64:linux:clang-10:cxx11-on:official:
-  extends: .test:x86-64:linux:clang-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:x86-64:linux:clang-10:cxx11-on:unsupported:
-  extends: .test:x86-64:linux:clang-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-.test:x86-64:linux:clang-10:cxx11-on:avx512:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: clang++-10
-    EIGEN_CI_CC_COMPILER: clang-10
-  needs: [ "build:x86-64:linux:clang-10:cxx11-on:avx512" ]
-  tags:
-    - eigen-runner
-    - linux
-    - x86-64
-    - avx512
-
-test:x86-64:linux:clang-10:cxx11-on:avx512:official:
-  extends: .test:x86-64:linux:clang-10:cxx11-on:avx512
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:x86-64:linux:clang-10:cxx11-on:avx512:unsupported:
-  extends: .test:x86-64:linux:clang-10:cxx11-on:avx512
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-##### AArch64 ##################################################################
-# GCC-10
-.test:aarch64:linux:gcc-10:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: g++-10
-    EIGEN_CI_CC_COMPILER: gcc-10
-  needs: [ "build:aarch64:linux:gcc-10:cxx11-on" ]
-  tags:
-    - eigen-runner
-    - linux
-    - aarch64
-
-test:aarch64:linux:gcc-10:cxx11-on:official:
-  extends: .test:aarch64:linux:gcc-10:cxx11-on
-  allow_failure: true
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:aarch64:linux:gcc-10:cxx11-on:unsupported:
-  extends: .test:aarch64:linux:gcc-10:cxx11-on
-  allow_failure: true
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-# Clang 10
-.test:aarch64:linux:clang-10:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: clang++-10
-    EIGEN_CI_CC_COMPILER: clang-10
-  needs: [ "build:aarch64:linux:clang-10:cxx11-on" ]
-  tags:
-    - eigen-runner
-    - linux
-    - aarch64
-
-test:aarch64:linux:clang-10:cxx11-on:official:
-  extends: .test:aarch64:linux:clang-10:cxx11-on
-  allow_failure: true
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:aarch64:linux:clang-10:cxx11-on:unsupported:
-  extends: .test:aarch64:linux:clang-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-##### ppc64le ##################################################################
-# GCC-10
-.test:ppc64le:linux:gcc-10:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: g++-10
-    EIGEN_CI_CC_COMPILER: gcc-10
-  needs: [ "build:ppc64le:linux:gcc-10:cxx11-on" ]
-  allow_failure: true
-  tags:
-    - eigen-runner
-    - linux
-    - ppc64le
-
-test:ppc64le:linux:gcc-10:cxx11-on:official:
-  extends: .test:ppc64le:linux:gcc-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:ppc64le:linux:gcc-10:cxx11-on:unsupported:
-  extends: .test:ppc64le:linux:gcc-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
-
-# Clang 10
-.test:ppc64le:linux:clang-10:cxx11-on:
-  extends: .test:linux:base
-  variables:
-    EIGEN_CI_CXX_COMPILER: clang++-10
-    EIGEN_CI_CC_COMPILER: clang-10
-  needs: [ "build:ppc64le:linux:clang-10:cxx11-on" ]
-  allow_failure: true
-  tags:
-    - eigen-runner
-    - linux
-    - ppc64le
-
-test:ppc64le:linux:clang-10:cxx11-on:official:
-  extends: .test:ppc64le:linux:clang-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Official"
-
-test:ppc64le:linux:clang-10:cxx11-on:unsupported:
-  extends: .test:ppc64le:linux:clang-10:cxx11-on
-  variables:
-    EIGEN_CI_TEST_LABEL: "Unsupported"
diff --git a/ci/test.linux.gitlab-ci.yml b/ci/test.linux.gitlab-ci.yml
new file mode 100644
index 0000000..b234183
--- /dev/null
+++ b/ci/test.linux.gitlab-ci.yml
@@ -0,0 +1,406 @@
+.test:linux:
+  extends: .common:linux:cross
+  stage: test
+  script:
+    - . ci/scripts/test.linux.script.sh
+  after_script:
+    - . ci/scripts/test.linux.after_script.sh
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+    - if: $CI_PIPELINE_SOURCE == "web"
+
+##### x86-64 ###################################################################
+.test:linux:x86-64:
+  extends: .test:linux
+  variables:
+    EIGEN_CI_TARGET_ARCH: x86_64
+    EIGEN_CI_CROSS_TARGET_TRIPLE: x86_64-linux-gnu
+  tags:
+    - eigen-runner
+    - linux
+    - x86-64
+
+# GCC-6 (minimum on Ubuntu 18.04)
+.test:linux:x86-64:gcc-6:default:
+  extends: .test:linux:x86-64
+  image: ubuntu:18.04
+  needs: [ build:linux:cross:x86-64:gcc-6:default ]
+  variables:
+    EIGEN_CI_INSTALL: g++-6
+    
+test:linux:x86-64:gcc-6:default:official:
+  extends: .test:linux:x86-64:gcc-6:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:gcc-6:default:unsupported:
+  extends: .test:linux:x86-64:gcc-6:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+    
+# GCC-10 (modern stable)
+.test:linux:x86-64:gcc-10:default:
+  extends: .test:linux:x86-64
+  needs: [ build:linux:cross:x86-64:gcc-10:default ]
+  variables:
+    EIGEN_CI_INSTALL: g++-10
+    
+test:linux:x86-64:gcc-10:default:official:
+  extends: .test:linux:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:gcc-10:default:unsupported:
+  extends: .test:linux:x86-64:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:linux:x86-64:gcc-10:avx2:
+  extends: .test:linux:x86-64
+  needs: [ build:linux:cross:x86-64:gcc-10:avx2 ]
+  variables:
+    EIGEN_CI_INSTALL: g++-10
+    
+test:linux:x86-64:gcc-10:avx2:official:
+  extends: .test:linux:x86-64:gcc-10:avx2
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:gcc-10:avx2:unsupported:
+  extends: .test:linux:x86-64:gcc-10:avx2
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+    
+.test:linux:x86-64:gcc-10:avx512dq:
+  extends: .test:linux:x86-64
+  needs: [ build:linux:cross:x86-64:gcc-10:avx512dq ]
+  variables:
+    EIGEN_CI_INSTALL: g++-10
+  tags:
+    - eigen-runner
+    - linux
+    - x86-64
+    - avx512
+    
+test:linux:x86-64:gcc-10:avx512dq:official:
+  extends: .test:linux:x86-64:gcc-10:avx512dq
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:gcc-10:avx512dq:unsupported:
+  extends: .test:linux:x86-64:gcc-10:avx512dq
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+# Clang-6 (minimum on Ubuntu 20.04)
+.test:linux:x86-64:clang-6:default:
+  extends: .test:linux:x86-64
+  image: ubuntu:20.04
+  needs: [ build:linux:cross:x86-64:clang-6:default ]
+  variables:
+    EIGEN_CI_INSTALL: clang-6.0 lld-6.0
+
+test:linux:x86-64:clang-6:default:official:
+  extends: .test:linux:x86-64:clang-6:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:clang-6:default:unsupported:
+  extends: .test:linux:x86-64:clang-6:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+# Clang-12 (modern stable)
+.test:linux:x86-64:clang-12:default:
+  extends: .test:linux:x86-64
+  needs: [ build:linux:cross:x86-64:clang-12:default ]
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+
+test:linux:x86-64:clang-12:default:official:
+  extends: .test:linux:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:clang-12:default:unsupported:
+  extends: .test:linux:x86-64:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:linux:x86-64:clang-12:avx2:
+  extends: .test:linux:x86-64
+  needs: [ build:linux:cross:x86-64:clang-12:avx2 ]
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+
+test:linux:x86-64:clang-12:avx2:official:
+  extends: .test:linux:x86-64:clang-12:avx2
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:clang-12:avx2:unsupported:
+  extends: .test:linux:x86-64:clang-12:avx2
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:linux:x86-64:clang-12:avx512dq:
+  extends: .test:linux:x86-64
+  needs: [ build:linux:cross:x86-64:clang-12:avx512dq ]
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+  tags:
+    - eigen-runner
+    - linux
+    - x86-64
+    - avx512
+
+test:linux:x86-64:clang-12:avx512dq:official:
+  extends: .test:linux:x86-64:clang-12:avx512dq
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:x86-64:clang-12:avx512dq:unsupported:
+  extends: .test:linux:x86-64:clang-12:avx512dq
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+##### CUDA #####################################################################
+.test:linux:cuda:
+  extends: .test:linux
+  allow_failure: true
+  variables:
+    EIGEN_CI_TEST_LABEL: gpu
+  tags: 
+    - eigen-runner
+    - linux
+    - x86-64
+    - cuda
+
+# NVidia no longer provides docker images < CUDA 11.0.3.
+# # GCC-7, CUDA-9.2
+# test:linux:cuda-9.2:gcc-7:
+#   extends: .test:linux:cuda
+#   image: nvidia/cuda:9.2-devel-ubuntu18.04
+#   needs: [ build:linux:cuda-9.2:gcc-7 ]
+#   variables:
+#     EIGEN_CI_CXX_COMPILER: g++-7
+#     EIGEN_CI_CC_COMPILER: gcc-7
+
+# # Clang-10, CUDA-9.2
+# test:linux:cuda-9.2:clang-10:
+#   extends: .test:linux:cuda
+#   image: nvidia/cuda:9.2-devel-ubuntu18.04
+#   needs: [ build:linux:cuda-9.2:clang-10 ]
+#   variables:
+#     EIGEN_CI_CXX_COMPILER: clang++-10
+#     EIGEN_CI_CC_COMPILER: clang-10
+
+# # GCC-8, CUDA-10.2
+# test:linux:cuda-10.2:gcc-8:
+#   extends: .test:linux:cuda
+#   image: nvidia/cuda:10.2-devel-ubuntu18.04
+#   needs: [ build:linux:cuda-10.2:gcc-8 ]
+#   variables:
+#     EIGEN_CI_CXX_COMPILER: g++-8
+#     EIGEN_CI_CC_COMPILER: gcc-8
+
+# # Clang-10, CUDA-10.2
+# test:linux:cuda-10.2:clang-10:
+#   extends: .test:linux:cuda
+#   image: nvidia/cuda:10.2-devel-ubuntu18.04
+#   needs: [ build:linux:cuda-10.2:clang-10 ]
+#   variables:
+#     EIGEN_CI_CXX_COMPILER: clang++-10
+#     EIGEN_CI_CC_COMPILER: clang-10
+
+# GCC-10, CUDA-11.4 
+test:linux:cuda-11.4:gcc-10:
+  extends: .test:linux:cuda
+  image: nvidia/cuda:11.4.3-devel-ubuntu20.04
+  needs: [ build:linux:cuda-11.4:gcc-10 ]
+  variables:
+    EIGEN_CI_CXX_COMPILER: g++-10
+    EIGEN_CI_CC_COMPILER: gcc-10
+
+# Clang-12, CUDA-11.4
+test:linux:cuda-11.4:clang-12:
+  extends: .test:linux:cuda
+  image: nvidia/cuda:11.4.3-devel-ubuntu20.04
+  needs: [ build:linux:cuda-11.4:clang-12 ]
+  variables:
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_CC_COMPILER: clang-12
+
+# GCC-10, CUDA-12.2
+test:linux:cuda-12.2:gcc-10:
+  extends: .test:linux:cuda
+  image: nvidia/cuda:12.2.0-devel-ubuntu20.04
+  needs: [ build:linux:cuda-12.2:gcc-10 ]
+  variables:
+    EIGEN_CI_CXX_COMPILER: g++-10
+    EIGEN_CI_CC_COMPILER: gcc-10
+
+# Clang-12, CUDA-12.2
+test:linux:cuda-12.2:clang-12:
+  extends: .test:linux:cuda
+  image: nvidia/cuda:12.2.0-devel-ubuntu20.04
+  needs: [ build:linux:cuda-12.2:clang-12 ]
+  variables:
+    EIGEN_CI_CXX_COMPILER: clang++-12
+    EIGEN_CI_CC_COMPILER: clang-12
+
+##### arm ######################################################################
+
+.test:linux:arm:
+  extends: .test:linux
+  variables:
+    EIGEN_CI_TARGET_ARCH: arm
+    EIGEN_CI_CROSS_TARGET_TRIPLE: arm-linux-gnueabihf
+    # Enable cross-compiled arm binary to run on aarch64.
+    EIGEN_CI_BEFORE_SCRIPT: "ln -s /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 /lib/ && export LD_LIBRARY_PATH=/usr/arm-linux-gnueabihf/lib/"
+  tags:
+    - eigen-runner
+    - linux
+    - aarch64
+
+.test:linux:arm:gcc-10:default:
+  extends: .test:linux:arm
+  needs: [ build:linux:cross:arm:gcc-10:default ]
+  variables:
+    EIGEN_CI_CROSS_INSTALL: g++-10-arm-linux-gnueabihf
+    
+test:linux:arm:gcc-10:default:official:
+  extends: .test:linux:arm:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:arm:gcc-10:default:unsupported:
+  extends: .test:linux:arm:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:linux:arm:clang-12:default:
+  extends: .test:linux:arm
+  needs: [ build:linux:cross:arm:clang-12:default ]
+  variables:
+    EIGEN_CI_CROSS_INSTALL: g++-10-arm-linux-gnueabihf clang-12
+
+test:linux:arm:clang-12:default:official:
+  extends: .test:linux:arm:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:arm:clang-12:default:unsupported:
+  extends: .test:linux:arm:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+##### aarch64 ##################################################################
+
+.test:linux:aarch64:
+  extends: .test:linux
+  variables:
+    EIGEN_CI_TARGET_ARCH: aarch64
+    EIGEN_CI_CROSS_TARGET_TRIPLE: aarch64-linux-gnu
+  tags:
+    - eigen-runner
+    - linux
+    - aarch64
+
+.test:linux:aarch64:gcc-10:default:
+  extends: .test:linux:aarch64
+  needs: [ build:linux:cross:aarch64:gcc-10:default ]
+  variables:
+    EIGEN_CI_INSTALL: g++-10
+    
+test:linux:aarch64:gcc-10:default:official:
+  extends: .test:linux:aarch64:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:aarch64:gcc-10:default:unsupported:
+  extends: .test:linux:aarch64:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:linux:aarch64:clang-12:default:
+  extends: .test:linux:aarch64
+  needs: [ build:linux:cross:aarch64:clang-12:default ]
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+
+test:linux:aarch64:clang-12:default:official:
+  extends: .test:linux:aarch64:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:aarch64:clang-12:default:unsupported:
+  extends: .test:linux:aarch64:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+##### ppc64le ##################################################################
+
+.test:linux:ppc64le:
+  extends: .test:linux
+  variables:
+    EIGEN_CI_TARGET_ARCH: ppc64le
+    EIGEN_CI_CROSS_TARGET_TRIPLE: powerpc64le-linux-gnu
+  tags:
+    - eigen-runner
+    - linux
+    - ppc64le
+
+.test:linux:ppc64le:gcc-10:default:
+  extends: .test:linux:ppc64le
+  needs: [ build:linux:cross:ppc64le:gcc-10:default ]
+  variables:
+    EIGEN_CI_INSTALL: g++-10
+    
+test:linux:ppc64le:gcc-10:default:official:
+  extends: .test:linux:ppc64le:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:ppc64le:gcc-10:default:unsupported:
+  extends: .test:linux:ppc64le:gcc-10:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:linux:ppc64le:clang-12:default:
+  extends: .test:linux:ppc64le
+  needs: [ build:linux:cross:ppc64le:clang-12:default ]
+  variables:
+    EIGEN_CI_INSTALL: clang-12
+
+test:linux:ppc64le:clang-12:default:official:
+  extends: .test:linux:ppc64le:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:linux:ppc64le:clang-12:default:unsupported:
+  extends: .test:linux:ppc64le:clang-12:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+##### MR Smoke Tests ###########################################################
+    
+test:linux:x86-64:gcc-10:default:smoketest:
+  extends: .test:linux:x86-64:gcc-10:default
+  needs: [ build:linux:cross:x86-64:gcc-10:default:smoketest ]
+  variables:
+    EIGEN_CI_TEST_LABEL: smoketest
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+  # Clear tags so it can run on any GitLab shared runner.  
+  tags: []
+
+test:linux:x86-64:clang-12:default:smoketest:
+  extends: .test:linux:x86-64:clang-12:default
+  needs: [ build:linux:cross:x86-64:clang-12:default:smoketest ]
+  variables:
+    EIGEN_CI_TEST_LABEL: smoketest
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+  # Clear tags so it can run on any GitLab shared runner.  
+  tags: []
diff --git a/ci/test.windows.gitlab-ci.yml b/ci/test.windows.gitlab-ci.yml
new file mode 100644
index 0000000..ac13059
--- /dev/null
+++ b/ci/test.windows.gitlab-ci.yml
@@ -0,0 +1,106 @@
+.test:windows:
+  extends: .common:windows
+  stage: test
+  script:
+    - ./ci/scripts/test.windows.script.ps1
+  after_script:
+    - ./ci/scripts/test.windows.after_script.ps1
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+    - if: $CI_PIPELINE_SOURCE == "web"
+  tags: 
+    - eigen-runner
+    - windows
+    - x86-64
+
+##### MSVC #####################################################################
+
+# MSVC 14.16 (VS 2017)
+.test:windows:x86-64:msvc-14.16:default:
+  extends: .test:windows
+  needs: [ build:windows:x86-64:msvc-14.16:default ]
+
+test:windows:x86-64:msvc-14.16:default:official:
+  extends: .test:windows:x86-64:msvc-14.16:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:windows:x86-64:msvc-14.16:default:unsupported:
+  extends: .test:windows:x86-64:msvc-14.16:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+# MSVC 14.29 (VS 2019)
+.test:windows:x86-64:msvc-14.29:default:
+  extends: .test:windows
+  needs: [ build:windows:x86-64:msvc-14.29:default ]
+
+test:windows:x86-64:msvc-14.29:default:official:
+  extends: .test:windows:x86-64:msvc-14.29:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:windows:x86-64:msvc-14.29:default:unsupported:
+  extends: .test:windows:x86-64:msvc-14.29:default
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:windows:x86-64:msvc-14.29:avx2:
+  extends: .test:windows
+  needs: [ build:windows:x86-64:msvc-14.29:avx2 ]
+
+test:windows:x86-64:msvc-14.29:avx2:official:
+  extends: .test:windows:x86-64:msvc-14.29:avx2
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:windows:x86-64:msvc-14.29:avx2:unsupported:
+  extends: .test:windows:x86-64:msvc-14.29:avx2
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+.test:windows:x86-64:msvc-14.29:avx512dq:
+  extends: .test:windows
+  needs: [ build:windows:x86-64:msvc-14.29:avx512dq ]
+  tags: 
+    - eigen-runner
+    - windows
+    - x86-64
+    - avx512
+
+test:windows:x86-64:msvc-14.29:avx512dq:official:
+  extends: .test:windows:x86-64:msvc-14.29:avx512dq
+  variables:
+    EIGEN_CI_TEST_LABEL: Official
+
+test:windows:x86-64:msvc-14.29:avx512dq:unsupported:
+  extends: .test:windows:x86-64:msvc-14.29:avx512dq
+  variables:
+    EIGEN_CI_TEST_LABEL: Unsupported
+
+##### MSVC + CUDA ##############################################################
+.test:windows:cuda:
+  extends: .test:windows
+  allow_failure: true
+  variables:
+    EIGEN_CI_TEST_LABEL: gpu
+  tags: 
+    - eigen-runner
+    - windows
+    - x86-64
+    - cuda
+
+# MSVC 14.16 + CUDA 9.2
+test:windows:x86-64:cuda-9.2:msvc-14.16:
+  extends: .test:windows:cuda
+  needs: [ build:windows:x86-64:cuda-9.2:msvc-14.16 ]
+  
+# MSVC 14.29 + CUDA 10.2
+test:windows:x86-64:cuda-10.2:msvc-14.29:
+  extends: .test:windows:cuda
+  needs: [ build:windows:x86-64:cuda-10.2:msvc-14.29 ]
+  
+# MSVC 14.29 + CUDA 11.4
+test:windows:x86-64:cuda-11.4:msvc-14.29:
+  extends: .test:windows:cuda
+  needs: [ build:windows:x86-64:cuda-11.4:msvc-14.29 ]