diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8ea12e4a466353bb7e3111953fe2c5129e663e07..29600ba1e433aee3a724a0a28591922d4f19d95c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -79,6 +79,9 @@ variables:
   ## Local installation directory for Siconos
   SICONOS_INSTALL_PREFIX: $TESTSUITE_PREFIX_CACHE/siconos
 
+  ## Local installation directory for Trilinos
+  TRILINOS_INSTALL_PREFIX: $TESTSUITE_PREFIX_CACHE/trilinos
+
   ## Local installation directory for HDF5
   HDF5_INSTALL_PREFIX: $TESTSUITE_PREFIX_CACHE/HDF5
 
@@ -106,6 +109,7 @@ variables:
   MBD_BUILD_DIR_MANUALS: $TESTSUITE_PREFIX_BUILD/mbdyn-manuals
 
   SICONOS_BUILD_DIR: $TESTSUITE_PREFIX_BUILD/siconos
+  TRILINOS_BUILD_DIR: $TESTSUITE_PREFIX_BUILD/trilinos
 
   HDF5_BUILD_DIR: $TESTSUITE_PREFIX_BUILD/hdf5
   NC_C_BUILD_DIR: $TESTSUITE_PREFIX_BUILD/netcdf-c
@@ -183,7 +187,7 @@ variables:
   ## NOTE: This option is not intended for debugging, but just to see if any assertion will fail.
   ## NOTE: Do not use -Ofast because a few tests are using std::isfinite and std::numeric_limits<double>::nan()
   MBD_COMPILER_FLAGS_DEBUG: "-O3 -Wall -march=native -mtune=native -Wno-unused-variable"
-  
+
   JUNIT_XML_KEEP_ALL_OUTPUT:
     value: "none"
     options:
@@ -402,12 +406,12 @@ variables:
     description: "GNU/Octave interpreter"
 
   GTEST_OCTAVE_EXEC:
-    value: "octave-cli"
+    value: "gtest-octave-cli"
     options:
       - "octave"
       - "octave-cli"
       - "gtest-octave-cli"
-    description: "GNU/Octave interpreter"
+    description: "GNU/Octave interpreter linked to Google-Test"
 
   GTEST_REPOSITORY:
     value: "https://github.com/google/googletest.git"
@@ -458,6 +462,88 @@ variables:
       - "4.4.0"
     description: "Branch of the Siconos library (http://siconos.gforge.inria.fr) to be pulled"
 
+  TRILINOS_REPOSITORY:
+    value: "https://github.com/trilinos/Trilinos.git"
+    description: "Repository of the Trilinos library (https://github.com/trilinos/Trilinos.git) to be cloned"
+
+  TRILINOS_BRANCH:
+    value: "trilinos-release-14-4-0" ## FIXME: In the end, we should switch to "master"
+    options:
+      - "master"
+      - "trilinos-release-9-0-0"
+      - "trilinos-release-9-0-1"
+      - "trilinos-release-9-0-2"
+      - "trilinos-release-9-0-3"
+      - "trilinos-release-10-0-5"
+      - "trilinos-release-10-0-branch-init"
+      - "trilinos-release-10-10-0"
+      - "trilinos-release-10-10-1"
+      - "trilinos-release-10-10-2"
+      - "trilinos-release-10-10-3"
+      - "trilinos-release-10-12-1"
+      - "trilinos-release-10-12-2"
+      - "trilinos-release-10-2-0"
+      - "trilinos-release-10-2-1"
+      - "trilinos-release-10-2-2"
+      - "trilinos-release-10-4-1"
+      - "trilinos-release-10-4-2"
+      - "trilinos-release-10-6-0"
+      - "trilinos-release-10-6-1"
+      - "trilinos-release-10-6-2"
+      - "trilinos-release-10-6-3"
+      - "trilinos-release-10-6-4"
+      - "trilinos-release-10-8-0"
+      - "trilinos-release-10-8-1"
+      - "trilinos-release-10-8-2"
+      - "trilinos-release-10-8-3"
+      - "trilinos-release-10-8-4"
+      - "trilinos-release-10-8-5"
+      - "trilinos-release-11-0-1"
+      - "trilinos-release-11-0-2"
+      - "trilinos-release-11-0-3"
+      - "trilinos-release-11-0-4"
+      - "trilinos-release-11-10-1"
+      - "trilinos-release-11-12-1"
+      - "trilinos-release-11-14-1"
+      - "trilinos-release-11-14-2"
+      - "trilinos-release-11-14-3"
+      - "trilinos-release-11-2-2"
+      - "trilinos-release-11-2-3"
+      - "trilinos-release-11-2-4"
+      - "trilinos-release-11-2-5"
+      - "trilinos-release-11-4-1"
+      - "trilinos-release-11-4-2"
+      - "trilinos-release-11-6-1"
+      - "trilinos-release-11-6-2"
+      - "trilinos-release-11-8-1"
+      - "trilinos-release-12-0-1"
+      - "trilinos-release-12-10-1"
+      - "trilinos-release-12-12-1"
+      - "trilinos-release-12-14-1"
+      - "trilinos-release-12-18-1"
+      - "trilinos-release-12-2-1"
+      - "trilinos-release-12-2-2"
+      - "trilinos-release-12-4-1"
+      - "trilinos-release-12-4-2"
+      - "trilinos-release-12-6-1"
+      - "trilinos-release-12-6-2"
+      - "trilinos-release-12-6-3"
+      - "trilinos-release-12-6-4"
+      - "trilinos-release-12-8-1"
+      - "trilinos-release-13-0-0"
+      - "trilinos-release-13-0-1"
+      - "trilinos-release-13-2-0"
+      - "trilinos-release-13-4-0"
+      - "trilinos-release-13-4-1"
+      - "trilinos-release-14-0-0"
+      - "trilinos-release-14-2-0"
+      - "trilinos-release-14-4-0"
+      - "trilinos-release-15-0-0"
+      - "trilinos-release-15-1-0"
+      - "trilinos-release-15-1-1"
+      - "trilinos-release-16-0-0"
+    description: "Branch of the Trilinos library (https://github.com/trilinos/Trilinos.git) to be pulled"
+
   NLOPT_REPOSITORY:
     value: "https://github.com/stevengj/nlopt.git"
     description: "Repository of the NLopt library (https://nlopt.readthedocs.io) to be cloned"
@@ -524,6 +610,7 @@ include:
     - local: '/testsuite/mbdyn-checkout-job.yml'
     - local: '/testsuite/mkl-build-job.yml'
     - local: '/testsuite/siconos-build-job.yml'
