Skip to content

Commit

Permalink
Resources can be found in install tree based on libdrake.so location
Browse files Browse the repository at this point in the history
When an executable depends on `libdrake.so`, it can find the resources in the
install tree based on the location of `libdrake.so`. The implementation to
find `libdrake.so` is platform specific (MacOS and Linux). In both cases, it
finds the location of the library loaded in the process. It extracts its
directory and appends its with the relative path to the resource files.
This should limit the necessity of programmatically add path to the candidate
directory in which the sentinel file is searched.
  • Loading branch information
Francois Budin committed Dec 8, 2017
1 parent 7cb2a38 commit 58e2e92
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 55 deletions.
17 changes: 0 additions & 17 deletions drake/bindings/pydrake/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,3 @@
from . import rbtree

from .pydrake_path import getDrakePath

# Adding searchable path as inferred by pydrake. This assumes that the python
# module has not been moved outside of the installation directory (in which
# the data has also been installed).
path = dirname(__file__)
version = ".".join(python_version_tuple()[:2])
# In the install tree. pydrake Python module is in
# `lib/python2.7/site-packages/pydrake/` whereas the data is installed in
# `share/drake`. From the current file location, the data is 4 directories
# up. If pydrake is not in the expected directory, `path` is not added to the
# resource search path.
if path.endswith("lib/python" + version + "/site-packages/pydrake"):
common.AddResourceSearchPath(
realpath(join(path,
pardir, pardir, pardir, pardir,
"share/drake"))
)
2 changes: 1 addition & 1 deletion drake/bindings/pydrake/pydrake_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@


def getDrakePath():
return pydrake.common.GetDrakePath()
return os.path.abspath(pydrake.common.GetDrakePath())
15 changes: 13 additions & 2 deletions drake/common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,22 @@ DRAKE_RESOURCE_SENTINEL = "//drake:.drake-find_resource-sentinel"

