diff --git a/.bazeliskrc b/.bazeliskrc index abfe9e9..d80defd 100644 --- a/.bazeliskrc +++ b/.bazeliskrc @@ -1,2 +1,2 @@ # Keep this version up to date with the version used by drake -USE_BAZEL_VERSION=7.3.1 +USE_BAZEL_VERSION=8.2.1 diff --git a/.gitignore b/.gitignore index 6139797..b24cae2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ /bazel-out /bazel-testlogs .clwb/ -MODULE.bazel MODULE.bazel.lock **/__pycache__/** -.vscode \ No newline at end of file +.vscode +*.ps \ No newline at end of file diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..746710c --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,2 @@ +# This is an empty BUILD file, to ensure that the project's root directory is a +# bazel package. diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..de7c9e3 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,78 @@ +## MODULE.bazel +module( + name = "c3" +) + +# If you don't want to support building on macOS, you may remove the next line. +# Note that it must appear prior to loading "rules_cc", per the documentation: +# https://github.com/bazelbuild/apple_support?tab=readme-ov-file#bazel-7-setup +bazel_dep(name = "apple_support", version = "1.17.1", repo_name = "build_bazel_apple_support") + +# Add the Bazel rules we need. +bazel_dep(name = "rules_cc", version = "0.1.1") +bazel_dep(name = "rules_python", version = "1.0.0") +bazel_dep(name = "bazel_skylib", version = "1.7.1") + +drake_dep_repositories = use_extension( + "@drake//tools/workspace:default.bzl", + "drake_dep_repositories", +) + +use_repo( + drake_dep_repositories, + "lcm", + "eigen", + "fmt", + "gflags", + "gtest", + "pybind11", + "gurobi", +) + +# Use gurobi_cxx, which Drake deprecated +gurobi_extension = use_extension("@c3//tools/workspace/gurobi:repository.bzl", "gurobi_extension") +use_repo(gurobi_extension, "my_gurobi") + +# Replace Drake's Gurobi repository with ours. +override_repo( + drake_dep_repositories, + gurobi = "my_gurobi", +) + +# Here we introduce Drake as a module dependency, but note that Drake is not +# published to any Bazel registry. Below, we'll override it with a github +# source archive. +bazel_dep(name = "drake") + +# You can also use DRAKE_COMMIT to choose a Drake release; e.g.: +DRAKE_COMMIT = "v1.42.0" +DRAKE_CHECKSUM = "d860c15f50397c8a946fcc79e0a58a91ebc56f2189ef9edfcac929aa04157f8b" + +# Before changing the COMMIT, temporarily uncomment the next line so that Bazel +# displays the suggested new value for the CHECKSUM. +# DRAKE_CHECKSUM = "0" * 64 + +# This declares the `@drake` module as a source code archive from github. +# See README.md for instructions to use a local path, instead. +archive_override( + module_name = "drake", + urls = [x.format(DRAKE_COMMIT) for x in [ + "https://github.com/RobotLocomotion/drake/archive/{}.tar.gz", + ]], + sha256 = DRAKE_CHECKSUM, + strip_prefix = "drake-{}".format(DRAKE_COMMIT.lstrip("v")), +) + +# Use the host system /usr/bin/python3. +python_repository = use_repo_rule( + "@drake//tools/workspace/python:repository.bzl", + "python_repository", +) + +python_repository( + name = "python", + linux_interpreter_path = "/usr/bin/python3", + requirements_flavor = "build", +) + +register_toolchains("@python//:all") \ No newline at end of file diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index d24060a..0000000 --- a/WORKSPACE +++ /dev/null @@ -1,34 +0,0 @@ -# -*- mode: python -*- -# vi: set ft=python : - -workspace(name = "c3") - -DRAKE_COMMIT = "v1.35.0" - -DRAKE_CHECKSUM = "f19ee360656c87db8b0688c676c9f2ab2ae71ea08691432979b32d71c236e768" -# Before changing the COMMIT, temporarily uncomment the next line so that Bazel -# displays the suggested new value for the CHECKSUM. -# DRAKE_CHECKSUM = "0" * 64 - -# Maybe download Drake. -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "drake", - sha256 = DRAKE_CHECKSUM, - strip_prefix = "drake-{}".format(DRAKE_COMMIT.strip("v")), - urls = [x.format(DRAKE_COMMIT) for x in [ - "https://github.com/RobotLocomotion/drake/archive/{}.tar.gz", - ]], -) - -# Reference external software libraries and tools per Drake's defaults. Some -# software will come from the host system (Ubuntu or macOS); other software -# will be downloaded in source or binary form from github or other sites. -load("@drake//tools/workspace:default.bzl", "add_default_workspace") - -add_default_workspace() - -load("@rules_python//python:repositories.bzl", "py_repositories") - -py_repositories() diff --git a/systems/c3_controller.cc b/systems/c3_controller.cc index 1c53607..e53946b 100644 --- a/systems/c3_controller.cc +++ b/systems/c3_controller.cc @@ -8,6 +8,8 @@ #include "core/c3_qp.h" #include "multibody/lcs_factory.h" +#include "drake/common/text_logging.h" + namespace c3 { using drake::multibody::ModelInstanceIndex; diff --git a/tools/workspace/gurobi/BUILD.bazel b/tools/workspace/gurobi/BUILD.bazel new file mode 100644 index 0000000..a0f3ced --- /dev/null +++ b/tools/workspace/gurobi/BUILD.bazel @@ -0,0 +1,13 @@ +load("@bazel_skylib//lib:selects.bzl", "selects") + +selects.config_setting_group( + name = "enabled", + match_any = [ + ":enabled_via_flag", + ], +) + +config_setting( + name = "enabled_via_flag", + flag_values = {"@drake//tools/flags:with_gurobi": "True"}, +) diff --git a/tools/workspace/gurobi/package-darwin.BUILD.bazel.in b/tools/workspace/gurobi/package-darwin.BUILD.bazel.in new file mode 100644 index 0000000..82cdc27 --- /dev/null +++ b/tools/workspace/gurobi/package-darwin.BUILD.bazel.in @@ -0,0 +1,55 @@ +# -*- bazel -*- + +load("@drake//tools/install:install.bzl", "install") +load("@drake//tools/skylark:cc.bzl", "cc_library") + +licenses(["by_exception_only"]) # Gurobi + +# This rule is only built if a glob() call fails. +genrule( + name = "error-message", + outs = ["error-message.h"], + cmd = "echo 'error: Gurobi 10.0 is not installed at {gurobi_home}' && false", # noqa + visibility = ["//visibility:private"], +) + +GUROBI_C_HDRS = glob([ + "gurobi-distro/include/gurobi_c.h", +], allow_empty = True) or [":error-message.h"] + +GUROBI_CXX_HDRS = glob([ + "gurobi-distro/include/gurobi_c.h", + "gurobi-distro/include/gurobi_c++.h", +], allow_empty = True) or [":error-message.h"] + +cc_library( + name = "gurobi_c", + hdrs = GUROBI_C_HDRS, + includes = ["gurobi-distro/include"], + linkopts = [ + "-L{gurobi_home}/lib", + "-lgurobi100", + "-Wl,-rpath,{gurobi_home}/lib", + ], + visibility = ["//visibility:public"], +) + +cc_library( + name = "gurobi_cxx", + hdrs = GUROBI_CXX_HDRS, + includes = ["gurobi-distro/include"], + linkopts = [ + "-L{gurobi_home}/lib", + "-lgurobi100", + "-lgurobi_stdc++", + "-Wl,-rpath,{gurobi_home}/lib", + ], + visibility = ["//visibility:public"], +) + +# For macOS, the Drake install step does not need any additional actions to +# install Gurobi, since Gurobi was already installed system-wide in /Library. +install( + name = "install", + visibility = ["//visibility:public"], +) diff --git a/tools/workspace/gurobi/package-linux.BUILD.bazel.in b/tools/workspace/gurobi/package-linux.BUILD.bazel.in new file mode 100644 index 0000000..33c9f7c --- /dev/null +++ b/tools/workspace/gurobi/package-linux.BUILD.bazel.in @@ -0,0 +1,82 @@ +# -*- bazel -*- + +load("@drake//tools/install:install.bzl", "install", "install_files") +load("@drake//tools/skylark:cc.bzl", "cc_library") + +licenses(["by_exception_only"]) # Gurobi + +# This rule is only built if a glob() call fails. +genrule( + name = "error-message", + outs = ["error-message.h"], + cmd = "echo 'error: Gurobi 10.0 is not installed at {gurobi_home}, export GUROBI_HOME to the correct value' && false", # noqa + visibility = ["//visibility:private"], +) + +GUROBI_C_HDRS = glob([ + "gurobi-distro/include/gurobi_c.h", +], allow_empty = True) or [":error-message.h"] + +GUROBI_CXX_HDRS = glob([ + "gurobi-distro/include/gurobi_c.h", + "gurobi-distro/include/gurobi_c++.h", +], allow_empty = True) or [":error-message.h"] + +# In the Gurobi package, libgurobi100.so is a symlink to libgurobi.so.10.0.*. +# However, if we use libgurobi.so.10.0.* in srcs, executables that link this +# library will be unable to find it at runtime in the Bazel sandbox, +# because the NEEDED statements in the executable will not square with the +# RPATH statements. I don't really know why this happens, but I suspect it +# might be a Bazel bug. + +GUROBI_C_SRCS = glob([ + "gurobi-distro/lib/libgurobi100.so", +], allow_empty = True) or [":error-message.h"] + +GUROBI_CXX_SRCS = glob([ + "gurobi-distro/lib/libgurobi100.so", + "gurobi-distro/lib/libgurobi_g++5.2.a", +], allow_empty = True) or [":error-message.h"] + +GUROBI_INSTALL_LIBRARIES = glob([ + "gurobi-distro/lib/libgurobi.so.10.0.*", + "gurobi-distro/lib/libgurobi100.so", +], allow_empty = True) or [":error-message.h"] + +GUROBI_DOCS = glob([ + "gurobi-distro/EULA.pdf", +], allow_empty = True) or [":error-message.h"] + +cc_library( + name = "gurobi_c", + srcs = GUROBI_C_SRCS, + hdrs = GUROBI_C_HDRS, + includes = ["gurobi-distro/include"], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], +) + +cc_library( + name = "gurobi_cxx", + srcs = GUROBI_CXX_SRCS, + hdrs = GUROBI_CXX_HDRS, + includes = ["gurobi-distro/include"], + linkopts = ["-pthread"], + visibility = ["//visibility:public"], +) + +install_files( + name = "install_libraries", + dest = ".", + files = GUROBI_INSTALL_LIBRARIES, + strip_prefix = ["gurobi-distro"], + visibility = ["//visibility:private"], +) + +install( + name = "install", + docs = GUROBI_DOCS, + doc_strip_prefix = ["gurobi-distro"], + visibility = ["//visibility:public"], + deps = [":install_libraries"], +) diff --git a/tools/workspace/gurobi/repository.bzl b/tools/workspace/gurobi/repository.bzl new file mode 100644 index 0000000..4e9907c --- /dev/null +++ b/tools/workspace/gurobi/repository.bzl @@ -0,0 +1,89 @@ +# This is a Bazel repository_rule for the Gurobi solver. See +# https://www.bazel.io/versions/master/docs/skylark/repository_rules.html + +# Finds the "latest" f'{path}/{prefix}*/{subdir}', where "latest" is determined +# by converting the part that matched the '*' to an integer and taking the +# match with the highest value. +def _find_latest(repo_ctx, path, prefix, subdir): + best_dir = None + best_version = None + for d in repo_ctx.path(path).readdir(): + if d.basename.startswith(prefix): + full_dir = d.get_child(subdir) + if full_dir.exists: + version = int(d.basename[len(prefix):]) + if best_version == None or version > best_version: + best_version = version + best_dir = str(full_dir) + + return best_dir or (path + "/" + prefix + "-notfound/" + subdir) + +# Ubuntu only: GUROBI_HOME should be the linux64 directory in the Gurobi 10.0 +# release. +# +def _gurobi_impl(repo_ctx): + os_name = repo_ctx.os.name + if os_name == "mac os x": + os_name = "darwin" + + if os_name == "darwin": + # Gurobi must be installed into its standard location. + gurobi_home = _find_latest( + repo_ctx, + "/Library", + "gurobi100", + "macos_universal2", + ) + repo_ctx.symlink(gurobi_home, "gurobi-distro") + elif os_name == "linux": + # The default directory for the downloaded gurobi is + # /opt/gurobi100*/linux64. If the user does not use the default + # directory, the he/she should set GUROBI_HOME environment variable to + # the gurobi file location. + gurobi_home = repo_ctx.getenv("GUROBI_HOME", "") + repo_ctx.symlink( + gurobi_home or _find_latest( + repo_ctx, + "/opt", + "gurobi100", + "linux64", + ), + "gurobi-distro", + ) + else: + # Defer error reporting to the BUILD file. + repo_ctx.symlink("/gurobi-notfound", "gurobi-distro") + os_name = "linux" + + # Emit the generated BUILD.bazel file. + repo_ctx.template( + "BUILD.bazel", + Label("@//tools/workspace/gurobi:" + + "package-{}.BUILD.bazel.in".format(os_name)), + substitutions = { + "{gurobi_home}": gurobi_home, + }, + executable = False, + ) + + # Capture whether or not Gurobi tests can run in parallel. + license_unlimited_int = repo_ctx.getenv("DRAKE_GUROBI_LICENSE_UNLIMITED", "0") # noqa: E501 + license_unlimited = bool(int(license_unlimited_int) == 1) + repo_ctx.file( + "defs.bzl", + content = "DRAKE_GUROBI_LICENSE_UNLIMITED = {}" + .format(license_unlimited), + executable = False, + ) + +gurobi_repository = repository_rule( + local = True, + implementation = _gurobi_impl, +) + +def _gurobi_extension_impl(module_ctx): + gurobi_repository(name = "my_gurobi") + +gurobi_extension = module_extension( + implementation = _gurobi_extension_impl, +)