+    - local: '/testsuite/trilinos-build-job.yml'
     - local: '/testsuite/hdf5-build-job.yml'
     - local: '/testsuite/netcdf-build-job.yml'
     - local: '/testsuite/nlopt-build-job.yml'
diff --git a/modules/module-octave/module-octave.cc b/modules/module-octave/module-octave.cc
index eb543747e4f58552250df6bdb0571c5ac462d58b..ef842e2d1bac15b4fd0ccefc424a5e0c94ddef6c 100644
--- a/modules/module-octave/module-octave.cc
+++ b/modules/module-octave/module-octave.cc
@@ -857,11 +857,13 @@ pHP(pHP)
 
 #if OCTAVE_MAJOR_VERSION >= 5
         interpreter.initialize ();
+
+#if OCTAVE_MAJOR_VERSION < 9
         if (! interpreter.is_initialized ()) {
                 silent_cerr("module-octave: initializing embedded Octave interpreter failed!" << std::endl);
           throw ErrGeneric(MBDYN_EXCEPT_ARGS);
         }
-        
+#endif
         int status = interpreter.execute ();
         if (status != 0) {
                 silent_cerr("module-octave: creating embedded Octave interpreter failed!" << std::endl);
diff --git a/testsuite/mbdyn-build-job.yml b/testsuite/mbdyn-build-job.yml
index e7e6c77c50ebe77503095623499a9c47cfb65b44..8f06e63173bac8b9dbd78de383a7c064b3f2ad1e 100644
--- a/testsuite/mbdyn-build-job.yml
+++ b/testsuite/mbdyn-build-job.yml
@@ -47,6 +47,8 @@ mbdyn-build-job:       # This job runs in the build stage, which runs first.
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: hdf5-build-job
       artifacts: true
     - job: netcdf-c-build-job
@@ -61,6 +63,8 @@ mbdyn-build-job:       # This job runs in the build stage, which runs first.
   script:
     - export MBD_INSTALL_PREFIX=${CI_PROJECT_DIR}/${MBD_INSTALL_PREFIX}
     - export SICONOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${SICONOS_INSTALL_PREFIX}
+    - export TRILINOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${TRILINOS_INSTALL_PREFIX}
+    - export TRILINOS_INC_DIR=${TRILINOS_INSTALL_PREFIX}/include
     - export HDF5_INSTALL_PREFIX=${CI_PROJECT_DIR}/${HDF5_INSTALL_PREFIX}
     - export NC_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_INSTALL_PREFIX}
     - export NC_CXX4_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_CXX4_INSTALL_PREFIX}
@@ -151,6 +155,8 @@ mbdyn-build-job-gcov:       # This job runs in the build stage, which runs first
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: hdf5-build-job
       artifacts: true
     - job: netcdf-c-build-job
@@ -166,6 +172,7 @@ mbdyn-build-job-gcov:       # This job runs in the build stage, which runs first
     - if: $MBD_TEST_COVERAGE_ENABLED == "yes"
   script:
     - export SICONOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${SICONOS_INSTALL_PREFIX}
+    - export TRILINOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${TRILINOS_INSTALL_PREFIX}
     - export HDF5_INSTALL_PREFIX=${CI_PROJECT_DIR}/${HDF5_INSTALL_PREFIX}
     - export NC_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_INSTALL_PREFIX}
     - export NC_CXX4_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_CXX4_INSTALL_PREFIX}
@@ -386,6 +393,8 @@ mbdyn-build-job-debug:       # This job runs in the build stage, which runs firs
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: hdf5-build-job
       artifacts: true
     - job: netcdf-c-build-job
@@ -402,6 +411,7 @@ mbdyn-build-job-debug:       # This job runs in the build stage, which runs firs
   script:
     - export MBD_INSTALL_PREFIX=${CI_PROJECT_DIR}/${MBD_INSTALL_PREFIX_DEBUG}
     - export SICONOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${SICONOS_INSTALL_PREFIX}
+    - export TRILINOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${TRILINOS_INSTALL_PREFIX}
     - export HDF5_INSTALL_PREFIX=${CI_PROJECT_DIR}/${HDF5_INSTALL_PREFIX}
     - export NC_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_INSTALL_PREFIX}
     - export NC_CXX4_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_CXX4_INSTALL_PREFIX}
diff --git a/testsuite/mbdyn-modules-test-job.yml b/testsuite/mbdyn-modules-test-job.yml
index 011076c539b4c5b5e1f8346a87c9568eb91de616..f6ccb535df12f2c9d81a4b8101f316a5ca6187ac 100644
--- a/testsuite/mbdyn-modules-test-job.yml
+++ b/testsuite/mbdyn-modules-test-job.yml
@@ -65,6 +65,8 @@ mbdyn-modules-test-job:   # This job runs in the test stage.
       - job: mkl-build-job
       - job: siconos-build-job
         artifacts: true
+      - job: trilinos-build-job
+        artifacts: true
       - job: hdf5-build-job
         artifacts: true
       - job: netcdf-c-build-job
@@ -127,6 +129,8 @@ mbdyn-modules-test-job-gcov:   # This job runs in the test stage.
       - job: mkl-build-job
       - job: siconos-build-job
         artifacts: true
+      - job: trilinos-build-job
+        artifacts: true
       - job: netcdf-c-build-job
         artifacts: true
       - job: netcdf-cxx4-build-job
@@ -170,7 +174,13 @@ mbdyn-modules-test-job-gcov:   # This job runs in the test stage.
       - sp_gradient_test --gtest_output=xml:${MBD_MODULE_TESTS_OUTPUT_DIR}/junit_xml_report_sp_gradient_test.xml >& /dev/null
       - ${TESTSUITE_TIME_CMD} testsuite/simple_testsuite.sh --use-reference-test-status "${MBD_MODULE_TESTS_USE_REFERENCE_TEST_STATUS}" --exit-status-mask "${MBD_MODULE_TESTS_EXIT_STATUS_MASK}" --abort-after-step "${MBD_MODULE_TESTS_ABORT_AFTER_STEP}" --timeout "${MBD_MODULE_TESTS_TIMEOUT}" --prefix-output "${MBD_MODULE_TESTS_OUTPUT_DIR}" --prefix-input "${MBD_MODULE_TESTS_INPUT_DIR}" --tasks "${MBD_MODULE_TESTS_NUM_TASKS}" ${MBD_SIMPLE_TESTSUITE_FLAGS} 2>&1 | tee -a "${log_file}" | awk -f testsuite/simple_testsuite_output_filter.awk
       - ${TESTSUITE_TIME_CMD} testsuite/simple_testsuite_patched.sh --use-reference-test-status "${MBD_MODULE_TESTS_USE_REFERENCE_TEST_STATUS}" --keep-output failed --abort-after "${MBD_MODULE_TESTS_ABORT_AFTER}" --linear-solvers "${MBD_MODULE_TESTS_LINEAR_SOLVERS}" --nonlinear-solvers "${MBD_MODULE_TESTS_NONLINEAR_SOLVERS}" --exit-status-mask "${MBD_MODULE_TESTS_EXIT_STATUS_MASK}" --timeout "${MBD_MODULE_TESTS_TIMEOUT}" --prefix-output "${MBD_MODULE_TESTS_OUTPUT_DIR}" --prefix-input "${MBD_MODULE_TESTS_INPUT_DIR}" --tasks "${MBD_MODULE_TESTS_NUM_TASKS}" ${MBD_SIMPLE_TESTSUITE_FLAGS} 2>&1 | tee -a "${log_file}"
