Skip to content

Commit

Permalink
[build] Use MODULE.bazel for all dependencies
Browse files Browse the repository at this point in the history
The WORKSPACE.bzlmod is no longer load-bearing, so as of this commit
it should be possible to consume Drake as a module external of other
downstream projects (instead of as a repository external).

Notable changes:

Document our stability promises for our module extension.

Adjust CMake logic to use its own MODULE.bazel.in that consumes Drake
as a module external, and to override choices for other dependencies
using conventional Bazel command-line flags instead of editing the
WORKSPACE file.

Adjust the pkg_config repository rule to handle the new distinction
between canonical vs apparent repository names. Ditto for the java
maven repositories (only relevant on macOS).

Adjust our lcm native code loader to accommodate the new runfiles
layout. Add test coverage for EventLog (which uses a distinctive
spelling of its import paths in upstream code).

Adjust labels used by our (non-symbolic) pybind11 macro to only ever
refer to the drake label, not the external label. Textual macros
resolve labels in the workspace context of the code calling them, not
Drake. Therefore they must only ever refer to Drake, since Drake's
externals are now invisible (by default) with bzlmod. We introduce
Drake aliases for the externals so that we can use a safe labels in
our macros. (This fix is only necessary for macros which we expect
downstream code to call, i.e., macros without a "drake_..." prefix in
their name. We still have plenty of other drake-specific macros that
refer to non-drake labels, but that's not a problem.) The longer term
fix for this will probably be switching from textual macros to
symbolic macros, but we don't attempt that here.

Adjust some of our hard-coded runfiles paths (header_lint test, wheel
build snopt, drake_models parse_test) to align with the new canonical
repository names.
  • Loading branch information
jwnimmer-tri committed Jan 8, 2025
1 parent c13c2cc commit d029d23
Show file tree
Hide file tree
Showing 24 changed files with 518 additions and 202 deletions.
25 changes: 8 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ project(drake
# (e.g., `-DCMAKE_BUILD_TYPE=Release`) and install Drake using those settings.
#
# We'll do that by converting the settings to generated Bazel inputs:
# - a `WORKSPACE.bazel` file that specifies dependencies; and
# - a generated `MODULE.bazel` that depends on the Drake module and customizes
# the toolchain selection.
# - a `.bazelrc` file that specifies configuration choices.
# and then running the `@drake//:install` program from that temporary workspace.

Expand Down Expand Up @@ -77,9 +78,8 @@ else()
endif()
endif()

# The version passed to find_package(Bazel) should match the
# minimum_bazel_version value in the call to versions.check() in WORKSPACE.
set(MINIMUM_BAZEL_VERSION 7.4)
# This version number should match bazel_compatibility in MODULE.bazel.
set(MINIMUM_BAZEL_VERSION 7.4.1)
find_package(Bazel ${MINIMUM_BAZEL_VERSION} MODULE)
if(NOT Bazel_FOUND)
set(Bazel_EXECUTABLE "${PROJECT_SOURCE_DIR}/third_party/com_github_bazelbuild_bazelisk/bazelisk.py")
Expand Down Expand Up @@ -355,17 +355,10 @@ function(symlink_external_repository_libs NAME TARGET)
file(CREATE_LINK "${location}" "${workspace}/${NAME}/lib/${other_basename}" SYMBOLIC)
endfunction()

set(BAZEL_WORKSPACE_EXTRA)
set(BAZEL_WORKSPACE_EXCLUDES)

# Our cmake/WORKSPACE.bzlmod always provides @python.
list(APPEND BAZEL_WORKSPACE_EXCLUDES "python")

macro(override_repository NAME)
set(repo "${CMAKE_CURRENT_BINARY_DIR}/external/workspace/${NAME}")
string(APPEND BAZEL_WORKSPACE_EXTRA
"local_repository(name = '${NAME}', path = '${repo}')\n")
list(APPEND BAZEL_WORKSPACE_EXCLUDES "${NAME}")
string(APPEND BAZEL_REPO_ENV
" --override_repository=+drake_dep_repositories+${NAME}=${repo}")
endmacro()

option(WITH_USER_EIGEN "Use user-provided Eigen3" OFF)
Expand Down Expand Up @@ -507,7 +500,7 @@ endif()
# N.B. If you are testing the CMake API and making changes to `installer.py`,
# you can change this target to something more lightweight, such as
# `//tools/install/dummy:install`.
set(BAZEL_INSTALL_TARGET //:install)
set(BAZEL_INSTALL_TARGET "@drake//:install")

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/install" CACHE STRING
Expand Down Expand Up @@ -553,10 +546,8 @@ endforeach()
# however, that the macOS wheel builds also need to know this path, so if it
# ever changes, tools/wheel/macos/build-wheel.sh will also need to be updated.
configure_file(cmake/bazel.rc.in drake_build_cwd/.bazelrc @ONLY)
configure_file(cmake/WORKSPACE.bzlmod.in drake_build_cwd/WORKSPACE.bzlmod @ONLY)
configure_file(cmake/MODULE.bazel.in drake_build_cwd/MODULE.bazel @ONLY)
file(CREATE_LINK "${PROJECT_SOURCE_DIR}/.bazeliskrc" drake_build_cwd/.bazeliskrc SYMBOLIC)
file(CREATE_LINK "${PROJECT_SOURCE_DIR}/MODULE.bazel" drake_build_cwd/MODULE.bazel SYMBOLIC)
file(CREATE_LINK "${PROJECT_SOURCE_DIR}/WORKSPACE" drake_build_cwd/WORKSPACE SYMBOLIC)

find_package(Git)

Expand Down
200 changes: 195 additions & 5 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
# This file lists Drake's external dependencies as known to bzlmod. It is used
# in concert with WORKSPACE.bzlmod (which has the workspace-style externals).

module(name = "drake")
module(
name = "drake",
# This version number should match MINIMUM_BAZEL_VERSION in CMakeLists.txt.
bazel_compatibility = [">=7.4.1"],
)

# Add starlark rules.

bazel_dep(name = "apple_support", version = "1.17.1", repo_name = "build_bazel_apple_support") # noqa
bazel_dep(name = "bazel_features", version = "1.22.0")
Expand All @@ -17,13 +23,197 @@ bazel_dep(name = "rules_python", version = "0.40.0")
bazel_dep(name = "rules_rust", version = "0.56.0")
bazel_dep(name = "rules_shell", version = "0.3.0")

# Customize our toolchains.

cc_configure = use_extension(
"@rules_cc//cc:extensions.bzl",
"cc_configure_extension",
)
use_repo(cc_configure, "local_config_cc")

# TODO(#20731) Move all of our dependencies from WORKSPACE.bzlmod into this
# file, so that downstream projects can consume Drake exclusively via bzlmod
# (and so that we can delete our WORKSPACE files prior to Bazel 9 which drops
# suppose for it).
# Load dependencies which are "public", i.e., made available to downstream
# projects.
#
# Downstream projects may load the same `drake_dep_repositories` module
# extension shown below and call its `use_repo` with whatever list of
# repositories they desire to cite from their project. It's safe to call
# `use_repo` on a subset of this list, or not call it at all downstream.
# Its only effect on a downstream project is to make the repository name
# visible to BUILD rules; Drake's own use of the repository is unaffected.

drake_dep_repositories = use_extension(
"@drake//tools/workspace:default.bzl",
"drake_dep_repositories",
)
use_repo(
drake_dep_repositories,
"blas",
"buildifier",
"drake_models",
"eigen",
"fmt",
"gflags",
"glib",
"glx",
"gtest",
"gurobi",
"lapack",
"lcm",
"libblas",
"liblapack",
"meshcat",
"mosek",
"opencl",
"opengl",
"pybind11",
"pycodestyle",
"python",
"snopt",
"spdlog",
"styleguide",
"x11",
"zlib",
)

# Load dependencies which are "private", i.e., not available for use by
# downstream projects. These are all "internal use only".
#
# TODO(jwnimmer-tri) By historical accident, not all of the repository names
# here end with "internal". We should work on improving the consistency, either
# by switching them to use BCR modules instead (e.g., nasm) or renaming them
# with any necessary deprecations (e.g., statsjs).

internal_repositories = use_extension(
"@drake//tools/workspace:default.bzl",
"internal_repositories",
)
use_repo(
internal_repositories,
"abseil_cpp_internal",
"bazelisk",
"ccd_internal",
"clang_cindex_python3_internal",
"clarabel_cpp_internal",
"clp_internal",
"coinutils_internal",
"com_jidesoft_jide_oss",
"common_robotics_utilities_internal",
"commons_io",
"conex_internal",
"csdp_internal",
"curl_internal",
"dm_control_internal",
"doxygen",
"fcl_internal",
"gfortran",
"github3_py_internal",
"gklib_internal",
"googlebenchmark",
"gymnasium_py",
"gz_math_internal",
"gz_utils_internal",
"highway_internal",
"ipopt_internal",
"libjpeg_turbo_internal",
"libpng_internal",
"libtiff_internal",
"metis_internal",
"mpmath_py_internal",
"msgpack_internal",
"mujoco_menagerie_internal",
"mumps_internal",
"mypy_extensions_internal",
"mypy_internal",
"nanoflann_internal",
"nasm",
"net_sf_jchart2d",
"nlohmann_internal",
"nlopt_internal",
"onetbb_internal",
"openusd_internal",
"org_apache_xmlgraphics_commons",
"osqp_internal",
"picosha2_internal",
"poisson_disk_sampling_internal",
"qdldl_internal",
"qhull_internal",
"ros_xacro_internal",
"rules_python_drake_constants",
"scs_internal",
"sdformat_internal",
"spgrid_internal",
"spral_internal",
"stable_baselines3_internal",
"statsjs",
"stduuid_internal",
"suitesparse_internal",
"sympy_py_internal",
"tinygltf_internal",
"tinyobjloader_internal",
"tinyxml2_internal",
"tomli_internal",
"typing_extensions_internal",
"uritemplate_py_internal",
"usockets_internal",
"uwebsockets_internal",
"voxelized_geometry_tools_internal",
"vtk_internal",
"xmlrunner_py",
"yaml_cpp_internal",
)

internal_crate_universe_repositories = use_extension(
"//tools/workspace:default.bzl",
"internal_crate_universe_repositories",
)
use_repo(
internal_crate_universe_repositories,
"crate__amd-0.2.2",
"crate__autocfg-1.4.0",
"crate__blas-0.22.0",
"crate__blas-sys-0.7.1",
"crate__cfg-if-1.0.0",
"crate__clarabel-0.9.0",
"crate__darling-0.14.4",
"crate__darling_core-0.14.4",
"crate__darling_macro-0.14.4",
"crate__derive_builder-0.11.2",
"crate__derive_builder_core-0.11.2",
"crate__derive_builder_macro-0.11.2",
"crate__either-1.13.0",
"crate__enum_dispatch-0.3.13",
"crate__equivalent-1.0.1",
"crate__fnv-1.0.7",
"crate__hashbrown-0.15.2",
"crate__ident_case-1.0.1",
"crate__indexmap-2.7.0",
"crate__itertools-0.11.0",
"crate__itoa-1.0.14",
"crate__lapack-0.19.0",
"crate__lapack-sys-0.14.0",
"crate__lazy_static-1.5.0",
"crate__libc-0.2.169",
"crate__memchr-2.7.4",
"crate__num-complex-0.4.6",
"crate__num-traits-0.2.19",
"crate__once_cell-1.19.0",
"crate__paste-1.0.15",
"crate__proc-macro2-1.0.92",
"crate__quote-1.0.38",
"crate__ryu-1.0.18",
"crate__serde-1.0.217",
"crate__serde_derive-1.0.217",
"crate__serde_json-1.0.134",
"crate__strsim-0.10.0",
"crate__syn-1.0.109",
"crate__syn-2.0.94",
"crate__thiserror-1.0.69",
"crate__thiserror-impl-1.0.69",
"crate__unicode-ident-1.0.14",
)

# TODO(#20731) More improvements are still needed to our MODULE organization:
# - Switch public API dependencies (e.g., eigen) to use modules.
# - Provide better configuration options for choosing dependencies.
# - Adjust the wheel build to build more dependencies as Bazel modules.
# - Deprecate non-bzlmod use of Drake downstream.
17 changes: 4 additions & 13 deletions WORKSPACE.bzlmod
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
# -*- bazel -*-
#
# This file lists Drake's workspace-style external dependencies. It is used in
# concert with MODULE.bazel (which has the module-style externals).
# This file is only ever used by drake/tools/clion/bazel_wrapper (for Drake
# Developers who develop using CLion; see https://drake.mit.edu/clion.html).
#
# Do not add any other new dependencies into this file.

workspace(name = "drake")

load("//tools/workspace:default.bzl", "add_default_workspace")

add_default_workspace(bzlmod = True)

# Add some special heuristic logic for using CLion with Drake.
load("//tools/clion:repository.bzl", "drake_clion_environment")

drake_clion_environment()

load("@bazel_skylib//lib:versions.bzl", "versions")

# This needs to be in WORKSPACE or a repository rule for native.bazel_version
# to actually be defined. The minimum_bazel_version value should match the
# version passed to the find_package(Bazel) call in the root CMakeLists.txt.
versions.check(minimum_bazel_version = "7.4")
25 changes: 25 additions & 0 deletions cmake/MODULE.bazel.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module(name = "drake_cmake")

bazel_dep(name = "rules_python", version = "0.40.0")

bazel_dep(name = "drake")
local_path_override(
module_name = "drake",
path = "@PROJECT_SOURCE_DIR@",
)

python_repository = use_repo_rule(
"@drake//tools/workspace/python:repository.bzl",
"python_repository",
)

# Use Drake's python repository rule to interrogate the interpreter chosen by
# the CMake find_program stanza, in support of compiling our C++ bindings.
python_repository(
name = "python",
linux_interpreter_path = "@Python_EXECUTABLE@",
macos_interpreter_path = "@Python_EXECUTABLE@",
requirements_flavor = "build",
)

register_toolchains("@python//:all")
23 changes: 0 additions & 23 deletions cmake/WORKSPACE.bzlmod.in

This file was deleted.

7 changes: 0 additions & 7 deletions cmake/bazel.rc.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,10 @@ startup --output_base="@BAZEL_OUTPUT_BASE@"
# Environment variables to be used in repository rules (if any).
common @BAZEL_REPO_ENV@

# Use the Python interpreter from our cmake/WORKSPACE.bzlmod.in.
build --extra_toolchains=@python//:all

# Disable the "convenience symlinks" intended for Bazel users; they only add
# confusion for the CMake use case.
build --symlink_prefix=/

# Use the source code and BUILD files from the Drake source tree.
# The WORKSPACE lives in the build directory, so by has no packages otherwise.
build --package_path="%workspace%:@PROJECT_SOURCE_DIR@"

# Fix macOS per https://github.com/bazelbuild/bazel/issues/14294.
build --notrim_test_configuration

Expand Down
Loading

0 comments on commit d029d23

Please sign in to comment.