Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Functions | Variables
recipes Namespace Reference

Classes

class  MalformedRecipesCfg
 

Functions

 parse (repo_root, recipes_cfg_path)
 
 _is_executable (path)
 
 _subprocess_call (argv, **kwargs)
 
 _git_check_call (argv, **kwargs)
 
 _git_output (argv, **kwargs)
 
 parse_args (argv)
 
 checkout_engine (engine_path, repo_root, recipes_cfg_path)
 
 main ()
 

Variables

 EngineDep = namedtuple('EngineDep', 'url revision branch')
 
 IS_WIN = sys.platform.startswith(('win', 'cygwin'))
 
str _BAT = '.bat' if IS_WIN else ''
 
str GIT = 'git' + _BAT
 
str CIPD = 'cipd' + _BAT
 
dict REQUIRED_BINARIES = {GIT, CIPD}
 

Detailed Description

'exec python3 -u -- "$0" ${1+"$@"} # 

Function Documentation

◆ _git_check_call()

recipes._git_check_call (   argv,
**  kwargs 
)
protected

Definition at line 125 of file recipes.py.

125def _git_check_call(argv, **kwargs):
126 argv = [GIT] + argv
127 logging.info('Running %r', argv)
128 subprocess.check_call(argv, **kwargs)
129
130

◆ _git_output()

recipes._git_output (   argv,
**  kwargs 
)
protected

Definition at line 131 of file recipes.py.

131def _git_output(argv, **kwargs):
132 argv = [GIT] + argv
133 logging.info('Running %r', argv)
134 return subprocess.check_output(argv, **kwargs)
135
136

◆ _is_executable()

recipes._is_executable (   path)
protected

Definition at line 116 of file recipes.py.

116def _is_executable(path):
117 return os.path.isfile(path) and os.access(path, os.X_OK)
118
119

◆ _subprocess_call()

recipes._subprocess_call (   argv,
**  kwargs 
)
protected

Definition at line 120 of file recipes.py.

120def _subprocess_call(argv, **kwargs):
121 logging.info('Running %r', argv)
122 return subprocess.call(argv, **kwargs)
123
124

◆ checkout_engine()

recipes.checkout_engine (   engine_path,
  repo_root,
  recipes_cfg_path 
)
Checks out the recipe_engine repo pinned in recipes.cfg.

Returns the path to the recipe engine repo.

Definition at line 155 of file recipes.py.

155def checkout_engine(engine_path, repo_root, recipes_cfg_path):
156 """Checks out the recipe_engine repo pinned in recipes.cfg.
157
158 Returns the path to the recipe engine repo.
159 """
160 dep, recipes_path = parse(repo_root, recipes_cfg_path)
161 if dep is None:
162 # we're running from the engine repo already!
163 return os.path.join(repo_root, recipes_path)
164
165 url = dep.url
166
167 if not engine_path and url.startswith('file://'):
168 engine_path = urlparse.urlparse(url).path
169
170 if not engine_path:
171 revision = dep.revision
172 branch = dep.branch
173
174 # Ensure that we have the recipe engine cloned.
175 engine_path = os.path.join(recipes_path, '.recipe_deps', 'recipe_engine')
176
177 # Note: this logic mirrors the logic in recipe_engine/fetch.py
178 _git_check_call(['init', engine_path], stdout=subprocess.DEVNULL)
179
180 try:
181 _git_check_call(['rev-parse', '--verify', f'{revision}^{{commit}}'],
182 cwd=engine_path,
183 stdout=subprocess.DEVNULL,
184 stderr=subprocess.DEVNULL)
185 except subprocess.CalledProcessError:
186 _git_check_call(['fetch', '--quiet', url, branch],
187 cwd=engine_path,
188 stdout=subprocess.DEVNULL)
189
190 try:
191 _git_check_call(['diff', '--quiet', revision], cwd=engine_path)
192 except subprocess.CalledProcessError:
193 index_lock = os.path.join(engine_path, '.git', 'index.lock')
194 try:
195 os.remove(index_lock)
196 except OSError as exc:
197 if exc.errno != errno.ENOENT:
198 logging.warning('failed to remove %r, reset will fail: %s',
199 index_lock, exc)
200 _git_check_call(['reset', '-q', '--hard', revision], cwd=engine_path)
201
202 # If the engine has refactored/moved modules we need to clean all .pyc files
203 # or things will get squirrely.
204 _git_check_call(['clean', '-qxf'], cwd=engine_path)
205
206 return engine_path
207
208

