[M120 Migration][Gamepad]Add gamepad event latency Test code
[platform/framework/web/chromium-efl.git] / build / action_helpers.py
1 # Copyright 2023 The Chromium Authors
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """Helper functions useful when writing scripts used by action() targets."""
5
6 import contextlib
7 import filecmp
8 import os
9 import pathlib
10 import posixpath
11 import shutil
12 import tempfile
13
14 import gn_helpers
15
16 from typing import Optional
17 from typing import Sequence
18
19
20 @contextlib.contextmanager
21 def atomic_output(path, mode='w+b', only_if_changed=True):
22   """Prevent half-written files and dirty mtimes for unchanged files.
23
24   Args:
25     path: Path to the final output file, which will be written atomically.
26     mode: The mode to open the file in (str).
27     only_if_changed: Whether to maintain the mtime if the file has not changed.
28   Returns:
29     A Context Manager that yields a NamedTemporaryFile instance. On exit, the
30     manager will check if the file contents is different from the destination
31     and if so, move it into place.
32
33   Example:
34     with action_helpers.atomic_output(output_path) as tmp_file:
35       subprocess.check_call(['prog', '--output', tmp_file.name])
36   """
37   # Create in same directory to ensure same filesystem when moving.
38   dirname = os.path.dirname(path) or '.'
39   os.makedirs(dirname, exist_ok=True)
40   with tempfile.NamedTemporaryFile(mode,
41                                    suffix=os.path.basename(path),
42                                    dir=dirname,
43                                    delete=False) as f:
44     try:
45       yield f
46
47       # File should be closed before comparison/move.
48       f.close()
49       if not (only_if_changed and os.path.exists(path)
50               and filecmp.cmp(f.name, path)):
51         shutil.move(f.name, path)
52     finally:
53       f.close()
54       if os.path.exists(f.name):
55         os.unlink(f.name)
56
57
58 def add_depfile_arg(parser):
59   if hasattr(parser, 'add_option'):
60     func = parser.add_option
61   else:
62     func = parser.add_argument
63   func('--depfile', help='Path to depfile (refer to "gn help depfile")')
64
65
66 def write_depfile(depfile_path: str,
67                   first_gn_output: str,
68                   inputs: Optional[Sequence[str]] = None) -> None:
69   """Writes a ninja depfile.
70
71   See notes about how to use depfiles in //build/docs/writing_gn_templates.md.
72
73   Args:
74     depfile_path: Path to file to write.
75     first_gn_output: Path of first entry in action's outputs.
76     inputs: List of inputs to add to depfile.
77   """
78   assert depfile_path != first_gn_output  # http://crbug.com/646165
79   assert not isinstance(inputs, str)  # Easy mistake to make
80
81   def _process_path(path):
82     assert not os.path.isabs(path), f'Found abs path in depfile: {path}'
83     if os.path.sep != posixpath.sep:
84       path = str(pathlib.Path(path).as_posix())
85     assert '\\' not in path, f'Found \\ in depfile: {path}'
86     return path.replace(' ', '\\ ')
87
88   sb = []
89   sb.append(_process_path(first_gn_output))
90   if inputs:
91     # Sort and uniquify to ensure file is hermetic.
92     # One path per line to keep it human readable.
93     sb.append(': \\\n ')
94     sb.append(' \\\n '.join(sorted(_process_path(p) for p in set(inputs))))
95   else:
96     sb.append(': ')
97   sb.append('\n')
98
99   path = pathlib.Path(depfile_path)
100   path.parent.mkdir(parents=True, exist_ok=True)
101   path.write_text(''.join(sb))
102
103
104 def parse_gn_list(value):
105   """Converts a "GN-list" command-line parameter into a list.
106
107   Conversions handled:
108     * None -> []
109     * '' -> []
110     * 'asdf' -> ['asdf']
111     * '["a", "b"]' -> ['a', 'b']
112     * ['["a", "b"]', 'c'] -> ['a', 'b', 'c']  (action='append')
113
114   This allows passing args like:
115   gn_list = [ "one", "two", "three" ]
116   args = [ "--items=$gn_list" ]
117   """
118   # Convert None to [].
119   if not value:
120     return []
121   # Convert a list of GN lists to a flattened list.
122   if isinstance(value, list):
123     ret = []
124     for arg in value:
125       ret.extend(parse_gn_list(arg))
126     return ret
127   # Convert normal GN list.
128   if value.startswith('['):
129     return gn_helpers.GNValueParser(value).ParseList()
130   # Convert a single string value to a list.
131   return [value]