1 #! /usr/bin/env python3
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 assert __name__
== '__main__'
9 To update ANGLE in Gecko, use Windows with git-bash, and setup depot_tools, python2, and
10 python3. Because depot_tools expects `python` to be `python2` (shame!), python2 must come
11 before python3 in your path.
13 Upstream: https://chromium.googlesource.com/angle/angle
15 Our repo: https://github.com/mozilla/angle
16 It has branches like 'firefox-60' which is the branch we use for pulling into
17 Gecko with this script.
19 This script leaves a record of the merge-base and cherry-picks that we pull into
20 Gecko. (gfx/angle/cherries.log)
22 ANGLE<->Chrome version mappings are here: https://omahaproxy.appspot.com/
23 An easy choice is to grab Chrome's Beta's ANGLE branch.
30 export PATH="$PATH:/path/to/depot_tools"
33 If this is a new repo, don't forget:
37 ./scripts/bootstrap.py
41 Update: (in the angle repo)
45 /path/to/gecko/gfx/angle/update-angle.py origin/chromium/XXXX
46 git push moz # Push the firefox-XX branch to github.com/mozilla/angle
58 from typing
import * # mypy annotations
60 REPO_DIR
= pathlib
.Path
.cwd()
61 GECKO_ANGLE_DIR
= pathlib
.Path(__file__
).parent
63 OUT_DIR
= pathlib
.Path('out')
66 '# Generated by update-angle.py',
68 "include('../../moz.build.common')",
71 ROOTS
= ['//:translator', '//:libEGL', '//:libGLESv2']
83 GN_ENV
= dict(os
.environ
)
84 GN_ENV
['DEPOT_TOOLS_WIN_TOOLCHAIN'] = '0'
86 (GIT_REMOTE
, ) = args
# Not always 'origin'!
88 # ------------------------------------------------------------------------------
90 def run_checked(*args
, **kwargs
):
93 return subprocess
.run(args
, check
=True, **kwargs
)
97 for k
in sorted(x
.keys()):
101 def collapse_dotdots(path
):
102 split
= path
.split('/')
106 if x
== '..' and ret
:
115 def dag_traverse(root_keys
: Sequence
[str], pre_recurse_func
: Callable
[[str], list]):
116 visited_keys
: Set
[str] = set()
119 if key
in visited_keys
:
121 visited_keys
.add(key
)
123 t
= pre_recurse_func(key
)
125 (next_keys
, post_recurse_func
) = t
128 post_recurse_func
= None
133 if post_recurse_func
:
134 post_recurse_func(key
)
141 # ------------------------------------------------------------------------------
143 print('Importing graph')
145 #shutil.rmtree(str(OUT_DIR), True)
146 OUT_DIR
.mkdir(exist_ok
=True)
149 # Build arguments go here.
150 # See "gn args <out_dir> --list" for available build arguments.
153 angle_enable_gl = false
154 angle_enable_gl_null = false
155 angle_enable_null = false
156 angle_enable_vulkan = false
158 args_gn_path
= OUT_DIR
/ 'args.gn'
159 args_gn_path
.write_bytes(GN_ARGS
)
162 run_checked('gn', 'gen', str(OUT_DIR
), shell
=True, env
=GN_ENV
)
163 except subprocess
.CalledProcessError
:
164 sys
.stderr
.buffer.write(b
'`gn` failed. Is depot_tools in your PATH?\n')
167 p
= run_checked('gn', 'desc', '--format=json', str(OUT_DIR
), '*', stdout
=subprocess
.PIPE
,
168 shell
=True, env
=GN_ENV
)
172 print('\nProcessing graph')
173 descs
= json
.loads(p
.stdout
.decode())
176 # HACKHACKHACK: Inject linux/mac sources instead of trying to merge graphs of different
178 descs
['//:angle_common']['sources'] += [
179 '//src/common/system_utils_linux.cpp',
180 '//src/common/system_utils_mac.cpp',
181 '//src/common/system_utils_posix.cpp',
185 # ------------------------------------------------------------------------------
187 LIBRARY_TYPES
= ('shared_library', 'static_library')
189 def flattened_target(target_name
: str, descs
: dict, stop_at_lib
: bool =True) -> dict:
190 flattened
= dict(descs
[target_name
])
192 EXPECTED_TYPES
= LIBRARY_TYPES
+ ('source_set', 'group', 'action')
197 dep_type
= dep
['type']
199 if stop_at_lib
and dep_type
in LIBRARY_TYPES
:
202 if dep_type
== 'copy':
203 assert not deps
, (target_name
, dep
['deps'])
205 assert dep_type
in EXPECTED_TYPES
, (k
, dep_type
)
206 for (k
,v
) in dep
.items():
207 if type(v
) in (list, tuple):
208 flattened
[k
] = flattened
.get(k
, []) + v
210 #flattened.setdefault(k, v)
214 dag_traverse(descs
[target_name
]['deps'], pre
)
217 # ------------------------------------------------------------------------------
218 # Check that includes are valid. (gn's version of this check doesn't seem to work!)
220 INCLUDE_REGEX
= re
.compile(b
'(?:^|\\n) *# *include +([<"])([^>"]+)[>"]')
221 assert INCLUDE_REGEX
.match(b
'#include "foo"')
222 assert INCLUDE_REGEX
.match(b
'\n#include "foo"')
225 b
'compiler/translator/TranslatorVulkan.h',
226 b
'libANGLE/renderer/d3d/d3d11/winrt/NativeWindow11WinRT.h',
227 b
'libANGLE/renderer/gl/glx/DisplayGLX.h',
228 b
'libANGLE/renderer/gl/cgl/DisplayCGL.h',
229 b
'libANGLE/renderer/gl/egl/ozone/DisplayOzone.h',
230 b
'libANGLE/renderer/gl/egl/android/DisplayAndroid.h',
231 b
'libANGLE/renderer/gl/wgl/DisplayWGL.h',
232 b
'libANGLE/renderer/null/DisplayNULL.h',
233 b
'libANGLE/renderer/vulkan/android/DisplayVkAndroid.h',
234 b
'libANGLE/renderer/vulkan/fuchsia/DisplayVkFuchsia.h',
235 b
'libANGLE/renderer/vulkan/win32/DisplayVkWin32.h',
236 b
'libANGLE/renderer/vulkan/xcb/DisplayVkXcb.h',
240 IGNORED_INCLUDE_PREFIXES
= {
255 def has_all_includes(target_name
: str, descs
: dict) -> bool:
256 flat
= flattened_target(target_name
, descs
, stop_at_lib
=False)
257 acceptable_sources
= flat
.get('sources', []) + flat
.get('outputs', [])
258 acceptable_sources
= (x
.rsplit('/', 1)[-1].encode() for x
in acceptable_sources
)
259 acceptable_sources
= set(acceptable_sources
)
262 desc
= descs
[target_name
]
263 for cur_file
in desc
.get('sources', []):
264 assert cur_file
.startswith('/'), cur_file
265 if not cur_file
.startswith('//'):
267 cur_file
= pathlib
.Path(cur_file
[2:])
268 text
= cur_file
.read_bytes()
269 for m
in INCLUDE_REGEX
.finditer(text
):
270 if m
.group(1) == b
'<':
273 if include
in IGNORED_INCLUDES
:
276 (prefix
, _
) = include
.split(b
'/', 1)
277 if prefix
in IGNORED_INCLUDE_PREFIXES
:
282 include_file
= include
.rsplit(b
'/', 1)[-1]
283 if include_file
not in acceptable_sources
:
284 #print(' acceptable_sources:')
285 #for x in sorted(acceptable_sources):
287 print('Warning in {}: {}: Invalid include: {}'.format(target_name
, cur_file
, include
))
289 #print('Looks valid:', m.group())
295 # Gather real targets:
297 def gather_libraries(roots
: Sequence
[str], descs
: dict) -> Set
[str]:
300 cur
= descs
[target_name
]
301 print(' ' + cur
['type'], target_name
)
302 assert has_all_includes(target_name
, descs
), target_name
304 if cur
['type'] in ('shared_library', 'static_library'):
305 libraries
.add(target_name
)
306 return (cur
['deps'], )
308 dag_traverse(roots
, fn
)
313 libraries
= gather_libraries(ROOTS
, descs
)
314 print(f
'\n{len(libraries)} libraries:')
319 print('\n--check complete.')
322 # ------------------------------------------------------------------------------
323 # Output to moz.builds
325 import vendor_from_git
328 vendor_from_git
.record_cherry_picks(GECKO_ANGLE_DIR
, GIT_REMOTE
)
333 return sorted(x
, key
=str.lower
)
335 def append_arr(dest
, name
, vals
, indent
=0):
339 dest
.append('{}{} += ['.format(' '*4*indent
, name
))
340 for x
in sortedi(vals
):
341 dest
.append("{}'{}',".format(' '*4*(indent
+1), x
))
342 dest
.append('{}]'.format(' '*4*indent
))
346 REGISTERED_DEFINES
= {
347 'ANGLE_CAPTURE_ENABLED': True,
348 'ANGLE_EGL_LIBRARY_NAME': False,
349 'ANGLE_ENABLE_D3D11': True,
350 'ANGLE_ENABLE_D3D9': True,
351 'ANGLE_ENABLE_DEBUG_ANNOTATIONS': True,
352 'ANGLE_ENABLE_NULL': False,
353 'ANGLE_ENABLE_OPENGL': False,
354 'ANGLE_ENABLE_OPENGL_NULL': False,
355 'ANGLE_ENABLE_ESSL': True,
356 'ANGLE_ENABLE_GLSL': True,
357 'ANGLE_ENABLE_HLSL': True,
358 'ANGLE_GENERATE_SHADER_DEBUG_INFO': True,
359 'ANGLE_GLESV2_LIBRARY_NAME': True,
360 'ANGLE_IS_64_BIT_CPU': False,
361 'ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES': False,
362 'ANGLE_USE_EGL_LOADER': True,
363 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS': False,
364 'CHROMIUM_BUILD': False,
365 'COMPONENT_BUILD': False,
366 'DYNAMIC_ANNOTATIONS_ENABLED': True,
367 'EGL_EGL_PROTOTYPES': True,
368 'EGL_EGLEXT_PROTOTYPES': True,
370 'FIELDTRIAL_TESTING_ENABLED': False,
371 'FULL_SAFE_BROWSING': False,
374 'GL_GLES_PROTOTYPES': True,
375 'GL_GLEXT_PROTOTYPES': True,
376 'GPU_INFO_USE_SETUPAPI': True,
377 'LIBANGLE_IMPLEMENTATION': True,
378 'LIBEGL_IMPLEMENTATION': True,
379 'LIBGLESV2_IMPLEMENTATION': True,
381 'NO_TCMALLOC': False,
383 # Else: gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp(89): error C2787: 'IDCompositionDevice': no GUID has been associated with this object
384 'NTDDI_VERSION': True,
386 'PSAPI_VERSION': False,
387 'SAFE_BROWSING_CSD': False,
388 'SAFE_BROWSING_DB_LOCAL': False,
391 'V8_DEPRECATION_WARNINGS': False,
393 'WIN32_LEAN_AND_MEAN': False,
394 'WINAPI_FAMILY': False,
398 # gfx/angle/targets/libANGLE
399 # In file included from c:/dev/mozilla/gecko4/gfx/angle/checkout/src/libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.cpp:10:
400 # In file included from c:/dev/mozilla/gecko4/gfx/angle/checkout/src\libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h:17:
401 # C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\winrt\Windows.ui.composition.interop.h(103,20): error: unknown type name 'POINTER_INFO'
402 # _In_ const POINTER_INFO& pointerInfo
405 'WTF_USE_DYNAMIC_ANNOTATIONS': False,
406 '_ATL_NO_OPENGL': True,
408 '_CRT_SECURE_NO_DEPRECATE': True,
410 '_HAS_EXCEPTIONS': True,
411 '_HAS_ITERATOR_DEBUGGING': False,
412 '_SCL_SECURE_NO_DEPRECATE': True,
415 '_USING_V110_SDK71_': False,
416 '_WIN32_WINNT': False,
421 'CR_CLANG_REVISION': True,
424 '_HAS_NODISCARD': False,
429 print('\nRun actions')
430 required_files
: Set
[str] = set()
432 run_checked('ninja', '-C', str(OUT_DIR
), ':commit_id')
433 required_files |
= set(descs
['//:commit_id']['outputs'])
438 print('\nExport targets')
440 # Clear our dest directories
441 targets_dir
= pathlib
.Path(GECKO_ANGLE_DIR
, 'targets')
442 checkout_dir
= pathlib
.Path(GECKO_ANGLE_DIR
, 'checkout')
444 shutil
.rmtree(targets_dir
, True)
445 shutil
.rmtree(checkout_dir
, True)
446 targets_dir
.mkdir(exist_ok
=True)
447 checkout_dir
.mkdir(exist_ok
=True)
451 def export_target(target_name
) -> Set
[str]:
452 #print(' ', target_name)
453 desc
= descs
[target_name
]
454 flat
= flattened_target(target_name
, descs
)
455 assert target_name
.startswith('//:'), target_name
456 name
= target_name
[3:]
458 required_files
: Set
[str] = set(flat
['sources'])
460 # Create our manifest lines
461 target_dir
= targets_dir
/ name
462 target_dir
.mkdir(exist_ok
=True)
464 lines
= list(COMMON_HEADER
)
467 for x
in sorted(set(desc
['defines'])):
469 (k
, v
) = x
.split('=', 1)
474 line
= f
"DEFINES['{k}'] = {v}"
475 if REGISTERED_DEFINES
[k
] == False:
479 print(f
'[{name}] Unrecognized define: {k}')
482 cxxflags
= set(desc
['cflags'] + desc
['cflags_cc'])
484 def fixup_paths(listt
):
486 assert x
.startswith('//'), x
487 yield '../../checkout/' + x
[2:]
489 sources_by_config
: Dict
[str,List
[str]] = {}
490 extras
: Dict
[str,str] = dict()
491 for x
in fixup_paths(flat
['sources']):
493 (b
, e
) = x
.rsplit('.', 1)
494 if e
in ['h', 'y', 'l', 'inc', 'inl']:
496 elif e
in ['cpp', 'cc', 'c']:
497 if b
.endswith('_win'):
498 config
= "CONFIG['OS_ARCH'] == 'WINNT'"
499 elif b
.endswith('_linux'):
500 # Include these on BSDs too.
501 config
= "CONFIG['OS_ARCH'] not in ('Darwin', 'WINNT')"
502 elif b
.endswith('_mac'):
503 config
= "CONFIG['OS_ARCH'] == 'Darwin'"
504 elif b
.endswith('_posix'):
505 config
= "CONFIG['OS_ARCH'] != 'WINNT'"
507 config
= '' # None can't compare against str.
509 sources_by_config
.setdefault(config
, []).append(x
)
512 assert 'RCFILE' not in extras
, (target_name
, extras
['RCFILE'], x
)
513 extras
['RCFILE'] = f
"'{x}'"
516 assert 'DEFFILE' not in extras
, (target_name
, extras
['DEFFILE'], x
)
517 extras
['DEFFILE'] = f
"'{x}'"
520 assert False, ("Unhandled ext:", x
)
522 ldflags
= set(desc
['ldflags'])
524 for x
in set(ldflags
):
525 if x
.startswith(DEF_PREFIX
):
526 def_path
= x
[len(DEF_PREFIX
):]
527 required_files
.add(def_path
)
528 assert 'DEFFILE' not in extras
531 def_path
= str(OUT_DIR
) + '/' + def_path
532 def_path
= '//' + collapse_dotdots(def_path
)
534 def_rel_path
= list(fixup_paths([def_path
]))[0]
535 extras
['DEFFILE'] = "'{}'".format(def_rel_path
)
537 os_libs
= list(map( lambda x
: x
[:-len('.lib')], set(desc
.get('libs', [])) ))
539 def append_arr_commented(dest
, name
, src
):
541 append_arr(lines
, name
, src
)
546 lines
= map(comment
, lines
)
549 append_arr(lines
, 'LOCAL_INCLUDES', fixup_paths(desc
['include_dirs']))
550 append_arr_commented(lines
, 'CXXFLAGS', cxxflags
)
552 for (config
,v
) in sorted_items(sources_by_config
):
555 lines
.append("if {}:".format(config
))
557 append_arr(lines
, 'SOURCES', v
, indent
=indent
)
559 dep_libs
: Set
[str] = set()
560 for dep_name
in set(flat
['deps']):
561 dep
= descs
[dep_name
]
562 if dep
['type'] in LIBRARY_TYPES
:
563 assert dep_name
.startswith('//:'), dep_name
564 dep_libs
.add(dep_name
[3:])
566 append_arr(lines
, 'USE_LIBS', dep_libs
)
567 append_arr(lines
, 'DIRS', ['../' + x
for x
in dep_libs
])
568 append_arr(lines
, 'OS_LIBS', os_libs
)
569 append_arr_commented(lines
, 'LDFLAGS', ldflags
)
571 for (k
,v
) in sorted(extras
.items()):
572 lines
.append('{} = {}'.format(k
, v
))
574 lib_type
= desc
['type']
575 if lib_type
== 'shared_library':
576 lines
.append(f
"GeckoSharedLibrary('{name}', linkage=None)")
577 elif lib_type
== 'static_library':
578 lines
.append(f
"Library('{name}')")
580 assert False, lib_type
584 mozbuild
= target_dir
/ 'moz.build'
585 print(' ', ' ', f
'Writing {mozbuild}')
586 data
= b
'\n'.join((x
.encode() for x
in lines
))
587 mozbuild
.write_bytes(data
)
589 return required_files
593 for target_name
in libraries
:
594 reqs
= export_target(target_name
)
595 required_files |
= reqs
599 print('\nMigrate required files')
602 for x
in required_files
:
604 sys
.stdout
.write(f
'\r Copying {i}/{len(required_files)}')
606 assert x
.startswith('//'), x
610 dest
= checkout_dir
/ x
612 dest
.parent
.mkdir(parents
=True, exist_ok
=True)
613 data
= src
.read_bytes()
614 data
= data
.replace(b
'\r\n', b
'\n')
615 dest
.write_bytes(data
)