◆ main()

recipes.main ( )

Definition at line 209 of file recipes.py.

209def main():
210 for required_binary in REQUIRED_BINARIES:
211 if not shutil.which(required_binary):
212 return f'Required binary is not found on PATH: {required_binary}'
213
214 if '--verbose' in sys.argv:
215 logging.getLogger().setLevel(logging.INFO)
216
217 args = sys.argv[1:]
218 engine_override, recipes_cfg_path = parse_args(args)
219
220 if recipes_cfg_path:
221 # calculate repo_root from recipes_cfg_path
222 repo_root = os.path.dirname(
223 os.path.dirname(os.path.dirname(recipes_cfg_path)))
224 else:
225 # find repo_root with git and calculate recipes_cfg_path
226 repo_root = (
227 _git_output(['rev-parse', '--show-toplevel'],
228 cwd=os.path.abspath(os.path.dirname(__file__))).strip())
229 repo_root = os.path.abspath(repo_root).decode()
230 recipes_cfg_path = os.path.join(repo_root, 'infra', 'config', 'recipes.cfg')
231 args = ['--package', recipes_cfg_path] + args
232 engine_path = checkout_engine(engine_override, repo_root, recipes_cfg_path)
233
234 vpython = 'vpython3' + _BAT
235 if not shutil.which(vpython):
236 return f'Required binary is not found on PATH: {vpython}'
237
238 # We unset PYTHONPATH here in case the user has conflicting environmental
239 # things we don't want them to leak through into the recipe_engine which
240 # manages its environment entirely via vpython.
241 #
242 # NOTE: os.unsetenv unhelpfully doesn't exist on all platforms until python3.9
243 # so we have to use the cutesy `pop` formulation below until then...
244 os.environ.pop("PYTHONPATH", None)
245
246 spec = '.vpython3'
247 debugger = os.environ.get('RECIPE_DEBUGGER', '')
248 if debugger.startswith('pycharm'):
249 spec = '.pycharm.vpython3'
250 elif debugger.startswith('vscode'):
251 spec = '.vscode.vpython3'
252
253 argv = ([
254 vpython,
255 '-vpython-spec',
256 os.path.join(engine_path, spec),
257 '-u',
258 os.path.join(engine_path, 'recipe_engine', 'main.py'),
259 ] + args)
260
261 if IS_WIN:
262 # No real 'exec' on windows; set these signals to ignore so that they
263 # propagate to our children but we still wait for the child process to quit.
264 import signal # pylint: disable=import-outside-toplevel
265 signal.signal(signal.SIGBREAK, signal.SIG_IGN) # pylint: disable=no-member
266 signal.signal(signal.SIGINT, signal.SIG_IGN)
267 signal.signal(signal.SIGTERM, signal.SIG_IGN)
268 return _subprocess_call(argv)
269
270 os.execvp(argv[0], argv)
271 return -1 # should never occur
272
273
Definition main.py:1
static DecodeResult decode(std::string path)
static void parse_args(int argc, char *argv[], Args *args)
Definition skqp_main.cpp:97

◆ parse()

recipes.parse (   repo_root,
  recipes_cfg_path 
)
Parse is a lightweight a recipes.cfg file parser.

Args:
  repo_root (str) - native path to the root of the repo we're trying to run
    recipes for.
  recipes_cfg_path (str) - native path to the recipes.cfg file to process.

Returns (as tuple):
  engine_dep (EngineDep|None): The recipe_engine dependency, or None, if the
    current repo IS the recipe_engine.
  recipes_path (str) - native path to where the recipes live inside of the
    current repo (i.e. the folder containing `recipes/` and/or
    `recipe_modules`)

