2
0
Fork 0
mirror of https://github.com/bazel-contrib/rules_foreign_cc synced 2024-11-29 12:33:51 +00:00
rules_foreign_cc/tools/build_defs/detect_root.bzl
2021-02-04 10:42:02 -08:00

86 lines
2.8 KiB
Python

# buildifier: disable=module-docstring
# buildifier: disable=function-docstring-header
def detect_root(source):
"""Detects the path to the topmost directory of the 'source' outputs.
To be used with external build systems to point to the source code/tools directories.
Args:
source (Target): A filegroup of source files
Returns:
string: The relative path to the root source directory
"""
sources = source.files.to_list()
if len(sources) == 0:
return ""
root = None
level = -1
# find topmost directory
for file in sources:
file_level = _get_level(file.path)
# If there is no level set or the current file's level
# is greather than what we have logged, update the root
if level == -1 or level > file_level:
root = file
level = file_level
if not root:
fail("No root source or directory was found")
if root.is_source:
return root.dirname
# Note this code path will never be hit due to a bug upstream Bazel
# https://github.com/bazelbuild/bazel/issues/12954
# If the root is not a source file, it must be a directory.
# Thus the path is returned
return root.path
def _get_level(path):
"""Determine the number of sub directories `path` is contained in
Args:
path (string): The target path
Returns:
int: The directory depth of `path`
"""
normalized = path
# This for loop ensures there are no double `//` substrings.
# A for loop is used because there's not currently a `while`
# or a better mechanism for guaranteeing all `//` have been
# cleaned up.
for i in range(len(path)):
new_normalized = normalized.replace("//", "/")
if len(new_normalized) == len(normalized):
break
normalized = new_normalized
return normalized.count("/")
# buildifier: disable=function-docstring-header
# buildifier: disable=function-docstring-args
# buildifier: disable=function-docstring-return
def filter_containing_dirs_from_inputs(input_files_list):
"""When the directories are also passed in the filegroup with the sources,
we get into a situation when we have containing in the sources list,
which is not allowed by Bazel (execroot creation code fails).
The parent directories will be created for us in the execroot anyway,
so we filter them out."""
# This puts directories in front of their children in list
sorted_list = sorted(input_files_list)
contains_map = {}
for input in input_files_list:
# If the immediate parent directory is already in the list, remove it
if contains_map.get(input.dirname):
contains_map.pop(input.dirname)
contains_map[input.path] = input
return contains_map.values()