Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(54)

Side by Side Diff: third_party/recipe_engine/recipes.py

Issue 1241323004: Cross-repo recipe package system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Moved show_me_the_modules into recipe_engine Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
iannucci 2015/08/06 23:57:13 maybe call this main.py and rename main.py to run.
luqui 2015/08/20 22:45:25 Done.
2
3 """Tool to interact with recipe repositories.
4
5 This tool operates on the nearest ancestor directory containing a
6 recipe_package.pyl file.
luqui 2015/08/07 19:30:36 Update docstring
luqui 2015/08/20 22:45:24 Done.
7 """
8
9 import argparse
10 import json
11 import logging
12 import os
13 import subprocess
14 import sys
15
16 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
iannucci 2015/08/06 23:57:13 !!!!!!!
luqui 2015/08/20 22:45:25 !!!!!!!
17
18
19 def get_package_config(args):
20 from recipe_engine import package
21
22 def try_path(path):
23 cand = os.path.realpath(
24 os.path.join(os.getcwd(), path, 'infra', 'config', 'recipes.cfg'))
25 if os.path.exists(cand):
26 return package.ProtoFile(cand)
27 else:
28 raise EnvironmentError(
29 'Recipes config file %s does not exist' % cand)
30
31 if args.package:
32 return try_path(args.package)
33
34 cwd = os.path.realpath(os.getcwd())
35 while os.path.dirname(cwd) != cwd:
36 try:
37 return try_path(cwd)
38 except EnvironmentError:
39 if not os.path.isdir(os.path.join(cwd, '.git')):
40 cwd = os.path.dirname(cwd)
41 else:
42 raise EnvironmentError(
43 'Will not cross git repository boundary %s when looking for '
44 'recipe package spec' % cwd)
45 else:
46 raise EnvironmentError(
47 'Reached root of filesystem when looking for recipe package spec')
48
49
50 def simulation_test(package, args):
51 from recipe_engine import simulation_test
52 simulation_test.main(package, args=json.loads(args.args))
53
54
55 def lint(package, args):
56 from recipe_engine import lint_test
57 lint_test.main(package)
58
59
60 def run(package, args):
61 from recipe_engine import main as recipe_main
62 from recipe_engine import loader
63 from recipe_engine import package
64 from recipe_engine.third_party import annotator
65
66 def get_properties_from_args(args):
67 properties = dict(x.split('=', 1) for x in args)
68 for key, val in properties.iteritems():
69 try:
70 properties[key] = ast.literal_eval(val)
71 except (ValueError, SyntaxError):
72 pass # If a value couldn't be evaluated, keep the string version
73 return properties
74
75 def get_properties_from_file(filename):
76 properties_file = sys.stdin if filename == '-' else open(filename)
77 properties = ast.literal_eval(properties_file.read())
78 assert isinstance(properties, dict)
79
80 arg_properties = get_properties_from_args(args.properties)
81 if args.properties_file and arg_properties:
82 raise Exception(
83 'Cannot specify both properties file and command-line properties')
84 elif args.properties_file:
85 properties = get_properties_from_file(args.properties_file)
86 else:
87 properties = arg_properties
88
89 properties['recipe'] = args.recipe
90
91 # TODO(luqui): Environment whitelist. Cf crbug.com/392992
92 os.environ['PYTHONUNBUFFERED'] = '1'
93 os.environ['PYTHONIOENCODING'] = 'UTF-8'
94
95 universe = loader.RecipeUniverse(package)
96
97 workdir = (args.workdir or
98 os.path.join(os.path.dirname(os.path.realpath(__file__)), 'workdir'))
99 logging.info('Using %s as work directory' % workdir)
100 if not os.path.exists(workdir):
101 os.makedirs(workdir)
102
103 old_cwd = os.getcwd()
104 os.chdir(workdir)
105 try:
106 ret = recipe_main.run_steps(
107 properties, annotator.StructuredAnnotationStream(), universe=universe)
108 return ret.status_code
109 finally:
110 os.chdir(old_cwd)
111
112
113 def roll(args):
iannucci 2015/08/06 23:57:13 maybe have option to do this in a loop until it ca
luqui 2015/08/20 22:45:25 On second thought, no. The reason for one at a ti
114 from recipe_engine import package
115 config_file = get_package_config(args)
116 context = package.PackageContext.from_proto_file(config_file)
117 package_spec = package.PackageSpec.load_proto(config_file)
118
119 for update in package_spec.iterate_consistent_updates(context):
120 config_file.write(update.spec.dump())
121 print 'Wrote %s' % config_file.path
122
123 updated_deps = {
124 dep_id: dep.revision
125 for dep_id, dep in update.spec.deps.iteritems()
126 if dep.revision != package_spec.deps[dep_id].revision
127 }
128 print 'To commit this roll, run:'
129 print ' '.join([
130 'git commit -a -m "Roll dependencies"',
131 ' '.join([ '-m "Roll %s to %s"' % (dep_id, rev)
132 for dep_id, rev in sorted(updated_deps.iteritems())]),
133 ])
134
135 break
136 else:
137 print 'No consistent rolls found'
138
139
140 def show_me_the_modules(package):
141 from recipe_engine import show_me_the_modules
142 show_me_the_modules.main(package)
143
144
145 def main():
146 from recipe_engine import package
147
148 # Super-annoyingly, we need to manually parse for simulation_test since
149 # argparse is bonkers and doesn't allow us to forward --help to subcommands.
150 if 'simulation_test' in sys.argv:
151 index = sys.argv.index('simulation_test')
152 sys.argv = sys.argv[:index+1] + [json.dumps(sys.argv[index+1:])]
153
154 parser = argparse.ArgumentParser(description='Do things with recipes.')
155
156 parser.add_argument(
157 '--package',
158 help='Package to operate on (directory containing '
159 'infra/config/recipes.cfg)')
160 parser.add_argument(
161 '--verbose', '-v', action='store_true',
162 help='Increase logging verboisty')
163 parser.add_argument(
164 '--bootstrap', action='store_true',
luqui 2015/08/07 19:30:36 --no-fetch Don't checkout recipe engine at HEAD.
luqui 2015/08/20 22:45:24 Done.
165 help='Use current rather than pinned version of recipe engine')
166
167 subp = parser.add_subparsers()
168
169 fetch_p = subp.add_parser(
170 'fetch',
171 help='Fetch and update dependencies.')
172 fetch_p.set_defaults(command='fetch')
173
174 simulation_test_p = subp.add_parser('simulation_test',
175 help='Generate or check expectations by simulating with mock actions')
176 simulation_test_p.set_defaults(command='simulation_test')
177 simulation_test_p.add_argument('args')
178
179 lint_p = subp.add_parser(
180 'lint',
181 help='Check recipes for stylistic and hygenic issues')
182 lint_p.set_defaults(command='lint')
183
184 run_p = subp.add_parser(
185 'run',
186 help='Run a recipe locally')
187 run_p.set_defaults(command='run')
188 run_p.add_argument(
189 '--properties-file',
190 help='A file containing a json blob of properties')
191 run_p.add_argument(
192 '--workdir',
193 help='The working directory of recipe execution')
194 run_p.add_argument(
195 'recipe',
196 help='The recipe to execute')
197 run_p.add_argument(
198 'properties', nargs=argparse.REMAINDER,
199 help='A list of property pairs; e.g. mastername=chromium.linux '
200 'issue=12345')
201
202 roll_p = subp.add_parser(
203 'roll',
204 help='Roll dependencies of a recipe package forward')
205 roll_p.set_defaults(command='roll')
206
207 show_me_the_modules_p = subp.add_parser(
208 'show_me_the_modules',
iannucci 2015/08/06 23:57:13 maybe 'doc'? doc <module name> doc <recipe name>
luqui 2015/08/20 22:45:25 Done
209 help='List all known modules reachable from the current package with '
210 'various info about each')
211 show_me_the_modules_p.set_defaults(command='show_me_the_modules')
212
213 args = parser.parse_args()
214 if args.verbose:
215 logging.getLogger().setLevel(logging.INFO)
216
217 if args.command == 'fetch' or args.command == 'roll':
218 package_deps = package.PackageDeps.create(
219 get_package_config(args), fetch=True)
220 else:
221 package_deps = package.PackageDeps.create(get_package_config(args))
iannucci 2015/08/06 23:57:13 let's have a `fetch = <condition>` line, and then
luqui 2015/08/20 22:45:24 Nope, because of error message stuff.
222
223 # We don't forward to pinned tool on rolling, because there might be new
224 # things to know about since then, who knows? Easier to look backward than
225 # forward.
226 if not args.bootstrap and args.command != 'roll':
227 tool = os.path.join(
228 package_deps.get_package('recipe_engine').recipes_dir, 'recipes.py')
229 cmd = [ sys.executable, '-u', tool, '--bootstrap' ] + sys.argv[1:]
230 logging.info(cmd)
231 return subprocess.call(cmd)
232
233 if args.command == 'fetch':
234 # We already did everything in the create() call above.
235 return 0
236 if args.command == 'simulation_test':
237 return simulation_test(package_deps, args)
238 elif args.command == 'lint':
239 return lint(package_deps, args)
240 elif args.command == 'run':
241 return run(package_deps, args)
242 elif args.command == 'roll':
243 return roll(args)
244 elif args.command == 'show_me_the_modules':
245 return show_me_the_modules(package_deps)
246 else:
247 print """Dear sir or madam,
248 It has come to my attention that a quite impossible condition has come
249 to pass in the specification you have issued a request for us to fulfill.
250 It is with a heavy heart that I inform you that, at the present juncture,
251 there is no conceivable next action to be taken upon your request, and as
252 such, we have decided to abort the request with a nonzero status code. We
253 hope that your larger goals have not been put at risk due to this
254 unfortunate circumstance, and wish you the best in deciding the next action
255 in your venture and larger life.
256
257 Warmly,
258 recipes.py
259 """
260 return 1
261
262 return 0
263
264 if __name__ == '__main__':
265 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698