-      - ${TESTSUITE_TIME_CMD} testsuite/octave_pkg_testsuite.sh --tasks "${OCT_PKG_TEST_JOB_NUM_TASKS}" --octave-pkg-list "${OCT_PKG_LIST}" --octave-pkg-test-dir "${OCT_PKG_TEST_OUTPUT_DIR}" --octave-pkg-test-mode "${OCT_PKG_TEST_MODE}" --octave-pkg-prefix "${OCT_PKG_INSTALL_PREFIX}" --octave-function-filter "${OCT_PKG_FUNCTION_FILTER}"  2>&1 | tee -a "${log_file}"
+      # - ${TESTSUITE_TIME_CMD} testsuite/octave_pkg_testsuite.sh --tasks "${OCT_PKG_TEST_JOB_NUM_TASKS}"
+      # --octave-pkg-list "${OCT_PKG_LIST}"
+      # --octave-pkg-test-dir "${OCT_PKG_TEST_OUTPUT_DIR}"
+      # --octave-pkg-test-mode "${OCT_PKG_TEST_MODE}"
+      # --octave-pkg-prefix "${OCT_PKG_INSTALL_PREFIX}" ##
+      # --octave-function-filter "${OCT_PKG_FUNCTION_FILTER}"  2>&1 | tee -a "${log_file}"
+      - ${TESTSUITE_TIME_CMD} ${GTEST_OCTAVE_EXEC} -q ${OCT_PKG_TESTSUITE_SCRIPT} --octave-pkg-prefix "${OCT_PKG_INSTALL_PREFIX}" --octave-pkg-list "${OCT_PKG_LIST}" --octave-pkg-test-dir "${OCT_PKG_TEST_OUTPUT_DIR}" --octave-exec "${GTEST_OCTAVE_EXEC}" --tasks "${OCT_PKG_TEST_JOB_NUM_TASKS}" 2>&1 | tee -a "${log_file}"
   artifacts:
      expire_in: 2 days
      name: mbdyn-modules-test-job-gcov
@@ -189,6 +199,8 @@ mbdyn-modules-test-job-debug:   # This job runs in the test stage.
       - job: mkl-build-job
       - job: siconos-build-job
         artifacts: true
+      - job: trilinos-build-job
+        artifacts: true
       - job: netcdf-c-build-job
         artifacts: true
       - job: netcdf-cxx4-build-job
diff --git a/testsuite/mbdyn-tests-private-test-job.yml b/testsuite/mbdyn-tests-private-test-job.yml
index 1a53958bf61fd4be68c7e5c29cbc9a1fb1424034..436ae9d3faab92570b9429493f9f248a66ddf77a 100644
--- a/testsuite/mbdyn-tests-private-test-job.yml
+++ b/testsuite/mbdyn-tests-private-test-job.yml
@@ -66,6 +66,8 @@ mbdyn-tests-private-test-job:   # This job runs in the test stage.
   needs:
      - job: siconos-build-job
        artifacts: true
+     - job: trilinos-build-job
+       artifacts: true
      - job: mkl-build-job
        artifacts: true
      - job: hdf5-build-job
@@ -123,6 +125,8 @@ mbdyn-tests-private-test-job-gcov:   # This job runs in the test stage.
        artifacts: true
      - job: siconos-build-job
        artifacts: true
+     - job: trilinos-build-job
+       artifacts: true
      - job: netcdf-c-build-job
        artifacts: true
      - job: netcdf-cxx4-build-job
@@ -179,6 +183,8 @@ mbdyn-tests-private-test-job-debug:   # This job runs in the test stage.
   needs:
      - job: siconos-build-job
        artifacts: true
+     - job: trilinos-build-job
+       artifacts: true
      - job: mkl-build-job
        artifacts: true
      - job: netcdf-c-build-job
diff --git a/testsuite/mbdyn-tests-public-test-job.yml b/testsuite/mbdyn-tests-public-test-job.yml
index 882c750481d7e39514deeb5fef18953eab754830..1fcf8bdc5fa1b066a2a1665eaab621d02332064e 100644
--- a/testsuite/mbdyn-tests-public-test-job.yml
+++ b/testsuite/mbdyn-tests-public-test-job.yml
@@ -60,6 +60,8 @@ mbdyn-tests-public-test-job:   # This job runs in the test stage.
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: hdf5-build-job
       artifacts: true
     - job: netcdf-c-build-job
@@ -115,6 +117,8 @@ mbdyn-tests-public-test-job-gcov:   # This job runs in the test stage.
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: netcdf-c-build-job
       artifacts: true
     - job: netcdf-cxx4-build-job
@@ -169,6 +173,8 @@ mbdyn-tests-public-test-job-debug:   # This job runs in the test stage.
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: netcdf-c-build-job
       artifacts: true
     - job: netcdf-cxx4-build-job
diff --git a/testsuite/mbdyn-testsuite-report-job.yml b/testsuite/mbdyn-testsuite-report-job.yml
index c557553e797827f0a8db503faf88c783fcaae805..5c958cbf14dd2d8e43e18853624605c88c3dfe84 100644
--- a/testsuite/mbdyn-testsuite-report-job.yml
+++ b/testsuite/mbdyn-testsuite-report-job.yml
@@ -52,6 +52,8 @@ mbdyn-testsuite-report-job-coverage:
   needs:
     - job: siconos-build-job
       artifacts: true
+    - job: trilinos-build-job
+      artifacts: true
     - job: netcdf-c-build-job
       artifacts: true
     - job: netcdf-cxx4-build-job
diff --git a/testsuite/mbdyn_build_job.sh b/testsuite/mbdyn_build_job.sh
index ffbd2834d92186b79bdba7da2d820b09d0b65761..710255a089b0d1e9429414575e236709593ef0b2 100755
--- a/testsuite/mbdyn_build_job.sh
+++ b/testsuite/mbdyn_build_job.sh
@@ -285,8 +285,18 @@ if test -d "${SUITESPARSE_INC_DIR}"; then
     CPPFLAGS="-I${SUITESPARSE_INC_DIR} ${CPPFLAGS}"
 fi
 
