1aaced190a
-- 3439d8a5cd745dfd2776593916eead65cb456afc by Kevin Zakka <kevinarmandzakka@gmail.com>: Respect an explicit CMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF The default-LTO guard checked the value rather than whether it was DEFINED, so an explicit -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF (used in CI to cut build time) was silently flipped back ON. Check DEFINED instead; default-on for Release is preserved when the caller makes no choice. -- bd15d1b264e5ab9594669b58fa1e0388d0a16f6b by Kevin Zakka <kevinarmandzakka@gmail.com>: Speed up CI ~10x faster on the POSIX jobs (~45m -> ~4-5m) and ~2x overall wall-clock. - Build Studio/Filament and WASM only in their dedicated canary jobs, not in every matrix job (WASM uses emcc, which ignores the host compiler). - Compiler matrix -> build_matrix.json with core/extended tiers: PRs build the core set, pushes to main run the full sweep. - ccache across build jobs (studio cache keyed on the Filament pin); fix the Python-bindings build so ccache hits (CCACHE_BASEDIR). - Disable IPO/LTO on POSIX (it was silently on); keep it on Windows where /GL-off exposes a latent test bug and build time is not the bottleneck. - Run ctest in parallel on POSIX (~2x); uv for Python installs (~29s -> ~8s). - Move MJX (compiler-independent) to a single dedicated job. - Restrict GITHUB_TOKEN to contents: read; bump actions off deprecated Node20. -- c0618ab7aef524b238e0a30f2f23c50c4ce0c9b1 by Kevin Zakka <kevinarmandzakka@gmail.com>: Build the gcc jobs with LTO off too Restores LTO-off for gcc (recovering the build-time win). That surfaces known gcc-12 -Wrestrict false positives in libstdc++ <char_traits> at -O3; two clean, behavior-preserving rewrites in the XML writer avoid them. Confirmed gcc-12 and gcc-14 build clean with LTO off (PR #3325). -- 2c38da0f48f77635e58e4b7931b86d60f3c253c7 by Kevin Zakka <kevinarmandzakka@gmail.com>: Respect explicit IPO=OFF in the Simulate and Sample options too Apply the same DEFINED check as cmake/MujocoOptions.cmake to the simulate/ and sample/ subprojects (which carry identical copies of the guard) to keep the three in sync, as the internal import requires. Since the guard now honors =OFF, stop forcing IPO=OFF on the tiny samples/simulate CI builds so they keep their default LTO and don't expose the gcc -Werror false positives that -O3-without-LTO triggers. COPYBARA_INTEGRATE_REVIEW=https://github.com/google-deepmind/mujoco/pull/3315 from google-deepmind:speed-up-ci 2c38da0f48f77635e58e4b7931b86d60f3c253c7 PiperOrigin-RevId: 930008698 Change-Id: Ic41dc87881833c95f2ebfc0602de1ed438db3d4f
422 lines
14 KiB
Bash
Executable File
422 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# Copyright 2025 DeepMind Technologies Limited
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# TODO(matijak): Make all cmake commands run from the top-level directory, and
|
|
# consider making the builds parallel.
|
|
|
|
|
|
# Wrap the compiler with ccache when it is available (set up by ccache-action in
|
|
# CI). This makes warm rebuilds - including the expensive, pinned Filament build -
|
|
# much faster. ccache is content-addressed on the full compiler invocation, so
|
|
# changing a flag or source forces a recompile: a stale object is never reused.
|
|
# Guarded by `command -v` so the script still works locally without ccache.
|
|
CCACHE_ARGS=""
|
|
if command -v ccache >/dev/null 2>&1; then
|
|
CCACHE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
|
|
fi
|
|
|
|
|
|
# Emit the build matrix for build.yml as a step output. On pull_request we run
|
|
# only the representative "core" compiler set; on push (e.g. to main) we run the
|
|
# full compiler sweep. Tiers are defined in build_matrix.json.
|
|
generate_matrix() {
|
|
echo "Generating build matrix for event '${GITHUB_EVENT_NAME}'..."
|
|
local file=".github/workflows/build_matrix.json"
|
|
local matrix
|
|
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
|
|
matrix="$(jq -c '{include: [.include[] | select(.tier == "core") | del(.tier)]}' "${file}")"
|
|
else
|
|
matrix="$(jq -c '{include: [.include[] | del(.tier)]}' "${file}")"
|
|
fi
|
|
echo "matrix=${matrix}" >> "${GITHUB_OUTPUT}"
|
|
echo "${matrix}" | jq .
|
|
}
|
|
|
|
|
|
prepare_linux() {
|
|
echo "Preparing Linux..."
|
|
sudo apt-get update && sudo apt-get install \
|
|
libgl1-mesa-dev \
|
|
libwayland-dev \
|
|
libxinerama-dev \
|
|
libxcursor-dev \
|
|
libxkbcommon-dev \
|
|
libxrandr-dev \
|
|
libxi-dev \
|
|
ninja-build
|
|
}
|
|
|
|
|
|
prepare_python() {
|
|
echo "Preparing Python..."
|
|
repo="${PWD}"
|
|
pushd "${TMPDIR}" > /dev/null
|
|
python -m venv venv
|
|
if [[ $RUNNER_OS == "Windows" ]]; then
|
|
mkdir venv/bin
|
|
fixpath="$(s="$(cat venv/Scripts/activate | grep VIRTUAL_ENV=)"; echo "${s:13:-1}")"
|
|
sed -i "s#$(printf "%q" "${fixpath}")#$(cygpath "${fixpath}")#g" venv/Scripts/activate
|
|
ln -s ../Scripts/activate venv/bin/activate
|
|
fi
|
|
source venv/bin/activate
|
|
# Install build deps with uv when available (set up by setup-uv in CI on
|
|
# POSIX) - much faster than pip. Fall back to pip otherwise (e.g. Windows,
|
|
# local dev). The venv is still created by `python -m venv`, so pip stays
|
|
# available for later steps (pip wheel / python -m build).
|
|
if command -v uv > /dev/null 2>&1; then
|
|
uv pip install --require-hashes -r "${repo}/python/build_requirements.txt"
|
|
uv pip install --require-hashes -r "${repo}/python/build_requirements_usd.txt"
|
|
else
|
|
python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements.txt"
|
|
python -m pip install --upgrade --require-hashes -r "${repo}/python/build_requirements_usd.txt"
|
|
fi
|
|
popd > /dev/null
|
|
}
|
|
|
|
|
|
npm_ci() {
|
|
echo "Installing NPM dependencies for WASM bindings..."
|
|
pushd wasm
|
|
npm ci
|
|
popd
|
|
}
|
|
|
|
|
|
setup_emsdk() {
|
|
echo "Setting up Emscripten..."
|
|
git clone https://github.com/emscripten-core/emsdk.git
|
|
./emsdk/emsdk install 4.0.10
|
|
./emsdk/emsdk activate 4.0.10
|
|
# Force installing emscripten's typescript dependencies. This is a
|
|
# workaround for the github update to a newer typescript, which gives an
|
|
# error on the deprecated `--outFile` flag.
|
|
pushd emsdk/upstream/emscripten
|
|
npm i
|
|
popd
|
|
}
|
|
|
|
|
|
configure_mujoco() {
|
|
echo "Configuring MuJoCo..."
|
|
# Disable IPO/LTO to cut build time. Skip this on Windows: turning off MSVC's
|
|
# whole-program optimization (/GL) exposes a latent heap corruption in
|
|
# SetConstTest.SleepingNotAllowed (a real bug worth a separate investigation),
|
|
# and Windows build time is not a CI bottleneck.
|
|
local ipo_off="-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF"
|
|
if [[ "${RUNNER_OS}" == "Windows" ]]; then
|
|
ipo_off=""
|
|
fi
|
|
mkdir build &&
|
|
cd build &&
|
|
cmake .. \
|
|
-DCMAKE_BUILD_TYPE:STRING=Release \
|
|
${ipo_off} \
|
|
-DCMAKE_INSTALL_PREFIX:STRING=${TMPDIR}/mujoco_install \
|
|
-DMUJOCO_BUILD_EXAMPLES:BOOL=OFF \
|
|
${CCACHE_ARGS} \
|
|
${CMAKE_ARGS}
|
|
}
|
|
|
|
|
|
build_mujoco() {
|
|
echo "Building MuJoCo..."
|
|
cmake --build . --config=Release ${CMAKE_BUILD_ARGS}
|
|
}
|
|
|
|
|
|
test_mujoco() {
|
|
echo "Testing MuJoCo..."
|
|
# ctest defaults to serial. The suite is ~1650 independent tests that use
|
|
# unique temp files (mkstemp / testing::TempDir) and declare no RUN_SERIAL /
|
|
# RESOURCE_LOCK, so running them in parallel is safe and ~2x faster on POSIX.
|
|
# Windows is kept serial conservatively: parallel-safety on the Windows file
|
|
# system is unverified and its test time is not a CI bottleneck.
|
|
if [[ "${RUNNER_OS}" == "Windows" ]]; then
|
|
ctest -C Release --output-on-failure .
|
|
else
|
|
local ncpu
|
|
ncpu="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo "${NUMBER_OF_PROCESSORS:-2}")"
|
|
ctest -C Release --output-on-failure --parallel "${ncpu}" .
|
|
fi
|
|
}
|
|
|
|
|
|
install_mujoco() {
|
|
echo "Installing MuJoCo..."
|
|
cmake --install .
|
|
}
|
|
|
|
|
|
copy_plugins_posix() {
|
|
echo "Copying plugins..."
|
|
mkdir -p ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp lib/libactuator.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp lib/libelasticity.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp lib/libsensor.* ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp lib/libsdf_plugin.* ${TMPDIR}/mujoco_install/mujoco_plugin
|
|
}
|
|
|
|
|
|
copy_plugins_window() {
|
|
echo "Copying plugins..."
|
|
mkdir -p ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp bin/Release/actuator.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp bin/Release/elasticity.dll ${TMPDIR}/mujoco_install/mujoco_plugin &&
|
|
cp bin/Release/sensor.dll ${TMPDIR}/mujoco_install/mujoco_plugin
|
|
}
|
|
|
|
|
|
configure_samples() {
|
|
echo "Configuring samples..."
|
|
# Samples are tiny, so they keep the default IPO/LTO: disabling it saves no
|
|
# meaningful build time and would expose the same gcc -Werror false positives
|
|
# that -O3-without-LTO triggers (see configure_mujoco).
|
|
mkdir build &&
|
|
cd build &&
|
|
cmake .. \
|
|
-DCMAKE_BUILD_TYPE:STRING=Release \
|
|
-Dmujoco_ROOT:STRING=${TMPDIR}/mujoco_install \
|
|
${CCACHE_ARGS} \
|
|
${CMAKE_ARGS}
|
|
}
|
|
|
|
|
|
configure_simulate() {
|
|
echo "Configuring simulate..."
|
|
# See configure_samples: keep the default IPO/LTO for this small build.
|
|
mkdir build &&
|
|
cd build &&
|
|
cmake .. \
|
|
-DCMAKE_BUILD_TYPE:STRING=Release \
|
|
-Dmujoco_ROOT:STRING=${TMPDIR}/mujoco_install \
|
|
${CCACHE_ARGS} \
|
|
${CMAKE_ARGS}
|
|
}
|
|
|
|
|
|
build_simulate() {
|
|
echo "Building simulate..."
|
|
cmake --build . --config=Release ${CMAKE_BUILD_ARGS}
|
|
}
|
|
|
|
|
|
configure_studio() {
|
|
echo "Configuring Studio..."
|
|
cmake -B build \
|
|
-DCMAKE_BUILD_TYPE:STRING=Release \
|
|
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF \
|
|
-DUSE_STATIC_LIBCXX=OFF \
|
|
-DBUILD_SHARED_LIBS=OFF \
|
|
-DMUJOCO_BUILD_EXAMPLES=OFF \
|
|
-DMUJOCO_BUILD_SIMULATE=OFF \
|
|
-DMUJOCO_BUILD_STUDIO=ON \
|
|
-DMUJOCO_BUILD_TESTS=OFF \
|
|
-DMUJOCO_TEST_PYTHON_UTIL=OFF \
|
|
-DMUJOCO_WITH_USD=OFF \
|
|
-DMUJOCO_USE_FILAMENT=ON \
|
|
${CCACHE_ARGS} \
|
|
${CMAKE_ARGS}
|
|
echo "Configuring Studio... DONE"
|
|
}
|
|
|
|
|
|
build_studio() {
|
|
echo "Building Studio..."
|
|
cmake --build build --config=Release --target mujoco_studio --parallel
|
|
echo "Building Studio... DONE"
|
|
}
|
|
|
|
|
|
make_python_sdist() {
|
|
echo "Making Python sdist..."
|
|
source ${TMPDIR}/venv/bin/activate &&
|
|
./make_sdist.sh
|
|
}
|
|
|
|
|
|
build_python_bindings() {
|
|
echo "Building Python bindings..."
|
|
source ${TMPDIR}/venv/bin/activate
|
|
# pip unpacks the sdist into a randomized temp dir every run, so the absolute
|
|
# source/include paths differ each time and defeat ccache (0% hit, full
|
|
# recompile). CCACHE_BASEDIR rewrites absolute paths under it to paths relative
|
|
# to the (also-in-temp) build cwd, cancelling the random component so objects
|
|
# hash identically across runs. CCACHE_SLOPPINESS ignores timestamp/path noise.
|
|
export CCACHE_BASEDIR="${TMPDIR}"
|
|
export CCACHE_SLOPPINESS="time_macros,include_file_mtime,include_file_ctime,pch_defines,locale,system_headers"
|
|
MUJOCO_PATH="${TMPDIR}/mujoco_install" \
|
|
MUJOCO_PLUGIN_PATH="${TMPDIR}/mujoco_install/mujoco_plugin" \
|
|
MUJOCO_CMAKE_ARGS="-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF ${CCACHE_ARGS} ${CMAKE_ARGS}" \
|
|
pip wheel -v --no-deps mujoco-*.tar.gz
|
|
}
|
|
|
|
|
|
install_python_bindings() {
|
|
echo "Installing Python bindings..."
|
|
source ${TMPDIR}/venv/bin/activate &&
|
|
pip install --no-index mujoco-*.whl
|
|
}
|
|
|
|
|
|
test_python_bindings() {
|
|
echo "Testing Python bindings..."
|
|
source ${TMPDIR}/venv/bin/activate &&
|
|
pytest -v --pyargs mujoco
|
|
}
|
|
|
|
|
|
build_test_wasm() {
|
|
echo "Building and testing WASM bindings..."
|
|
source emsdk/emsdk_env.sh
|
|
export PATH="$(pwd)/node_modules/.bin:$PATH"
|
|
echo "Build MuJoCo with Emscripten (Multi-Threaded)..."
|
|
emcmake cmake -B build_wasm_mt \
|
|
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF \
|
|
-DMUJOCO_WASM_THREADS=ON \
|
|
${CCACHE_ARGS} \
|
|
$WASM_CMAKE_ARGS
|
|
cmake --build build_wasm_mt --parallel $(nproc)
|
|
|
|
echo "Run bindings tests for Multi-Threaded version..."
|
|
npm run test --prefix ./wasm
|
|
|
|
echo "Moving Multi-Thread version under mt subfolder..."
|
|
mkdir -p wasm/dist/mt
|
|
mv wasm/dist/mujoco.* wasm/dist/mt/
|
|
|
|
echo "Build MuJoCo with Emscripten (Single-Threaded)..."
|
|
emcmake cmake -B build_wasm_st \
|
|
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION:BOOL=OFF \
|
|
-DMUJOCO_WASM_THREADS=OFF \
|
|
${CCACHE_ARGS} \
|
|
$WASM_CMAKE_ARGS
|
|
cmake --build build_wasm_st --parallel $(nproc)
|
|
|
|
echo "Run bindings tests for Single-Threaded version..."
|
|
npm run test --prefix ./wasm
|
|
}
|
|
|
|
package_wasm() {
|
|
echo "Publishing WASM bindings..."
|
|
cp wasm/package.npm.json wasm/dist/package.json
|
|
cp wasm/README.md wasm/dist/README.md
|
|
VERSION="${VERSION:-${GITHUB_REF#refs/tags/}}"
|
|
npm --prefix wasm/dist version "${VERSION}" --no-git-tag-version
|
|
npm pack --dry-run ./wasm/dist
|
|
npm publish ./wasm/dist --access public --provenance
|
|
}
|
|
|
|
|
|
package_mjx() {
|
|
echo "Packaging MJX..."
|
|
source ${TMPDIR}/venv/bin/activate &&
|
|
python -m build .
|
|
}
|
|
|
|
|
|
install_mjx() {
|
|
echo "Installing MJX..."
|
|
source ${TMPDIR}/venv/bin/activate
|
|
# The MJX requirements (jax, jaxlib, scipy, ...) are a big install; use uv when
|
|
# available. Keep pip for the local --no-index wheel.
|
|
if command -v uv > /dev/null 2>&1; then
|
|
uv pip install --require-hashes -r requirements.txt
|
|
else
|
|
pip install --require-hashes -r requirements.txt
|
|
fi
|
|
pip install --no-index dist/mujoco_mjx-*.whl
|
|
}
|
|
|
|
|
|
test_mjx() {
|
|
echo "Testing MJX..."
|
|
source ${TMPDIR}/venv/bin/activate &&
|
|
pytest -n auto -v -k 'not IntegrationTest' --pyargs mujoco.mjx
|
|
}
|
|
|
|
|
|
notify_team_chat() {
|
|
CHATMSG="$(cat <<-'EOF' | python3
|
|
import json
|
|
import os
|
|
env = lambda x: os.getenv(x, '')
|
|
data = dict(
|
|
result=env('JOB_URL'),
|
|
job=env('CHATMSG_JOB_ID'),
|
|
commit=env('GITHUB_SHA')[:6],
|
|
name=env('CHATMSG_AUTHOR_NAME').replace('```', ''),
|
|
email=env('CHATMSG_AUTHOR_EMAIL'),
|
|
msg=env('CHATMSG_COMMIT_MESSAGE').replace('```', '')
|
|
)
|
|
text = '<{result}|*FAILURE*>: job `{job}` commit `{commit}`\n```Author: {name} <{email}>\n\n{msg}```'.format(**data)
|
|
print(json.dumps({'text' : text}))
|
|
EOF
|
|
)" &&
|
|
|
|
curl "$GCHAT_API_URL&threadKey=$GITHUB_SHA&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD" \
|
|
-X POST \
|
|
-H "Content-Type: application/json" \
|
|
--data-raw "${CHATMSG}"
|
|
}
|
|
|
|
|
|
build_mujoco_live() {
|
|
echo "Setting up Emscripten SDK..."
|
|
source emsdk/emsdk_env.sh
|
|
|
|
echo "Building Filament tools, targeting host platform..."
|
|
cmake -S . -B build_host -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DUSE_STATIC_LIBCXX=OFF \
|
|
-DMUJOCO_BUILD_STUDIO=ON \
|
|
-DMUJOCO_USE_FILAMENT=ON \
|
|
-DMUJOCO_BUILD_TESTS=OFF \
|
|
-DMUJOCO_BUILD_EXAMPLES=OFF \
|
|
-DMUJOCO_BUILD_SIMULATE=OFF
|
|
cmake --build build_host --target matc resgen cmgen mujoco_filament_assets -j$(nproc)
|
|
|
|
echo "Building WASM app..."
|
|
emcmake cmake -S . -B build_wasm -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DMUJOCO_BUILD_STUDIO=ON \
|
|
-DMUJOCO_USE_FILAMENT=ON \
|
|
-DMUJOCO_BUILD_TESTS_WASM=OFF \
|
|
-DMUJOCO_NATIVE_BUILD_DIR=$(pwd)/build_host
|
|
cmake --build build_wasm --target mujoco_studio -j$(nproc)
|
|
}
|
|
|
|
|
|
# Discover functions defined in this script by finding identifiers followed by
|
|
# "()" and capturing the identifier as a valid function name.
|
|
VALID_FUNCTIONS=()
|
|
while IFS= read -r func_name; do
|
|
VALID_FUNCTIONS+=("$func_name")
|
|
done < <(grep -E '^[[:alnum:]_]+\(\)' "$0" | sed 's/().*$//')
|
|
|
|
# Exit with an error if the requested function is not found.
|
|
if [[ ! " ${VALID_FUNCTIONS[*]} " =~ " ${1} " ]]; then
|
|
echo "Usage: $0 {$(IFS='|'; echo "${VALID_FUNCTIONS[*]}")}, got '$1'"
|
|
exit 1
|
|
fi
|
|
|
|
# Set options to print the commands being run, and cause the script to exit with
|
|
# an error code if any command fails. Note we do this just before executing
|
|
# the requested function to avoid cluttering the output with the above command
|
|
# discovery code.
|
|
set -xe
|
|
|
|
# Execute the requested function.
|
|
"$1"
|