Flutter Engine
The Flutter Engine
Classes | Functions | Variables
recipes Namespace Reference

Classes

class  MalformedRecipesCfg
 

Functions

def parse (repo_root, recipes_cfg_path)
 
def parse_args (argv)
 
def checkout_engine (engine_path, repo_root, recipes_cfg_path)
 
def main ()
 

Variables

 EngineDep = namedtuple('EngineDep', 'url revision branch')
 
 IS_WIN = sys.platform.startswith(('win', 'cygwin'))
 
string GIT = 'git' + _BAT
 
string CIPD = 'cipd' + _BAT
 
dictionary REQUIRED_BINARIES = {GIT, CIPD}
 

Function Documentation

◆ checkout_engine()

def 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
def parse(repo_root, recipes_cfg_path)
Definition: recipes.py:56
def checkout_engine(engine_path, repo_root, recipes_cfg_path)
Definition: recipes.py:155

◆ main()

def 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 overwrite PYTHONPATH here on purpose; We don't want any conflicting
239 # environmental path leaking through into the recipe_engine which manages its
240 # environment entirely via vpython.
241 os.environ['PYTHONPATH'] = engine_path
242
243 spec = '.vpython3'
244 debugger = os.environ.get('RECIPE_DEBUGGER', '')
245 if debugger.startswith('pycharm'):
246 spec = '.pycharm.vpython3'
247 elif debugger.startswith('vscode'):
248 spec = '.vscode.vpython3'
249
250 argv = ([
251 vpython,
252 '-vpython-spec',
253 os.path.join(engine_path, spec),
254 '-u',
255 os.path.join(engine_path, 'recipe_engine', 'main.py'),
256 ] + args)
257
258 if IS_WIN:
259 # No real 'exec' on windows; set these signals to ignore so that they
260 # propagate to our children but we still wait for the child process to quit.
261 import signal # pylint: disable=import-outside-toplevel
262 signal.signal(signal.SIGBREAK, signal.SIG_IGN) # pylint: disable=no-member
263 signal.signal(signal.SIGINT, signal.SIG_IGN)
264 signal.signal(signal.SIGTERM, signal.SIG_IGN)
265 return _subprocess_call(argv)
266
267 os.execvp(argv[0], argv)
268 return -1 # should never occur
269
270
def main()
Definition: recipes.py:209
static DecodeResult decode(std::string path)
Definition: png_codec.cpp:124
static void parse_args(int argc, char *argv[], Args *args)
Definition: skqp_main.cpp:97

◆ parse()

def 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
EngineDep
Definition: recipes.py:46

◆ parse_args()

def 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

◆ CIPD

string 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

string 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

dictionary recipes.REQUIRED_BINARIES = {GIT, CIPD}

Definition at line 113 of file recipes.py.