-if test -d "${TRILINOS_INC_DIR}"; then
-    CPPFLAGS="-I${TRILINOS_INC_DIR} ${CPPFLAGS}"
+if test -d "${TRILINOS_INSTALL_PREFIX}"; then
+    TRILINOS_INC_DIR="${TRILINOS_INC_DIR:-${TRILINOS_INSTALL_PREFIX}/include}"
+
+    if test -d "${TRILINOS_INC_DIR}"; then
+        CPPFLAGS="-I${TRILINOS_INC_DIR} ${CPPFLAGS}"
+    fi
+    
+    TRILINOS_LIB_DIR="${TRILINOS_LIB_DIR:-${TRILINOS_INSTALL_PREFIX}/lib}"
+
+    if test -d "${TRILINOS_LIB_DIR}"; then
+        LDFLAGS="-L${TRILINOS_LIB_DIR} -Wl,-rpath=${TRILINOS_LIB_DIR} ${LDFLAGS}"
+    fi
 fi
 
 if test -d "${NUMPY_INC_DIR}"; then
diff --git a/testsuite/octave-pkg-build-job.yml b/testsuite/octave-pkg-build-job.yml
index 7b5057ba6ac289aabe6741048c8a33e73f59d74c..bbd48f5917f0b5892deeb34ffcaf947c09692438 100644
--- a/testsuite/octave-pkg-build-job.yml
+++ b/testsuite/octave-pkg-build-job.yml
@@ -80,6 +80,8 @@ octave-pkg-build-job-gcov:       # This job runs in the build stage, which runs
   needs:
       - job: siconos-build-job
         artifacts: true
+      - job: trilinos-build-job
+        artifacts: true
       - job: mkl-build-job
       - job: hdf5-build-job
         artifacts: true
@@ -94,7 +96,7 @@ octave-pkg-build-job-gcov:       # This job runs in the build stage, which runs
       - job: mbdyn-build-job-gcov
         artifacts: true
   rules:
-      - if: $MBD_TEST_COVERAGE_ENABLED == "yes"        
+      - if: $MBD_TEST_COVERAGE_ENABLED == "yes"
   script:
         - export MKL_INSTALL_PREFIX=${HOME}/${MKL_INSTALL_PREFIX}
         - export MKL_PKG_CONFIG
@@ -103,7 +105,7 @@ octave-pkg-build-job-gcov:       # This job runs in the build stage, which runs
         - export NC_INSTALL_PREFIX=${CI_PROJECT_DIR}/${NC_INSTALL_PREFIX}
         - export OCT_PKG_BUILD_DIR=${CI_PROJECT_DIR}/${OCT_PKG_BUILD_DIR_GCOV}
         - export OCT_PKG_INSTALL_PREFIX=${CI_PROJECT_DIR}/${OCT_PKG_INSTALL_PREFIX_GCOV}
-        - export GTEST_INSTALL_PREFIX=${CI_PROJECT_DIR}/${GTEST_INSTALL_PREFIX}        
+        - export GTEST_INSTALL_PREFIX=${CI_PROJECT_DIR}/${GTEST_INSTALL_PREFIX}
         - export OCTAVE_EXEC
         - export OCT_PKG_LIST
         - export MBD_COMPILER_FLAGS="${MBD_COMPILER_FLAGS_GCOV}"
diff --git a/testsuite/octave-pkg-test-job.yml b/testsuite/octave-pkg-test-job.yml
index 612f4471aaddfb03b59482cf943d54474ec339dd..439382222842adea459b53197480c1f3a0284eae 100644
--- a/testsuite/octave-pkg-test-job.yml
+++ b/testsuite/octave-pkg-test-job.yml
@@ -45,6 +45,8 @@ octave-pkg-test-job:   # This job runs in the test stage.
       - job: mkl-build-job
       - job: siconos-build-job
         artifacts: true
+      - job: trilinos-build-job
+        artifacts: true
       - job: hdf5-build-job
         artifacts: true
       - job: netcdf-c-build-job
@@ -66,6 +68,7 @@ octave-pkg-test-job:   # This job runs in the test stage.
       - OCT_PKG_INSTALL_PREFIX=${CI_PROJECT_DIR}/${OCT_PKG_INSTALL_PREFIX}
       - GMSH_INSTALL_PREFIX=${CI_PROJECT_DIR}/${GMSH_INSTALL_PREFIX}
       - OCT_PKG_TEST_OUTPUT_DIR=${CI_PROJECT_DIR}/${OCT_PKG_TEST_OUTPUT_DIR}
+      - OCT_PKG_TESTSUITE_SCRIPT="${CI_PROJECT_DIR}/testsuite/octave_pkg_testsuite.m"
       - export PATH=${MBD_INSTALL_PREFIX}/bin:${GMSH_INSTALL_PREFIX}/bin:${OCT_PKG_INSTALL_PREFIX}/bin:${PATH}
       - export AWKPATH=${MBD_INSTALL_PREFIX}/share/awk:${AWKPATH}
       - export TESTSUITE_TIME_CMD
@@ -83,7 +86,6 @@ octave-pkg-test-job:   # This job runs in the test stage.
       - which gmsh | tee -a "${log_file}"
       - gmsh --version | tee -a "${log_file}"
       - which octave | tee -a "${log_file}"
-      - gtest_octave_cli_test_func='__run_test_suite__'
       - |
         case "${OCTAVE_EXEC}" in
           gtest-*)
@@ -93,9 +95,12 @@ octave-pkg-test-job:   # This job runs in the test stage.
             gtest_octave_cli_flags=""
             ;;
         esac
