blob: ab680d6be0f4da4519d5dde8c0a2a96e5dbbe8b4 [file] [log] [blame]
"""sysroot_library is a repository rule for importing libraries from a Chrome OS sysroot."""
PATH_SEPARATOR = ":"
def _execute_bash(repo_ctx, cmd):
return repo_ctx.execute(["/bin/bash", "-c", cmd]).stdout.strip("\n")
def _find_compiler(repo_ctx):
compiler_name = repo_ctx.os.environ.get("CC")
compiler = _execute_bash(repo_ctx, "type -p {}".format(compiler_name))
if compiler:
return compiler
else:
fail("Unable to locate compiler: {}".format(compiler_name))
def _get_system_include_paths(repo_ctx):
compiler = _find_compiler(repo_ctx)
sysroot = repo_ctx.os.environ.get("SYSROOT")
cmd = """
SYSROOT="{sysroot}" \\
{compiler} -print-search-dirs | \\
sed -n -e 's/^libraries: =//p'
""".format(
sysroot = sysroot,
compiler = compiler,
)
paths = _execute_bash(repo_ctx, cmd)
return paths.split(PATH_SEPARATOR)
def _find_lib_path(repo_ctx, archive_names):
compiler = _find_compiler(repo_ctx)
sysroot = repo_ctx.os.environ.get("SYSROOT")
for archive_name in archive_names:
cmd = """
SYSROOT="{sysroot}" \\
{compiler} -x c - -o /dev/null -Wl,--trace -l:{archive_name} -nostdlib <<<'int main(){{}}' | \\
sed -e '/.*\\.o$/d'
""".format(
sysroot = sysroot or "",
compiler = compiler,
archive_name = archive_name,
)
path = _execute_bash(repo_ctx, cmd)
if path:
return (archive_name, path)
return (None, None)
def _find_header_path(repo_ctx, header_name, includes):
system_includes = _get_system_include_paths(repo_ctx)
all_includes = includes + system_includes
for directory in all_includes:
cmd = """
test -f "{dir}/{hdr}" && echo "{dir}/{hdr}"
""".format(dir = directory, hdr = header_name)
result = _execute_bash(repo_ctx, cmd)
if result:
return result
return None
def _sysroot_library_impl(repo_ctx):
repo_name = repo_ctx.attr.name
includes = repo_ctx.attr.includes
hdrs = repo_ctx.attr.hdrs
optional_hdrs = repo_ctx.attr.optional_hdrs
deps = repo_ctx.attr.deps
static_lib_names = repo_ctx.attr.static_lib_names
shared_lib_names = repo_ctx.attr.shared_lib_names
static_lib_name, static_lib_path = _find_lib_path(
repo_ctx,
static_lib_names,
)
shared_lib_name, shared_lib_path = _find_lib_path(
repo_ctx,
shared_lib_names,
)
if not static_lib_path and not shared_lib_path:
fail("Library {} could not be found".format(repo_name))
hdr_names = []
hdr_paths = []
for hdr in hdrs:
hdr_path = _find_header_path(repo_ctx, hdr, includes)
if hdr_path:
repo_ctx.symlink(hdr_path, hdr)
hdr_names.append(hdr)
hdr_paths.append(hdr_path)
else:
fail("Could not find required header {}".format(hdr))
for hdr in optional_hdrs:
hdr_path = _find_header_path(repo_ctx, hdr, includes)
if hdr_path:
repo_ctx.symlink(hdr_path, hdr)
hdr_names.append(hdr)
hdr_paths.append(hdr_path)
hdrs_param = "hdrs = {},".format(str(hdr_names))
# This is needed for the case when quote-includes and system-includes
# alternate in the include chain, i.e.
# #include <SDL2/SDL.h> -> #include "SDL_main.h"
# -> #include <SDL2/_real_SDL_config.h> -> #include "SDL_platform.h"
# The problem is that the quote-includes are assumed to be
# in the same directory as the header they are included from -
# they have no subdir prefix ("SDL2/") in their paths
include_subdirs = {}
for hdr in hdr_names:
path_segments = hdr.split("/")
path_segments.pop()
current_path_segments = ["external", repo_name]
for segment in path_segments:
current_path_segments.append(segment)
current_path = "/".join(current_path_segments)
include_subdirs.update({current_path: None})
includes_param = "includes = {},".format(str(include_subdirs.keys()))
deps_names = []
for dep in deps:
dep_name = repr("@" + dep)
deps_names.append(dep_name)
deps_param = "deps = [{}],".format(",".join(deps_names))
link_hdrs_command = "mkdir -p $(RULEDIR)/remote \n"
remote_hdrs = []
for path, hdr in zip(hdr_paths, hdr_names):
remote_hdr = "remote/" + hdr
remote_hdrs.append(remote_hdr)
link_hdrs_command += "cp {path} $(RULEDIR)/{hdr}\n ".format(
path = path,
hdr = remote_hdr,
)
link_remote_static_lib_genrule = ""
link_remote_shared_lib_genrule = ""
remote_static_library_param = ""
remote_shared_library_param = ""
static_library_param = ""
shared_library_param = ""
if static_lib_path:
repo_ctx.symlink(static_lib_path, static_lib_name)
static_library_param = "static_library = \"{}\",".format(
static_lib_name,
)
remote_static_library = "remote/" + static_lib_name
link_library_command = """
mkdir -p $(RULEDIR)/remote && cp {path} $(RULEDIR)/{lib}""".format(
path = static_lib_path,
lib = remote_static_library,
)
remote_static_library_param = """
static_library = "remote_link_static_library","""
link_remote_static_lib_genrule = """
genrule(
name = "remote_link_static_library",
outs = ["{remote_static_library}"],
cmd = {link_library_command}
)
""".format(
link_library_command = repr(link_library_command),
remote_static_library = remote_static_library,
)
if shared_lib_path:
repo_ctx.symlink(shared_lib_path, shared_lib_name)
shared_library_param = "shared_library = \"{}\",".format(
shared_lib_name,
)
remote_shared_library = "remote/" + shared_lib_name
link_library_command = """
mkdir -p $(RULEDIR)/remote && cp {path} $(RULEDIR)/{lib}""".format(
path = shared_lib_path,
lib = remote_shared_library,
)
remote_shared_library_param = """
shared_library = "remote_link_shared_library","""
link_remote_shared_lib_genrule = """
genrule(
name = "remote_link_shared_library",
outs = ["{remote_shared_library}"],
cmd = {link_library_command}
)
""".format(
link_library_command = repr(link_library_command),
remote_shared_library = remote_shared_library,
)
repo_ctx.file(
"BUILD",
executable = False,
content =
"""
load("@bazel_tools//tools/build_defs/cc:cc_import.bzl", "cc_import")
cc_import(
name = "local_includes",
{static_library}
{shared_library}
{hdrs}
{deps}
{includes}
)
genrule(
name = "remote_link_headers",
outs = {remote_hdrs},
cmd = {link_hdrs_command}
)
{link_remote_static_lib_genrule}
{link_remote_shared_lib_genrule}
cc_import(
name = "remote_includes",
hdrs = [":remote_link_headers"],
{remote_static_library}
{remote_shared_library}
{deps}
{includes}
)
alias(
name = "{name}",
actual = select({{
"@bazel_tools//src/conditions:remote": "remote_includes",
"//conditions:default": "local_includes",
}}),
visibility = ["//visibility:public"],
)
""".format(
static_library = static_library_param,
shared_library = shared_library_param,
hdrs = hdrs_param,
deps = deps_param,
hdr_names = str(hdr_names),
link_hdrs_command = repr(link_hdrs_command),
name = repo_name,
includes = includes_param,
remote_hdrs = remote_hdrs,
link_remote_static_lib_genrule = link_remote_static_lib_genrule,
link_remote_shared_lib_genrule = link_remote_shared_lib_genrule,
remote_static_library = remote_static_library_param,
remote_shared_library = remote_shared_library_param,
),
)
sysroot_library = repository_rule(
implementation = _sysroot_library_impl,
local = True,
remotable = True,
attrs = {
"deps": attr.string_list(doc = """
List of names of system libraries this target depends upon.
"""),
"hdrs": attr.string_list(
mandatory = True,
allow_empty = False,
doc = """
List of the library's public headers which must be imported.
""",
),
"includes": attr.string_list(doc = """
List of directories that should be browsed when looking for headers.
"""),
"optional_hdrs": attr.string_list(doc = """
List of the library's headers that should be imported if present.
"""),
"shared_lib_names": attr.string_list(doc = """
List of possible shared library names in order of preference.
"""),
"static_lib_names": attr.string_list(doc = """
List of possible static library names in order of preference.
"""),
},
doc =
"""sysroot_library is a repository rule for importing non-Bazel libraries
from a Chrome OS sysroot.
Currently `sysroot_library` requires two experimental flags:
--experimental_starlark_cc_import
--experimental_repo_remote_exec
""",
)