| #!/usr/bin/env python3 | |
| # Copyright (C) 2026 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """Prevents code outside Trace Processor from depending on its internals. | |
| Targets under src/trace_processor/util are intentionally reusable. All other | |
| src/trace_processor targets are implementation details and must not be direct | |
| dependencies of targets outside that directory. | |
| """ | |
| import argparse | |
| import os | |
| import sys | |
| # gn_utils remains infrastructure for interrogating GN build descriptions. | |
| ROOT_DIR = os.path.dirname( | |
| os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| sys.path.insert(0, os.path.join(ROOT_DIR, 'tools')) | |
| import gn_utils | |
| TP_PREFIX = '//src/trace_processor' | |
| ALLOWED_TP_PREFIX = '//src/trace_processor/util' | |
| # These root executables aggregate Trace Processor's own tests and benchmarks. | |
| TP_TEST_AGGREGATORS = { | |
| '//:all', | |
| '//:perfetto_benchmarks', | |
| '//:perfetto_integrationtests', | |
| '//:perfetto_trace_processor_unittests', | |
| '//:perfetto_unittests', | |
| '//:trace_processor_minimal_smoke_tests', | |
| } | |
| # Existing boundary violations. Do not add entries: move the shared code out of | |
| # Trace Processor instead. | |
| LEGACY_ALLOWED_EDGES = { | |
| ('//src/profiling/memory:end_to_end_tests', '//src/trace_processor:lib'), | |
| ('//src/protozero/text_to_proto:unittests', | |
| '//src/trace_processor:gen_cc_test_messages_descriptor'), | |
| ('//src/tools/protoprofile:common', '//src/trace_processor:lib'), | |
| ('//src/tools/protoprofile:common', | |
| '//src/trace_processor/importers/proto:gen_cc_android_extension_descriptor' | |
| ), | |
| ('//src/tools/protoprofile:common', | |
| '//src/trace_processor/importers/proto:gen_cc_trace_descriptor'), | |
| ('//src/traceconv:lib', '//src/trace_processor:export_json'), | |
| ('//src/traceconv:lib', '//src/trace_processor:lib'), | |
| ('//src/traceconv:lib', '//src/trace_processor:storage_minimal'), | |
| ('//src/traceconv:pprofbuilder', | |
| '//src/trace_processor/containers:containers'), | |
| ('//test:perfetto_end_to_end_integrationtests', | |
| '//src/trace_processor:trace_processor_shell'), | |
| ('//ui:ui', '//src/trace_processor:trace_processor.wasm'), | |
| ('//ui:ui', '//src/trace_processor:trace_processor_memory64.wasm'), | |
| } | |
| def is_in(label, prefix): | |
| return label == prefix or label.startswith((prefix + '/', prefix + ':')) | |
| def check(desc): | |
| errors = [] | |
| for label, target in desc.items(): | |
| source = gn_utils.label_without_toolchain(label) | |
| if is_in(source, TP_PREFIX) or source in TP_TEST_AGGREGATORS: | |
| continue | |
| for dependency in target.get('deps', []): | |
| dependency = gn_utils.label_without_toolchain(dependency) | |
| edge = (source, dependency) | |
| if (is_in(dependency, TP_PREFIX) and | |
| not is_in(dependency, ALLOWED_TP_PREFIX) and | |
| edge not in LEGACY_ALLOWED_EDGES): | |
| errors.append( | |
| f'target "{source}" depends on Trace Processor internal target ' | |
| f'"{dependency}"; only {ALLOWED_TP_PREFIX} targets are reusable.') | |
| return sorted(set(errors)) | |
| def main(): | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument( | |
| '--out', | |
| help='use an existing GN out directory instead of a temporary one') | |
| args = parser.parse_args() | |
| if args.out: | |
| errors = check(gn_utils.load_build_description(args.out)) | |
| else: | |
| with gn_utils.BuildDescription('') as build: | |
| errors = check(build.desc) | |
| for error in errors: | |
| print(error, file=sys.stderr) | |
| return 1 if errors else 0 | |
| if __name__ == '__main__': | |
| sys.exit(main()) |