-      # - ${OCTAVE_EXEC} ${gtest_octave_cli_flags} -qfH --eval "${gtest_octave_cli_test_func}" | tee -a "${log_file}"
-      - chmod +x testsuite/octave_pkg_testsuite.sh
-      - ${TESTSUITE_TIME_CMD} testsuite/octave_pkg_testsuite.sh --tasks "${OCT_PKG_TEST_JOB_NUM_TASKS}" --octave-pkg-list "${OCT_PKG_LIST}" --octave-pkg-test-dir "${OCT_PKG_TEST_OUTPUT_DIR}" --octave-pkg-test-mode "${OCT_PKG_TEST_MODE}" --octave-pkg-prefix "${OCT_PKG_INSTALL_PREFIX}" --octave-function-filter "${OCT_PKG_FUNCTION_FILTER}"  2>&1 | tee -a "${log_file}"
+      - ${GTEST_OCTAVE_EXEC} -q ${OCT_PKG_TESTSUITE_SCRIPT} --octave-pkg-list "${OCT_PKG_LIST}" --octave-pkg-test-dir "${OCT_PKG_TEST_OUTPUT_DIR}" --octave-exec "${GTEST_OCTAVE_EXEC}" --tasks "${OCT_PKG_TEST_JOB_NUM_TASKS}"
+
+      ## FIXME: remove legacy code ...
+      # - chmod +x testsuite/octave_pkg_testsuite.sh
+      # - ${TESTSUITE_TIME_CMD} testsuite/octave_pkg_testsuite.sh --tasks "${OCT_PKG_TEST_JOB_NUM_TASKS}" --octave-pkg-list "${OCT_PKG_LIST}" --octave-pkg-test-dir "${OCT_PKG_TEST_OUTPUT_DIR}" --octave-pkg-test-mode "${OCT_PKG_TEST_MODE}" --octave-pkg-prefix "${OCT_PKG_INSTALL_PREFIX}" --octave-function-filter "${OCT_PKG_FUNCTION_FILTER}"  2>&1 | tee -a "${log_file}"
+
   artifacts:
     name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}
     expire_in: 2 days
diff --git a/testsuite/octave_pkg_testsuite.m b/testsuite/octave_pkg_testsuite.m
new file mode 100644
index 0000000000000000000000000000000000000000..2a0f8e05b7e99418a4083d6c49b8ed0b6a7ee7fd
--- /dev/null
+++ b/testsuite/octave_pkg_testsuite.m
@@ -0,0 +1,186 @@
+#!/usr/bin/env gtest-octave-cli
+
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2023
+#
+# Pierangelo Masarati	<pierangelo.masarati@polimi.it>
+# Paolo Mantegazza	<paolo.mantegazza@polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# AUTHOR: Reinhard Resch <mbdyn-user@a1.net>
+# Copyright (C) 2024(-2024) all rights reserved.
+
+# The copyright of this code is transferred
+# to Pierangelo Masarati and Paolo Mantegazza
+# for use in the software MBDyn as described
+# in the GNU Public License version 2.1
+
+clear all;
+
+total.N = 0;
+total.NMAX = 0;
+total.NREGRESSION = 0;
+total_pkg = total([]);
+test_data.pkg_name = {};
+test_data.octave_pkg_test_dir = "";
+test_data.mbdyn_exec = "mbdyn";
+test_data.mbdyn_args_add ="-CGF";
+test_data.octave_pkg_prefix = [];
+
+try
+  [prog_dir, prog_name, prog_ext] = fileparts(__FILE__);
+
+  addpath(prog_dir);
+
+  args = argv();
+
+  pkg load mboct-octave-pkg;
+
+  output_file = "";
+
+  idx = int32(0);
+
+  opts.number_of_processors = int32(1);
+  opts.reuse_subprocess = false;
+  opts.verbose = false;
+  opts.user_hook_func = @octave_pkg_testsuite_hook;
+
+  while (++idx < numel(args))
+    switch(args{idx})
+      case {"--octave-pkg-list", "-p"}
+        pkg_list = strsplit(args{++idx});
+        add_pkg_name = cell(1, numel(pkg_list));
+        num_pkg = int32(0);
+        for i=1:numel(pkg_list)
+          pkg_flags = strsplit(pkg_list{i}, ":");
+          switch (pkg_flags{4})
+            case "yes"
+              add_pkg_name{++num_pkg} = pkg_flags{1};
+          endswitch
+        endfor
+        test_data.pkg_name = {test_data.pkg_name{:}, add_pkg_name{1:num_pkg}};
+      case "--octave-exec"
+        opts.octave_exec = args{++idx};
+      case "--octave-args-append"
+        opts.octave_args_append = args{++idx};
+      case "--octave-pkg-test-dir"
+        test_data.octave_pkg_test_dir = make_absolute_filename(args{++idx});
+      case "--verbose"
+        opts.verbose = true;
+      case "--mbdyn-exec"
+        test_data.mbdyn_exec = args{++idx};
+      case "--mbdyn-args-add"
+        test_data.mbdyn_args_add = args{++idx};
+      case "--octave-pkg-prefix"
+        test_data.octave_pkg_prefix = args{++idx};
+      case {"--tasks", "-t"}
+        [opts.number_of_processors, cnt, msg] = sscanf(args{++idx}, "%d", "C");
+
+        if (cnt ~= 1)
+          error("invalid argument %s \"%s\": %s", args{idx - 1}, args{idx}, msg);
+        endif
+      case {"--help", "-h"}
+        fprintf(stderr, "octave_pkg_testsuite.m\n");
+        fprintf(stderr, "\t\t\t--package-name|-p <test_data.pkg_name>\n");
+        return;
+      otherwise
+        error("unknown argument \"%s\"", args{idx});
+    endswitch
+  endwhile
+
+  if (isempty(test_data.pkg_name))
+    error("missing argument --package-name <PKG_NAME>");
+  endif
+
+  if (isempty(test_data.octave_pkg_test_dir))
+    error("missing argument --octave-pkg-test-dir <DIR_NAME>");
+  endif
+
+  if (~isempty(test_data.octave_pkg_prefix))
+    pkg_list_type = "local_list";
+    fprintf(stderr, "\npkg(\"%s\", \"%s\");\n\n", pkg_list_type, test_data.octave_pkg_prefix);
+    pkg(pkg_list_type, test_data.octave_pkg_prefix);
+  endif
+
+  sigterm_dumps_octave_core(false);
+
+  test_data.pkg_functions = {};
+  test_data.pkg_index = zeros(0, 0, "int32");
+
+  for j=1:numel(test_data.pkg_name)
+    pkg("load", test_data.pkg_name{j});
+    pkg_list = pkg('list', '-verbose', test_data.pkg_name{j});
+    pkg_files = {dir(fullfile(pkg_list{1}.dir, '*.m')).name, dir(fullfile(pkg_list{1}.dir, '*.tst')).name};
+
+    test_data.pkg_index(end + (1:numel(pkg_files))) = repmat(int32(j), 1, numel(pkg_files));
+    test_data.pkg_functions = {test_data.pkg_functions{:}, pkg_files{:}};
+  endfor
+
+  test_data.enable_profile = true;
+  test_data.test_args = {"normal"};
+
+  opts.number_of_parameters = numel(test_data.pkg_functions);
+  opts.gtest_output_junit_xml = fullfile(test_data.octave_pkg_test_dir, '%d', 'junit_xml_report_octave_assert.xml');
+  opts.redirect_stdout = fullfile(test_data.octave_pkg_test_dir, '%d', 'fntests.out');
+
+  status = run_parallel(opts, @octave_pkg_testsuite_exec, test_data);
+
+  total_pkg = repmat(total, 1, numel(test_data.pkg_name));
+
+  for i=1:numel(status)
+    NREGRESSION = status{i}.test.NMAX - status{i}.test.N;
+    total_pkg(status{i}.pkg_index).N += status{i}.test.N;
+    total_pkg(status{i}.pkg_index).NMAX += status{i}.test.NMAX;
+    total_pkg(status{i}.pkg_index).NREGRESSION += NREGRESSION;
+
+    total.N += status{i}.test.N;
+    total.NMAX += status{i}.test.NMAX;
+    total.NREGRESSION += NREGRESSION;
+
+    printf("%3d: test(\"%s\":\"%s\"): %2d/%2d passed, %2d/%2d failed)\n", i, test_data.pkg_name{status{i}.pkg_index}, status{i}.pkg_function, status{i}.test.N, status{i}.test.NMAX, NREGRESSION, status{i}.test.NMAX);
+  endfor
+catch
+  gtest_fail(lasterror(), __FILE__);
+  printf("%s FAILED\n", __FILE__);
+  exit(1);
+end_try_catch
+
+if (total.NREGRESSION > 0)
+  rc = 1;
+else
+  rc = 0;
+endif
+
+printf("\nTest summary per package:\n\n");
+for i=1:numel(total_pkg)
+  printf("%2d: Test summary \"%s\":\n", i, test_data.pkg_name{i});
+  printf("%3d/%3d tests passed\n", total_pkg(i).N, total_pkg(i).NMAX);
+  printf("%3d/%3d tests failed\n\n", total_pkg(i).NREGRESSION, total_pkg(i).NMAX);
+endfor
+
+printf("\nTest summary for all packages:\n");
+printf("%3d/%3d tests passed\n", total.N, total.NMAX);
+printf("%3d/%3d tests failed\n\n", total.NREGRESSION, total.NMAX);
+
+exit(rc);
diff --git a/testsuite/octave_pkg_testsuite.sh b/testsuite/octave_pkg_testsuite.sh
index 9dedc4c32615c34b322dd9d815569898f5651e73..75ab3583edb06f563341269e9e9e55399806f5cd 100755
--- a/testsuite/octave_pkg_testsuite.sh
+++ b/testsuite/octave_pkg_testsuite.sh
@@ -275,7 +275,7 @@ function octave_pkg_testsuite_run()
             ;;
     esac
 
