Skip to content

Commit

Permalink
[workspace] Remove determine_os logic
Browse files Browse the repository at this point in the history
Centralize choice of C++20 to be project-wide, instead of OS-specific.

Simplify CMakeLists to use whatever OS codename it finds for the bazel
customizations, with only warnings (not errors) when none is found.

Overall, these changes remove some barriers to building Drake on
non-Ubuntu linuxen.
  • Loading branch information
jwnimmer-tri committed Mar 4, 2024
1 parent 67afbfb commit 47a9f25
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 274 deletions.
65 changes: 19 additions & 46 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,56 +52,29 @@ if(APPLE)
endif()
list(APPEND BAZELRC_IMPORTS "tools/macos-arch-${MACOS_ARCH}.bazelrc")
else()
find_program(LSB_RELEASE_EXECUTABLE NAMES lsb_release)

if(NOT LSB_RELEASE_EXECUTABLE)
message(FATAL_ERROR "Could NOT find the lsb_release executable")
endif()
list(APPEND BAZELRC_IMPORTS "tools/ubuntu.bazelrc")

find_program(LSB_RELEASE_EXECUTABLE NAMES lsb_release)
mark_as_advanced(LSB_RELEASE_EXECUTABLE)

execute_process(COMMAND "${LSB_RELEASE_EXECUTABLE}" --id --short
RESULT_VARIABLE LSB_RELEASE_ID_SHORT_RESULT_VARIABLE
OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT_OUTPUT_VARIABLE
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(LSB_RELEASE_ID_SHORT_RESULT_VARIABLE EQUAL 0)
set(UNIX_DISTRIBUTION_ID "${LSB_RELEASE_ID_SHORT_OUTPUT_VARIABLE}")
endif()

if(NOT UNIX_DISTRIBUTION_ID STREQUAL Ubuntu)
message(WARNING
"Distribution ${UNIX_DISTRIBUTION_ID} is NOT supported"
)
endif()

string(TOLOWER "${UNIX_DISTRIBUTION_ID}" UNIX_DISTRIBUTION_ID)

execute_process(COMMAND "${LSB_RELEASE_EXECUTABLE}" --codename --short
RESULT_VARIABLE LSB_RELEASE_CODENAME_SHORT_RESULT_VARIABLE
OUTPUT_VARIABLE LSB_RELEASE_CODENAME_SHORT_OUTPUT_VARIABLE
OUTPUT_STRIP_TRAILING_WHITESPACE
)

if(LSB_RELEASE_CODENAME_SHORT_RESULT_VARIABLE EQUAL 0)
set(UNIX_DISTRIBUTION_CODENAME
"${LSB_RELEASE_CODENAME_SHORT_OUTPUT_VARIABLE}"
)
endif()

# The supported releases should match those listed in both
# doc/_pages/from_source.md and tools/workspace/os.bzl.
if(NOT UNIX_DISTRIBUTION_CODENAME MATCHES "^(jammy|noble)$")
message(FATAL_ERROR
"Release ${UNIX_DISTRIBUTION_CODENAME} is NOT supported. Please use "
"Ubuntu 22.04 (Jammy) or Ubuntu 24.04 (Noble)."
if(NOT LSB_RELEASE_EXECUTABLE)
message(WARNING "Could NOT find the lsb_release executable")
else()
execute_process(COMMAND "${LSB_RELEASE_EXECUTABLE}" --codename --short
RESULT_VARIABLE LSB_RELEASE_CODENAME_SHORT_RESULT_VARIABLE
OUTPUT_VARIABLE UNIX_DISTRIBUTION_CODENAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT LSB_RELEASE_CODENAME_SHORT_RESULT_VARIABLE EQUAL 0)
message(WARNING "Could NOT run the lsb_release executable")
else()
set(MAYBE_RC "tools/ubuntu-${UNIX_DISTRIBUTION_CODENAME}.bazelrc")
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/${MAYBE_RC}")
message(WARNING "Could NOT find config file ${MAYBE_RC}")
else()
list(APPEND BAZELRC_IMPORTS "${MAYBE_RC}")
endif()
endif()
endif()

list(APPEND BAZELRC_IMPORTS "tools/ubuntu.bazelrc")
list(APPEND BAZELRC_IMPORTS
"tools/ubuntu-${UNIX_DISTRIBUTION_CODENAME}.bazelrc")
endif()

# The version passed to find_package(Bazel) should match the
Expand Down
4 changes: 2 additions & 2 deletions doc/_pages/from_source.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ title: Source Installation
The following table shows the configurations and platforms that Drake
officially supports:

<!-- The operating system requirements should match those listed in both the
root CMakeLists.txt and tools/workspace/os.bzl. -->
<!-- The operating system requirements should match those listed in the root
CMakeLists.txt. -->
<!-- The minimum compiler versions should match those listed in both the root
CMakeLists.txt and tools/workspace/cc/repository.bzl. -->

Expand Down
4 changes: 4 additions & 0 deletions tools/bazel.rc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ build -c opt
build --strip=never
build --strict_system_includes

# Use C++20 by default.
build --cxxopt=-std=c++20
build --host_cxxopt=-std=c++20

# Default test options.
build --test_output=errors
build --test_summary=terse
Expand Down
4 changes: 0 additions & 4 deletions tools/macos.bazelrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# Common options for macOS, no matter the arch (x86 or arm).

# Use C++20 by default.
build --cxxopt=-std=c++20
build --host_cxxopt=-std=c++20

# Suppress numerous "'_FORTIFY_SOURCE' macro redefined" warnings when using
# sanitizers.
build:asan --copt=-Wno-macro-redefined
Expand Down
4 changes: 0 additions & 4 deletions tools/ubuntu-jammy.bazelrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# Use C++20 by default.
build --cxxopt=-std=c++20
build --host_cxxopt=-std=c++20

# Options for explicitly using Clang.
common:clang --repo_env=CC=clang-14
common:clang --repo_env=CXX=clang++-14
Expand Down
4 changes: 0 additions & 4 deletions tools/ubuntu-noble.bazelrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
# Use C++20 by default.
build --cxxopt=-std=c++20
build --host_cxxopt=-std=c++20

# Options for explicitly using Clang.
common:clang --repo_env=CC=clang-14
common:clang --repo_env=CXX=clang++-14
Expand Down
7 changes: 2 additions & 5 deletions tools/workspace/fmt/repository.bzl
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
load("//tools/workspace:os.bzl", "determine_os")
load("//tools/workspace:os.bzl", "is_wheel_build")
load("//tools/workspace:github.bzl", "setup_github_repository")
load("//tools/workspace:pkg_config.bzl", "setup_pkg_config_repository")

def _impl(repo_ctx):
os_result = determine_os(repo_ctx)
if os_result.error != None:
fail(os_result.error)
if os_result.is_manylinux or os_result.is_macos_wheel:
if is_wheel_build(repo_ctx):
# Compile from downloaded github sources.
error = setup_github_repository(repo_ctx).error
else:
Expand Down
200 changes: 4 additions & 196 deletions tools/workspace/os.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -47,204 +47,12 @@ def exec_using_which(repository_ctx, command):
# Success.
return struct(stdout = result.stdout, error = None)

def _make_result(
error = None,
macos_release = None,
ubuntu_release = None,
is_wheel = False,
homebrew_prefix = None,
macos_arch_result = None):
"""Return a fully-populated struct result for determine_os, below."""
is_macos = (macos_release != None) and not is_wheel
is_macos_wheel = (macos_release != None) and is_wheel
is_ubuntu = (ubuntu_release != None) and not is_wheel
is_manylinux = (ubuntu_release != None) and is_wheel
if is_macos:
target = "macos"
elif is_macos_wheel:
target = "macos_wheel"
elif is_ubuntu:
target = "ubuntu"
elif is_manylinux:
target = "manylinux"
else:
target = None
return struct(
error = error,
target = target,
is_macos = is_macos,
is_macos_wheel = is_macos_wheel,
is_ubuntu = is_ubuntu,
is_manylinux = is_manylinux,
ubuntu_release = ubuntu_release,
macos_release = macos_release,
homebrew_prefix = homebrew_prefix,
macos_arch_result = macos_arch_result,
)

def _determine_linux(repository_ctx):
"""Handle determine_os on Linux."""

# Shared error message text across different failure cases.
error_prologue = "could not determine Linux distribution: "

# Allow the user to override the OS selection.
drake_os = repository_ctx.os.environ.get("DRAKE_OS", "")
is_manylinux = False
if len(drake_os) > 0:
if drake_os == "manylinux":
is_manylinux = True
else:
return _make_result(error = "{}{} DRAKE_OS={}".format(
error_prologue,
"unknown value for environment variable",
drake_os,
))

# Get distro name.
lsb = exec_using_which(repository_ctx, ["lsb_release", "-si"])
if lsb.error != None:
return _make_result(error = error_prologue + lsb.error)
distro = lsb.stdout.strip()

# Some Ubuntu derivatives reply with their specific name in `-i` mode but
# declare Ubuntu compatibility in /etc. Check for that as a fallback.
if distro != "Ubuntu":
result = repository_ctx.execute([
"/bin/sh",
"-c",
". /etc/lsb-release; echo $DISTRIB_ID",
])
if result.return_code == 0:
maybe_distro = result.stdout.strip()
if maybe_distro == "Ubuntu":
distro = maybe_distro

if distro == "Ubuntu":
lsb = exec_using_which(repository_ctx, ["lsb_release", "-sr"])
if lsb.error != None:
return _make_result(error = error_prologue + lsb.error)
ubuntu_release = lsb.stdout.strip()

# Match supported Ubuntu release(s). These should match those listed in
# both doc/_pages/from_source.md and the root CMakeLists.txt.
if ubuntu_release in ["22.04", "24.04"]:
return _make_result(
ubuntu_release = ubuntu_release,
is_wheel = is_manylinux,
)

# Nothing matched.
return _make_result(
error = (error_prologue +
"unsupported '%s' release '%s'" %
(distro, ubuntu_release)),
)

# Nothing matched.
return _make_result(
error = error_prologue + "unsupported distribution '%s'" % distro,
)

def _determine_macos(repository_ctx):
"""Handle determine_os on macOS."""

# Shared error message text across different failure cases.
error_prologue = "could not determine macOS version: "

# Allow the user to override the OS selection.
drake_os = repository_ctx.os.environ.get("DRAKE_OS", "")
is_macos_wheel = False
if len(drake_os) > 0:
if drake_os == "macos_wheel":
is_macos_wheel = True
else:
return _make_result(error = "{}{} DRAKE_OS={}".format(
error_prologue,
"unknown value for environment variable",
drake_os,
))

# Run sw_vers to determine macOS version.
sw_vers = exec_using_which(repository_ctx, [
"sw_vers",
"-productVersion",
])
if sw_vers.error != None:
return _make_result(error = error_prologue + sw_vers.error)

# Match supported macOS release(s).
(macos_release,) = sw_vers.stdout.strip().split(".")[:1]
if macos_release not in ["12", "13", "14"]:
print("WARNING: unsupported macOS '%s'" % macos_release)

# Check which arch we should be using.
arch_result = exec_using_which(repository_ctx, ["/usr/bin/arch"])
macos_arch_result = arch_result.stdout.strip()
if macos_arch_result == "arm64":
homebrew_prefix = "/opt/homebrew"
else:
homebrew_prefix = "/usr/local"

return _make_result(
macos_release = macos_release,
is_wheel = is_macos_wheel,
homebrew_prefix = homebrew_prefix,
macos_arch_result = macos_arch_result,
)

def determine_os(repository_ctx):
def is_wheel_build(repository_ctx):
"""
DO NOT USE THIS IN NEW CODE. We are working to remove this from Drake.
A repository_rule helper function that determines which of the supported
build environments (OS versions or wheel environments) we should target.
We support four options, which are mutually exclusive and collectively
exhaustive: "macos" or "macos_wheel" or "ubuntu" or "manylinux".
The "manylinux" target indicates this build will be packaged into a Python
wheel that conforms to a "manylinux" standard such as manylinux_2_31; see
https://github.com/pypa/manylinux. Currently we compile this in an Ubuntu
container using only the most basic host packages from Ubuntu (libc,
libstdc++, etc.). In this case, the value of is_ubuntu will be False, but
ubuntu_release will still be provided.
The "macos_wheel" target indicates this build will be packaged into a
Python wheel.
In case of an error, the "error" attribute of the struct will be set, and
all of the other fields will be None or False.
Argument:
repository_ctx: The context passed to the repository_rule calling this.
Result:
a struct, with attributes:
- error: str iff any error occurred, else None
- target: str "macos" or "macos_wheel" or "ubuntu" or "manylinux"
- is_macos: True iff targeting a macOS non-wheel build
- is_macos_wheel: True iff targeting a macOS wheel build
- is_ubuntu: True iff targeting an Ubuntu non-wheel build
- is_manylinux: True iff targeting a Linux wheel build
- ubuntu_release: str like "22.04" (set any time the build
platform is Ubuntu, even for builds targeting "manylinux")
- macos_release: str like "11" or "12" (set any time the build platform
is macOS, even for builds targeting "macos_wheel")
- homebrew_prefix: str "/usr/local" or "/opt/homebrew" (set any time
the build platform is macOS, even for builds targeting
"macos_wheel")
Returns true iff this build is a Python wheel flavor.
"""

os_name = repository_ctx.os.name
if os_name == "mac os x":
return _determine_macos(repository_ctx)
elif os_name == "linux":
return _determine_linux(repository_ctx)
else:
return _make_result(error = "unknown or unsupported OS '%s'" % os_name)
drake_os = repository_ctx.os.environ.get("DRAKE_OS", "")
return drake_os in ["manylinux", "macos_wheel"]

def os_specific_alias(repository_ctx, mapping):
"""
Expand Down
11 changes: 7 additions & 4 deletions tools/workspace/pkg_config.bzl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
load("//tools/workspace:execute.bzl", "path", "which")
load("//tools/workspace:os.bzl", "determine_os")
load("//tools/workspace:os.bzl", "is_wheel_build")

_DEFAULT_TEMPLATE = Label("@drake//tools/workspace:pkg_config.BUILD.tpl")

Expand Down Expand Up @@ -50,9 +50,12 @@ def setup_pkg_config_repository(repository_ctx):
[],
))

os_result = determine_os(repository_ctx)

if os_result.is_manylinux or os_result.is_macos_wheel:
if is_wheel_build(repository_ctx):
# TODO(jwnimmer-tri) Ultimately, we want the wheel build to use Bazel
# to compile all dependencies. At the moment, however, some are built
# using CMake files at drake/tools/wheel/image/dependencies. To find
# the libraries installed by those builds, we need to add some custom
# paths when calling pkg-config.
pkg_config_paths.insert(0, "/opt/drake-dependencies/share/pkgconfig")
pkg_config_paths.insert(0, "/opt/drake-dependencies/lib/pkgconfig")

Expand Down
12 changes: 3 additions & 9 deletions tools/workspace/spdlog/repository.bzl
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
load("//tools/workspace:os.bzl", "determine_os")
load(
"//tools/workspace:github.bzl",
"setup_github_repository",
)
load("//tools/workspace:os.bzl", "is_wheel_build")
load("//tools/workspace:github.bzl", "setup_github_repository")
load("//tools/workspace:pkg_config.bzl", "setup_pkg_config_repository")

def _impl(repo_ctx):
os_result = determine_os(repo_ctx)
if os_result.error != None:
fail(os_result.error)
if os_result.is_manylinux or os_result.is_macos_wheel:
if is_wheel_build(repo_ctx):
# Compile it from downloaded github sources.
error = setup_github_repository(repo_ctx).error
else:
Expand Down

0 comments on commit 47a9f25

Please sign in to comment.