Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
analyzer.py806 linesDownload Raw Back to generator
1# Copyright (c) 2014 Google Inc. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6This script is intended for use as a GYP_GENERATOR. It takes as input (by way of
7the generator flag config_path) the path of a json file that dictates the files
8and targets to search for. The following keys are supported:
9files: list of paths (relative) of the files to search for.
10test_targets: unqualified target names to search for. Any target in this list
11that depends upon a file in |files| is output regardless of the type of target
12or chain of dependencies.
13additional_compile_targets: Unqualified targets to search for in addition to
14test_targets. Targets in the combined list that depend upon a file in |files|
15are not necessarily output. For example, if the target is of type none then the
16target is not output (but one of the descendants of the target will be).
17
18The following is output:
19error: only supplied if there is an error.
20compile_targets: minimal set of targets that directly or indirectly (for
21  targets of type none) depend on the files in |files| and is one of the
22  supplied targets or a target that one of the supplied targets depends on.
23  The expectation is this set of targets is passed into a build step. This list
24  always contains the output of test_targets as well.
25test_targets: set of targets from the supplied |test_targets| that either
26  directly or indirectly depend upon a file in |files|. This list if useful
27  if additional processing needs to be done for certain targets after the
28  build, such as running tests.
29status: outputs one of three values: none of the supplied files were found,
30  one of the include files changed so that it should be assumed everything
31  changed (in this case test_targets and compile_targets are not output) or at
32  least one file was found.
33invalid_targets: list of supplied targets that were not found.
34
35Example:
36Consider a graph like the following:
37  A     D
38 / \
39B   C
40A depends upon both B and C, A is of type none and B and C are executables.
41D is an executable, has no dependencies and nothing depends on it.
42If |additional_compile_targets| = ["A"], |test_targets| = ["B", "C"] and
43files = ["b.cc", "d.cc"] (B depends upon b.cc and D depends upon d.cc), then
44the following is output:
45|compile_targets| = ["B"] B must built as it depends upon the changed file b.cc
46and the supplied target A depends upon it. A is not output as a build_target
47as it is of type none with no rules and actions.
48|test_targets| = ["B"] B directly depends upon the change file b.cc.
49
50Even though the file d.cc, which D depends upon, has changed D is not output
51as it was not supplied by way of |additional_compile_targets| or |test_targets|.
52
53If the generator flag analyzer_output_path is specified, output is written
54there. Otherwise output is written to stdout.
55
56In Gyp the "all" target is shorthand for the root targets in the files passed
57to gyp. For example, if file "a.gyp" contains targets "a1" and
58"a2", and file "b.gyp" contains targets "b1" and "b2" and "a2" has a dependency
59on "b2" and gyp is supplied "a.gyp" then "all" consists of "a1" and "a2".
60Notice that "b1" and "b2" are not in the "all" target as "b.gyp" was not
61directly supplied to gyp. OTOH if both "a.gyp" and "b.gyp" are supplied to gyp
62then the "all" target includes "b1" and "b2".
63"""
64
65import json
66import os
67import posixpath
68
69import gyp.common
70
71debug = False
72
73found_dependency_string = "Found dependency"
74no_dependency_string = "No dependencies"
75# Status when it should be assumed that everything has changed.
76all_changed_string = "Found dependency (all)"
77
78# MatchStatus is used indicate if and how a target depends upon the supplied
79# sources.
80# The target's sources contain one of the supplied paths.
81MATCH_STATUS_MATCHES = 1
82# The target has a dependency on another target that contains one of the
83# supplied paths.
84MATCH_STATUS_MATCHES_BY_DEPENDENCY = 2
85# The target's sources weren't in the supplied paths and none of the target's
86# dependencies depend upon a target that matched.
87MATCH_STATUS_DOESNT_MATCH = 3
88# The target doesn't contain the source, but the dependent targets have not yet
89# been visited to determine a more specific status yet.
90MATCH_STATUS_TBD = 4
91
92generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
93
94generator_wants_static_library_dependencies_adjusted = False
95
96generator_default_variables = {}
97for dirname in [
98    "INTERMEDIATE_DIR",
99    "SHARED_INTERMEDIATE_DIR",
100    "PRODUCT_DIR",
101    "LIB_DIR",
102    "SHARED_LIB_DIR",
103]:
104    generator_default_variables[dirname] = "!!!"
105
106for unused in [
107    "RULE_INPUT_PATH",
108    "RULE_INPUT_ROOT",
109    "RULE_INPUT_NAME",
110    "RULE_INPUT_DIRNAME",
111    "RULE_INPUT_EXT",
112    "EXECUTABLE_PREFIX",
113    "EXECUTABLE_SUFFIX",
114    "STATIC_LIB_PREFIX",
115    "STATIC_LIB_SUFFIX",
116    "SHARED_LIB_PREFIX",
117    "SHARED_LIB_SUFFIX",
118    "CONFIGURATION_NAME",
119]:
120    generator_default_variables[unused] = ""
121
122
123def _ToGypPath(path):
124    """Converts a path to the format used by gyp."""
125    if os.sep == "\\" and os.altsep == "/":
126        return path.replace("\\", "/")
127    return path
128
129
130def _ResolveParent(path, base_path_components):
131    """Resolves |path|, which starts with at least one '../'. Returns an empty
132    string if the path shouldn't be considered. See _AddSources() for a
133    description of |base_path_components|."""
134    depth = 0
135    while path.startswith("../"):
136        depth += 1
137        path = path[3:]
138    # Relative includes may go outside the source tree. For example, an action may
139    # have inputs in /usr/include, which are not in the source tree.
140    if depth > len(base_path_components):
141        return ""
142    if depth == len(base_path_components):
143        return path
144    return (
145        "/".join(base_path_components[0 : len(base_path_components) - depth])
146        + "/"
147        + path
148    )
149
150
151def _AddSources(sources, base_path, base_path_components, result):
152    """Extracts valid sources from |sources| and adds them to |result|. Each
153    source file is relative to |base_path|, but may contain '..'. To make
154    resolving '..' easier |base_path_components| contains each of the
155    directories in |base_path|. Additionally each source may contain variables.
156    Such sources are ignored as it is assumed dependencies on them are expressed
157    and tracked in some other means."""
158    # NOTE: gyp paths are always posix style.
159    for source in sources:
160        if not len(source) or source.startswith(("!!!", "$")):
161            continue
162        # variable expansion may lead to //.
163        org_source = source
164        source = source[0] + source[1:].replace("//", "/")
165        if source.startswith("../"):
166            source = _ResolveParent(source, base_path_components)
167            if len(source):
168                result.append(source)
169            continue
170        result.append(base_path + source)
171        if debug:
172            print("AddSource", org_source, result[len(result) - 1])
173
174
175def _ExtractSourcesFromAction(action, base_path, base_path_components, results):
176    if "inputs" in action:
177        _AddSources(action["inputs"], base_path, base_path_components, results)
178
179
180def _ToLocalPath(toplevel_dir, path):
181    """Converts |path| to a path relative to |toplevel_dir|."""
182    if path == toplevel_dir:
183        return ""
184    if path.startswith(toplevel_dir + "/"):
185        return path[len(toplevel_dir) + len("/") :]
186    return path
187
188
189def _ExtractSources(target, target_dict, toplevel_dir):
190    # |target| is either absolute or relative and in the format of the OS. Gyp
191    # source paths are always posix. Convert |target| to a posix path relative to
192    # |toplevel_dir_|. This is done to make it easy to build source paths.
193    base_path = posixpath.dirname(_ToLocalPath(toplevel_dir, _ToGypPath(target)))
194    base_path_components = base_path.split("/")
195
196    # Add a trailing '/' so that _AddSources() can easily build paths.
197    if len(base_path):
198        base_path += "/"
199
200    if debug:
201        print("ExtractSources", target, base_path)
202
203    results = []
204    if "sources" in target_dict:
205        _AddSources(target_dict["sources"], base_path, base_path_components, results)
206    # Include the inputs from any actions. Any changes to these affect the
207    # resulting output.
208    if "actions" in target_dict:
209        for action in target_dict["actions"]:
210            _ExtractSourcesFromAction(action, base_path, base_path_components, results)
211    if "rules" in target_dict:
212        for rule in target_dict["rules"]:
213            _ExtractSourcesFromAction(rule, base_path, base_path_components, results)
214
215    return results
216
217
218class Target:
219    """Holds information about a particular target:
220    deps: set of Targets this Target depends upon. This is not recursive, only the
221      direct dependent Targets.
222    match_status: one of the MatchStatus values.
223    back_deps: set of Targets that have a dependency on this Target.
224    visited: used during iteration to indicate whether we've visited this target.
225      This is used for two iterations, once in building the set of Targets and
226      again in _GetBuildTargets().
227    name: fully qualified name of the target.
228    requires_build: True if the target type is such that it needs to be built.
229      See _DoesTargetTypeRequireBuild for details.
230    added_to_compile_targets: used when determining if the target was added to the
231      set of targets that needs to be built.
232    in_roots: true if this target is a descendant of one of the root nodes.
233    is_executable: true if the type of target is executable.
234    is_static_library: true if the type of target is static_library.
235    is_or_has_linked_ancestor: true if the target does a link (eg executable), or
236      if there is a target in back_deps that does a link."""
237
238    def __init__(self, name):
239        self.deps = set()
240        self.match_status = MATCH_STATUS_TBD
241        self.back_deps = set()
242        self.name = name
243        # TODO(sky): I don't like hanging this off Target. This state is specific
244        # to certain functions and should be isolated there.
245        self.visited = False
246        self.requires_build = False
247        self.added_to_compile_targets = False
248        self.in_roots = False
249        self.is_executable = False
250        self.is_static_library = False
251        self.is_or_has_linked_ancestor = False
252
253
254class Config:
255    """Details what we're looking for
256    files: set of files to search for
257    targets: see file description for details."""
258
259    def __init__(self):
260        self.files = []
261        self.targets = set()
262        self.additional_compile_target_names = set()
263        self.test_target_names = set()
264
265    def Init(self, params):
266        """Initializes Config. This is a separate method as it raises an exception
267        if there is a parse error."""
268        generator_flags = params.get("generator_flags", {})
269        config_path = generator_flags.get("config_path", None)
270        if not config_path:
271            return
272        try:
273            f = open(config_path)
274            config = json.load(f)
275            f.close()
276        except OSError:
277            raise Exception("Unable to open file " + config_path)
278        except ValueError as e:
279            raise Exception("Unable to parse config file " + config_path + str(e))
280        if not isinstance(config, dict):
281            raise Exception("config_path must be a JSON file containing a dictionary")
282        self.files = config.get("files", [])
283        self.additional_compile_target_names = set(
284            config.get("additional_compile_targets", [])
285        )
286        self.test_target_names = set(config.get("test_targets", []))
287
288
289def _WasBuildFileModified(build_file, data, files, toplevel_dir):
290    """Returns true if the build file |build_file| is either in |files| or
291    one of the files included by |build_file| is in |files|. |toplevel_dir| is
292    the root of the source tree."""
293    if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files:
294        if debug:
295            print("gyp file modified", build_file)
296        return True
297
298    # First element of included_files is the file itself.
299    if len(data[build_file]["included_files"]) <= 1:
300        return False
301
302    for include_file in data[build_file]["included_files"][1:]:
303        # |included_files| are relative to the directory of the |build_file|.
304        rel_include_file = _ToGypPath(
305            gyp.common.UnrelativePath(include_file, build_file)
306        )
307        if _ToLocalPath(toplevel_dir, rel_include_file) in files:
308            if debug:
309                print(
310                    "included gyp file modified, gyp_file=",
311                    build_file,
312                    "included file=",
313                    rel_include_file,
314                )
315            return True
316    return False
317
318
319def _GetOrCreateTargetByName(targets, target_name):
320    """Creates or returns the Target at targets[target_name]. If there is no
321    Target for |target_name| one is created. Returns a tuple of whether a new
322    Target was created and the Target."""
323    if target_name in targets:
324        return False, targets[target_name]
325    target = Target(target_name)
326    targets[target_name] = target
327    return True, target
328
329
330def _DoesTargetTypeRequireBuild(target_dict):
331    """Returns true if the target type is such that it needs to be built."""
332    # If a 'none' target has rules or actions we assume it requires a build.
333    return bool(
334        target_dict["type"] != "none"
335        or target_dict.get("actions")
336        or target_dict.get("rules")
337    )
338
339
340def _GenerateTargets(data, target_list, target_dicts, toplevel_dir, files, build_files):
341    """Returns a tuple of the following:
342    . A dictionary mapping from fully qualified name to Target.
343    . A list of the targets that have a source file in |files|.
344    . Targets that constitute the 'all' target. See description at top of file
345      for details on the 'all' target.
346    This sets the |match_status| of the targets that contain any of the source
347    files in |files| to MATCH_STATUS_MATCHES.
348    |toplevel_dir| is the root of the source tree."""
349    # Maps from target name to Target.
350    name_to_target = {}
351
352    # Targets that matched.
353    matching_targets = []
354
355    # Queue of targets to visit.
356    targets_to_visit = target_list[:]
357
358    # Maps from build file to a boolean indicating whether the build file is in
359    # |files|.
360    build_file_in_files = {}
361
362    # Root targets across all files.
363    roots = set()
364
365    # Set of Targets in |build_files|.
366    build_file_targets = set()
367
368    while len(targets_to_visit) > 0:
369        target_name = targets_to_visit.pop()
370        created_target, target = _GetOrCreateTargetByName(name_to_target, target_name)
371        if created_target:
372            roots.add(target)
373        elif target.visited:
374            continue
375
376        target.visited = True
377        target.requires_build = _DoesTargetTypeRequireBuild(target_dicts[target_name])
378        target_type = target_dicts[target_name]["type"]
379        target.is_executable = target_type == "executable"
380        target.is_static_library = target_type == "static_library"
381        target.is_or_has_linked_ancestor = target_type in {
382            "executable",
383            "shared_library",
384        }
385
386        build_file = gyp.common.ParseQualifiedTarget(target_name)[0]
387        if build_file not in build_file_in_files:
388            build_file_in_files[build_file] = _WasBuildFileModified(
389                build_file, data, files, toplevel_dir
390            )
391
392        if build_file in build_files:
393            build_file_targets.add(target)
394
395        # If a build file (or any of its included files) is modified we assume all
396        # targets in the file are modified.
397        if build_file_in_files[build_file]:
398            print("matching target from modified build file", target_name)
399            target.match_status = MATCH_STATUS_MATCHES
400            matching_targets.append(target)
401        else:
402            sources = _ExtractSources(
403                target_name, target_dicts[target_name], toplevel_dir
404            )
405            for source in sources:
406                if _ToGypPath(os.path.normpath(source)) in files:
407                    print("target", target_name, "matches", source)
408                    target.match_status = MATCH_STATUS_MATCHES
409                    matching_targets.append(target)
410                    break
411
412        # Add dependencies to visit as well as updating back pointers for deps.
413        for dep in target_dicts[target_name].get("dependencies", []):
414            targets_to_visit.append(dep)
415
416            created_dep_target, dep_target = _GetOrCreateTargetByName(
417                name_to_target, dep
418            )
419            if not created_dep_target:
420                roots.discard(dep_target)
421
422            target.deps.add(dep_target)
423            dep_target.back_deps.add(target)
424
425    return name_to_target, matching_targets, roots & build_file_targets
426
427
428def _GetUnqualifiedToTargetMapping(all_targets, to_find):
429    """Returns a tuple of the following:
430    . mapping (dictionary) from unqualified name to Target for all the
431      Targets in |to_find|.
432    . any target names not found. If this is empty all targets were found."""
433    result = {}
434    if not to_find:
435        return {}, []
436    to_find = set(to_find)
437    for target_name in all_targets:
438        extracted = gyp.common.ParseQualifiedTarget(target_name)
439        if len(extracted) > 1 and extracted[1] in to_find:
440            to_find.remove(extracted[1])
441            result[extracted[1]] = all_targets[target_name]
442            if not to_find:
443                return result, []
444    return result, list(to_find)
445
446
447def _DoesTargetDependOnMatchingTargets(target):
448    """Returns true if |target| or any of its dependencies is one of the
449    targets containing the files supplied as input to analyzer. This updates
450    |matches| of the Targets as it recurses.
451    target: the Target to look for."""
452    if target.match_status == MATCH_STATUS_DOESNT_MATCH:
453        return False
454    if target.match_status in {
455        MATCH_STATUS_MATCHES,
456        MATCH_STATUS_MATCHES_BY_DEPENDENCY,
457    }:
458        return True
459    for dep in target.deps:
460        if _DoesTargetDependOnMatchingTargets(dep):
461            target.match_status = MATCH_STATUS_MATCHES_BY_DEPENDENCY
462            print("\t", target.name, "matches by dep", dep.name)
463            return True
464    target.match_status = MATCH_STATUS_DOESNT_MATCH
465    return False
466
467
468def _GetTargetsDependingOnMatchingTargets(possible_targets):
469    """Returns the list of Targets in |possible_targets| that depend (either
470    directly on indirectly) on at least one of the targets containing the files
471    supplied as input to analyzer.
472    possible_targets: targets to search from."""
473    found = []
474    print("Targets that matched by dependency:")
475    for target in possible_targets:
476        if _DoesTargetDependOnMatchingTargets(target):
477            found.append(target)
478    return found
479
480
481def _AddCompileTargets(target, roots, add_if_no_ancestor, result):
482    """Recurses through all targets that depend on |target|, adding all targets
483    that need to be built (and are in |roots|) to |result|.
484    roots: set of root targets.
485    add_if_no_ancestor: If true and there are no ancestors of |target| then add
486    |target| to |result|. |target| must still be in |roots|.
487    result: targets that need to be built are added here."""
488    if target.visited:
489        return
490
491    target.visited = True
492    target.in_roots = target in roots
493
494    for back_dep_target in target.back_deps:
495        _AddCompileTargets(back_dep_target, roots, False, result)
496        target.added_to_compile_targets |= back_dep_target.added_to_compile_targets
497        target.in_roots |= back_dep_target.in_roots
498        target.is_or_has_linked_ancestor |= back_dep_target.is_or_has_linked_ancestor
499
500    # Always add 'executable' targets. Even though they may be built by other
501    # targets that depend upon them it makes detection of what is going to be
502    # built easier.
503    # And always add static_libraries that have no dependencies on them from
504    # linkables. This is necessary as the other dependencies on them may be
505    # static libraries themselves, which are not compile time dependencies.
506    if target.in_roots and (
507        target.is_executable
508        or (
509            not target.added_to_compile_targets
510            and (add_if_no_ancestor or target.requires_build)
511        )
512        or (
513            target.is_static_library
514            and add_if_no_ancestor
515            and not target.is_or_has_linked_ancestor
516        )
517    ):
518        print(
519            "\t\tadding to compile targets",
520            target.name,
521            "executable",
522            target.is_executable,
523            "added_to_compile_targets",
524            target.added_to_compile_targets,
525            "add_if_no_ancestor",
526            add_if_no_ancestor,
527            "requires_build",
528            target.requires_build,
529            "is_static_library",
530            target.is_static_library,
531            "is_or_has_linked_ancestor",
532            target.is_or_has_linked_ancestor,
533        )
534        result.add(target)
535        target.added_to_compile_targets = True
536
537
538def _GetCompileTargets(matching_targets, supplied_targets):
539    """Returns the set of Targets that require a build.
540    matching_targets: targets that changed and need to be built.
541    supplied_targets: set of targets supplied to analyzer to search from."""
542    result = set()
543    for target in matching_targets:
544        print("finding compile targets for match", target.name)
545        _AddCompileTargets(target, supplied_targets, True, result)
546    return result
547
548
549def _WriteOutput(params, **values):
550    """Writes the output, either to stdout or a file is specified."""
551    if "error" in values:
552        print("Error:", values["error"])
553    if "status" in values:
554        print(values["status"])
555    if "targets" in values:
556        values["targets"].sort()
557        print("Supplied targets that depend on changed files:")
558        for target in values["targets"]:
559            print("\t", target)
560    if "invalid_targets" in values:
561        values["invalid_targets"].sort()
562        print("The following targets were not found:")
563        for target in values["invalid_targets"]:
564            print("\t", target)
565    if "build_targets" in values:
566        values["build_targets"].sort()
567        print("Targets that require a build:")
568        for target in values["build_targets"]:
569            print("\t", target)
570    if "compile_targets" in values:
571        values["compile_targets"].sort()
572        print("Targets that need to be built:")
573        for target in values["compile_targets"]:
574            print("\t", target)
575    if "test_targets" in values:
576        values["test_targets"].sort()
577        print("Test targets:")
578        for target in values["test_targets"]:
579            print("\t", target)
580
581    output_path = params.get("generator_flags", {}).get("analyzer_output_path", None)
582    if not output_path:
583        print(json.dumps(values))
584        return
585    try:
586        f = open(output_path, "w")
587        f.write(json.dumps(values) + "\n")
588        f.close()
589    except OSError as e:
590        print("Error writing to output file", output_path, str(e))
591
592
593def _WasGypIncludeFileModified(params, files):
594    """Returns true if one of the files in |files| is in the set of included
595    files."""
596    if params["options"].includes:
597        for include in params["options"].includes:
598            if _ToGypPath(os.path.normpath(include)) in files:
599                print("Include file modified, assuming all changed", include)
600                return True
601    return False
602
603
604def _NamesNotIn(names, mapping):
605    """Returns a list of the values in |names| that are not in |mapping|."""
606    return [name for name in names if name not in mapping]
607
608
609def _LookupTargets(names, mapping):
610    """Returns a list of the mapping[name] for each value in |names| that is in
611    |mapping|."""
612    return [mapping[name] for name in names if name in mapping]
613
614
615def CalculateVariables(default_variables, params):
616    """Calculate additional variables for use in the build (called by gyp)."""
617    flavor = gyp.common.GetFlavor(params)
618    if flavor == "mac":
619        default_variables.setdefault("OS", "mac")
620    elif flavor == "win":
621        default_variables.setdefault("OS", "win")
622        gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
623    else:
624        operating_system = flavor
625        if flavor == "android":
626            operating_system = "linux"  # Keep this legacy behavior for now.
627        default_variables.setdefault("OS", operating_system)
628
629
630class TargetCalculator:
631    """Calculates the matching test_targets and matching compile_targets."""
632
633    def __init__(
634        self,
635        files,
636        additional_compile_target_names,
637        test_target_names,
638        data,
639        target_list,
640        target_dicts,
641        toplevel_dir,
642        build_files,
643    ):
644        self._additional_compile_target_names = set(additional_compile_target_names)
645        self._test_target_names = set(test_target_names)
646        (
647            self._name_to_target,
648            self._changed_targets,
649            self._root_targets,
650        ) = _GenerateTargets(
651            data, target_list, target_dicts, toplevel_dir, frozenset(files), build_files
652        )
653        (
654            self._unqualified_mapping,
655            self.invalid_targets,
656        ) = _GetUnqualifiedToTargetMapping(
657            self._name_to_target, self._supplied_target_names_no_all()
658        )
659
660    def _supplied_target_names(self):
661        return self._additional_compile_target_names | self._test_target_names
662
663    def _supplied_target_names_no_all(self):
664        """Returns the supplied test targets without 'all'."""
665        result = self._supplied_target_names()
666        result.discard("all")
667        return result
668
669    def is_build_impacted(self):
670        """Returns true if the supplied files impact the build at all."""
671        return self._changed_targets
672
673    def find_matching_test_target_names(self):
674        """Returns the set of output test targets."""
675        assert self.is_build_impacted()
676        # Find the test targets first. 'all' is special cased to mean all the
677        # root targets. To deal with all the supplied |test_targets| are expanded
678        # to include the root targets during lookup. If any of the root targets
679        # match, we remove it and replace it with 'all'.
680        test_target_names_no_all = set(self._test_target_names)
681        test_target_names_no_all.discard("all")
682        test_targets_no_all = _LookupTargets(
683            test_target_names_no_all, self._unqualified_mapping
684        )
685        test_target_names_contains_all = "all" in self._test_target_names
686        if test_target_names_contains_all:
687            test_targets = list(set(test_targets_no_all) | set(self._root_targets))
688        else:
689            test_targets = list(test_targets_no_all)
690        print("supplied test_targets")
691        for target_name in self._test_target_names:
692            print("\t", target_name)
693        print("found test_targets")
694        for target in test_targets:
695            print("\t", target.name)
696        print("searching for matching test targets")
697        matching_test_targets = _GetTargetsDependingOnMatchingTargets(test_targets)
698        matching_test_targets_contains_all = test_target_names_contains_all and set(
699            matching_test_targets
700        ) & set(self._root_targets)
701        if matching_test_targets_contains_all:
702            # Remove any of the targets for all that were not explicitly supplied,
703            # 'all' is subsequently added to the matching names below.
704            matching_test_targets = list(
705                set(matching_test_targets) & set(test_targets_no_all)
706            )
707        print("matched test_targets")
708        for target in matching_test_targets:
709            print("\t", target.name)
710        matching_target_names = [
711            gyp.common.ParseQualifiedTarget(target.name)[1]
712            for target in matching_test_targets
713        ]
714        if matching_test_targets_contains_all:
715            matching_target_names.append("all")
716            print("\tall")
717        return matching_target_names
718
719    def find_matching_compile_target_names(self):
720        """Returns the set of output compile targets."""
721        assert self.is_build_impacted()
722        # Compile targets are found by searching up from changed targets.
723        # Reset the visited status for _GetBuildTargets.
724        for target in self._name_to_target.values():
725            target.visited = False
726
727        supplied_targets = _LookupTargets(
728            self._supplied_target_names_no_all(), self._unqualified_mapping
729        )
730        if "all" in self._supplied_target_names():
731            supplied_targets = list(set(supplied_targets) | set(self._root_targets))
732        print("Supplied test_targets & compile_targets")
733        for target in supplied_targets:
734            print("\t", target.name)
735        print("Finding compile targets")
736        compile_targets = _GetCompileTargets(self._changed_targets, supplied_targets)
737        return [
738            gyp.common.ParseQualifiedTarget(target.name)[1]
739            for target in compile_targets
740        ]
741
742
743def GenerateOutput(target_list, target_dicts, data, params):
744    """Called by gyp as the final stage. Outputs results."""
745    config = Config()
746    try:
747        config.Init(params)
748
749        if not config.files:
750            raise Exception(
751                "Must specify files to analyze via config_path generator flag"
752            )
753
754        toplevel_dir = _ToGypPath(os.path.abspath(params["options"].toplevel_dir))
755        if debug:
756            print("toplevel_dir", toplevel_dir)
757
758        if _WasGypIncludeFileModified(params, config.files):
759            result_dict = {
760                "status": all_changed_string,
761                "test_targets": list(config.test_target_names),
762                "compile_targets": list(
763                    config.additional_compile_target_names | config.test_target_names
764                ),
765            }
766            _WriteOutput(params, **result_dict)
767            return
768
769        calculator = TargetCalculator(
770            config.files,
771            config.additional_compile_target_names,
772            config.test_target_names,
773            data,
774            target_list,
775            target_dicts,
776            toplevel_dir,
777            params["build_files"],
778        )
779        if not calculator.is_build_impacted():
780            result_dict = {
781                "status": no_dependency_string,
782                "test_targets": [],
783                "compile_targets": [],
784            }
785            if calculator.invalid_targets:
786                result_dict["invalid_targets"] = calculator.invalid_targets
787            _WriteOutput(params, **result_dict)
788            return
789
790        test_target_names = calculator.find_matching_test_target_names()
791        compile_target_names = calculator.find_matching_compile_target_names()
792        found_at_least_one_target = compile_target_names or test_target_names
793        result_dict = {
794            "test_targets": test_target_names,
795            "status": found_dependency_string
796            if found_at_least_one_target
797            else no_dependency_string,
798            "compile_targets": list(set(compile_target_names) | set(test_target_names)),
799        }
800        if calculator.invalid_targets:
801            result_dict["invalid_targets"] = calculator.invalid_targets
802        _WriteOutput(params, **result_dict)
803
804    except Exception as e:
805        _WriteOutput(params, error=str(e))
806 
codekingpro/portable-devtools · Team Ai