drake_cc_library(
name = "find_resource",
srcs = ["find_resource.cc"],
hdrs = ["find_resource.h"],
srcs = [
"find_loaded_library.cc",
"find_resource.cc",
],
hdrs = [
"find_loaded_library.h",
"find_resource.h",
],
data = [
DRAKE_RESOURCE_SENTINEL,
],
# for libdrake.so path on linux
linkopts = select({
"//tools/cc_toolchain:linux": ["-ldl"],
"//conditions:default": [],
}),
deps = [
":essential",
"@spruce",
Expand Down
124 changes: 124 additions & 0 deletions drake/common/find_loaded_library.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "drake/common/find_loaded_library.h"

#ifdef __APPLE__
#include <dlfcn.h>

#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#else // Not __APPLE__
#include <libgen.h>
#include <string.h>

#include <link.h>
#endif

using std::string;

namespace drake {

#ifdef __APPLE__

// This code has been adapted from:
// https://stackoverflow.com/questions/4309117/determining-programmatically-what-modules-are-loaded-in-another-process-os-x/23229148#23229148

// Reads memory from MacOS specific structures into an `unsigned char*`.
unsigned char * readProcessMemory(mach_vm_address_t addr,
mach_msg_type_number_t* size) {
mach_msg_type_number_t dataCnt =
reinterpret_cast<mach_msg_type_number_t>(*size);
vm_offset_t readMem;

kern_return_t kr = vm_read(mach_task_self(), addr, *size,
&readMem, &dataCnt);
if (kr) {
return NULL;
}
return ( reinterpret_cast<unsigned char *>(readMem));
}

// Gets the list of all the dynamic libraries that have been loaded. Finds
// `library_name` in the list, and returns its directory path appended
// with relative directory to find resource files in drake install tree.
// This function is specific to MacOS
optional<string> loaded_library_path(const std::string &library_name) {
optional<string> binary_dirname;
struct task_dyld_info dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
// Getinformation from current process.
if (task_info(mach_task_self(), TASK_DYLD_INFO,
reinterpret_cast<task_info_t>(&dyld_info), &count) == KERN_SUCCESS) {
// Recover list of dynamic libraries.
mach_msg_type_number_t size = sizeof(struct dyld_all_image_infos);
uint8_t* data =
readProcessMemory(dyld_info.all_image_info_addr, &size);
if (!data) {
return binary_dirname;
}
struct dyld_all_image_infos* infos =
reinterpret_cast<struct dyld_all_image_infos *>(data);

// Recover number of dynamic libraries in list.
mach_msg_type_number_t size2 =
sizeof(struct dyld_image_info) * infos->infoArrayCount;
uint8_t* info_addr = readProcessMemory(
reinterpret_cast<mach_vm_address_t>(infos->infoArray), &size2);
if (!info_addr) {
return binary_dirname;
}
struct dyld_image_info* info =
reinterpret_cast<struct dyld_image_info*>(info_addr);

// Loop over the dynamic libraries until `library_name` is found.
for (uint32_t i=0; i < infos->infoArrayCount; i++) {
const char * pos_slash = strrchr(info[i].imageFilePath, '/');
if (!strcmp(pos_slash + 1, library_name.c_str())) {
binary_dirname = string(info[i].imageFilePath,
pos_slash - info[i].imageFilePath);
break;
}
}
}
return binary_dirname;
}
#else // Not __APPLE__

// This code has been adapted from:
// http://syprog.blogspot.ru/2011/12/listing-loaded-shared-objects-in-linux.html

// Chained list of shared objects
struct lmap {
void* base_address; // Base address of the shared object
char* path; // Absolute file name (path) of the shared object
void* not_needed; // Pointer to the dynamic section of the SO
struct lmap *next, *prev; // chain of loaded objects
};

// Content of that dlopen is saved in this structure.
struct something {
void* pointers[3];
struct something* ptr;
};

// Gets the list of all the shared objects that have been loaded. Finds
// `library_name` in the list, and returns its directory path appended
// with relative directory to find resource files in drake install tree.
// This function is specific to Linux.
optional<string> loaded_library_path(const std::string &library_name) {
optional<string> binary_dirname;
struct lmap* pl;
void* ph = dlopen(NULL, RTLD_NOW);
struct something* p = reinterpret_cast<struct something*>(ph);
p = p->ptr;
pl = reinterpret_cast<struct lmap*>(p->ptr);
// Loop over loaded shared objects until `library_name` is found.
while (NULL != pl) {
if (!strcmp(basename(pl->path), library_name.c_str())) {
binary_dirname = string(dirname(pl->path));
break;
}
pl = pl->next;
}
return binary_dirname;
}
#endif
} // namespace drake
14 changes: 14 additions & 0 deletions drake/common/find_loaded_library.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string>
#include <vector>

#include "drake/common/drake_optional.h"

namespace drake {


optional<std::string> loaded_library_path(const std::string &library_name);


} // namespace drake
28 changes: 26 additions & 2 deletions drake/common/find_resource.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@
#include <utility>
#include <vector>

#ifdef __APPLE__
#include <dlfcn.h>

#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#else // Not __APPLE__
#include <libgen.h>
#include <string.h>

#include <link.h>
#endif

#include <spruce.hh>

#include "drake/common/drake_throw.h"
#include "drake/common/find_loaded_library.h"
#include "drake/common/never_destroyed.h"

using std::string;
Expand Down Expand Up @@ -106,6 +119,14 @@ optional<std::string> getenv_optional(const char* const name) {
return nullopt;
}

optional<std::string> resource_path_from_libdrake() {
optional<std::string> libdrake_dir = loaded_library_path("libdrake.so");
if (libdrake_dir) {
libdrake_dir = libdrake_dir.value() + "/../share/drake";
}
return libdrake_dir;
}

bool is_relative_path(const string& path) {
// TODO(jwnimmer-tri) Prevent .. escape?
return !path.empty() && (path[0] != '/');
Expand Down Expand Up @@ -217,8 +238,11 @@ Result FindResource(string resource_path) {
spruce::path candidate_dir(search_path);
candidate_dirs.emplace_back(check_candidate_dir(candidate_dir));
}

// (3) Search in cwd (and its parent, grandparent, etc.) to find Drake's
// (3) Find where `librake.so` is, and add search path that corresponds to
// resource folder in install tree based on `libdrake.so` location.
static optional<string> from_libdrake = resource_path_from_libdrake();
candidate_dirs.emplace_back(from_libdrake);
// (4) Search in cwd (and its parent, grandparent, etc.) to find Drake's
// resource-root sentinel file.
candidate_dirs.emplace_back(find_sentinel_dir());

Expand Down
38 changes: 6 additions & 32 deletions drake/examples/kuka_iiwa_arm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ load(
"drake_cc_library",
"drake_cc_binary",
"drake_cc_googletest",
"drake_example_cc_binary",
)
load("//tools/install:install_data.bzl", "install", "install_data")
load("//tools/lint:lint.bzl", "add_lint_tests")
Expand Down Expand Up @@ -74,7 +75,7 @@ drake_cc_library(
],
)

drake_cc_binary(
drake_example_cc_binary(
name = "iiwa_controller",
srcs = ["iiwa_controller.cc"],
data = [
Expand All @@ -84,15 +85,11 @@ drake_cc_binary(
deps = [
":iiwa_common",
":lcm_plan_interpolator",
"//drake/common:text_logging_gflags",
"//drake/lcm",
"//drake/systems/lcm",
"//drake/systems/lcm:lcm_driven_loop",
"@com_github_gflags_gflags//:gflags",
],
)

drake_cc_binary(
drake_example_cc_binary(
name = "iiwa_wsg_simulation",
srcs = ["iiwa_wsg_simulation.cc"],
add_test_rule = 1,
Expand All @@ -108,25 +105,12 @@ drake_cc_binary(
":iiwa_common",
":iiwa_lcm",
":oracular_state_estimator",
"//drake/common:text_logging_gflags",
"//drake/examples/kuka_iiwa_arm/iiwa_world:iiwa_wsg_diagram_factory",
"//drake/lcm",
"//drake/manipulation/schunk_wsg:schunk_wsg_constants",
"//drake/manipulation/schunk_wsg:schunk_wsg_controller",
"//drake/manipulation/schunk_wsg:schunk_wsg_lcm",
"//drake/manipulation/util:world_sim_tree_builder",
"//drake/multibody/rigid_body_plant",
"//drake/systems/analysis",
"//drake/systems/controllers:inverse_dynamics_controller",
"//drake/systems/controllers:pid_controller",
"//drake/systems/primitives:constant_vector_source",
"//drake/systems/primitives:matrix_gain",
"//drake/util:lcm_util",
"@com_github_gflags_gflags//:gflags",
],
)

drake_cc_binary(
drake_example_cc_binary(
name = "kuka_simulation",
srcs = ["kuka_simulation.cc"],
add_test_rule = 1,
Expand All @@ -140,31 +124,21 @@ drake_cc_binary(
deps = [
":iiwa_common",
":iiwa_lcm",
"//drake/common:find_resource",
"//drake/common:text_logging_gflags",
"//drake/lcm",
"//drake/manipulation/util:sim_diagram_builder",
"//drake/manipulation/util:world_sim_tree_builder",
"//drake/multibody/rigid_body_plant",
"//drake/multibody/rigid_body_plant:frame_visualizer",
"//drake/systems/analysis:simulator",
"//drake/systems/controllers:inverse_dynamics_controller",
"//drake/systems/primitives:constant_vector_source",
"@com_github_gflags_gflags//:gflags",
],
)

drake_cc_binary(
name = "kuka_plan_runner",
srcs = ["kuka_plan_runner.cc"],
srcs = ["kuka_plan_runner.cc"] + ["//tools/install/libdrake:libdrake.so"],
data = [
":models",
"//drake/manipulation/models/iiwa_description:models",
],
deps = [
":iiwa_common",
":iiwa_lcm",
"//drake/common:find_resource",
"//tools/install/libdrake:drake_shared_library",
"@lcmtypes_bot2_core",
"@lcmtypes_robotlocomotion",
],
Expand Down
1 change: 1 addition & 0 deletions tools/drake.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ load(
"drake_cc_googletest",
"drake_cc_library",
"drake_cc_test",
"drake_example_cc_binary",
)
5 changes: 4 additions & 1 deletion tools/install/libdrake/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ cc_library(
cc_library(
name = "drake_shared_library",
hdrs = [":libdrake_headers"],
visibility = ["//drake/bindings/pydrake:__pkg__"],
visibility = [
"//drake/bindings:__subpackages__",
"//drake/examples:__subpackages__",
],
deps = [
":gurobi_deps",
":mosek_deps",
Expand Down
35 changes: 35 additions & 0 deletions tools/skylark/drake_cc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,38 @@ def drake_cc_googletest(
name = name,
deps = deps,
**kwargs)

def drake_example_cc_binary(
name,
srcs = [],
data = [],
deps = [],
copts = [],
gcc_copts = [],
linkstatic = 1,
testonly = 0,
add_test_rule = 0,
test_rule_args = [],
test_rule_data = [],
test_rule_size = None,
test_rule_flaky = 0,
**kwargs):
"""Creates a rule to declare a C++ binary.
"""
drake_cc_binary(
name = name,
srcs = srcs +
["//tools/install/libdrake:libdrake.so",
"//drake/lcmtypes:drake_lcmtypes_headers"],
data = data,
deps = deps + ["//tools/install/libdrake:drake_shared_library"],
copts = copts,
gcc_copts = gcc_copts,
linkstatic = linkstatic,
testonly = testonly,
add_test_rule = add_test_rule,
test_rule_args = test_rule_args,
test_rule_data = test_rule_data,
test_rule_size = test_rule_size,
test_rule_flaky = test_rule_flaky,
**kwargs)

0 comments on commit 58e2e92

Please sign in to comment.