-    OCTAVE_CMD=$(printf '%s%s%s%s %s --eval %s' "${TIMEOUT_CMD}" "${octave_pkg_timing_cmd}" "${OCTAVE_EXEC}" "${octave_pkg_gtest_flags}" "${OCTAVE_CMD_ARGS}" "${octave_code_cmd}")
+    OCTAVE_CMD=$(printf '%s%s%s%s %s %s' "${TIMEOUT_CMD}" "${octave_pkg_timing_cmd}" "${OCTAVE_EXEC}" "${octave_pkg_gtest_flags}" "${OCTAVE_CMD_ARGS}" "${octave_code_cmd}")
 
     case "${OCT_PKG_PRINT_RES}" in
         all|*disk*)
@@ -446,16 +446,17 @@ for pkgname_and_flags in ${OCT_PKG_LIST}; do
         oct_pkg_prefix_cmd=$(printf "pkg('local_list','%s');" "${OCT_PKG_INSTALL_PREFIX}/octave_packages")
     fi
 
-    oct_pkg_sigterm_dumps_core="sigterm_dumps_octave_core(false);"
-    oct_pkg_load_cmd=$(printf "pkg('load','%s');" "${pkgname}")
-    oct_pkg_list_cmd=$(printf "p=pkg('list','%s');" "${pkgname}")
-    oct_pkg_run_test_suite_cmd="[PASS,FAIL,XFAIL,XBUG,SKIP,RTSKIP,REGRESS]=__run_test_suite__({p{1}.dir},{p{1}.dir});if(REGRESS);exit(1);end;"
+    # oct_pkg_sigterm_dumps_core="sigterm_dumps_octave_core(false);"
+    # oct_pkg_load_cmd=$(printf "pkg('load','%s');" "${pkgname}")
+    # oct_pkg_list_cmd=$(printf "p=pkg('list','%s');" "${pkgname}")
+    # oct_pkg_run_test_suite_cmd="[PASS,FAIL,XFAIL,XBUG,SKIP,RTSKIP,REGRESS]=__run_test_suite__({p{1}.dir},{p{1}.dir});if(REGRESS);exit(1);end;"
 
     case "${OCT_PKG_TEST_MODE}" in
         pkg)
-            oct_pkg_profile_data="${OCT_PKG_TEST_DIR}/oct_pkg_profile_data_${octave_pkg_testsuite_pid}_${pkgname}.mat"
-            oct_pkg_profile_off_cmd=$(printf "${oct_pkg_profile_off_fmt}" "${oct_pkg_profile_data}")
-            OCTAVE_CODE="${oct_pkg_sigterm_dumps_core}${oct_pkg_prefix_cmd}${oct_pkg_load_cmd}${oct_pkg_list_cmd}${oct_pkg_profile_on_cmd}${oct_pkg_run_test_suite_cmd}${oct_pkg_profile_off_cmd}"
+            # oct_pkg_profile_data="${OCT_PKG_TEST_DIR}/oct_pkg_profile_data_${octave_pkg_testsuite_pid}_${pkgname}.mat"
+            # oct_pkg_profile_off_cmd=$(printf "${oct_pkg_profile_off_fmt}" "${oct_pkg_profile_data}")
+            # OCTAVE_CODE="${oct_pkg_sigterm_dumps_core}${oct_pkg_prefix_cmd}${oct_pkg_load_cmd}${oct_pkg_list_cmd}${oct_pkg_profile_on_cmd}${oct_pkg_run_test_suite_cmd}${oct_pkg_profile_off_cmd}"
+            OCTAVE_CODE="__run_test_suite__"
             ;;
         single)
             OCTAVE_CMD_FUNCTIONS=$(printf "p=pkg('list','-verbose','%s');dir(fullfile(p{1}.dir,'*.m'));dir(fullfile(p{1}.dir,'*.tst'));" "${pkgname}")
@@ -469,10 +470,11 @@ for pkgname_and_flags in ${OCT_PKG_LIST}; do
             ((oct_pkg_func_index=0))
             for pkg_function_name in ${OCTAVE_PKG_FUNCTIONS}; do
                 ((++oct_pkg_func_index))
