diff --git a/cmake/CheckAvxSupport.cmake b/cmake/CheckAvxSupport.cmake new file mode 100644 index 00000000..ab8ad644 --- /dev/null +++ b/cmake/CheckAvxSupport.cmake @@ -0,0 +1,53 @@ +# Copyright 2021 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 +# +# https://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. + +include(CheckCSourceCompiles) + +# Assigns compiler options to the given variable based on availability of AVX. +function(get_avx_compile_options OUTPUT_VAR) + message(VERBOSE "Checking if AVX is available...") + + if(MSVC) + set(CMAKE_REQUIRED_FLAGS "/arch:AVX") + else() + set(CMAKE_REQUIRED_FLAGS "-mavx") + endif() + + if(APPLE AND "x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES) + message(STATUS "Building x86_64 on macOS, forcing CAN_BUILD_AVX to TRUE.") + set(CAN_BUILD_AVX TRUE) + else() + check_c_source_compiles( + " + #include + int main(int argc, char* argv[]) { + __m256d ymm; + return 0; + } + " + CAN_BUILD_AVX + ) + endif() + + if(CAN_BUILD_AVX) + message(VERBOSE "Checking if AVX is available... AVX available.") + set("${OUTPUT_VAR}" + ${CMAKE_REQUIRED_FLAGS} + PARENT_SCOPE + ) + else() + message(VERBOSE "Checking if AVX is available... AVX not available.") + set("${OUTPUT_VAR}" PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/FindOrFetch.cmake b/cmake/FindOrFetch.cmake new file mode 100644 index 00000000..4eeb617e --- /dev/null +++ b/cmake/FindOrFetch.cmake @@ -0,0 +1,128 @@ +# Copyright 2021 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 +# +# https://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. +# +#.rst: +# FindOrFetch +# ---------------------- +# +# Find or fetch a package in order to satisfy target dependencies. +# +# FindOrFetch([USE_SYSTEM_PACKAGE [ON/OFF]] +# [PACKAGE_NAME [name]] +# [LIBRARY_NAME [name]] +# [GIT_REPO [repo]] +# [GIT_TAG [tag]] +# [PATCH_COMMAND [cmd] [args]] +# [TARGETS [targets]] +# [EXCLUDE_FROM_ALL]) +# +# The command has the following parameters: +# +# Arguments: +# - ``USE_SYSTEM_PACKAGE`` one-value argument on whether to search for the +# package in the system (ON) or whether to fetch the library from a git +# repository (OFF). +# - ``PACKAGE_NAME`` name of the system-package. Ignored if +# ``USE_SYSTEM_PACKAGE`` is ``OFF``. +# - ``LIBRARY_NAME`` name of the library. Ignored if +# ``USE_SYSTEM_PACKAGE`` is ``ON``. +# - ``GIT_REPO`` git repository to fetch the library from. Ignored if +# ``USE_SYSTEM_PACKAGE`` is ``ON``. +# - ``GIT_TAG`` tag reference when fetching the library from the git +# repository. Ignored if ``USE_SYSTEM_PACKAGE`` is ``ON``. +# - ``TARGETS`` list of targets to be satisfied. If any of these targets are +# not currently defined, this macro will attempt to either find or fetch the +# package. + +if(COMMAND FindOrFetch) + return() +endif() + +macro(FindOrFetch) + if(NOT FetchContent) + include(FetchContent) + endif() + + # Parse arguments. + set(options EXCLUDE_FROM_ALL) + set(one_value_args + USE_SYSTEM_PACKAGE + PACKAGE_NAME + LIBRARY_NAME + GIT_REPO + GIT_TAG + ) + set(multi_value_args PATCH_COMMAND TARGETS) + cmake_parse_arguments( + _ARGS + "${options}" + "${one_value_args}" + "${multi_value_args}" + ${ARGN} + ) + + # Check if all targets are found. + if(NOT _ARGS_TARGETS) + message(FATAL_ERROR "mujoco::FindOrFetch: TARGETS must be specified.") + endif() + set(targets_found TRUE) + foreach(target ${_ARGS_TARGETS}) + if(NOT TARGET ${target}) + message(STATUS "mujoco::FindOrFetch: target `${target}` not defined.") + set(targets_found FALSE) + break() + endif() + endforeach() + + # If targets are not found, use `find_package` or `FetchContent...` to get it. + if(NOT targets_found) + if(${_ARGS_USE_SYSTEM_PACKAGE}) + message( + STATUS + "mujoco::FindOrFetch: Attempting to find `${_ARGS_PACKAGE_NAME}` in system packages..." + ) + find_package(${_ARGS_PACKAGE_NAME} REQUIRED) + message(STATUS "mujoco::FindOrFetch: Found `${_ARGS_PACKAGE_NAME}` in system packages.") + else() + message( + STATUS + "mujoco::FindOrFetch: Attempting to fetch `${_ARGS_LIBRARY_NAME}` from `${_ARGS_GIT_REPO}`..." + ) + FetchContent_Declare( + ${_ARGS_LIBRARY_NAME} + GIT_REPOSITORY ${_ARGS_GIT_REPO} + GIT_TAG ${_ARGS_GIT_TAG} + GIT_SHALLOW FALSE + PATCH_COMMAND ${_ARGS_PATCH_COMMAND} + ) + if(${_ARGS_EXCLUDE_FROM_ALL}) + FetchContent_GetProperties(${_ARGS_LIBRARY_NAME}) + if(NOT ${${_ARGS_LIBRARY_NAME}_POPULATED}) + FetchContent_Populate(${_ARGS_LIBRARY_NAME}) + add_subdirectory( + ${${_ARGS_LIBRARY_NAME}_SOURCE_DIR} ${${_ARGS_LIBRARY_NAME}_BINARY_DIR} + EXCLUDE_FROM_ALL + ) + endif() + else() + FetchContent_MakeAvailable(${_ARGS_LIBRARY_NAME}) + endif() + message( + STATUS "mujoco::FindOrFetch: Fetched `${_ARGS_LIBRARY_NAME}` from `${_ARGS_GIT_REPO}`." + ) + endif() + else() + message(STATUS "mujoco::FindOrFetch: `${_ARGS_PACKAGE_NAME}` targets found.") + endif() +endmacro() diff --git a/cmake/MujocoLinkOptions.cmake b/cmake/MujocoLinkOptions.cmake new file mode 100644 index 00000000..242767f9 --- /dev/null +++ b/cmake/MujocoLinkOptions.cmake @@ -0,0 +1,67 @@ +# Copyright 2021 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 +# +# https://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. + +include(CheckCSourceCompiles) + +# Gets the appropriate linker options for building MuJoCo, based on features available on the +# linker. +function(get_mujoco_extra_link_options OUTPUT_VAR) + if(MSVC) + set(EXTRA_LINK_OPTIONS /OPT:REF /OPT:ICF=5) + else() + set(EXTRA_LINK_OPTIONS) + + if(WIN32) + set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld-link") + check_c_source_compiles("int main() {}" SUPPORTS_LLD) + if(SUPPORTS_LLD) + set(EXTRA_LINK_OPTIONS + ${EXTRA_LINK_OPTIONS} + -fuse-ld=lld-link + -Wl,/OPT:REF + -Wl,/OPT:ICF + ) + endif() + else() + set(CMAKE_REQUIRED_FLAGS "-fuse-ld=lld") + check_c_source_compiles("int main() {}" SUPPORTS_LLD) + if(SUPPORTS_LLD) + set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -fuse-ld=lld) + else() + set(CMAKE_REQUIRED_FLAGS "-fuse-ld=gold") + check_c_source_compiles("int main() {}" SUPPORTS_GOLD) + if(SUPPORTS_GOLD) + set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -fuse-ld=gold) + endif() + endif() + + set(CMAKE_REQUIRED_FLAGS ${EXTRA_LINK_OPTIONS} "-Wl,--gc-sections") + check_c_source_compiles("int main() {}" SUPPORTS_GC_SECTIONS) + if(SUPPORTS_GC_SECTIONS) + set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -Wl,--gc-sections) + else() + set(CMAKE_REQUIRED_FLAGS ${EXTRA_LINK_OPTIONS} "-Wl,-dead_strip") + check_c_source_compiles("int main() {}" SUPPORTS_DEAD_STRIP) + if(SUPPORTS_DEAD_STRIP) + set(EXTRA_LINK_OPTIONS ${EXTRA_LINK_OPTIONS} -Wl,-dead_strip) + endif() + endif() + endif() + endif() + + set("${OUTPUT_VAR}" + ${EXTRA_LINK_OPTIONS} + PARENT_SCOPE + ) +endfunction() diff --git a/cmake/ShellTests.cmake b/cmake/ShellTests.cmake new file mode 100644 index 00000000..e1a75340 --- /dev/null +++ b/cmake/ShellTests.cmake @@ -0,0 +1,52 @@ +# Copyright 2021 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 +# +# https://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. + +function(add_mujoco_shell_test TEST_NAME TARGET_BINARY) + find_program(BASH_PROGRAM bash) + if(BASH_PROGRAM) + # Set up the test directory + set(TEST_TMPDIR "${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME}") + add_test(NAME ${TEST_NAME}_setup + COMMAND ${BASH_PROGRAM} "${PROJECT_SOURCE_DIR}/cmake/setup_test_dir.sh" ${TEST_TMPDIR} + ) + set_tests_properties(${TEST_NAME}_setup PROPERTIES FIXTURES_SETUP ${TEST_NAME}_fixture) + add_test(NAME ${TEST_NAME}_cleanup + COMMAND ${BASH_PROGRAM} "${PROJECT_SOURCE_DIR}/cmake/cleanup_test_dir.sh" + ${TEST_TMPDIR} + ) + set_tests_properties(${TEST_NAME}_cleanup PROPERTIES FIXTURES_CLEANUP ${TEST_NAME}_fixture) + add_test( + NAME ${TEST_NAME} + COMMAND ${BASH_PROGRAM} "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.sh" + WORKING_DIRECTORY $ + ) + set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED ${TEST_NAME}_fixture) + set_property( + TEST "${TEST_NAME}" + PROPERTY ENVIRONMENT + "CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}" + "TARGET_BINARY=$" + "TEST_TMPDIR=${TEST_TMPDIR}" + ) + if(WIN32) + # Define the directory containing the mujoco DLL library so that it can be added to the PATH. + # We modify the PATH in the script as it is more reliable. + set_property( + TEST "${TEST_NAME}" + APPEND + PROPERTY ENVIRONMENT "MUJOCO_DLL_DIR=$" + ) + endif() + endif() +endfunction() diff --git a/python/make_sdist.sh b/python/make_sdist.sh index a667ca2d..d422ed68 100755 --- a/python/make_sdist.sh +++ b/python/make_sdist.sh @@ -47,11 +47,11 @@ python "${package_dir}"/mujoco/codegen/generate_function_traits.py > \ export PYTHONPATH="${old_pythonpath}" # Copy over the LICENSE file. -cp "${package_dir}"/../../LICENSE . +cp "${package_dir}"/../LICENSE . # Copy over CMake scripts. mkdir cmake -cp "${package_dir}"/../../cmake/*.cmake cmake +cp "${package_dir}"/../cmake/*.cmake cmake python setup.py sdist --formats=gztar tar -tf dist/mujoco-*.tar.gz diff --git a/python/mujoco/errors.h b/python/mujoco/errors.h index 7dc7d2e7..d805876e 100644 --- a/python/mujoco/errors.h +++ b/python/mujoco/errors.h @@ -119,7 +119,12 @@ struct MjErrorIntercepter { template MUJOCO_ALWAYS_INLINE static constexpr auto WrapFunc(Callable&& callable) { +#if defined(__GNUC__) && !defined(__clang__) + // GCC can't inline functions that call setjmp + return [callable](Args... args) mutable { +#else return [callable](Args... args) MUJOCO_ALWAYS_INLINE_LAMBDA_MUTABLE { +#endif _mjPRIVATE__set_tls_error_fn(&MjErrorHandler); // DON'T MIX RAII WITH SETJMP! diff --git a/python/mujoco/util/crossplatform.h b/python/mujoco/util/crossplatform.h index be172900..0c71f2b6 100644 --- a/python/mujoco/util/crossplatform.h +++ b/python/mujoco/util/crossplatform.h @@ -25,7 +25,11 @@ (defined(__GNUC__) && !defined(__clang__)) #define MUJOCO_ALWAYS_INLINE __attribute__((always_inline)) #define MUJOCO_ALWAYS_INLINE_LAMBDA MUJOCO_ALWAYS_INLINE +#if defined(__clang__) #define MUJOCO_ALWAYS_INLINE_LAMBDA_MUTABLE MUJOCO_ALWAYS_INLINE_LAMBDA mutable +#else +#define MUJOCO_ALWAYS_INLINE_LAMBDA_MUTABLE mutable MUJOCO_ALWAYS_INLINE_LAMBDA +#endif #elif defined(_MSC_VER) #define MUJOCO_ALWAYS_INLINE __forceinline #if _MSC_VER >= 1927 && _MSVC_LANG >= 202002L