Definition at line 56 of file recipes.py.

56def parse(repo_root, recipes_cfg_path):
57 """Parse is a lightweight a recipes.cfg file parser.
58
59 Args:
60 repo_root (str) - native path to the root of the repo we're trying to run
61 recipes for.
62 recipes_cfg_path (str) - native path to the recipes.cfg file to process.
63
64 Returns (as tuple):
65 engine_dep (EngineDep|None): The recipe_engine dependency, or None, if the
66 current repo IS the recipe_engine.
67 recipes_path (str) - native path to where the recipes live inside of the
68 current repo (i.e. the folder containing `recipes/` and/or
69 `recipe_modules`)
70 """
71 with open(recipes_cfg_path, 'r', encoding='utf-8') as file:
72 recipes_cfg = json.load(file)
73
74 try:
75 if (version := recipes_cfg['api_version']) != 2:
76 raise MalformedRecipesCfg(f'unknown version {version}', recipes_cfg_path)
77
78 # If we're running ./recipes.py from the recipe_engine repo itself, then
79 # return None to signal that there's no EngineDep.
80 repo_name = recipes_cfg.get('repo_name')
81 if not repo_name:
82 repo_name = recipes_cfg['project_id']
83 if repo_name == 'recipe_engine':
84 return None, recipes_cfg.get('recipes_path', '')
85
86 engine = recipes_cfg['deps']['recipe_engine']
87
88 if 'url' not in engine:
89 raise MalformedRecipesCfg(
90 'Required field "url" in dependency "recipe_engine" not found',
91 recipes_cfg_path)
92
93 engine.setdefault('revision', '')
94 engine.setdefault('branch', 'refs/heads/main')
95 recipes_path = recipes_cfg.get('recipes_path', '')
96
97 # TODO(iannucci): only support absolute refs
98 if not engine['branch'].startswith('refs/'):
99 engine['branch'] = 'refs/heads/' + engine['branch']
100
101 recipes_path = os.path.join(repo_root,
102 recipes_path.replace('/', os.path.sep))
103 return EngineDep(**engine), recipes_path
104 except KeyError as ex:
105 raise MalformedRecipesCfg(str(ex), recipes_cfg_path) from ex
106
107

◆ parse_args()

recipes.parse_args (   argv)
This extracts a subset of the arguments that this bootstrap script cares
about. Currently this consists of:
  * an override for the recipe engine in the form of `-O recipe_engine=/path`
  * the --package option.

Definition at line 137 of file recipes.py.

137def parse_args(argv):
138 """This extracts a subset of the arguments that this bootstrap script cares
139 about. Currently this consists of:
140 * an override for the recipe engine in the form of `-O recipe_engine=/path`
141 * the --package option.
142 """
143 override_prefix = 'recipe_engine='
144
145 parser = argparse.ArgumentParser(add_help=False)
146 parser.add_argument('-O', '--project-override', action='append')
147 parser.add_argument('--package', type=os.path.abspath)
148 args, _ = parser.parse_known_args(argv)
149 for override in args.project_override or ():
150 if override.startswith(override_prefix):
151 return override[len(override_prefix):], args.package
152 return None, args.package
153
154

Variable Documentation

◆ _BAT

str recipes._BAT = '.bat' if IS_WIN else ''
protected

Definition at line 110 of file recipes.py.

◆ CIPD

str recipes.CIPD = 'cipd' + _BAT

Definition at line 112 of file recipes.py.

◆ EngineDep

recipes.EngineDep = namedtuple('EngineDep', 'url revision branch')

Definition at line 46 of file recipes.py.

◆ GIT

str recipes.GIT = 'git' + _BAT

Definition at line 111 of file recipes.py.

◆ IS_WIN

recipes.IS_WIN = sys.platform.startswith(('win', 'cygwin'))

Definition at line 108 of file recipes.py.

◆ REQUIRED_BINARIES

dict recipes.REQUIRED_BINARIES = {GIT, CIPD}

Definition at line 113 of file recipes.py.