-                oct_pkg_test_function_cmd=$(printf "[N,NMAX,NXFAIL,NBUG,NSKIP,NRTSKIP,NREGRESSION]=test('%s','verbose','octave_pkg_testsuite_test_%03d_%s');if(NREGRESSION);exit(1);end;" "${pkg_function_name}" "${oct_pkg_func_index}" "${pkg_function_name}")
-                oct_pkg_profile_data=`printf '%s/oct_pkg_profile_data_%d_%s_%03d.mat' "${OCT_PKG_TEST_DIR}" ${octave_pkg_testsuite_pid} "${pkgname}" $((oct_pkg_func_index))`
-                oct_pkg_profile_off_cmd=$(printf "${oct_pkg_profile_off_fmt}" "${oct_pkg_profile_data}")
-                OCTAVE_CODE="${OCTAVE_CODE} ${oct_pkg_sigterm_dumps_core}${oct_pkg_prefix_cmd}${oct_pkg_load_cmd}${oct_pkg_profile_on_cmd}${oct_pkg_test_function_cmd}${oct_pkg_profile_off_cmd}"
+                # oct_pkg_test_function_cmd=$(printf "test('%s');" "${pkg_function_name}")
+                # oct_pkg_profile_data=`printf '%s/oct_pkg_profile_data_%d_%s_%03d.mat' "${OCT_PKG_TEST_DIR}" ${octave_pkg_testsuite_pid} "${pkgname}" $((oct_pkg_func_index))`
+                # oct_pkg_profile_off_cmd=$(printf "${oct_pkg_profile_off_fmt}" "${oct_pkg_profile_data}")
+                # OCTAVE_CODE="${OCTAVE_CODE} ${oct_pkg_sigterm_dumps_core}${oct_pkg_prefix_cmd}${oct_pkg_load_cmd}${oct_pkg_profile_on_cmd}${oct_pkg_test_function_cmd}${oct_pkg_profile_off_cmd}"
+                OCTAVE_CODE="${OCTAVE_CODE} ${pkg_function_name}"
             done
             ;;
         *)
