Skip to content

Commit

Permalink
Add support for depending on shared libraries on linux.
Browse files Browse the repository at this point in the history
  • Loading branch information
mfarrugi committed Nov 12, 2017
1 parent 5543a0b commit 3bc477a
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 73 deletions.
38 changes: 21 additions & 17 deletions examples/matrix/BUILD
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
package(default_visibility = ["//visibility:public"])

load("@//rust:rust.bzl", "rust_library", "rust_test")
load("@//rust:rust.bzl", "rust_library", "rust_test", "rust_binary")

cc_library(
name = "native_matrix",
srcs = ["src/matrix.c"],
hdrs = ["src/matrix.h"],
copts = ["-std=c99"],
)

cc_test(
name = "native_matrix_test",
srcs = ["src/matrix_test.c"],
copts = ["-std=c99"],
rust_library(
name = "matrix",
srcs = [
"src/ffi.rs",
"src/lib.rs",
"src/matrix.rs",
],
deps = [
":native_matrix",
"@libc//:libc",
"//matrix/c:native_matrix",
],
)

rust_test(
name = "matrix_test",
deps = [":matrix"],
)

## Do the same as above, but with a dynamic c library.

rust_library(
name = "matrix",
name = "matrix_dynamically_linked",
srcs = [
"src/ffi.rs",
"src/lib.rs",
"src/matrix.rs",
],
deps = [
":native_matrix",
"@libc//:libc",
"//matrix/c:native_matrix_so",
],
)

rust_test(
name = "matrix_test",
deps = [":matrix"],
name = "matrix_dylib_test",
deps = [":matrix_dynamically_linked"],
)
35 changes: 35 additions & 0 deletions examples/matrix/c/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
cc_library(
name = "native_matrix",
srcs = ["matrix.c"],
hdrs = ["matrix.h"],
copts = ["-std=c99"],
visibility = ["//visibility:public"],
)

cc_test(
name = "native_matrix_test",
srcs = ["matrix_test.c"],
copts = ["-std=c99"],
deps = [
":native_matrix",
],
)

## Do the same as above, but with a dynamic c library.

cc_library(
name = "native_matrix_so",
srcs = [":libnative_matrix_so.so"],
hdrs = ["matrix.h"],
visibility = ["//visibility:public"],
)

cc_binary(
name = "libnative_matrix_so.so",
srcs = [
"matrix.c",
"matrix.h",
],
copts = ["-std=c99"],
linkshared = True,
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include "matrix/src/matrix.h"
#include "matrix/c/matrix.h"

#include <string.h>
#include <stdio.h>
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include "matrix/src/matrix.h"
#include "matrix/c/matrix.h"

#include <assert.h>
#include <stdint.h>
Expand Down
2 changes: 1 addition & 1 deletion examples/matrix/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub struct Matrix {
pub data: *mut uint64_t,
}

#[link(name = "native_matrix")]
// #[link(name = "native_matrix")] // Don't need this, BUILD file manages linking already.
extern {
pub fn matrix_new(rows: size_t, cols: size_t, data: *const uint64_t) -> *mut Matrix;
pub fn matrix_at(matrix: *const Matrix, row: size_t, col: size_t, n: *mut uint64_t) -> c_int;
Expand Down
2 changes: 1 addition & 1 deletion examples/matrix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ extern crate libc;

mod ffi;

pub mod matrix;
pub mod matrix;
4 changes: 2 additions & 2 deletions examples/matrix/src/matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::ptr;

/// Wrapper around pointer to FFI Matrix struct.
pub struct Matrix {
pub matrix: *mut ffi::Matrix,
matrix: *mut ffi::Matrix,
}

/// Wrapper around low-level FFI Matrix API.
Expand All @@ -38,7 +38,7 @@ impl Matrix {
let mut data_copy: Vec<u64> = vec![0; data.len()];
data_copy.clone_from_slice(data);
unsafe {
let mut matrix: *mut ffi::Matrix = ffi::matrix_new(rows, cols, data_copy.as_ptr());
let matrix: *mut ffi::Matrix = ffi::matrix_new(rows, cols, data_copy.as_ptr());
if matrix.is_null() {
panic!("Failed to allocate Matrix.");
}
Expand Down
99 changes: 51 additions & 48 deletions rust/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ rust_repositories()
[Cargo](https://crates.io/).
"""

load(":toolchain.bzl", "build_rustc_command", "build_rustdoc_command", "build_rustdoc_test_command")
load(":toolchain.bzl", "build_rustc_command", "build_rustdoc_command", "build_rustdoc_test_command", "relative_path")

RUST_FILETYPE = FileType([".rs"])

A_FILETYPE = FileType([".a"])
SO_FILETYPE = FileType([".so"])

LIBRARY_CRATE_TYPES = [
"lib",
Expand All @@ -70,38 +71,10 @@ HTML_MD_FILETYPE = FileType([

CSS_FILETYPE = FileType([".css"])

def _path_parts(path):
"""Takes a path and returns a list of its parts with all "." elements removed.
The main use case of this function is if one of the inputs to _relative()
is a relative path, such as "./foo".
Args:
path_parts: A list containing parts of a path.
Returns:
Returns a list containing the path parts with all "." elements removed.
"""
path_parts = path.split("/")
return [part for part in path_parts if part != "."]

def _relative(src_path, dest_path):
"""Returns the relative path from src_path to dest_path."""
src_parts = _path_parts(src_path)
dest_parts = _path_parts(dest_path)
n = 0
done = False
for src_part, dest_part in zip(src_parts, dest_parts):
if src_part != dest_part:
break
n += 1

relative_path = ""
for i in range(n, len(src_parts)):
relative_path += "../"
relative_path += "/".join(dest_parts[n:])

return relative_path
def _get_lib_name(lib):
""" Returns the name of a library artifact, eg. libabc.a -> abc"""
libname, ext = lib.basename.split(".", 2)
return libname[3:]

def _create_setup_cmd(lib, deps_dir, in_runfiles):
"""
Expand All @@ -110,7 +83,7 @@ def _create_setup_cmd(lib, deps_dir, in_runfiles):
"""
lib_path = lib.short_path if in_runfiles else lib.path
return (
"ln -sf " + _relative(deps_dir, lib_path) + " " +
"ln -sf " + relative_path(deps_dir, lib_path) + " " +
deps_dir + "/" + lib.basename + "\n"
)

Expand All @@ -134,6 +107,7 @@ def _setup_deps(deps, name, working_dir, allow_cc_deps=False,
Returns a struct containing the following fields:
libs:
transitive_libs:
transitive_dylibs:
setup_cmd:
search_flags:
link_flags:
Expand All @@ -146,39 +120,50 @@ def _setup_deps(deps, name, working_dir, allow_cc_deps=False,

libs = depset()
transitive_libs = depset()
symlinked_libs = depset()
transitive_dylibs = depset(order="topological") # Dylib link flag ordering matters.
transitive_staticlibs = depset()
link_flags = []
for dep in deps:
if hasattr(dep, "rust_lib"):
# This dependency is a rust_library
libs += [dep.rust_lib]
transitive_libs += [dep.rust_lib] + dep.transitive_libs
symlinked_libs += [dep.rust_lib] + dep.transitive_libs
link_flags += [(
"--extern " + dep.label.name + "=" +
deps_dir + "/" + dep.rust_lib.basename
)]
has_rlib = True

transitive_dylibs += dep.transitive_dylibs
transitive_staticlibs += A_FILETYPE.filter(dep.transitive_libs)

elif hasattr(dep, "cc"):
# This dependency is a cc_library
if not allow_cc_deps:
fail("Only rust_library, rust_binary, and rust_test targets can " +
"depend on cc_library")

# This dependency is a cc_library
native_libs = A_FILETYPE.filter(dep.cc.libs)
libs += native_libs
transitive_libs += native_libs
symlinked_libs += native_libs
link_flags += ["-l static=" + dep.label.name]
static_libs = A_FILETYPE.filter(dep.cc.libs)
dynamic_libs = SO_FILETYPE.filter(dep.cc.libs)

libs += static_libs + dynamic_libs

transitive_libs += static_libs + dynamic_libs
transitive_dylibs += dynamic_libs
transitive_staticlibs += static_libs

has_native = True

else:
fail("rust_library, rust_binary and rust_test targets can only depend " +
"on rust_library or cc_library targets.")

for symlinked_lib in symlinked_libs:
setup_cmd += [_create_setup_cmd(symlinked_lib, deps_dir, in_runfiles)]
link_flags += ["-l static=" + _get_lib_name(lib) for lib in transitive_staticlibs.to_list()]
link_flags += ["-l dylib=" + _get_lib_name(lib) for lib in transitive_dylibs.to_list()]

# Create symlinks to all transitive libs in our deps_dir.
for transitive_lib in transitive_libs:
setup_cmd += [_create_setup_cmd(transitive_lib, deps_dir, in_runfiles)]

search_flags = []
if has_rlib:
Expand All @@ -189,6 +174,7 @@ def _setup_deps(deps, name, working_dir, allow_cc_deps=False,
return struct(
libs = list(libs),
transitive_libs = list(transitive_libs),
transitive_dylibs = transitive_dylibs,
setup_cmd = setup_cmd,
search_flags = search_flags,
link_flags = link_flags)
Expand Down Expand Up @@ -277,6 +263,7 @@ def _rust_library_impl(ctx):
rust_srcs = ctx.files.srcs,
rust_deps = ctx.attr.deps,
transitive_libs = depinfo.transitive_libs,
transitive_dylibs = depinfo.transitive_dylibs,
rust_lib = rust_lib)

def _rust_binary_impl(ctx):
Expand Down Expand Up @@ -325,9 +312,14 @@ def _rust_binary_impl(ctx):
progress_message = ("Compiling Rust binary %s (%d files)"
% (ctx.label.name, len(ctx.files.srcs))))

runfiles = ctx.runfiles(
files = depinfo.transitive_dylibs.to_list() + ctx.files.data,
collect_data = True)

return struct(rust_srcs = ctx.files.srcs,
crate_root = main_rs,
rust_deps = ctx.attr.deps)
rust_deps = ctx.attr.deps,
runfiles = runfiles)

def _rust_test_common(ctx, test_binary):
"""Builds a Rust test binary.
Expand Down Expand Up @@ -373,6 +365,7 @@ def _rust_test_common(ctx, test_binary):
rust_flags = ["--test"])

compile_inputs = (target.srcs +
ctx.files.data +
depinfo.libs +
depinfo.transitive_libs +
[toolchain.rustc] +
Expand All @@ -388,19 +381,26 @@ def _rust_test_common(ctx, test_binary):
use_default_shell_env = True,
progress_message = ("Compiling Rust test %s (%d files)"
% (ctx.label.name, len(target.srcs))))
return depinfo

def _rust_test_impl(ctx):
"""
Implementation for rust_test Skylark rule.
"""
_rust_test_common(ctx, ctx.outputs.executable)
depinfo = _rust_test_common(ctx, ctx.outputs.executable)

runfiles = ctx.runfiles(
files = depinfo.transitive_dylibs.to_list() + ctx.files.data,
collect_data = True)

return struct(runfiles = runfiles)

def _rust_bench_test_impl(ctx):
"""Implementation for the rust_bench_test Skylark rule."""
rust_bench_test = ctx.outputs.executable
test_binary = ctx.new_file(ctx.configuration.bin_dir,
"%s_bin" % rust_bench_test.basename)
_rust_test_common(ctx, test_binary)
depinfo = _rust_test_common(ctx, test_binary)

ctx.file_action(
output = rust_bench_test,
Expand All @@ -410,7 +410,10 @@ def _rust_bench_test_impl(ctx):
"%s --bench\n" % test_binary.short_path]),
executable = True)

runfiles = ctx.runfiles(files = [test_binary], collect_data = True)
runfiles = ctx.runfiles(
files = depinfo.transitive_dylibs.to_list() + [test_binary],
collect_data = True)

return struct(runfiles = runfiles)

def _build_rustdoc_flags(ctx):
Expand Down
Loading

0 comments on commit 3bc477a

Please sign in to comment.