blob: 94945befc32164a2d8fcd49fc47713f701f8707d [file] [log] [blame]
#!/usr/bin/env python3.8
# Copyright 2021 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Example usage:
$ fx set ...
$ scripts/gn/gen_visibility_globs.py \
--all="//build/config/rust:deny_unused_results" \
--allow="//build/config/rust:allow_unused_results"
The output is useful for instance if you have a visibility allowlist that you want to maintain with
shortened glob paths.
To run this script's self-tests:
$ TEST=1 scripts/gn/gen_visibility_globs.py
"""
import argparse
import sys
import gn_util
import unittest
import os
def main():
parser = argparse.ArgumentParser(
description="Prints a visibility list for gn configs")
parser.add_argument(
"--all",
help="gn target that produces a universe of labels to consider",
required=True)
parser.add_argument(
"--allow",
help="gn target whose current users we want to produce an allowlist for",
required=True)
parser.add_argument(
"--verbose",
action="store_true",
default=False,
help="Print progress and stats")
parser.add_argument(
"--ignore-suffix",
help="comma-separated list of label suffixes to ignore",
default="")
def verbose(*vargs, **kwargs):
if args.verbose:
print(*vargs, **kwargs)
args = parser.parse_args()
verbose("Getting all labels list...")
all_labels = gn_util.gn_refs(args.all)
verbose(f"Found {len(all_labels)} elements in universe.")
ignore_suffix = args.ignore_suffix.split(",")
all_labels = (
label for label in all_labels
if not any(label.endswith(suffix) for suffix in ignore_suffix))
allow_labels = gn_util.gn_refs(args.allow)
verbose(f"Found {len(allow_labels)} elements to allow.")
tree = Node("/")
update_tree(tree, all_labels, False)
update_tree(tree, allow_labels, True)
for path, include in tree.glob_collect():
path = "/".join(path)
if include:
print(f"\"{path}\",")
else:
verbose(f"ignored {path}")
return 0
class Node:
def __init__(self, name, value=None):
self.name = name
self.children = []
self.children = dict()
self.value = value
def ensure_child(self, name):
if name not in self.children:
self.children[name] = Node(name)
return self.children[name]
def visit(self, path=None):
if path is None:
path = []
p = path + [self.name]
if len(self.children) == 0:
yield p
for k, c in self.children.items():
for d in c.visit(p):
yield d
def glob_collect(self, path=None):
if path is None:
path = []
p = path + [self.name]
if len(self.children) == 0:
yield p, self.value
return
child_items = []
can_glob = True
for k, c in self.children.items():
for v, include in c.glob_collect(p):
child_items.append((v, include))
can_glob = can_glob and include
if can_glob:
yield p + ["*"], True
else:
for v, i in child_items:
yield v, i
def get_label_parts(label):
"""returns the parts of an absolute label as a list"""
return label[2:].replace(":", "/").split("/")
def update_tree(tree, labels, value):
"""updates the tree for all the labels in `labels` to assume `value`."""
for l in labels:
parts = get_label_parts(l)
n = tree
for part in parts:
n = n.ensure_child(part)
n.value = value
class Test(unittest.TestCase):
def test_label_parts(self):
self.assertEqual(
get_label_parts("//foo/bar:baz"), ["foo", "bar", "baz"])
def test_build_tree(self):
tree = Node("/")
update_tree(tree, ["//foo/bar:baz", "//foo/bar:bar"], False)
bar = tree.children["foo"].children["bar"]
self.assertIsNone(bar.value)
self.assertFalse(bar.children["bar"].value)
self.assertFalse(bar.children["baz"].value)
items = []
for x in tree.visit():
items.append("/".join(x))
items.sort()
self.assertEqual(items, ["//foo/bar/bar", "//foo/bar/baz"])
update_tree(tree, ["//foo/bar:baz"], True)
self.assertFalse(bar.children["bar"].value)
self.assertTrue(bar.children["baz"].value)
def test_glob_collect(self):
tree = Node("/")
update_tree(
tree, ["//foo/bar:baz", "//foo/bar:bar", "//foo/apple:banana"],
False)
update_tree(tree, ["//foo/bar:baz", "//foo/bar:bar"], True)
accepted = []
denied = []
for i, inc in tree.glob_collect():
p = "/".join(i)
if inc:
accepted.append(p)
else:
denied.append(p)
self.assertEqual(accepted, ["//foo/bar/*"])
self.assertEqual(denied, ["//foo/apple/banana"])
if __name__ == "__main__":
if os.getenv("TEST") is not None:
unittest.main()
else:
sys.exit(main())