diff --git a/testsuite/octave_pkg_testsuite_exec.m b/testsuite/octave_pkg_testsuite_exec.m
new file mode 100644
index 0000000000000000000000000000000000000000..5fc388f60e3bd81f473ee10f07486664b7eb9891
--- /dev/null
+++ b/testsuite/octave_pkg_testsuite_exec.m
@@ -0,0 +1,86 @@
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2023
+#
+# Pierangelo Masarati	<pierangelo.masarati@polimi.it>
+# Paolo Mantegazza	<paolo.mantegazza@polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# AUTHOR: Reinhard Resch <mbdyn-user@a1.net>
+# Copyright (C) 2024(-2024) all rights reserved.
+
+# The copyright of this code is transferred
+# to Pierangelo Masarati and Paolo Mantegazza
+# for use in the software MBDyn as described
+# in the GNU Public License version 2.1
+
+function status = octave_pkg_testsuite_exec(idx, test_data)
+  status = struct();
+
+  try
+    printf("%d:%s\n", idx, test_data.pkg_functions{idx});
+
+    sigterm_dumps_octave_core(false);
+
+    status.pkg_index = test_data.pkg_index(idx);
+    status.pkg_function = test_data.pkg_functions{idx};
+
+    output_dir = fullfile(test_data.octave_pkg_test_dir, sprintf('%d', idx));
+
+    ## NOTE: mboct-mbdyn-pkg will fill use the format "%03d", just in case that multiple instances of MBDyn will be started from the same test function.
+
+    junit_xml_report_file = fullfile(output_dir, "junit_xml_report_octave_%03d.xml");
+    mbdyn_solver_command = sprintf("%s %s --gtest_output=xml:%s", test_data.mbdyn_exec, test_data.mbdyn_args_add, junit_xml_report_file);
+    mbdyn_solver_command_var_name = "MBOCT_MBDYN_PKG_MBDYN_SOLVER_COMMAND";
+
+    printf("%s=%s\n", mbdyn_solver_command_var_name, mbdyn_solver_command);
+
+    putenv(mbdyn_solver_command_var_name, mbdyn_solver_command);
+
+    if (test_data.enable_profile)
+      profile('off');
+      profile('clear');
+      profile('on');
+    endif
+
+    printf("Run test %d: \"%s\" ...\n", idx, test_data.pkg_functions{idx});
+
+    [status.test.N, status.test.NMAX] = test(test_data.pkg_functions{idx}, test_data.test_args{:});
+
+    printf("Exit status of test %d: \"%s\"\n", idx, test_data.pkg_functions{idx});
+    printf("N=%d\n", status.test.N);
+    printf("NMAX=%d\n", status.test.NMAX);
+
+    if (test_data.enable_profile)
+      profile('off');
+
+      status.prof = profile('info');
+
+      profile('clear');
+    endif
+  catch
+    err = lasterror();
+    error_report(err);
+    rethrow(err);
+  end_try_catch
+endfunction
diff --git a/testsuite/octave_pkg_testsuite_hook.m b/testsuite/octave_pkg_testsuite_hook.m
new file mode 100644
index 0000000000000000000000000000000000000000..0b7e980c9764b28070285cd4b8f6378c300a19b7
--- /dev/null
+++ b/testsuite/octave_pkg_testsuite_hook.m
@@ -0,0 +1,53 @@
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2023
+#
+# Pierangelo Masarati	<pierangelo.masarati@polimi.it>
+# Paolo Mantegazza	<paolo.mantegazza@polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# AUTHOR: Reinhard Resch <mbdyn-user@a1.net>
+# Copyright (C) 2024(-2024) all rights reserved.
+
+# The copyright of this code is transferred
+# to Pierangelo Masarati and Paolo Mantegazza
+# for use in the software MBDyn as described
+# in the GNU Public License version 2.1
+
+function octave_pkg_testsuite_hook(idx, flags, test_data, pid, status)
+  switch (flags)
+    case "pre:spawn"      
+      printf("%d:%s:%s:%s\n", idx, flags, test_data.pkg_name{test_data.pkg_index(idx)}, test_data.pkg_functions{idx});
+      output_dir = fullfile(test_data.octave_pkg_test_dir, sprintf('%d', idx));
+
+      [status, msg] = mkdir(output_dir);
+
+      if (~status)
+        error("failed to create directory \"%s\"", output_dir);
+      endif
+
+      putenv("TMPDIR", output_dir);
+    case "post:spawn"
+      printf("%d:%s:%s:%s:pid=%d:status=%d\n", idx, flags, test_data.pkg_name{test_data.pkg_index(idx)}, test_data.pkg_functions{idx}, pid, status);
+  endswitch
+endfunction
diff --git a/testsuite/trilinos-build-job.yml b/testsuite/trilinos-build-job.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d131f1df8373be31242b3a9fc481324fd86e3586
--- /dev/null
+++ b/testsuite/trilinos-build-job.yml
@@ -0,0 +1,121 @@
+# MBDyn (C) is a multibody analysis code.
+# http://www.mbdyn.org
+#
+# Copyright (C) 1996-2023
+#
+# Pierangelo Masarati	<pierangelo.masarati@polimi.it>
+# Paolo Mantegazza	<paolo.mantegazza@polimi.it>
+#
+# Dipartimento di Ingegneria Aerospaziale - Politecnico di Milano
+# via La Masa, 34 - 20156 Milano, Italy
+# http://www.aero.polimi.it
+#
+# Changing this copyright notice is forbidden.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation (version 2 of the License).
+#
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# AUTHOR: Reinhard Resch <mbdyn-user@a1.net>
+# Copyright (C) 2023(-2023) all rights reserved.
+
+# The copyright of this code is transferred
+# to Pierangelo Masarati and Paolo Mantegazza
+# for use in the software MBDyn as described
+# in the GNU Public License version 2.1
+
+trilinos-build-job:       # This job runs in the build stage, which runs first.
+  stage: build
+  needs:
+      - job: mbdyn-cleanup-job
+      - job: mkl-build-job
+        artifacts: true
+  variables:
+      UMFPACK_INCLUDE_DIR: "/usr/include/suitesparse"
+      UMFPACK_LIBRARY: "/usr/lib64/libumfpack.so"
+      TRILINOS_CONFIG: "-DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DTrilinos_ENABLE_NOX=ON -DTrilinos_ENABLE_Epetra=ON -DTrilinos_ENABLE_EpetraExt=ON -DTrilinos_ENABLE_Amesos=ON -DTrilinos_ENABLE_AztecOO=ON -DEpetra_ENABLE_MPI=OFF -DNOX_ENABLE_Epetra=ON -DNOX_ENABLE_EpetraExt=ON -DNOX_ENABLE_ABSTRACT_IMPLEMENTATION_EPETRA=ON -DNOX_ENABLE_AztecOO=ON -DNOX_ENABLE_Ifpack=ON -DTrilinos_ENABLE_TESTS=OFF"
+  script:
+      - echo "trilinos installation job"
+      - TRILINOS_INSTALL_PREFIX=${CI_PROJECT_DIR}/${TRILINOS_INSTALL_PREFIX}
+      - TRILINOS_BUILD_DIR=${CI_PROJECT_DIR}/${TRILINOS_BUILD_DIR}
+      - MKL_INSTALL_PREFIX=${HOME}/${MKL_INSTALL_PREFIX}
+      - echo "Detecting MKL ..."
+      - |
+        if test -d "${MKL_INSTALL_PREFIX}"; then
+          echo "Search for ${MKL_PKG_CONFIG}.pc ..."
+          MKL_PKG_CONFIG_FILE=`find "${MKL_INSTALL_PREFIX}" '(' -type f -and -name "${MKL_PKG_CONFIG}.pc" ')'`
+          if test -f "${MKL_PKG_CONFIG_FILE}"; then
+            MKL_PKG_CONFIG_PATH=`dirname "${MKL_PKG_CONFIG_FILE}"`
+            echo "MKL_PKG_CONFIG_PATH=${MKL_PKG_CONFIG_PATH}"
+          else
+            echo "File ${MKL_PKG_CONFIG}.pc not found"
+          fi
+        fi
+      - |
+        if test -d "${MKL_PKG_CONFIG_PATH}"; then
+          export PKG_CONFIG_PATH="${MKL_PKG_CONFIG_PATH}:${PKG_CONFIG_PATH}"
+        else
+          echo "Warning: MKL_PKG_CONFIG_PATH does not exist"
+        fi
+      - |
+        if pkg-config --validate "${MKL_PKG_CONFIG}"; then
+          if test -z "${PARDISO_INC}"; then
+            export PARDISO_INC=`pkg-config --cflags-only-I ${MKL_PKG_CONFIG} | sed 's/^-I\>//g'`
+          fi
+          if test -z "${PARDISO_LIBS}"; then
+            export PARDISO_LIBS=`pkg-config --libs ${MKL_PKG_CONFIG}`
+            export PARDISO_LIBS="${PARDISO_LIBS} `pkg-config --libs-only-L ${MKL_PKG_CONFIG} | sed  's/^-L\//-Wl,-rpath=\//g'`"
+          fi
+        else
+          echo "Warning: MKL pkg-config ${MKL_PKG_CONFIG} is not valid"
+        fi
+      - |
+        if test -d "${TRILINOS_INSTALL_PREFIX}" && test "${MBD_CLEAN_ALL}" = "no"; then
+          echo "trilinos was already installed"
+          exit 0
+        fi
+      - |
+        if test "${MBD_CLEAN_ALL}" = "yes"; then
+          rm -rf "${TRILINOS_BUILD_DIR}"
+        fi
+      - |
+        if ! test -d "${TRILINOS_BUILD_DIR}"; then
+          git clone -b "${TRILINOS_BRANCH}" "${TRILINOS_REPOSITORY}" "${TRILINOS_BUILD_DIR}"
+        fi
+      - cd ${TRILINOS_BUILD_DIR}
+      - git checkout ${TRILINOS_BRANCH}
+      - |
+        if ! git pull --force; then
+          echo "git pull failed"
+        fi
+      - |
+        if ! test -d build_dir; then
+          mkdir build_dir
+        fi
+      - cd build_dir
+      - |
+        if ! test -f Makefile || test ${CI_PROJECT_DIR}/testsuite/trilinos-build-job.yml -nt Makefile; then
+          cmake -S .. -DCMAKE_INSTALL_PREFIX="${TRILINOS_INSTALL_PREFIX}" ${TRILINOS_CONFIG}
+        fi
+      - make -j${MBD_NUM_BUILD_JOBS}
+      - rm -rf "${TRILINOS_INSTALL_PREFIX}"
+      - make install
+  artifacts:
+    name: ${CI_JOB_NAME_SLUG}-${CI_COMMIT_REF_SLUG}
+    expire_in: 24h
+    paths:
+      - $TRILINOS_INSTALL_PREFIX
+  cache:
+   key: ${CI_JOB_NAME_SLUG}-${CI_COMMIT_REF_SLUG}
+   paths:
+      - $TRILINOS_BUILD_DIR