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

Side by Side Diff: build/android/gradle/generate_gradle.py

Issue 2362613002: generate_gradle.py: Allow multiple --target and add --all (Closed)
Patch Set: Created 4 years, 3 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
« no previous file with comments | « build/android/gradle/build.gradle.jinja ('k') | build/android/gyp/jinja_template.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 The Chromium Authors. All rights reserved. 2 # Copyright 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Generates an Android Studio project from a GN target.""" 6 """Generates an Android Studio project from a GN target."""
7 7
8 import argparse 8 import argparse
9 import codecs 9 import codecs
10 import logging 10 import logging
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 def _WriteFile(path, data): 60 def _WriteFile(path, data):
61 """Writes |data| to |path|, constucting parent directories if necessary.""" 61 """Writes |data| to |path|, constucting parent directories if necessary."""
62 logging.info('Writing %s', path) 62 logging.info('Writing %s', path)
63 dirname = os.path.dirname(path) 63 dirname = os.path.dirname(path)
64 if not os.path.exists(dirname): 64 if not os.path.exists(dirname):
65 os.makedirs(dirname) 65 os.makedirs(dirname)
66 with codecs.open(path, 'w', 'utf-8') as output_file: 66 with codecs.open(path, 'w', 'utf-8') as output_file:
67 output_file.write(data) 67 output_file.write(data)
68 68
69 69
70 def _RunNinja(output_dir, ninja_targets): 70 def _RunNinja(output_dir, args):
71 cmd = ['ninja', '-C', output_dir, '-j50'] 71 cmd = ['ninja', '-C', output_dir, '-j50']
72 cmd.extend(ninja_targets) 72 cmd.extend(args)
73 if len(args) < 10:
jbudorick 2016/09/22 02:34:46 Why < 10? What about only logging part of cmd if i
agrieve 2016/09/22 15:51:25 meh, removed the check. It shows only when running
74 logging.info('Running: %r', cmd)
75 subprocess.check_call(cmd)
76
77
78 def _QueryForAllGnTargets(output_dir):
79 cmd = ['ninja', '-C', output_dir, '-t', 'targets']
73 logging.info('Running: %r', cmd) 80 logging.info('Running: %r', cmd)
74 subprocess.check_call(cmd) 81 ninja_output = build_utils.CheckOutput(cmd)
82 ret = []
83 SUFFIX_LEN = len('__build_config')
84 for line in ninja_output.splitlines():
85 ninja_target = line.rsplit(':', 1)[0]
86 # Ignore root aliases by ensure a : exists.
87 if ':' in ninja_target and ninja_target.endswith('__build_config'):
jbudorick 2016/09/22 02:34:46 Why is this checking for ':'? We'd never hit a __b
agrieve 2016/09/22 15:51:25 It's because we're querying for ninja rather than
jbudorick 2016/09/22 15:57:43 Ah, yeah, I was thinking about the GN targets.
88 ret.append('//' + ninja_target[:-SUFFIX_LEN])
89 return ret
75 90
76 91
77 class _ProjectEntry(object): 92 class _ProjectEntry(object):
78 """Helper class for various path transformations.""" 93 """Helper class for various path transformations."""
79 def __init__(self, gn_target): 94 def __init__(self, gn_target):
80 assert gn_target.startswith('//'), gn_target 95 assert gn_target.startswith('//'), gn_target
81 if ':' not in gn_target: 96 if ':' not in gn_target:
82 gn_target = '%s:%s' % (gn_target, os.path.basename(gn_target)) 97 gn_target = '%s:%s' % (gn_target, os.path.basename(gn_target))
83 self._gn_target = gn_target 98 self._gn_target = gn_target
84 self._build_config = None 99 self._build_config = None
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 subpath = os.path.relpath(target_path, prefix) 172 subpath = os.path.relpath(target_path, prefix)
158 symlinked_path = os.path.join(symlink_dir, subpath) 173 symlinked_path = os.path.join(symlink_dir, subpath)
159 symlinked_dir = os.path.dirname(symlinked_path) 174 symlinked_dir = os.path.dirname(symlinked_path)
160 if not os.path.exists(symlinked_dir): 175 if not os.path.exists(symlinked_dir):
161 os.makedirs(symlinked_dir) 176 os.makedirs(symlinked_dir)
162 relpath = os.path.relpath(target_path, symlinked_dir) 177 relpath = os.path.relpath(target_path, symlinked_dir)
163 logging.debug('Creating symlink %s -> %s', symlinked_path, relpath) 178 logging.debug('Creating symlink %s -> %s', symlinked_path, relpath)
164 os.symlink(relpath, symlinked_path) 179 os.symlink(relpath, symlinked_path)
165 180
166 181
167 def _CreateJavaSourceDir(entry_output_dir, java_sources_file): 182 def _CreateJavaSourceDir(output_dir, entry_output_dir, java_sources_file):
168 """Computes and constructs when necessary the list of java source directories. 183 """Computes and constructs when necessary the list of java source directories.
169 184
170 1. Computes the root java source directories from the list of files. 185 1. Computes the root java source directories from the list of files.
171 2. Determines whether there are any .java files in them that are not included 186 2. Determines whether there are any .java files in them that are not included
172 in |java_sources_file|. 187 in |java_sources_file|.
173 3. If not, returns the list of java source directories. If so, constructs a 188 3. If not, returns the list of java source directories. If so, constructs a
174 tree of symlinks within |entry_output_dir| of all files in 189 tree of symlinks within |entry_output_dir| of all files in
175 |java_sources_file|. 190 |java_sources_file|.
176 """ 191 """
177 java_dirs = [] 192 java_dirs = []
178 if java_sources_file: 193 if java_sources_file:
179 java_files = _RebasePath(build_utils.ReadSourcesList(java_sources_file)) 194 java_files = _RebasePath(build_utils.ReadSourcesList(java_sources_file))
180 java_dirs = _ComputeJavaSourceDirs(java_files) 195 java_dirs = _ComputeJavaSourceDirs(java_files)
181 196
182 found_java_files = build_utils.FindInDirectories(java_dirs, '*.java') 197 found_java_files = build_utils.FindInDirectories(java_dirs, '*.java')
183 unwanted_java_files = set(found_java_files) - set(java_files) 198 unwanted_java_files = set(found_java_files) - set(java_files)
184 missing_java_files = set(java_files) - set(found_java_files) 199 missing_java_files = set(java_files) - set(found_java_files)
200 # Warn only about non-generated files that are missing.
201 missing_java_files = [p for p in missing_java_files
202 if not p.startswith(output_dir)]
185 if unwanted_java_files: 203 if unwanted_java_files:
186 logging.debug('Target requires .java symlinks: %s', entry_output_dir) 204 logging.debug('Target requires .java symlinks: %s', entry_output_dir)
187 symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR) 205 symlink_dir = os.path.join(entry_output_dir, _JAVA_SUBDIR)
188 _CreateSymlinkTree(entry_output_dir, symlink_dir, java_files, java_dirs) 206 _CreateSymlinkTree(entry_output_dir, symlink_dir, java_files, java_dirs)
189 java_dirs = [symlink_dir] 207 java_dirs = [symlink_dir]
208
190 if missing_java_files: 209 if missing_java_files:
191 logging.warning('Some java files were not found: %s', missing_java_files) 210 logging.warning('Some java files were not found: %s', missing_java_files)
192 211
193 return java_dirs 212 return java_dirs
194 213
195 214
196 def _GenerateLocalProperties(sdk_dir): 215 def _GenerateLocalProperties(sdk_dir):
197 """Returns the data for project.properties as a string.""" 216 """Returns the data for project.properties as a string."""
198 return '\n'.join([ 217 return '\n'.join([
199 '# Generated by //build/android/gradle/generate_gradle.py', 218 '# Generated by //build/android/gradle/generate_gradle.py',
200 'sdk.dir=%s' % sdk_dir, 219 'sdk.dir=%s' % sdk_dir,
201 '']) 220 ''])
202 221
203 222
204 def _GenerateGradleFile(build_config, config_json, java_dirs, relativize, 223 def _GenerateGradleFile(build_config, config_json, java_dirs, relativize,
205 use_gradle_process_resources): 224 use_gradle_process_resources, jinja_processor):
206 """Returns the data for a project's build.gradle.""" 225 """Returns the data for a project's build.gradle."""
207 deps_info = build_config['deps_info'] 226 deps_info = build_config['deps_info']
208 gradle = build_config['gradle'] 227 gradle = build_config['gradle']
209 228
210 if deps_info['type'] == 'android_apk': 229 if deps_info['type'] == 'android_apk':
211 target_type = 'android_apk' 230 target_type = 'android_apk'
212 elif deps_info['type'] == 'java_library' and not deps_info['is_prebuilt']: 231 elif deps_info['type'] == 'java_library' and not deps_info['is_prebuilt']:
213 if deps_info['requires_android']: 232 if deps_info['requires_android']:
214 target_type = 'android_library' 233 target_type = 'android_library'
215 else: 234 else:
(...skipping 12 matching lines...) Expand all
228 variables['java_dirs'] = relativize(java_dirs) 247 variables['java_dirs'] = relativize(java_dirs)
229 variables['prebuilts'] = relativize(gradle['dependent_prebuilt_jars']) 248 variables['prebuilts'] = relativize(gradle['dependent_prebuilt_jars'])
230 deps = [_ProjectEntry.FromBuildConfigPath(p) 249 deps = [_ProjectEntry.FromBuildConfigPath(p)
231 for p in gradle['dependent_android_projects']] 250 for p in gradle['dependent_android_projects']]
232 251
233 variables['android_project_deps'] = [d.ProjectName() for d in deps] 252 variables['android_project_deps'] = [d.ProjectName() for d in deps]
234 deps = [_ProjectEntry.FromBuildConfigPath(p) 253 deps = [_ProjectEntry.FromBuildConfigPath(p)
235 for p in gradle['dependent_java_projects']] 254 for p in gradle['dependent_java_projects']]
236 variables['java_project_deps'] = [d.ProjectName() for d in deps] 255 variables['java_project_deps'] = [d.ProjectName() for d in deps]
237 256
238 processor = jinja_template.JinjaProcessor(host_paths.DIR_SOURCE_ROOT) 257 return jinja_processor.Render(_JINJA_TEMPLATE_PATH, variables)
239 return processor.Render(_JINJA_TEMPLATE_PATH, variables)
240 258
241 259
242 def _GenerateRootGradle(): 260 def _GenerateRootGradle(jinja_processor):
243 """Returns the data for the root project's build.gradle.""" 261 """Returns the data for the root project's build.gradle."""
244 variables = {'template_type': 'root'} 262 variables = {'template_type': 'root'}
245 processor = jinja_template.JinjaProcessor(host_paths.DIR_SOURCE_ROOT) 263 return jinja_processor.Render(_JINJA_TEMPLATE_PATH, variables)
246 return processor.Render(_JINJA_TEMPLATE_PATH, variables)
247 264
248 265
249 def _GenerateSettingsGradle(project_entries): 266 def _GenerateSettingsGradle(project_entries):
250 """Returns the data for settings.gradle.""" 267 """Returns the data for settings.gradle."""
251 project_name = os.path.basename(os.path.dirname(host_paths.DIR_SOURCE_ROOT)) 268 project_name = os.path.basename(os.path.dirname(host_paths.DIR_SOURCE_ROOT))
252 lines = [] 269 lines = []
253 lines.append('// Generated by //build/android/gradle/generate_gradle.py') 270 lines.append('// Generated by //build/android/gradle/generate_gradle.py')
254 lines.append('rootProject.name = "%s"' % project_name) 271 lines.append('rootProject.name = "%s"' % project_name)
255 lines.append('rootProject.projectDir = settingsDir') 272 lines.append('rootProject.projectDir = settingsDir')
256 lines.append('') 273 lines.append('')
(...skipping 13 matching lines...) Expand all
270 assert _IsSubpathOf(extracted_path, entry_output_dir) 287 assert _IsSubpathOf(extracted_path, entry_output_dir)
271 if os.path.exists(extracted_path): 288 if os.path.exists(extracted_path):
272 shutil.rmtree(extracted_path) 289 shutil.rmtree(extracted_path)
273 290
274 for srcjar_path, extracted_path in srcjar_tuples: 291 for srcjar_path, extracted_path in srcjar_tuples:
275 logging.info('Extracting %s to %s', srcjar_path, extracted_path) 292 logging.info('Extracting %s to %s', srcjar_path, extracted_path)
276 with zipfile.ZipFile(srcjar_path) as z: 293 with zipfile.ZipFile(srcjar_path) as z:
277 z.extractall(extracted_path) 294 z.extractall(extracted_path)
278 295
279 296
280 def _FindAllProjectEntries(main_entry): 297 def _FindAllProjectEntries(main_entries):
281 """Returns the list of all _ProjectEntry instances given the root project.""" 298 """Returns the list of all _ProjectEntry instances given the root project."""
282 found = set() 299 found = set()
283 to_scan = [main_entry] 300 to_scan = list(main_entries)
284 while to_scan: 301 while to_scan:
285 cur_entry = to_scan.pop() 302 cur_entry = to_scan.pop()
286 if cur_entry in found: 303 if cur_entry in found:
287 continue 304 continue
288 found.add(cur_entry) 305 found.add(cur_entry)
289 build_config = cur_entry.BuildConfig() 306 build_config = cur_entry.BuildConfig()
290 sub_config_paths = build_config['deps_info']['deps_configs'] 307 sub_config_paths = build_config['deps_info']['deps_configs']
291 to_scan.extend( 308 to_scan.extend(
292 _ProjectEntry.FromBuildConfigPath(p) for p in sub_config_paths) 309 _ProjectEntry.FromBuildConfigPath(p) for p in sub_config_paths)
293 return list(found) 310 return list(found)
294 311
295 312
296 def main(): 313 def main():
297 parser = argparse.ArgumentParser() 314 parser = argparse.ArgumentParser()
298 parser.add_argument('--output-directory', 315 parser.add_argument('--output-directory',
299 help='Path to the root build directory.') 316 help='Path to the root build directory.')
300 parser.add_argument('-v', 317 parser.add_argument('-v',
301 '--verbose', 318 '--verbose',
302 dest='verbose_count', 319 dest='verbose_count',
303 default=0, 320 default=0,
304 action='count', 321 action='count',
305 help='Verbose level') 322 help='Verbose level')
306 parser.add_argument('--target', 323 parser.add_argument('--target',
307 help='GN target to generate project for.', 324 dest='targets',
308 default='//chrome/android:chrome_public_apk') 325 action='append',
326 help='GN target to generate project for. '
327 'May be repeated.',
328 default=['//chrome/android:chrome_public_test_apk'])
309 parser.add_argument('--project-dir', 329 parser.add_argument('--project-dir',
310 help='Root of the output project.', 330 help='Root of the output project.',
311 default=os.path.join('$CHROMIUM_OUTPUT_DIR', 'gradle')) 331 default=os.path.join('$CHROMIUM_OUTPUT_DIR', 'gradle'))
332 parser.add_argument('--all',
333 action='store_true',
334 help='Generate all java targets (slows down IDE)')
312 parser.add_argument('--use-gradle-process-resources', 335 parser.add_argument('--use-gradle-process-resources',
313 action='store_true', 336 action='store_true',
314 help='Have gradle generate R.java rather than ninja') 337 help='Have gradle generate R.java rather than ninja')
315 args = parser.parse_args() 338 args = parser.parse_args()
316 if args.output_directory: 339 if args.output_directory:
317 constants.SetOutputDirectory(args.output_directory) 340 constants.SetOutputDirectory(args.output_directory)
318 constants.CheckOutputDirectory() 341 constants.CheckOutputDirectory()
319 output_dir = constants.GetOutDirectory() 342 output_dir = constants.GetOutDirectory()
320 devil_chromium.Initialize(output_directory=output_dir) 343 devil_chromium.Initialize(output_directory=output_dir)
321 run_tests_helper.SetLogLevel(args.verbose_count) 344 run_tests_helper.SetLogLevel(args.verbose_count)
322 345
323 gradle_output_dir = os.path.abspath( 346 gradle_output_dir = os.path.abspath(
324 args.project_dir.replace('$CHROMIUM_OUTPUT_DIR', output_dir)) 347 args.project_dir.replace('$CHROMIUM_OUTPUT_DIR', output_dir))
325 logging.warning('Creating project at: %s', gradle_output_dir) 348 logging.warning('Creating project at: %s', gradle_output_dir)
326 349
327 main_entry = _ProjectEntry(args.target) 350 if args.all:
351 # Run GN gen if necessary.
352 _RunNinja(output_dir, ['build.ninja'])
jbudorick 2016/09/22 02:34:46 This is ... building build.ninja? I take it that's
agrieve 2016/09/22 15:51:25 Added a comment - it's faster than running gn gen
jbudorick 2016/09/22 15:57:43 ah, sgtm
353 # Query ninja for all __build_config targets.
354 targets = _QueryForAllGnTargets(output_dir)
355 else:
356 targets = args.targets
357
358 main_entries = [_ProjectEntry(t) for t in targets]
328 logging.warning('Building .build_config files...') 359 logging.warning('Building .build_config files...')
329 _RunNinja(output_dir, [main_entry.NinjaBuildConfigTarget()]) 360 _RunNinja(output_dir, [e.NinjaBuildConfigTarget() for e in main_entries])
330 361
331 all_entries = _FindAllProjectEntries(main_entry) 362 all_entries = _FindAllProjectEntries(main_entries)
332 logging.info('Found %d dependent build_config targets.', len(all_entries)) 363 logging.info('Found %d dependent build_config targets.', len(all_entries))
333 364
365 jinja_processor = jinja_template.JinjaProcessor(host_paths.DIR_SOURCE_ROOT)
334 config_json = build_utils.ReadJson( 366 config_json = build_utils.ReadJson(
335 os.path.join(output_dir, 'gradle', 'config.json')) 367 os.path.join(output_dir, 'gradle', 'config.json'))
336 project_entries = [] 368 project_entries = []
337 srcjar_tuples = [] 369 srcjar_tuples = []
338 for entry in all_entries: 370 for entry in all_entries:
339 build_config = entry.BuildConfig() 371 build_config = entry.BuildConfig()
340 if build_config['deps_info']['type'] not in ('android_apk', 'java_library'): 372 if build_config['deps_info']['type'] not in ('android_apk', 'java_library'):
341 continue 373 continue
342 374
343 entry_output_dir = os.path.join(gradle_output_dir, entry.GradleSubdir()) 375 entry_output_dir = os.path.join(gradle_output_dir, entry.GradleSubdir())
344 relativize = lambda x, d=entry_output_dir: _RebasePath(x, d) 376 relativize = lambda x, d=entry_output_dir: _RebasePath(x, d)
345 377
346 srcjars = _RebasePath(build_config['gradle'].get('bundled_srcjars', [])) 378 srcjars = _RebasePath(build_config['gradle'].get('bundled_srcjars', []))
347 if not args.use_gradle_process_resources: 379 if not args.use_gradle_process_resources:
348 srcjars += _RebasePath(build_config['javac']['srcjars']) 380 srcjars += _RebasePath(build_config['javac']['srcjars'])
349 381
350 java_sources_file = build_config['gradle'].get('java_sources_file') 382 java_sources_file = build_config['gradle'].get('java_sources_file')
351 if java_sources_file: 383 if java_sources_file:
352 java_sources_file = _RebasePath(java_sources_file) 384 java_sources_file = _RebasePath(java_sources_file)
353 385
354 java_dirs = _CreateJavaSourceDir(entry_output_dir, java_sources_file) 386 java_dirs = _CreateJavaSourceDir(output_dir, entry_output_dir,
387 java_sources_file)
355 if srcjars: 388 if srcjars:
356 java_dirs.append(os.path.join(entry_output_dir, _SRCJARS_SUBDIR)) 389 java_dirs.append(os.path.join(entry_output_dir, _SRCJARS_SUBDIR))
357 390
358 data = _GenerateGradleFile(build_config, config_json, java_dirs, relativize, 391 data = _GenerateGradleFile(build_config, config_json, java_dirs, relativize,
359 args.use_gradle_process_resources) 392 args.use_gradle_process_resources,
393 jinja_processor)
360 if data: 394 if data:
361 project_entries.append(entry) 395 project_entries.append(entry)
362 srcjar_tuples.extend( 396 srcjar_tuples.extend(
363 (s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR)) for s in srcjars) 397 (s, os.path.join(entry_output_dir, _SRCJARS_SUBDIR)) for s in srcjars)
364 _WriteFile(os.path.join(entry_output_dir, 'build.gradle'), data) 398 _WriteFile(os.path.join(entry_output_dir, 'build.gradle'), data)
365 399
366 _WriteFile(os.path.join(gradle_output_dir, 'build.gradle'), 400 _WriteFile(os.path.join(gradle_output_dir, 'build.gradle'),
367 _GenerateRootGradle()) 401 _GenerateRootGradle(jinja_processor))
368 402
369 _WriteFile(os.path.join(gradle_output_dir, 'settings.gradle'), 403 _WriteFile(os.path.join(gradle_output_dir, 'settings.gradle'),
370 _GenerateSettingsGradle(project_entries)) 404 _GenerateSettingsGradle(project_entries))
371 405
372 sdk_path = _RebasePath(config_json['android_sdk_root']) 406 sdk_path = _RebasePath(config_json['android_sdk_root'])
373 _WriteFile(os.path.join(gradle_output_dir, 'local.properties'), 407 _WriteFile(os.path.join(gradle_output_dir, 'local.properties'),
374 _GenerateLocalProperties(sdk_path)) 408 _GenerateLocalProperties(sdk_path))
375 409
376 if srcjar_tuples: 410 if srcjar_tuples:
377 logging.warning('Building all .srcjar files...') 411 logging.warning('Building all .srcjar files...')
378 targets = _RebasePath([s[0] for s in srcjar_tuples], output_dir) 412 targets = _RebasePath([s[0] for s in srcjar_tuples], output_dir)
379 _RunNinja(output_dir, targets) 413 _RunNinja(output_dir, targets)
380 _ExtractSrcjars(gradle_output_dir, srcjar_tuples) 414 _ExtractSrcjars(gradle_output_dir, srcjar_tuples)
381 logging.warning('Project created successfully!') 415 logging.warning('Project created successfully!')
382 logging.warning('Generated projects work best with Android Studio 2.2') 416 logging.warning('Generated projects work best with Android Studio 2.2')
383 logging.warning('For more tips: https://chromium.googlesource.com/chromium' 417 logging.warning('For more tips: https://chromium.googlesource.com/chromium'
384 '/src.git/+/master/docs/android_studio.md') 418 '/src.git/+/master/docs/android_studio.md')
385 419
386 420
387 if __name__ == '__main__': 421 if __name__ == '__main__':
388 main() 422 main()
OLDNEW
« no previous file with comments | « build/android/gradle/build.gradle.jinja ('k') | build/android/gyp/jinja_template.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698