| # Copyright 2023 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| import subprocess |
| import multiprocessing |
| import functools |
| |
| def _convert_gn_sources_list_to_dict(gn_sources_list, build_dir): |
| """ Given a list of gn sources, transform them into standard filepaths and |
| place them in a dictionary. """ |
| outpath_pattern = "//%s/" % build_dir |
| gn_sources_dict = {} |
| for line in gn_sources_list: |
| fixedline = line.replace(outpath_pattern, "").replace("//", |
| "../../").strip() |
| gn_sources_dict[fixedline] = True |
| return gn_sources_dict |
| |
| |
| def _get_sources_for_gn_target(all_transitive_sources, gn_path, build_dir, |
| target_name): |
| """ Given a particular target, stores all the source files for that target in |
| the given multiprocessing.Manager().dict(). |
| |
| Example input: |
| Args: |
| |
| target_name: The name of a GN target e.g. |
| '//base/allocator/partition_allocator:partition_alloc' |
| all_transitive_sources: A multiprocess.Manager().dict(). |
| |
| Returns: |
| Nothing, but at the end of this function's execution, args[1] will look |
| like: |
| { |
| '//base/allocator/.../atomic_ref_count.h': True, |
| '//base/allocator/.../bit_cast.h': True, |
| ... |
| }""" |
| if target_name is not None: |
| get_sources_command = [gn_path, "desc", build_dir, target_name, "sources"] |
| sources_output = subprocess.run(get_sources_command, |
| check=False, |
| capture_output=True) |
| if sources_output.returncode != 0: |
| # Some `gn desc` are expected to fail |
| # (because there's no `sources` for them). |
| pass |
| else: |
| for file in sources_output.stdout.decode(encoding='utf-8').split("\n"): |
| all_transitive_sources[file] = True |
| |
| |
| def _fetch_all_transitive_sources_for_gn_target(gn_target, build_dir, gn_path): |
| """Fetches a list of all transitive source dependencies for a GN target. |
| |
| For a given GN target and build directory, returns a list with all the |
| *transitive sources* upon which that target depends. |
| |
| This list can be useful for constructing a CodeQL database, since that list |
| will be the 'minimal set' of commands required to generate a database. |
| |
| Args: |
| gn_target: The name of a GN target e.g. |
| `//components:components_unittests`. |
| build_dir: The relative path to a Chromium build directory e.g. |
| `out/release`. |
| Returns: |
| A list of sources, for example: |
| ['//base/allocator.../atomic_ref_count.h', |
| '//base/allocator/.../partition_alloc_base/bit_cast.h', |
| ... |
| '//ui/platform_window/extensions/workspace_extension.cc', |
| ...] |
| """ |
| get_deps_command = [gn_path, "desc", build_dir, gn_target, "deps", "--all"] |
| deps_output = subprocess.run(get_deps_command, |
| check=True, |
| capture_output=True) |
| target_names = deps_output.stdout.decode(encoding='utf-8').split("\n") |
| my_cpu_count = int(multiprocessing.cpu_count()) |
| all_transitive_sources = multiprocessing.Manager().dict() |
| with multiprocessing.Pool(my_cpu_count) as p: |
| p.map( |
| functools.partial(_get_sources_for_gn_target, all_transitive_sources, |
| gn_path, build_dir), target_names) |
| return all_transitive_sources.keys() |
| |
| |
| def dictionary_of_all_transitive_sources(gn_target, build_dir, gn_path): |
| """Constructs a dictionary of all transitive GN source deps for a target. |
| |
| For a given GN target (e.g. `//components:components_unittests`) and the |
| path to some build directory (e.g. `out/release`), outputs a list of all |
| *transitive sources* for that GN target, in the form of a dictionary where |
| each entry has the value True. |
| |
| Args: |
| gn_target: The name of a GN target e.g. |
| `//components:components_unittests`. |
| build_dir: The relative path to a Chromium build directory e.g. |
| `out/release`. |
| Returns: |
| A dictionary that maps sources to True, for example: |
| |
| {'//base/allocator.../atomic_ref_count.h': True, |
| '//base/allocator/.../partition_alloc_base/bit_cast.h': True, |
| ... |
| '//ui/platform_window/extensions/workspace_extension.cc': True, |
| ...} |
| """ |
| gn_sources_list = _fetch_all_transitive_sources_for_gn_target( |
| gn_target, build_dir, gn_path) |
| return _convert_gn_sources_list_to_dict(gn_sources_list, build_dir) |