def doc(self, params): self.ensure_bootstrapped() if not path.exists(path.join(self.config["tools"]["rust-root"], "doc")): Registrar.dispatch("bootstrap-rust-docs", context=self.context) rust_docs = path.join(self.config["tools"]["rust-root"], "doc") docs = path.join(self.get_target_dir(), "doc") if not path.exists(docs): os.makedirs(docs) if read_file(path.join(docs, "version_info.html"), if_exists=True) != \ read_file(path.join(rust_docs, "version_info.html")): print("Copying Rust documentation.") # copytree doesn't like the destination already existing. for name in os.listdir(rust_docs): if not name.startswith('.'): full_name = path.join(rust_docs, name) destination = path.join(docs, name) if path.isdir(full_name): if path.exists(destination): rmtree(destination) copytree(full_name, destination) else: copy2(full_name, destination) return subprocess.call(["cargo", "doc"] + params, env=self.build_env(), cwd=self.servo_crate())
def ensure_bootstrapped(self, target=None, rustup_components=None): if self.context.bootstrapped: return target_platform = target or host_triple() # Always check if all needed MSVC dependencies are installed if "msvc" in target_platform: Registrar.dispatch("bootstrap", context=self.context) if self.config["tools"]["use-rustup"]: self.ensure_rustup_version() toolchain = self.rust_toolchain() status = subprocess.call( ["rustup", "run", toolchain.encode("utf-8"), "rustc", "--version"], stdout=open(os.devnull, "wb"), stderr=subprocess.STDOUT, ) if status: check_call(["rustup", "toolchain", "install", "--profile", "minimal", toolchain]) installed = check_output( ["rustup", "component", "list", "--installed", "--toolchain", toolchain] ) for component in set(rustup_components or []) | {"rustc-dev"}: if component.encode("utf-8") not in installed: check_call(["rustup", "component", "add", "--toolchain", toolchain, component]) if target and "uwp" not in target and target.encode("utf-8") not in check_output( ["rustup", "target", "list", "--installed", "--toolchain", toolchain] ): check_call(["rustup", "target", "add", "--toolchain", toolchain, target]) self.context.bootstrapped = True
def install(self, release=False, dev=False): try: binary_path = self.get_binary_path(release, dev, android=True) except BuildNotFound: print("Servo build not found. Building servo...") result = Registrar.dispatch("build", context=self.context, release=release, dev=dev) if result: return result try: binary_path = self.get_binary_path(release, dev, android=True) except BuildNotFound: print( "Rebuilding Servo did not solve the missing build problem." ) return 1 apk_path = binary_path + ".apk" if not path.exists(apk_path): result = Registrar.dispatch("package", context=self.context, release=release, dev=dev) if result is not 0: return result print(["adb", "install", "-r", apk_path]) return subprocess.call(["adb", "install", "-r", apk_path], env=self.build_env())
def ensure_clobbered(self, target_dir=None): if target_dir is None: target_dir = self.get_target_dir() auto = True if os.environ.get('AUTOCLOBBER', False) else False src_clobber = os.path.join(self.context.topdir, 'CLOBBER') target_clobber = os.path.join(target_dir, 'CLOBBER') if not os.path.exists(target_dir): os.makedirs(target_dir) if not os.path.exists(target_clobber): # Simply touch the file. with open(target_clobber, 'a'): pass if auto: if os.path.getmtime(src_clobber) > os.path.getmtime( target_clobber): print('Automatically clobbering target directory: {}'.format( target_dir)) try: Registrar.dispatch("clean", context=self.context, verbose=True) print('Successfully completed auto clobber.') except subprocess.CalledProcessError as error: sys.exit(error) else: print("Clobber not needed.")
def install(self, release=False, dev=False, android=False): try: binary_path = self.get_binary_path(release, dev, android=android) except BuildNotFound: print("Servo build not found. Building servo...") result = Registrar.dispatch( "build", context=self.context, release=release, dev=dev, android=android ) if result: return result try: binary_path = self.get_binary_path(release, dev, android=android) except BuildNotFound: print("Rebuilding Servo did not solve the missing build problem.") return 1 if android: pkg_path = binary_path + ".apk" exec_command = ["adb", "install", "-r", pkg_path] elif is_windows(): pkg_path = path.join(path.dirname(binary_path), 'msi', 'Servo.msi') exec_command = ["msiexec", "/i", pkg_path] if not path.exists(pkg_path): result = Registrar.dispatch( "package", context=self.context, release=release, dev=dev, android=android ) if result != 0: return result print(" ".join(exec_command)) return subprocess.call(exec_command, env=self.build_env())
def ensure_bootstrapped(self, target=None): if self.context.bootstrapped: return rust_root = self.config["tools"]["rust-root"] rustc_path = path.join(rust_root, "rustc", "bin", "rustc" + BIN_SUFFIX) rustc_binary_exists = path.exists(rustc_path) base_target_path = path.join(rust_root, "rustc", "lib", "rustlib") target_exists = True if target is not None: target_path = path.join(base_target_path, target) target_exists = path.exists(target_path) if not (self.config['tools']['system-rust'] or (rustc_binary_exists and target_exists)): print("looking for rustc at %s" % (rustc_path)) Registrar.dispatch("bootstrap-rust", context=self.context, target=filter(None, [target])) cargo_path = path.join(self.config["tools"]["cargo-root"], "cargo", "bin", "cargo" + BIN_SUFFIX) cargo_binary_exists = path.exists(cargo_path) if not self.config["tools"]["system-cargo"] and not cargo_binary_exists: Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def doc(self, params): self.ensure_bootstrapped() if not path.exists(path.join(self.config["tools"]["rust-root"], "doc")): Registrar.dispatch("bootstrap-rust-docs", context=self.context) rust_docs = path.join(self.config["tools"]["rust-root"], "doc") docs = path.join(self.get_target_dir(), "doc") if not path.exists(docs): os.makedirs(docs) if read_file(path.join(docs, "version_info.html"), if_exists=True) != read_file( path.join(rust_docs, "version_info.html") ): print("Copying Rust documentation.") # copytree doesn't like the destination already existing. for name in os.listdir(rust_docs): if not name.startswith("."): full_name = path.join(rust_docs, name) destination = path.join(docs, name) if path.isdir(full_name): if path.exists(destination): rmtree(destination) copytree(full_name, destination) else: copy2(full_name, destination) return subprocess.call(["cargo", "doc"] + params, env=self.build_env(), cwd=self.servo_crate())
def test(self): test_start = time() for t in ["tidy", "unit", "ref", "content", "wpt"]: Registrar.dispatch("test-%s" % t, context=self.context) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def ensure_bootstrapped(self): if self.context.bootstrapped: return subprocess.check_call(["git", "submodule", "sync"]) submodules = subprocess.check_output(["git", "submodule", "status"]) for line in submodules.split('\n'): components = line.strip().split(' ') if len(components) > 1 and components[0].startswith(('-', '+')): module_path = components[1] subprocess.check_call([ "git", "submodule", "update", "--init", "--recursive", "--", module_path ]) if not self.config["tools"]["system-rust"] and \ not path.exists(path.join( self.config["tools"]["rust-root"], "bin", "rustc")): Registrar.dispatch("bootstrap-rust", context=self.context) if not self.config["tools"]["system-cargo"] and \ not path.exists(path.join( self.config["tools"]["cargo-root"], "bin", "cargo")): Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def ensure_clobbered(self, target_dir=None): if target_dir is None: target_dir = self.get_target_dir() auto = True if os.environ.get('AUTOCLOBBER', False) else False src_clobber = os.path.join(self.context.topdir, 'CLOBBER') target_clobber = os.path.join(target_dir, 'CLOBBER') if not os.path.exists(target_dir): os.makedirs(target_dir) if not os.path.exists(target_clobber): # Simply touch the file. with open(target_clobber, 'a'): pass if auto: if os.path.getmtime(src_clobber) > os.path.getmtime(target_clobber): print('Automatically clobbering target directory: {}'.format(target_dir)) try: Registrar.dispatch("clean", context=self.context, verbose=True) print('Successfully completed auto clobber.') except subprocess.CalledProcessError as error: sys.exit(error) else: print("Clobber not needed.")
def ensure_bootstrapped(self, target=None): if self.context.bootstrapped: return rust_root = self.config["tools"]["rust-root"] rustc_path = path.join( rust_root, "rustc", "bin", "rustc" + BIN_SUFFIX ) rustc_binary_exists = path.exists(rustc_path) base_target_path = path.join(rust_root, "rustc", "lib", "rustlib") target_exists = True if target is not None: target_path = path.join(base_target_path, target) target_exists = path.exists(target_path) if not (self.config['tools']['system-rust'] or (rustc_binary_exists and target_exists)): print("looking for rustc at %s" % (rustc_path)) Registrar.dispatch("bootstrap-rust", context=self.context, target=filter(None, [target])) cargo_path = path.join(self.config["tools"]["cargo-root"], "cargo", "bin", "cargo" + BIN_SUFFIX) cargo_binary_exists = path.exists(cargo_path) if not self.config["tools"]["system-cargo"] and not cargo_binary_exists: Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def setUp(self): self.remove_cats = [] for cat in ("build", "post-build", "misc", "testing"): if cat in Registrar.categories: continue Registrar.register_category(cat, cat, cat) self.remove_cats.append(cat)
def ensure_bootstrapped(self, targets=[]): if self.context.bootstrapped: return rust_root = self.config["tools"]["rust-root"] rustc_path = path.join( rust_root, "rustc", "bin", "rustc" + BIN_SUFFIX ) rustc_binary_exists = path.exists(rustc_path) base_target_path = path.join(rust_root, "rustc", "lib", "rustlib") target_paths = [path.join(base_target_path, t) for t in targets] all_targets_exist = all([path.exists(p) for p in target_paths]) if (not self.config['tools']['system-rust'] and (not rustc_binary_exists or not all_targets_exist)): print("looking for rustc at %s" % (rustc_path)) Registrar.dispatch("bootstrap-rust", context=self.context, target=targets) cargo_path = path.join(self.config["tools"]["cargo-root"], "cargo", "bin", "cargo" + BIN_SUFFIX) cargo_binary_exists = path.exists(cargo_path) if not self.config["tools"]["system-cargo"] and not cargo_binary_exists: Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def test(self, params, render_mode=DEFAULT_RENDER_MODE, release=False, faster=False, no_progress=False, self_test=False): suites = OrderedDict([ ("tidy", {"kwargs": {"faster": faster, "no_progress": no_progress, "self_test": self_test}, "include_arg": "include"}), ("wpt", {"kwargs": {"release": release}, "paths": [path.abspath(path.join("tests", "wpt", "web-platform-tests")), path.abspath(path.join("tests", "wpt", "mozilla"))], "include_arg": "include"}), ("css", {"kwargs": {"release": release}, "paths": [path.abspath(path.join("tests", "wpt", "css-tests"))], "include_arg": "include"}), ("unit", {"kwargs": {}, "paths": [path.abspath(path.join("tests", "unit"))], "include_arg": "test_name"}), ("compiletest", {"kwargs": {"release": release}, "paths": [path.abspath(path.join("tests", "compiletest"))], "include_arg": "test_name"}) ]) suites_by_prefix = {path: k for k, v in suites.iteritems() if "paths" in v for path in v["paths"]} selected_suites = OrderedDict() if params is None: params = suites.keys() for arg in params: found = False if arg in suites and arg not in selected_suites: selected_suites[arg] = [] found = True elif os.path.exists(path.abspath(arg)): abs_path = path.abspath(arg) for prefix, suite in suites_by_prefix.iteritems(): if abs_path.startswith(prefix): if suite not in selected_suites: selected_suites[suite] = [] selected_suites[suite].append(arg) found = True break if not found: print("%s is not a valid test path or suite name" % arg) return 1 test_start = time() for suite, tests in selected_suites.iteritems(): props = suites[suite] kwargs = props.get("kwargs", {}) if tests: kwargs[props["include_arg"]] = tests Registrar.dispatch("test-%s" % suite, context=self.context, **kwargs) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def ensure_bootstrapped(self, target=None, rustup_components=None): if self.context.bootstrapped: return target_platform = target or host_triple() # Always check if all needed MSVC dependencies are installed if "msvc" in target_platform: Registrar.dispatch("bootstrap", context=self.context) if self.config["tools"]["use-rustup"]: self.ensure_rustup_version() toolchain = self.rust_toolchain() status = subprocess.call( [ "rustup", "run", toolchain.encode("utf-8"), "rustc", "--version" ], stdout=open(os.devnull, "wb"), stderr=subprocess.STDOUT, ) if status: check_call([ "rustup", "toolchain", "install", "--profile", "minimal", toolchain ]) installed = check_output([ "rustup", "component", "list", "--installed", "--toolchain", toolchain ]) required_components = { # For components/script_plugins, https://github.com/rust-lang/rust/pull/67469 "rustc-dev", # https://github.com/rust-lang/rust/issues/72594#issuecomment-633779564 "llvm-tools-preview", } for component in set(rustup_components or []) | required_components: if component.encode("utf-8") not in installed: check_call([ "rustup", "component", "add", "--toolchain", toolchain, component ]) if target and "uwp" not in target and target.encode( "utf-8") not in check_output([ "rustup", "target", "list", "--installed", "--toolchain", toolchain ]): check_call([ "rustup", "target", "add", "--toolchain", toolchain, target ]) self.context.bootstrapped = True
def test(self, params): if params: return self.infer_test_by_dir(params) test_start = time() for t in ["tidy", "ref", "wpt", "css", "unit"]: Registrar.dispatch("test-%s" % t, context=self.context) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def test(self, params): if params: return self.infer_test_by_dir(params) test_start = time() for t in ["tidy", "ref", "content", "wpt", "unit"]: Registrar.dispatch("test-%s" % t, context=self.context) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def ensure_bootstrapped(self, target=None): if self.context.bootstrapped: return target_platform = target or host_triple() # Always check if all needed MSVC dependencies are installed if "msvc" in target_platform: Registrar.dispatch("bootstrap", context=self.context) self.context.bootstrapped = True
def test(self, params, render_mode=DEFAULT_RENDER_MODE, release=False, tidy_all=False, no_progress=False, self_test=False, all_suites=False): suites = copy.deepcopy(TEST_SUITES) suites["tidy"]["kwargs"] = {"all_files": tidy_all, "no_progress": no_progress, "self_test": self_test, "stylo": False} suites["wpt"]["kwargs"] = {"release": release} suites["css"]["kwargs"] = {"release": release} suites["unit"]["kwargs"] = {} suites["compiletest"]["kwargs"] = {"release": release} selected_suites = OrderedDict() if params is None: if all_suites: params = suites.keys() else: print("Specify a test path or suite name, or pass --all to run all test suites.\n\nAvailable suites:") for s in suites: print(" %s" % s) return 1 for arg in params: found = False if arg in suites and arg not in selected_suites: selected_suites[arg] = [] found = True else: suite = self.suite_for_path(arg) if suite is not None: if suite not in selected_suites: selected_suites[suite] = [] selected_suites[suite].append(arg) found = True break if not found: print("%s is not a valid test path or suite name" % arg) return 1 test_start = time() for suite, tests in selected_suites.iteritems(): props = suites[suite] kwargs = props.get("kwargs", {}) if tests: kwargs[props["include_arg"]] = tests Registrar.dispatch("test-%s" % suite, context=self.context, **kwargs) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def run_test_list_or_dispatch(self, requested_paths, correct_suite, correct_function, **kwargs): if not requested_paths: return correct_function(**kwargs) # Paths specified on command line. Ensure they can be handled, re-dispatch otherwise. all_handled = True for test_path in requested_paths: suite = self.suite_for_path(test_path) if suite is not None and correct_suite != suite: all_handled = False print("Warning: %s is not a %s test. Delegating to test-%s." % (test_path, correct_suite, suite)) if all_handled: return correct_function(**kwargs) # Dispatch each test to the correct suite via test() Registrar.dispatch("test", context=self.context, params=requested_paths)
def test(self, params, render_mode=DEFAULT_RENDER_MODE, release=False, tidy_all=False, no_progress=False, self_test=False, all_suites=False): suites = copy.deepcopy(TEST_SUITES) suites["tidy"]["kwargs"] = {"all_files": tidy_all, "no_progress": no_progress, "self_test": self_test} suites["wpt"]["kwargs"] = {"release": release} suites["css"]["kwargs"] = {"release": release} suites["unit"]["kwargs"] = {} suites["compiletest"]["kwargs"] = {"release": release} selected_suites = OrderedDict() if params is None: if all_suites: params = suites.keys() else: print("Specify a test path or suite name, or pass --all to run all test suites.\n\nAvailable suites:") for s in suites: print(" %s" % s) return 1 for arg in params: found = False if arg in suites and arg not in selected_suites: selected_suites[arg] = [] found = True else: suite = self.suite_for_path(arg) if suite is not None: if suite not in selected_suites: selected_suites[suite] = [] selected_suites[suite].append(arg) found = True break if not found: print("%s is not a valid test path or suite name" % arg) return 1 test_start = time() for suite, tests in selected_suites.iteritems(): props = suites[suite] kwargs = props.get("kwargs", {}) if tests: kwargs[props["include_arg"]] = tests Registrar.dispatch("test-%s" % suite, context=self.context, **kwargs) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def install(self, release=False, dev=False, android=False, target=None): env = self.build_env() if target and android: print("Please specify either --target or --android.") sys.exit(1) if not android: android = self.handle_android_target(target) try: binary_path = self.get_binary_path(release, dev, android=android) except BuildNotFound: print("Servo build not found. Building servo...") result = Registrar.dispatch("build", context=self.context, release=release, dev=dev, android=android) if result: return result try: binary_path = self.get_binary_path(release, dev, android=android) except BuildNotFound: print( "Rebuilding Servo did not solve the missing build problem." ) return 1 if android: pkg_path = binary_path + ".apk" exec_command = [ self.android_adb_path(env), "install", "-r", pkg_path ] elif is_windows(): pkg_path = path.join(path.dirname(binary_path), 'msi', 'Servo.msi') exec_command = ["msiexec", "/i", pkg_path] if not path.exists(pkg_path): result = Registrar.dispatch("package", context=self.context, release=release, dev=dev, android=android) if result != 0: return result print(" ".join(exec_command)) return subprocess.call(exec_command, env=env)
def ensure_built_tests(self, release=False): if self.context.built_tests: return returncode = Registrar.dispatch("build-tests", context=self.context, release=release) if returncode: sys.exit(returncode) self.context.built_tests = True
def ensure_built_tests(self): if self.context.built_tests: return returncode = Registrar.dispatch('build-tests', context=self.context) if returncode: sys.exit(returncode) self.context.built_tests = True
def install(self, release=False, dev=False, android=False, emulator=False, usb=False, target=None): env = self.build_env() if target and android: print("Please specify either --target or --android.") sys.exit(1) if not android: android = self.handle_android_target(target) try: binary_path = self.get_binary_path(release, dev, android=android) except BuildNotFound: print("Servo build not found. Building servo...") result = Registrar.dispatch( "build", context=self.context, release=release, dev=dev, android=android ) if result: return result try: binary_path = self.get_binary_path(release, dev, android=android) except BuildNotFound: print("Rebuilding Servo did not solve the missing build problem.") return 1 if android: pkg_path = self.get_apk_path(release) exec_command = [self.android_adb_path(env)] if emulator and usb: print("Cannot install to both emulator and USB at the same time.") return 1 if emulator: exec_command += ["-e"] if usb: exec_command += ["-d"] exec_command += ["install", "-r", pkg_path] elif is_windows(): pkg_path = path.join(path.dirname(binary_path), 'msi', 'Servo.msi') exec_command = ["msiexec", "/i", pkg_path] if not path.exists(pkg_path): result = Registrar.dispatch( "package", context=self.context, release=release, dev=dev, android=android ) if result != 0: return result print(" ".join(exec_command)) return subprocess.call(exec_command, env=env)
def test(self, params, render_mode=DEFAULT_RENDER_MODE): if params: return self.infer_test_by_dir(params) test_and_args = [ ("tidy", {}), ("ref", {"kind": render_mode}), ("wpt", {}), ("css", {}), ("unit", {}), ] test_start = time() for t, args in test_and_args: Registrar.dispatch("test-%s" % t, context=self.context, **args) elapsed = time() - test_start print("Tests completed in %0.2fs" % elapsed)
def ensure_bootstrapped(self): if self.context.bootstrapped: return submodules = subprocess.check_output(["git", "submodule", "status"]) for line in submodules.split('\n'): components = line.strip().split(' ') if len(components) > 1 and components[0].startswith(('-', '+')): module_path = components[1] subprocess.check_call(["git", "submodule", "update", "--init", "--recursive", "--", module_path]) if not self.config["tools"]["system-rust"] and \ not path.exists(path.join(self.context.topdir, "rust", "bin", "rustc")): Registrar.dispatch("bootstrap-rust", context=self.context) if not self.config["tools"]["system-cargo"] and \ not path.exists(path.join(self.context.topdir, "cargo", "bin", "cargo")): Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def ensure_bootstrapped(self, targets=[]): if self.context.bootstrapped: return if not self.config["tools"]["system-rust"] and \ (not path.exists(path.join( self.config["tools"]["rust-root"], "rustc", "bin", "rustc" + BIN_SUFFIX)) or not all([path.exists(path.join( self.config["tools"]["rust-root"], "rustc", "lib", "rustlib", x )) for x in targets])): print("looking for rustc at %s" % path.join( self.config["tools"]["rust-root"], "rustc", "bin", "rustc" + BIN_SUFFIX)) Registrar.dispatch("bootstrap-rust", context=self.context, target=targets) if not self.config["tools"]["system-cargo"] and \ not path.exists(path.join( self.config["tools"]["cargo-root"], "cargo", "bin", "cargo" + BIN_SUFFIX)): Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def test_invalid_context_message(self): """Test that commands which do not pass all their conditions print the proper failure message.""" def is_bar(): """Bar must be true""" fail_conditions = [is_bar] for name in ('cmd_bar', 'cmd_foobar'): result, stdout, stderr = self._run_mach([name]) self.assertEquals(1, result) fail_msg = Registrar._condition_failed_message(name, fail_conditions) self.assertEquals(fail_msg.rstrip(), stdout.rstrip()) for name in ('cmd_bar_ctx', 'cmd_foobar_ctx'): result, stdout, stderr = self._run_mach([name], _populate_context) self.assertEquals(1, result) fail_msg = Registrar._condition_failed_message(name, fail_conditions) self.assertEquals(fail_msg.rstrip(), stdout.rstrip())
def ensure_bootstrapped(self, target=None): if self.context.bootstrapped: return target_platform = target or host_triple() rust_root = self.config["tools"]["rust-root"] rustc_path = path.join( rust_root, "rustc", "bin", "rustc" + BIN_SUFFIX ) rustc_binary_exists = path.exists(rustc_path) base_target_path = path.join(rust_root, "rustc", "lib", "rustlib") target_path = path.join(base_target_path, target_platform) target_exists = path.exists(target_path) # Always check if all needed MSVC dependencies are installed if "msvc" in target_platform: Registrar.dispatch("bootstrap", context=self.context) if not (self.config['tools']['system-rust'] or (rustc_binary_exists and target_exists)): print("Looking for rustc at %s" % (rustc_path)) Registrar.dispatch("bootstrap-rust", context=self.context, target=filter(None, [target]), stable=self._use_stable_rust) cargo_path = path.join(self.config["tools"]["cargo-root"], "cargo", "bin", "cargo" + BIN_SUFFIX) cargo_binary_exists = path.exists(cargo_path) if not self.config["tools"]["system-cargo"] and not cargo_binary_exists: Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def install(self, release=False, dev=False): try: binary_path = self.get_binary_path(release, dev, android=True) except BuildNotFound: print("Servo build not found. Building servo...") result = Registrar.dispatch("build", context=self.context, release=release, dev=dev) if result: return result try: binary_path = self.get_binary_path(release, dev, android=True) except BuildNotFound: print("Rebuilding Servo did not solve the missing build problem.") return 1 apk_path = binary_path + ".apk" if not path.exists(apk_path): result = Registrar.dispatch("package", context=self.context, release=release, dev=dev) if result != 0: return result print(["adb", "install", "-r", apk_path]) return subprocess.call(["adb", "install", "-r", apk_path], env=self.build_env())
def ensure_bootstrapped(self, targets=[]): if self.context.bootstrapped: return if not self.config["tools"]["system-rust"] and \ (not path.exists(path.join( self.config["tools"]["rust-root"], "rustc", "bin", "rustc" + BIN_SUFFIX)) or not all([path.exists(path.join( self.config["tools"]["rust-root"], "rustc", "lib", "rustlib", x )) for x in targets])): print("looking for rustc at %s" % path.join(self.config["tools"]["rust-root"], "rustc", "bin", "rustc" + BIN_SUFFIX)) Registrar.dispatch("bootstrap-rust", context=self.context, target=targets) if not self.config["tools"]["system-cargo"] and \ not path.exists(path.join( self.config["tools"]["cargo-root"], "cargo", "bin", "cargo" + BIN_SUFFIX)): Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def install(self, release=False, dev=False): binary_path = self.get_binary_path(release, dev, android=True) if not path.exists(binary_path): # TODO: Run the build command automatically? print("Servo build not found. Please run `./mach build` to compile Servo.") return 1 apk_path = binary_path + ".apk" if not path.exists(apk_path): result = Registrar.dispatch("package", context=self.context, release=release, dev=dev) if result is not 0: return result print(["adb", "install", "-r", apk_path]) return subprocess.call(["adb", "install", "-r", apk_path], env=self.build_env())
def ensure_bootstrapped(self): if self.context.bootstrapped: return Registrar.dispatch("update-submodules", context=self.context) if not self.config["tools"]["system-rust"] and \ not path.exists(path.join( self.config["tools"]["rust-root"], "rustc", "bin", "rustc")): Registrar.dispatch("bootstrap-rust", context=self.context) if not self.config["tools"]["system-cargo"] and \ not path.exists(path.join( self.config["tools"]["cargo-root"], "cargo", "bin", "cargo")): Registrar.dispatch("bootstrap-cargo", context=self.context) self.context.bootstrapped = True
def ensure_bootstrapped(self, target=None): if self.context.bootstrapped: return if not (path.exists(self.context.maven_dir)): print("Installing Maven") Registrar.dispatch("bootstrap-maven", context=self.context) if not (path.exists(self.context.m2repo_dir)): print("Preparing local Maven repository") Registrar.dispatch("bootstrap-m2repo", context=self.context) if not (path.exists(self.context.git_dir)): print("Fetching git repositories") Registrar.dispatch("bootstrap-git", context=self.context) self.context.bootstrapped = True
def build(self, target=None, release=False, dev=False, jobs=None, features=None, android=None, magicleap=None, no_package=False, verbose=False, very_verbose=False, debug_mozjs=False, params=None, with_debug_assertions=False, libsimpleservo=False, with_frame_pointer=False): opts = params or [] if android is None: android = self.config["build"]["android"] features = features or self.servo_features() if target and android: print("Please specify either --target or --android.") sys.exit(1) # https://github.com/servo/servo/issues/22069 if debug_mozjs and magicleap: print("Please specify either --debug-mozjs or --magicleap.") sys.exit(1) if android: target = self.config["android"]["target"] if not magicleap: features += ["native-bluetooth"] if magicleap and not target: target = "aarch64-linux-android" if target and not android and not magicleap: android = self.handle_android_target(target) target_path = base_path = self.get_target_dir() if android: target_path = path.join(target_path, "android") base_path = path.join(target_path, target) elif magicleap: target_path = path.join(target_path, "magicleap") base_path = path.join(target_path, target) release_path = path.join(base_path, "release", "servo") dev_path = path.join(base_path, "debug", "servo") release_exists = path.exists(release_path) dev_exists = path.exists(dev_path) if not (release or dev): if self.config["build"]["mode"] == "dev": dev = True elif self.config["build"]["mode"] == "release": release = True elif release_exists and not dev_exists: release = True elif dev_exists and not release_exists: dev = True else: print("Please specify either --dev (-d) for a development") print(" build, or --release (-r) for an optimized build.") sys.exit(1) if release and dev: print("Please specify either --dev or --release.") sys.exit(1) if release: opts += ["--release"] servo_path = release_path else: servo_path = dev_path if jobs is not None: opts += ["-j", jobs] if verbose: opts += ["-v"] if very_verbose: opts += ["-vv"] if target: if self.config["tools"]["use-rustup"]: # 'rustup target add' fails if the toolchain is not installed at all. self.call_rustup_run(["rustc", "--version"]) check_call(["rustup" + BIN_SUFFIX, "target", "add", "--toolchain", self.toolchain(), target]) opts += ["--target", target] env = self.build_env(target=target, is_build=True) self.ensure_bootstrapped(target=target) self.ensure_clobbered() self.add_manifest_path(opts, android, libsimpleservo) if debug_mozjs: features += ["debugmozjs"] if with_frame_pointer: env['RUSTFLAGS'] = env.get('RUSTFLAGS', "") + " -C force-frame-pointers=yes" features += ["profilemozjs"] if self.config["build"]["webgl-backtrace"]: features += ["webgl-backtrace"] if self.config["build"]["dom-backtrace"]: features += ["dom-backtrace"] if features: opts += ["--features", "%s" % ' '.join(features)] build_start = time() env["CARGO_TARGET_DIR"] = target_path if with_debug_assertions: env['RUSTFLAGS'] = env.get('RUSTFLAGS', "") + " -C debug_assertions" if sys.platform == "win32": env["CC"] = "clang-cl.exe" env["CXX"] = "clang-cl.exe" if android: if "ANDROID_NDK" not in env: print("Please set the ANDROID_NDK environment variable.") sys.exit(1) if "ANDROID_SDK" not in env: print("Please set the ANDROID_SDK environment variable.") sys.exit(1) android_platform = self.config["android"]["platform"] android_toolchain_name = self.config["android"]["toolchain_name"] android_toolchain_prefix = self.config["android"]["toolchain_prefix"] android_lib = self.config["android"]["lib"] android_arch = self.config["android"]["arch"] # Build OpenSSL for android env["OPENSSL_VERSION"] = "1.0.2k" make_cmd = ["make"] if jobs is not None: make_cmd += ["-j" + jobs] openssl_dir = path.join(target_path, target, "native", "openssl") if not path.exists(openssl_dir): os.makedirs(openssl_dir) shutil.copy(path.join(self.android_support_dir(), "openssl.makefile"), openssl_dir) shutil.copy(path.join(self.android_support_dir(), "openssl.sh"), openssl_dir) # Check if the NDK version is 12 if not os.path.isfile(path.join(env["ANDROID_NDK"], 'source.properties')): print("ANDROID_NDK should have file `source.properties`.") print("The environment variable ANDROID_NDK may be set at a wrong path.") sys.exit(1) with open(path.join(env["ANDROID_NDK"], 'source.properties')) as ndk_properties: lines = ndk_properties.readlines() if lines[1].split(' = ')[1].split('.')[0] != '12': print("Currently only support NDK 12.") sys.exit(1) env["RUST_TARGET"] = target env["ANDROID_TOOLCHAIN_NAME"] = android_toolchain_name with cd(openssl_dir): status = call( make_cmd + ["-f", "openssl.makefile"], env=env, verbose=verbose) if status: return status openssl_dir = path.join(openssl_dir, "openssl-{}".format(env["OPENSSL_VERSION"])) env['OPENSSL_LIB_DIR'] = openssl_dir env['OPENSSL_INCLUDE_DIR'] = path.join(openssl_dir, "include") env['OPENSSL_STATIC'] = 'TRUE' # Android builds also require having the gcc bits on the PATH and various INCLUDE # path munging if you do not want to install a standalone NDK. See: # https://dxr.mozilla.org/mozilla-central/source/build/autoconf/android.m4#139-161 os_type = platform.system().lower() if os_type not in ["linux", "darwin"]: raise Exception("Android cross builds are only supported on Linux and macOS.") cpu_type = platform.machine().lower() host_suffix = "unknown" if cpu_type in ["i386", "i486", "i686", "i768", "x86"]: host_suffix = "x86" elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]: host_suffix = "x86_64" host = os_type + "-" + host_suffix host_cc = env.get('HOST_CC') or _get_exec_path(["clang"]) or _get_exec_path(["gcc"]) host_cxx = env.get('HOST_CXX') or _get_exec_path(["clang++"]) or _get_exec_path(["g++"]) llvm_toolchain = path.join(env['ANDROID_NDK'], "toolchains", "llvm", "prebuilt", host) gcc_toolchain = path.join(env['ANDROID_NDK'], "toolchains", android_toolchain_prefix + "-4.9", "prebuilt", host) gcc_libs = path.join(gcc_toolchain, "lib", "gcc", android_toolchain_name, "4.9.x") env['PATH'] = (path.join(llvm_toolchain, "bin") + ':' + path.join(gcc_toolchain, "bin") + ':' + env['PATH']) env['ANDROID_SYSROOT'] = path.join(env['ANDROID_NDK'], "platforms", android_platform, "arch-" + android_arch) support_include = path.join(env['ANDROID_NDK'], "sources", "android", "support", "include") cxx_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", "llvm-libc++", "libcxx", "include") clang_include = path.join(llvm_toolchain, "lib64", "clang", "3.8", "include") sysroot_include = path.join(env['ANDROID_SYSROOT'], "usr", "include") env['HOST_CC'] = host_cc env['HOST_CXX'] = host_cxx env['HOST_CFLAGS'] = '' env['HOST_CXXFLAGS'] = '' env['CC'] = path.join(llvm_toolchain, "bin", "clang") env['CPP'] = path.join(llvm_toolchain, "bin", "clang") + " -E" env['CXX'] = path.join(llvm_toolchain, "bin", "clang++") env['ANDROID_TOOLCHAIN'] = gcc_toolchain env['GCC_TOOLCHAIN'] = gcc_toolchain gcc_toolchain_bin = path.join(gcc_toolchain, android_toolchain_name, "bin") env['AR'] = path.join(gcc_toolchain_bin, "ar") env['RANLIB'] = path.join(gcc_toolchain_bin, "ranlib") env['OBJCOPY'] = path.join(gcc_toolchain_bin, "objcopy") env['YASM'] = path.join(env['ANDROID_NDK'], 'prebuilt', host, 'bin', 'yasm') # A cheat-sheet for some of the build errors caused by getting the search path wrong... # # fatal error: 'limits' file not found # -- add -I cxx_include # unknown type name '__locale_t' (when running bindgen in mozjs_sys) # -- add -isystem sysroot_include # error: use of undeclared identifier 'UINTMAX_C' # -- add -D__STDC_CONSTANT_MACROS # # Also worth remembering: autoconf uses C for its configuration, # even for C++ builds, so the C flags need to line up with the C++ flags. env['CFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-isystem", sysroot_include, "-L" + gcc_libs]) env['CXXFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-I" + support_include, "-I" + cxx_include, "-I" + clang_include, "-isystem", sysroot_include, "-L" + gcc_libs, "-D__STDC_CONSTANT_MACROS", "-D__NDK_FPABI__="]) env["NDK_ANDROID_VERSION"] = android_platform.replace("android-", "") env['CPPFLAGS'] = ' '.join(["--sysroot", env['ANDROID_SYSROOT']]) env["CMAKE_ANDROID_ARCH_ABI"] = android_lib env["CMAKE_TOOLCHAIN_FILE"] = path.join(self.android_support_dir(), "toolchain.cmake") # Set output dir for gradle aar files aar_out_dir = self.android_aar_dir() if not os.path.exists(aar_out_dir): os.makedirs(aar_out_dir) env["AAR_OUT_DIR"] = aar_out_dir # GStreamer and its dependencies use pkg-config and this flag is required # to make it work in a cross-compilation context. env["PKG_CONFIG_ALLOW_CROSS"] = '1' # Build the name of the package containing all GStreamer dependencies # according to the build target. gst_lib = "gst-build-{}".format(self.config["android"]["lib"]) gst_lib_zip = "gstreamer-{}-1.14.3-20190201-081639.zip".format(self.config["android"]["lib"]) gst_dir = os.path.join(target_path, "gstreamer") gst_lib_path = os.path.join(gst_dir, gst_lib) pkg_config_path = os.path.join(gst_lib_path, "pkgconfig") env["PKG_CONFIG_PATH"] = pkg_config_path if not os.path.exists(gst_lib_path): # Download GStreamer dependencies if they have not already been downloaded # This bundle is generated with `libgstreamer_android_gen` # Follow these instructions to build and deploy new binaries # https://github.com/servo/libgstreamer_android_gen#build print("Downloading GStreamer dependencies") gst_url = "https://servo-deps.s3.amazonaws.com/gstreamer/%s" % gst_lib_zip print(gst_url) urllib.urlretrieve(gst_url, gst_lib_zip) zip_ref = zipfile.ZipFile(gst_lib_zip, "r") zip_ref.extractall(gst_dir) os.remove(gst_lib_zip) # Change pkgconfig info to make all GStreamer dependencies point # to the libgstreamer_android.so bundle. for each in os.listdir(pkg_config_path): if each.endswith('.pc'): print("Setting pkgconfig info for %s" % each) pc = os.path.join(pkg_config_path, each) expr = "s#libdir=.*#libdir=%s#g" % gst_lib_path subprocess.call(["perl", "-i", "-pe", expr, pc]) if magicleap: if platform.system() not in ["Darwin"]: raise Exception("Magic Leap builds are only supported on macOS. " "If you only wish to test if your code builds, " "run ./mach build -p libmlservo.") ml_sdk = env.get("MAGICLEAP_SDK") if not ml_sdk: raise Exception("Magic Leap builds need the MAGICLEAP_SDK environment variable") ml_support = path.join(self.get_top_dir(), "support", "magicleap") # We pretend to be an Android build env.setdefault("ANDROID_VERSION", "21") env.setdefault("ANDROID_NDK", env["MAGICLEAP_SDK"]) env.setdefault("ANDROID_NDK_VERSION", "16.0.0") env.setdefault("ANDROID_PLATFORM_DIR", path.join(env["MAGICLEAP_SDK"], "lumin")) env.setdefault("ANDROID_TOOLCHAIN_DIR", path.join(env["MAGICLEAP_SDK"], "tools", "toolchains")) env.setdefault("ANDROID_CLANG", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "clang")) # A random collection of search paths env.setdefault("STLPORT_LIBS", " ".join([ "-L" + path.join(env["MAGICLEAP_SDK"], "lumin", "stl", "libc++-lumin", "lib"), "-lc++" ])) env.setdefault("STLPORT_CPPFLAGS", " ".join([ "-I" + path.join(env["MAGICLEAP_SDK"], "lumin", "stl", "libc++-lumin", "include") ])) env.setdefault("CPPFLAGS", " ".join([ "--no-standard-includes", "--sysroot=" + env["ANDROID_PLATFORM_DIR"], "-I" + path.join(env["ANDROID_PLATFORM_DIR"], "usr", "include"), "-isystem" + path.join(env["ANDROID_TOOLCHAIN_DIR"], "lib64", "clang", "3.8", "include"), ])) env.setdefault("CFLAGS", " ".join([ env["CPPFLAGS"], "-L" + path.join(env["ANDROID_TOOLCHAIN_DIR"], "lib", "gcc", target, "4.9.x"), ])) env.setdefault("CXXFLAGS", " ".join([ # Sigh, Angle gets confused if there's another EGL around "-I./gfx/angle/checkout/include", env["STLPORT_CPPFLAGS"], env["CFLAGS"] ])) # The toolchain commands env.setdefault("AR", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-ar")) env.setdefault("AS", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-as")) env.setdefault("CC", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang")) env.setdefault("CPP", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang -E")) env.setdefault("CXX", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang++")) env.setdefault("LD", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-ld")) env.setdefault("OBJCOPY", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-objcopy")) env.setdefault("OBJDUMP", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-objdump")) env.setdefault("RANLIB", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-ranlib")) env.setdefault("STRIP", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-strip")) # Undo all of that when compiling build tools for the host env.setdefault("HOST_CFLAGS", "") env.setdefault("HOST_CXXFLAGS", "") env.setdefault("HOST_CC", "gcc") env.setdefault("HOST_CXX", "g++") env.setdefault("HOST_LD", "ld") # Some random build configurations env.setdefault("HARFBUZZ_SYS_NO_PKG_CONFIG", "1") env.setdefault("PKG_CONFIG_ALLOW_CROSS", "1") env.setdefault("CMAKE_TOOLCHAIN_FILE", path.join(ml_support, "toolchain.cmake")) # The Open SSL configuration env.setdefault("OPENSSL_DIR", path.join(target_path, target, "native", "openssl")) env.setdefault("OPENSSL_VERSION", "1.0.2k") env.setdefault("OPENSSL_STATIC", "1") # Override the linker set in .cargo/config env.setdefault("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", path.join(ml_support, "fake-ld.sh")) # Only build libmlservo opts += ["--package", "libmlservo"] # Download and build OpenSSL if necessary status = call(path.join(ml_support, "openssl.sh"), env=env, verbose=verbose) if status: return status if very_verbose: print (["Calling", "cargo", "build"] + opts) for key in env: print((key, env[key])) status = self.call_rustup_run(["cargo", "build"] + opts, env=env, verbose=verbose) elapsed = time() - build_start # Do some additional things if the build succeeded if status == 0: if android and not no_package: flavor = None if "googlevr" in features: flavor = "googlevr" elif "oculusvr" in features: flavor = "oculusvr" rv = Registrar.dispatch("package", context=self.context, release=release, dev=dev, target=target, flavor=flavor) if rv: return rv if sys.platform == "win32": servo_exe_dir = path.join(base_path, "debug" if dev else "release") msvc_x64 = "64" if "x86_64" in (target or host_triple()) else "" # on msvc builds, use editbin to change the subsystem to windows, but only # on release builds -- on debug builds, it hides log output if not dev: call(["editbin", "/nologo", "/subsystem:windows", path.join(servo_exe_dir, "servo.exe")], verbose=verbose) # on msvc, we need to copy in some DLLs in to the servo.exe dir for ssl_lib in ["libcryptoMD.dll", "libsslMD.dll"]: shutil.copy(path.join(env['OPENSSL_LIB_DIR'], "../bin" + msvc_x64, ssl_lib), servo_exe_dir) # Search for the generated nspr4.dll build_path = path.join(servo_exe_dir, "build") nspr4 = "nspr4.dll" nspr4_path = None for root, dirs, files in os.walk(build_path): if nspr4 in files: nspr4_path = path.join(root, nspr4) break if nspr4_path is None: print("WARNING: could not find nspr4.dll") else: shutil.copy(nspr4_path, servo_exe_dir) # copy needed gstreamer DLLs in to servo.exe dir gst_x64 = "X86_64" if msvc_x64 == "64" else "X86" gst_root = "" gst_default_path = path.join("C:\\gstreamer\\1.0", gst_x64) gst_env = "GSTREAMER_1_0_ROOT_" + gst_x64 if os.path.exists(path.join(gst_default_path, "bin", "libffi-7.dll")) or \ os.path.exists(path.join(gst_default_path, "bin", "ffi-7.dll")): gst_root = gst_default_path elif os.environ.get(gst_env) is not None: gst_root = os.environ.get(gst_env) else: print("Could not found GStreamer installation directory.") status = 1 gst_dlls = [ ["libffi-7.dll", "ffi-7.dll"], ["libgio-2.0-0.dll", "gio-2.0-0.dll"], ["libglib-2.0-0.dll", "glib-2.0-0.dll"], ["libgmodule-2.0-0.dll", "gmodule-2.0-0.dll"], ["libgobject-2.0-0.dll", "gobject-2.0-0.dll"], ["libgstapp-1.0-0.dll", "gstapp-1.0-0.dll"], ["libgstaudio-1.0-0.dll", "gstaudio-1.0-0.dll"], ["libgstbase-1.0-0.dll", "gstbase-1.0-0.dll"], ["libgstgl-1.0-0.dll", "gstgl-1.0-0.dll"], ["libgstpbutils-1.0-0.dll", "gstpbutils-1.0-0.dll"], ["libgstplayer-1.0-0.dll", "gstplayer-1.0-0.dll"], ["libgstreamer-1.0-0.dll", "gstreamer-1.0-0.dll"], ["libgstrtp-1.0-0.dll", "gstrtp-1.0-0.dll"], ["libgstsdp-1.0-0.dll", "gstsdp-1.0-0.dll"], ["libgsttag-1.0-0.dll", "gsttag-1.0-0.dll"], ["libgstvideo-1.0-0.dll", "gstvideo-1.0-0.dll"], ["libgstwebrtc-1.0-0.dll", "gstwebrtc-1.0-0.dll"], ["libintl-8.dll", "intl-8.dll"], ["liborc-0.4-0.dll", "orc-0.4-0.dll"], ["libwinpthread-1.dll", "winpthread-1.dll"], ["libz.dll", "libz-1.dll", "z-1.dll"] ] if gst_root: for gst_lib in gst_dlls: if isinstance(gst_lib, str): gst_lib = [gst_lib] for lib in gst_lib: try: shutil.copy(path.join(gst_root, "bin", lib), servo_exe_dir) break except: pass else: print("ERROR: could not find required GStreamer DLL: " + str(gst_lib)) sys.exit(1) # copy some MSVC DLLs to servo.exe dir msvc_redist_dir = None vs_platform = os.environ.get("PLATFORM", "").lower() vc_dir = os.environ.get("VCINSTALLDIR", "") vs_version = os.environ.get("VisualStudioVersion", "") msvc_deps = [ "api-ms-win-crt-runtime-l1-1-0.dll", "msvcp140.dll", "vcruntime140.dll", ] # Check if it's Visual C++ Build Tools or Visual Studio 2015 vs14_vcvars = path.join(vc_dir, "vcvarsall.bat") is_vs14 = True if os.path.isfile(vs14_vcvars) or vs_version == "14.0" else False if is_vs14: msvc_redist_dir = path.join(vc_dir, "redist", vs_platform, "Microsoft.VC140.CRT") elif vs_version == "15.0": redist_dir = path.join(os.environ.get("VCINSTALLDIR", ""), "Redist", "MSVC") if os.path.isdir(redist_dir): for p in os.listdir(redist_dir)[::-1]: redist_path = path.join(redist_dir, p) for v in ["VC141", "VC150"]: # there are two possible paths # `x64\Microsoft.VC*.CRT` or `onecore\x64\Microsoft.VC*.CRT` redist1 = path.join(redist_path, vs_platform, "Microsoft.{}.CRT".format(v)) redist2 = path.join(redist_path, "onecore", vs_platform, "Microsoft.{}.CRT".format(v)) if os.path.isdir(redist1): msvc_redist_dir = redist1 break elif os.path.isdir(redist2): msvc_redist_dir = redist2 break if msvc_redist_dir: break if msvc_redist_dir: redist_dirs = [ msvc_redist_dir, path.join(os.environ["WindowsSdkDir"], "Redist", "ucrt", "DLLs", vs_platform), ] for msvc_dll in msvc_deps: dll_found = False for dll_dir in redist_dirs: dll = path.join(dll_dir, msvc_dll) servo_dir_dll = path.join(servo_exe_dir, msvc_dll) if os.path.isfile(dll): if os.path.isfile(servo_dir_dll): # avoid permission denied error when overwrite dll in servo build directory os.chmod(servo_dir_dll, stat.S_IWUSR) shutil.copy(dll, servo_exe_dir) dll_found = True break if not dll_found: print("DLL file `{}` not found!".format(msvc_dll)) status = 1 elif sys.platform == "darwin": # On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools # like Instruments.app. try: import Cocoa icon_path = path.join(self.get_top_dir(), "resources", "servo.png") icon = Cocoa.NSImage.alloc().initWithContentsOfFile_(icon_path) if icon is not None: Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(icon, servo_path, 0) except ImportError: pass # Generate Desktop Notification if elapsed-time > some threshold value notify_build_done(self.config, elapsed, status == 0) print("Build %s in %s" % ("Completed" if status == 0 else "FAILED", format_duration(elapsed))) return status
def build(self, release=False, dev=False, jobs=None, params=None, media_stack=None, no_package=False, verbose=False, very_verbose=False, target=None, android=False, magicleap=False, libsimpleservo=False, features=None, uwp=False, win_arm64=False, **kwargs): # Force the UWP-enabled target if the convenience UWP flags are passed. if uwp and not target: if win_arm64: target = 'aarch64-uwp-windows-msvc' else: target = 'x86_64-uwp-windows-msvc' opts = params or [] features = features or [] target, android = self.pick_target_triple(target, android, magicleap) # Infer UWP build if only provided a target. if not uwp: uwp = target and 'uwp' in target features += self.pick_media_stack(media_stack, target) target_path = base_path = self.get_target_dir() if android: target_path = path.join(target_path, "android") base_path = path.join(target_path, target) elif magicleap: target_path = path.join(target_path, "magicleap") base_path = path.join(target_path, target) release_path = path.join(base_path, "release", "servo") dev_path = path.join(base_path, "debug", "servo") release_exists = path.exists(release_path) dev_exists = path.exists(dev_path) if not (release or dev): if self.config["build"]["mode"] == "dev": dev = True elif self.config["build"]["mode"] == "release": release = True elif release_exists and not dev_exists: release = True elif dev_exists and not release_exists: dev = True else: print("Please specify either --dev (-d) for a development") print(" build, or --release (-r) for an optimized build.") sys.exit(1) if release and dev: print("Please specify either --dev or --release.") sys.exit(1) if release: opts += ["--release"] servo_path = release_path else: servo_path = dev_path if jobs is not None: opts += ["-j", jobs] if verbose: opts += ["-v"] if very_verbose: opts += ["-vv"] env = self.build_env(target=target, is_build=True, uwp=uwp, features=features) self.ensure_bootstrapped(target=target) self.ensure_clobbered() build_start = time() env["CARGO_TARGET_DIR"] = target_path host = host_triple() target_triple = target or host_triple() if 'apple-darwin' in host and target_triple == host: if 'CXXFLAGS' not in env: env['CXXFLAGS'] = '' env["CXXFLAGS"] += "-mmacosx-version-min=10.10" if 'windows' in host: vs_dirs = self.vs_dirs() if host != target_triple and 'windows' in target_triple: if os.environ.get('VisualStudioVersion') or os.environ.get( 'VCINSTALLDIR'): print( "Can't cross-compile for Windows inside of a Visual Studio shell.\n" "Please run `python mach build [arguments]` to bypass automatic " "Visual Studio shell, and make sure the VisualStudioVersion and " "VCINSTALLDIR environment variables are not set.") sys.exit(1) vcinstalldir = vs_dirs['vcdir'] if not os.path.exists(vcinstalldir): print("Can't find Visual C++ %s installation at %s." % (vs_dirs['vs_version'], vcinstalldir)) sys.exit(1) env['PKG_CONFIG_ALLOW_CROSS'] = "1" if uwp: # Ensure libstd is ready for the new UWP target. check_call(["rustup", "component", "add", "rust-src"]) env['RUST_SYSROOT'] = path.expanduser('~\\.xargo') # Don't try and build a desktop port. libsimpleservo = True arches = { "aarch64": { "angle": "arm64", "gst": "ARM64", "gst_root": "arm64", }, "x86_64": { "angle": "x64", "gst": "X86_64", "gst_root": "x64", }, } arch = arches.get(target_triple.split('-')[0]) if not arch: print("Unsupported UWP target.") sys.exit(1) # Ensure that the NuGet ANGLE package containing libEGL is accessible # to the Rust linker. append_to_path_env(angle_root(target_triple, env), env, "LIB") # Don't want to mix non-UWP libraries with vendored UWP libraries. if "gstreamer" in env['LIB']: print( "Found existing GStreamer library path in LIB. Please remove it." ) sys.exit(1) # Override any existing GStreamer installation with the vendored libraries. env["GSTREAMER_1_0_ROOT_" + arch['gst']] = path.join( self.msvc_package_dir("gstreamer-uwp"), arch['gst_root']) env["PKG_CONFIG_PATH"] = path.join( self.msvc_package_dir("gstreamer-uwp"), arch['gst_root'], "lib", "pkgconfig") if 'windows' in host: process = subprocess.Popen( '("%s" %s > nul) && "python" -c "import os; print(repr(os.environ))"' % (os.path.join(vs_dirs['vcdir'], "Auxiliary", "Build", "vcvarsall.bat"), "x64"), stdout=subprocess.PIPE, shell=True) stdout, stderr = process.communicate() exitcode = process.wait() encoding = locale.getpreferredencoding( ) # See https://stackoverflow.com/a/9228117 if exitcode == 0: decoded = stdout.decode(encoding) if decoded.startswith("environ("): decoded = decoded.strip()[8:-1] os.environ.update(eval(decoded)) else: print("Failed to run vcvarsall. stderr:") print(stderr.decode(encoding)) exit(1) # Ensure that GStreamer libraries are accessible when linking. if 'windows' in target_triple: gst_root = gstreamer_root(target_triple, env) if gst_root: append_to_path_env(os.path.join(gst_root, "lib"), env, "LIB") if android: if "ANDROID_NDK" not in env: print("Please set the ANDROID_NDK environment variable.") sys.exit(1) if "ANDROID_SDK" not in env: print("Please set the ANDROID_SDK environment variable.") sys.exit(1) android_platform = self.config["android"]["platform"] android_toolchain_name = self.config["android"]["toolchain_name"] android_toolchain_prefix = self.config["android"][ "toolchain_prefix"] android_lib = self.config["android"]["lib"] android_arch = self.config["android"]["arch"] # Build OpenSSL for android env["OPENSSL_VERSION"] = "1.1.1d" make_cmd = ["make"] if jobs is not None: make_cmd += ["-j" + jobs] openssl_dir = path.join(target_path, target, "native", "openssl") if not path.exists(openssl_dir): os.makedirs(openssl_dir) shutil.copy( path.join(self.android_support_dir(), "openssl.makefile"), openssl_dir) shutil.copy(path.join(self.android_support_dir(), "openssl.sh"), openssl_dir) # Check if the NDK version is 15 if not os.path.isfile( path.join(env["ANDROID_NDK"], 'source.properties')): print("ANDROID_NDK should have file `source.properties`.") print( "The environment variable ANDROID_NDK may be set at a wrong path." ) sys.exit(1) with open(path.join(env["ANDROID_NDK"], 'source.properties')) as ndk_properties: lines = ndk_properties.readlines() if lines[1].split(' = ')[1].split('.')[0] != '15': print( "Currently only support NDK 15. Please re-run `./mach bootstrap-android`." ) sys.exit(1) env["RUST_TARGET"] = target with cd(openssl_dir): status = call(make_cmd + ["-f", "openssl.makefile"], env=env, verbose=verbose) if status: return status openssl_dir = path.join( openssl_dir, "openssl-{}".format(env["OPENSSL_VERSION"])) env['OPENSSL_LIB_DIR'] = openssl_dir env['OPENSSL_INCLUDE_DIR'] = path.join(openssl_dir, "include") env['OPENSSL_STATIC'] = 'TRUE' # Android builds also require having the gcc bits on the PATH and various INCLUDE # path munging if you do not want to install a standalone NDK. See: # https://dxr.mozilla.org/mozilla-central/source/build/autoconf/android.m4#139-161 os_type = platform.system().lower() if os_type not in ["linux", "darwin"]: raise Exception( "Android cross builds are only supported on Linux and macOS." ) cpu_type = platform.machine().lower() host_suffix = "unknown" if cpu_type in ["i386", "i486", "i686", "i768", "x86"]: host_suffix = "x86" elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]: host_suffix = "x86_64" host = os_type + "-" + host_suffix host_cc = env.get('HOST_CC') or _get_exec_path( ["clang"]) or _get_exec_path(["gcc"]) host_cxx = env.get('HOST_CXX') or _get_exec_path( ["clang++"]) or _get_exec_path(["g++"]) llvm_toolchain = path.join(env['ANDROID_NDK'], "toolchains", "llvm", "prebuilt", host) gcc_toolchain = path.join(env['ANDROID_NDK'], "toolchains", android_toolchain_prefix + "-4.9", "prebuilt", host) gcc_libs = path.join(gcc_toolchain, "lib", "gcc", android_toolchain_name, "4.9.x") env['PATH'] = (path.join(llvm_toolchain, "bin") + ':' + env['PATH']) env['ANDROID_SYSROOT'] = path.join(env['ANDROID_NDK'], "sysroot") support_include = path.join(env['ANDROID_NDK'], "sources", "android", "support", "include") cpufeatures_include = path.join(env['ANDROID_NDK'], "sources", "android", "cpufeatures") cxx_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", "llvm-libc++", "include") clang_include = path.join(llvm_toolchain, "lib64", "clang", "3.8", "include") cxxabi_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", "llvm-libc++abi", "include") sysroot_include = path.join(env['ANDROID_SYSROOT'], "usr", "include") arch_include = path.join(sysroot_include, android_toolchain_name) android_platform_dir = path.join(env['ANDROID_NDK'], "platforms", android_platform, "arch-" + android_arch) arch_libs = path.join(android_platform_dir, "usr", "lib") clang_include = path.join(llvm_toolchain, "lib64", "clang", "5.0", "include") android_api = android_platform.replace('android-', '') env['HOST_CC'] = host_cc env['HOST_CXX'] = host_cxx env['HOST_CFLAGS'] = '' env['HOST_CXXFLAGS'] = '' env['CC'] = path.join(llvm_toolchain, "bin", "clang") env['CPP'] = path.join(llvm_toolchain, "bin", "clang") + " -E" env['CXX'] = path.join(llvm_toolchain, "bin", "clang++") env['ANDROID_TOOLCHAIN'] = gcc_toolchain env['ANDROID_TOOLCHAIN_DIR'] = gcc_toolchain env['ANDROID_VERSION'] = android_api env['ANDROID_PLATFORM_DIR'] = android_platform_dir env['GCC_TOOLCHAIN'] = gcc_toolchain gcc_toolchain_bin = path.join(gcc_toolchain, android_toolchain_name, "bin") env['AR'] = path.join(gcc_toolchain_bin, "ar") env['RANLIB'] = path.join(gcc_toolchain_bin, "ranlib") env['OBJCOPY'] = path.join(gcc_toolchain_bin, "objcopy") env['YASM'] = path.join(env['ANDROID_NDK'], 'prebuilt', host, 'bin', 'yasm') # A cheat-sheet for some of the build errors caused by getting the search path wrong... # # fatal error: 'limits' file not found # -- add -I cxx_include # unknown type name '__locale_t' (when running bindgen in mozjs_sys) # -- add -isystem sysroot_include # error: use of undeclared identifier 'UINTMAX_C' # -- add -D__STDC_CONSTANT_MACROS # # Also worth remembering: autoconf uses C for its configuration, # even for C++ builds, so the C flags need to line up with the C++ flags. env['CFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-isystem", sysroot_include, "-I" + arch_include, "-B" + arch_libs, "-L" + arch_libs, "-D__ANDROID_API__=" + android_api, ]) env['CXXFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-I" + cpufeatures_include, "-I" + cxx_include, "-I" + clang_include, "-isystem", sysroot_include, "-I" + cxxabi_include, "-I" + clang_include, "-I" + arch_include, "-I" + support_include, "-L" + gcc_libs, "-B" + arch_libs, "-L" + arch_libs, "-D__ANDROID_API__=" + android_api, "-D__STDC_CONSTANT_MACROS", "-D__NDK_FPABI__=", ]) env['CPPFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "-I" + arch_include, ]) env["NDK_ANDROID_VERSION"] = android_api env["ANDROID_ABI"] = android_lib env["ANDROID_PLATFORM"] = android_platform env["NDK_CMAKE_TOOLCHAIN_FILE"] = path.join( env['ANDROID_NDK'], "build", "cmake", "android.toolchain.cmake") env["CMAKE_TOOLCHAIN_FILE"] = path.join(self.android_support_dir(), "toolchain.cmake") # Set output dir for gradle aar files aar_out_dir = self.android_aar_dir() if not os.path.exists(aar_out_dir): os.makedirs(aar_out_dir) env["AAR_OUT_DIR"] = aar_out_dir # GStreamer and its dependencies use pkg-config and this flag is required # to make it work in a cross-compilation context. env["PKG_CONFIG_ALLOW_CROSS"] = '1' # Build the name of the package containing all GStreamer dependencies # according to the build target. gst_lib = "gst-build-{}".format(self.config["android"]["lib"]) gst_lib_zip = "gstreamer-{}-1.16.0-20190517-095630.zip".format( self.config["android"]["lib"]) gst_dir = os.path.join(target_path, "gstreamer") gst_lib_path = os.path.join(gst_dir, gst_lib) pkg_config_path = os.path.join(gst_lib_path, "pkgconfig") env["PKG_CONFIG_PATH"] = pkg_config_path if not os.path.exists(gst_lib_path): # Download GStreamer dependencies if they have not already been downloaded # This bundle is generated with `libgstreamer_android_gen` # Follow these instructions to build and deploy new binaries # https://github.com/servo/libgstreamer_android_gen#build print("Downloading GStreamer dependencies") gst_url = "https://servo-deps.s3.amazonaws.com/gstreamer/%s" % gst_lib_zip print(gst_url) urllib.request.urlretrieve(gst_url, gst_lib_zip) zip_ref = zipfile.ZipFile(gst_lib_zip, "r") zip_ref.extractall(gst_dir) os.remove(gst_lib_zip) # Change pkgconfig info to make all GStreamer dependencies point # to the libgstreamer_android.so bundle. for each in os.listdir(pkg_config_path): if each.endswith('.pc'): print("Setting pkgconfig info for %s" % each) pc = os.path.join(pkg_config_path, each) expr = "s#libdir=.*#libdir=%s#g" % gst_lib_path subprocess.call(["perl", "-i", "-pe", expr, pc]) if magicleap: if platform.system() not in ["Darwin"]: raise Exception( "Magic Leap builds are only supported on macOS. " "If you only wish to test if your code builds, " "run ./mach build -p libmlservo.") ml_sdk = env.get("MAGICLEAP_SDK") if not ml_sdk: raise Exception( "Magic Leap builds need the MAGICLEAP_SDK environment variable" ) if not os.path.exists(ml_sdk): raise Exception( "Path specified by MAGICLEAP_SDK does not exist.") ml_support = path.join(self.get_top_dir(), "support", "magicleap") # We pretend to be an Android build env.setdefault("ANDROID_VERSION", "21") env.setdefault("ANDROID_NDK", env["MAGICLEAP_SDK"]) env.setdefault("ANDROID_NDK_VERSION", "16.0.0") env.setdefault("ANDROID_PLATFORM_DIR", path.join(env["MAGICLEAP_SDK"], "lumin")) env.setdefault( "ANDROID_TOOLCHAIN_DIR", path.join(env["MAGICLEAP_SDK"], "tools", "toolchains")) env.setdefault( "ANDROID_CLANG", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "clang")) # A random collection of search paths env.setdefault( "STLPORT_LIBS", " ".join([ "-L" + path.join(env["MAGICLEAP_SDK"], "lumin", "stl", "libc++-lumin", "lib"), "-lc++" ])) env.setdefault( "STLPORT_CPPFLAGS", " ".join([ "-I" + path.join(env["MAGICLEAP_SDK"], "lumin", "stl", "libc++-lumin", "include") ])) env.setdefault( "CPPFLAGS", " ".join([ "--no-standard-includes", "--sysroot=" + env["ANDROID_PLATFORM_DIR"], "-I" + path.join(env["ANDROID_PLATFORM_DIR"], "usr", "include"), "-isystem" + path.join(env["ANDROID_TOOLCHAIN_DIR"], "lib64", "clang", "3.8", "include"), ])) env.setdefault( "CFLAGS", " ".join([ env["CPPFLAGS"], "-L" + path.join(env["ANDROID_TOOLCHAIN_DIR"], "lib", "gcc", target, "4.9.x"), ])) env.setdefault( "CXXFLAGS", " ".join([ # Sigh, Angle gets confused if there's another EGL around "-I./gfx/angle/checkout/include", env["STLPORT_CPPFLAGS"], env["CFLAGS"] ])) # The toolchain commands env.setdefault( "AR", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-ar")) env.setdefault( "AS", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang")) env.setdefault( "CC", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang")) env.setdefault( "CPP", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang -E")) env.setdefault( "CXX", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-clang++")) env.setdefault( "LD", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-ld")) env.setdefault( "OBJCOPY", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-objcopy")) env.setdefault( "OBJDUMP", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-objdump")) env.setdefault( "RANLIB", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-ranlib")) env.setdefault( "STRIP", path.join(env["ANDROID_TOOLCHAIN_DIR"], "bin", "aarch64-linux-android-strip")) # Undo all of that when compiling build tools for the host env.setdefault("HOST_CFLAGS", "") env.setdefault("HOST_CXXFLAGS", "") env.setdefault("HOST_CC", "/usr/local/opt/llvm/bin/clang") env.setdefault("HOST_CXX", "/usr/local/opt/llvm/bin/clang++") env.setdefault("HOST_LD", "ld") # Some random build configurations env.setdefault("HARFBUZZ_SYS_NO_PKG_CONFIG", "1") env.setdefault("PKG_CONFIG_ALLOW_CROSS", "1") env.setdefault("CMAKE_TOOLCHAIN_FILE", path.join(ml_support, "toolchain.cmake")) env.setdefault("_LIBCPP_INLINE_VISIBILITY", "__attribute__((__always_inline__))") # The Open SSL configuration env.setdefault("OPENSSL_DIR", path.join(target_path, target, "native", "openssl")) env.setdefault("OPENSSL_VERSION", "1.1.1d") env.setdefault("OPENSSL_STATIC", "1") # GStreamer configuration env.setdefault( "GSTREAMER_DIR", path.join(target_path, target, "native", "gstreamer-1.16.0")) env.setdefault( "GSTREAMER_URL", "https://servo-deps.s3.amazonaws.com/gstreamer/gstreamer-magicleap-1.16.0-20190823-104505.tgz" ) env.setdefault( "PKG_CONFIG_PATH", path.join(env["GSTREAMER_DIR"], "system", "lib64", "pkgconfig")) # Override the linker set in .cargo/config env.setdefault("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", path.join(ml_support, "fake-ld.sh")) # Only build libmlservo opts += ["--package", "libmlservo"] # Download and build OpenSSL if necessary status = call(path.join(ml_support, "openssl.sh"), env=env, verbose=verbose) if status: return status # Download prebuilt Gstreamer if necessary if not os.path.exists(path.join(env["GSTREAMER_DIR"], "system")): if not os.path.exists(env["GSTREAMER_DIR"] + ".tgz"): check_call([ 'curl', '-L', '-f', '-o', env["GSTREAMER_DIR"] + ".tgz", env["GSTREAMER_URL"], ]) check_call([ 'mkdir', '-p', env["GSTREAMER_DIR"], ]) check_call([ 'tar', 'xzf', env["GSTREAMER_DIR"] + ".tgz", '-C', env["GSTREAMER_DIR"], ]) # https://internals.rust-lang.org/t/exploring-crate-graph-build-times-with-cargo-build-ztimings/10975 # Prepend so that e.g. `-Ztimings` (which means `-Ztimings=info,html`) # given on the command line can override it opts = ["-Ztimings=info"] + opts if very_verbose: print(["Calling", "cargo", "build"] + opts) for key in env: print((key, env[key])) if sys.platform == "win32": env.setdefault("CC", "clang-cl.exe") env.setdefault("CXX", "clang-cl.exe") if uwp: env.setdefault("CFLAGS", "") env.setdefault("CXXFLAGS", "") env["CFLAGS"] += " -DWINAPI_FAMILY=WINAPI_FAMILY_APP" env["CXXFLAGS"] += " -DWINAPI_FAMILY=WINAPI_FAMILY_APP" else: env.setdefault("CC", "clang") env.setdefault("CXX", "clang++") status = self.run_cargo_build_like_command( "build", opts, env=env, verbose=verbose, target=target, android=android, magicleap=magicleap, libsimpleservo=libsimpleservo, uwp=uwp, features=features, **kwargs) elapsed = time() - build_start # Do some additional things if the build succeeded if status == 0: if android and not no_package: flavor = None if "googlevr" in features: flavor = "googlevr" elif "oculusvr" in features: flavor = "oculusvr" rv = Registrar.dispatch("package", context=self.context, release=release, dev=dev, target=target, flavor=flavor) if rv: return rv if sys.platform == "win32": servo_exe_dir = os.path.dirname( self.get_binary_path(release, dev, target=target, simpleservo=libsimpleservo)) assert os.path.exists(servo_exe_dir) # on msvc builds, use editbin to change the subsystem to windows, but only # on release builds -- on debug builds, it hides log output if not dev and not libsimpleservo: call([ "editbin", "/nologo", "/subsystem:windows", path.join(servo_exe_dir, "servo.exe") ], verbose=verbose) # on msvc, we need to copy in some DLLs in to the servo.exe dir and the directory for unit tests. for ssl_lib in ["libssl.dll", "libcrypto.dll"]: ssl_path = path.join(env['OPENSSL_LIB_DIR'], "../bin", ssl_lib) shutil.copy(ssl_path, servo_exe_dir) shutil.copy(ssl_path, path.join(servo_exe_dir, "deps")) build_path = path.join(servo_exe_dir, "build") assert os.path.exists(build_path) def package_generated_shared_libraries(libs, build_path, servo_exe_dir): for root, dirs, files in os.walk(build_path): remaining_libs = list(libs) for lib in libs: if lib in files: shutil.copy(path.join(root, lib), servo_exe_dir) remaining_libs.remove(lib) continue libs = remaining_libs if not libs: return True for lib in libs: print("WARNING: could not find " + lib) # UWP build has its own ANGLE library that it packages. if not uwp: print("Packaging EGL DLLs") egl_libs = ["libEGL.dll", "libGLESv2.dll"] if not package_generated_shared_libraries( egl_libs, build_path, servo_exe_dir): status = 1 # copy needed gstreamer DLLs in to servo.exe dir print("Packaging gstreamer DLLs") if not package_gstreamer_dlls(env, servo_exe_dir, target_triple, uwp): status = 1 # UWP app packaging already bundles all required DLLs for us. print("Packaging MSVC DLLs") if not package_msvc_dlls(servo_exe_dir, target_triple, vs_dirs['vcdir'], vs_dirs['vs_version']): status = 1 elif sys.platform == "darwin": # On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools # like Instruments.app. try: import Cocoa icon_path = path.join(self.get_top_dir(), "resources", "servo_1024.png") icon = Cocoa.NSImage.alloc().initWithContentsOfFile_( icon_path) if icon is not None: Cocoa.NSWorkspace.sharedWorkspace( ).setIcon_forFile_options_(icon, servo_path, 0) except ImportError: pass # Generate Desktop Notification if elapsed-time > some threshold value notify_build_done(self.config, elapsed, status == 0) print("Build %s in %s" % ("Completed" if status == 0 else "FAILED", format_duration(elapsed))) return status
def build(self, target=None, release=False, dev=False, jobs=None, features=None, android=None, no_package=False, verbose=False, very_verbose=False, debug_mozjs=False, params=None, with_debug_assertions=False): opts = params or [] if android is None: android = self.config["build"]["android"] features = features or self.servo_features() base_path = self.get_target_dir() release_path = path.join(base_path, "release", "servo") dev_path = path.join(base_path, "debug", "servo") release_exists = path.exists(release_path) dev_exists = path.exists(dev_path) if not (release or dev): if self.config["build"]["mode"] == "dev": dev = True elif self.config["build"]["mode"] == "release": release = True elif release_exists and not dev_exists: release = True elif dev_exists and not release_exists: dev = True else: print("Please specify either --dev (-d) for a development") print(" build, or --release (-r) for an optimized build.") sys.exit(1) if release and dev: print("Please specify either --dev or --release.") sys.exit(1) if target and android: print("Please specify either --target or --android.") sys.exit(1) if release: opts += ["--release"] servo_path = release_path else: servo_path = dev_path if jobs is not None: opts += ["-j", jobs] if verbose: opts += ["-v"] if very_verbose: opts += ["-vv"] if android: target = self.config["android"]["target"] if target: if self.config["tools"]["use-rustup"]: # 'rustup target add' fails if the toolchain is not installed at all. self.call_rustup_run(["rustc", "--version"]) check_call(["rustup" + BIN_SUFFIX, "target", "add", "--toolchain", self.toolchain(), target]) opts += ["--target", target] if not android: android = self.handle_android_target(target) self.ensure_bootstrapped(target=target) self.ensure_clobbered() if debug_mozjs: features += ["debugmozjs"] if features: opts += ["--features", "%s" % ' '.join(features)] build_start = time() env = self.build_env(target=target, is_build=True) if with_debug_assertions: env['RUSTFLAGS'] = env.get('RUSTFLAGS', "") + " -C debug_assertions" if android: if "ANDROID_NDK" not in env: print("Please set the ANDROID_NDK environment variable.") sys.exit(1) if "ANDROID_SDK" not in env: print("Please set the ANDROID_SDK environment variable.") sys.exit(1) android_platform = self.config["android"]["platform"] android_toolchain_name = self.config["android"]["toolchain_name"] android_toolchain_prefix = self.config["android"]["toolchain_prefix"] android_lib = self.config["android"]["lib"] android_arch = self.config["android"]["arch"] # Build OpenSSL for android env["OPENSSL_VERSION"] = "1.0.2k" make_cmd = ["make"] if jobs is not None: make_cmd += ["-j" + jobs] android_dir = self.android_build_dir(dev) openssl_dir = path.join(android_dir, "native", "openssl") if not path.exists(openssl_dir): os.makedirs(openssl_dir) shutil.copy(path.join(self.android_support_dir(), "openssl.makefile"), openssl_dir) shutil.copy(path.join(self.android_support_dir(), "openssl.sh"), openssl_dir) # Check if the NDK version is 12 if not os.path.isfile(path.join(env["ANDROID_NDK"], 'source.properties')): print("ANDROID_NDK should have file `source.properties`.") print("The environment variable ANDROID_NDK may be set at a wrong path.") sys.exit(1) with open(path.join(env["ANDROID_NDK"], 'source.properties')) as ndk_properties: lines = ndk_properties.readlines() if lines[1].split(' = ')[1].split('.')[0] != '12': print("Currently only support NDK 12.") sys.exit(1) env["RUST_TARGET"] = target env["ANDROID_TOOLCHAIN_NAME"] = android_toolchain_name with cd(openssl_dir): status = call( make_cmd + ["-f", "openssl.makefile"], env=env, verbose=verbose) if status: return status openssl_dir = path.join(openssl_dir, "openssl-{}".format(env["OPENSSL_VERSION"])) env['OPENSSL_LIB_DIR'] = openssl_dir env['OPENSSL_INCLUDE_DIR'] = path.join(openssl_dir, "include") env['OPENSSL_STATIC'] = 'TRUE' # Android builds also require having the gcc bits on the PATH and various INCLUDE # path munging if you do not want to install a standalone NDK. See: # https://dxr.mozilla.org/mozilla-central/source/build/autoconf/android.m4#139-161 os_type = platform.system().lower() if os_type not in ["linux", "darwin"]: raise Exception("Android cross builds are only supported on Linux and macOS.") cpu_type = platform.machine().lower() host_suffix = "unknown" if cpu_type in ["i386", "i486", "i686", "i768", "x86"]: host_suffix = "x86" elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]: host_suffix = "x86_64" host = os_type + "-" + host_suffix host_cc = env.get('HOST_CC') or _get_exec_path(["clang"]) or _get_exec_path(["gcc"]) host_cxx = env.get('HOST_CXX') or _get_exec_path(["clang++"]) or _get_exec_path(["g++"]) llvm_toolchain = path.join(env['ANDROID_NDK'], "toolchains", "llvm", "prebuilt", host) gcc_toolchain = path.join(env['ANDROID_NDK'], "toolchains", android_toolchain_prefix + "-4.9", "prebuilt", host) gcc_libs = path.join(gcc_toolchain, "lib", "gcc", android_toolchain_name, "4.9.x") env['PATH'] = (path.join(llvm_toolchain, "bin") + ':' + path.join(gcc_toolchain, "bin") + ':' + env['PATH']) env['ANDROID_SYSROOT'] = path.join(env['ANDROID_NDK'], "platforms", android_platform, "arch-" + android_arch) support_include = path.join(env['ANDROID_NDK'], "sources", "android", "support", "include") cxx_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", "llvm-libc++", "libcxx", "include") sysroot_include = path.join(env['ANDROID_SYSROOT'], "usr", "include") env['HOST_CC'] = host_cc env['HOST_CXX'] = host_cxx env['HOST_CFLAGS'] = '' env['HOST_CXXFLAGS'] = '' env['CC'] = path.join(llvm_toolchain, "bin", "clang") env['CPP'] = path.join(llvm_toolchain, "bin", "clang") + " -E" env['CXX'] = path.join(llvm_toolchain, "bin", "clang++") env['ANDROID_TOOLCHAIN'] = gcc_toolchain env['GCC_TOOLCHAIN'] = gcc_toolchain gcc_toolchain_bin = path.join(gcc_toolchain, android_toolchain_name, "bin") env['AR'] = path.join(gcc_toolchain_bin, "ar") env['RANLIB'] = path.join(gcc_toolchain_bin, "ranlib") env['OBJCOPY'] = path.join(gcc_toolchain_bin, "objcopy") env['YASM'] = path.join(env['ANDROID_NDK'], 'prebuilt', host, 'bin', 'yasm') # A cheat-sheet for some of the build errors caused by getting the search path wrong... # # fatal error: 'limits' file not found # -- add -I cxx_include # unknown type name '__locale_t' (when running bindgen in mozjs_sys) # -- add -isystem sysroot_include # error: use of undeclared identifier 'UINTMAX_C' # -- add -D__STDC_CONSTANT_MACROS # # Also worth remembering: autoconf uses C for its configuration, # even for C++ builds, so the C flags need to line up with the C++ flags. env['CFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-isystem", sysroot_include, "-L" + gcc_libs]) env['CXXFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-I" + support_include, "-I" + cxx_include, "-isystem", sysroot_include, "-L" + gcc_libs, "-D__STDC_CONSTANT_MACROS", "-D__NDK_FPABI__="]) env["NDK_ANDROID_VERSION"] = android_platform.replace("android-", "") env['CPPFLAGS'] = ' '.join(["--sysroot", env['ANDROID_SYSROOT']]) env["CMAKE_ANDROID_ARCH_ABI"] = android_lib env["CMAKE_TOOLCHAIN_FILE"] = path.join(self.android_support_dir(), "toolchain.cmake") # Set output dir for gradle aar files aar_out_dir = self.android_aar_dir() if not os.path.exists(aar_out_dir): os.makedirs(aar_out_dir) env["AAR_OUT_DIR"] = aar_out_dir if very_verbose: print (["Calling", "cargo", "build"] + opts) for key in env: print((key, env[key])) status = self.call_rustup_run(["cargo", "build"] + opts, env=env, verbose=verbose) elapsed = time() - build_start # Do some additional things if the build succeeded if status == 0: if android and not no_package: Registrar.dispatch("package", context=self.context, release=release, dev=dev, target=target) if sys.platform == "win32": servo_exe_dir = path.join(base_path, "debug" if dev else "release") msvc_x64 = "64" if "x86_64" in (target or host_triple()) else "" # on msvc builds, use editbin to change the subsystem to windows, but only # on release builds -- on debug builds, it hides log output if not dev: call(["editbin", "/nologo", "/subsystem:windows", path.join(servo_exe_dir, "servo.exe")], verbose=verbose) # on msvc, we need to copy in some DLLs in to the servo.exe dir for ssl_lib in ["libcryptoMD.dll", "libsslMD.dll"]: shutil.copy(path.join(env['OPENSSL_LIB_DIR'], "../bin" + msvc_x64, ssl_lib), servo_exe_dir) elif sys.platform == "darwin": # On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools # like Instruments.app. try: import Cocoa icon_path = path.join(self.get_top_dir(), "resources", "servo.png") icon = Cocoa.NSImage.alloc().initWithContentsOfFile_(icon_path) if icon is not None: Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(icon, servo_path, 0) except ImportError: pass # Generate Desktop Notification if elapsed-time > some threshold value notify_build_done(self.config, elapsed, status == 0) print("Build %s in %s" % ("Completed" if status == 0 else "FAILED", format_duration(elapsed))) return status
def build(self, target=None, release=False, dev=False, jobs=None, features=None, android=None, no_package=False, verbose=False, very_verbose=False, debug_mozjs=False, params=None, with_debug_assertions=False, libsimpleservo=False): opts = params or [] if android is None: android = self.config["build"]["android"] features = features or self.servo_features() base_path = self.get_target_dir() release_path = path.join(base_path, "release", "servo") dev_path = path.join(base_path, "debug", "servo") release_exists = path.exists(release_path) dev_exists = path.exists(dev_path) if not (release or dev): if self.config["build"]["mode"] == "dev": dev = True elif self.config["build"]["mode"] == "release": release = True elif release_exists and not dev_exists: release = True elif dev_exists and not release_exists: dev = True else: print("Please specify either --dev (-d) for a development") print(" build, or --release (-r) for an optimized build.") sys.exit(1) if release and dev: print("Please specify either --dev or --release.") sys.exit(1) if target and android: print("Please specify either --target or --android.") sys.exit(1) if release: opts += ["--release"] servo_path = release_path else: servo_path = dev_path if jobs is not None: opts += ["-j", jobs] if verbose: opts += ["-v"] if very_verbose: opts += ["-vv"] if android: target = self.config["android"]["target"] if target: if self.config["tools"]["use-rustup"]: # 'rustup target add' fails if the toolchain is not installed at all. self.call_rustup_run(["rustc", "--version"]) check_call([ "rustup" + BIN_SUFFIX, "target", "add", "--toolchain", self.toolchain(), target ]) opts += ["--target", target] if not android: android = self.handle_android_target(target) self.ensure_bootstrapped(target=target) self.ensure_clobbered() self.add_manifest_path(opts, android, libsimpleservo) if debug_mozjs: features += ["debugmozjs"] if self.config["build"]["webgl-backtrace"]: features += ["webgl-backtrace"] if self.config["build"]["dom-backtrace"]: features += ["dom-backtrace"] if features: opts += ["--features", "%s" % ' '.join(features)] build_start = time() env = self.build_env(target=target, is_build=True) if with_debug_assertions: env['RUSTFLAGS'] = env.get('RUSTFLAGS', "") + " -C debug_assertions" if android: if "ANDROID_NDK" not in env: print("Please set the ANDROID_NDK environment variable.") sys.exit(1) if "ANDROID_SDK" not in env: print("Please set the ANDROID_SDK environment variable.") sys.exit(1) android_platform = self.config["android"]["platform"] android_toolchain_name = self.config["android"]["toolchain_name"] android_toolchain_prefix = self.config["android"][ "toolchain_prefix"] android_lib = self.config["android"]["lib"] android_arch = self.config["android"]["arch"] # Build OpenSSL for android env["OPENSSL_VERSION"] = "1.0.2k" make_cmd = ["make"] if jobs is not None: make_cmd += ["-j" + jobs] android_dir = self.android_build_dir(dev) openssl_dir = path.join(android_dir, "native", "openssl") if not path.exists(openssl_dir): os.makedirs(openssl_dir) shutil.copy( path.join(self.android_support_dir(), "openssl.makefile"), openssl_dir) shutil.copy(path.join(self.android_support_dir(), "openssl.sh"), openssl_dir) # Check if the NDK version is 12 if not os.path.isfile( path.join(env["ANDROID_NDK"], 'source.properties')): print("ANDROID_NDK should have file `source.properties`.") print( "The environment variable ANDROID_NDK may be set at a wrong path." ) sys.exit(1) with open(path.join(env["ANDROID_NDK"], 'source.properties')) as ndk_properties: lines = ndk_properties.readlines() if lines[1].split(' = ')[1].split('.')[0] != '12': print("Currently only support NDK 12.") sys.exit(1) env["RUST_TARGET"] = target env["ANDROID_TOOLCHAIN_NAME"] = android_toolchain_name with cd(openssl_dir): status = call(make_cmd + ["-f", "openssl.makefile"], env=env, verbose=verbose) if status: return status openssl_dir = path.join( openssl_dir, "openssl-{}".format(env["OPENSSL_VERSION"])) env['OPENSSL_LIB_DIR'] = openssl_dir env['OPENSSL_INCLUDE_DIR'] = path.join(openssl_dir, "include") env['OPENSSL_STATIC'] = 'TRUE' # Android builds also require having the gcc bits on the PATH and various INCLUDE # path munging if you do not want to install a standalone NDK. See: # https://dxr.mozilla.org/mozilla-central/source/build/autoconf/android.m4#139-161 os_type = platform.system().lower() if os_type not in ["linux", "darwin"]: raise Exception( "Android cross builds are only supported on Linux and macOS." ) cpu_type = platform.machine().lower() host_suffix = "unknown" if cpu_type in ["i386", "i486", "i686", "i768", "x86"]: host_suffix = "x86" elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]: host_suffix = "x86_64" host = os_type + "-" + host_suffix host_cc = env.get('HOST_CC') or _get_exec_path( ["clang"]) or _get_exec_path(["gcc"]) host_cxx = env.get('HOST_CXX') or _get_exec_path( ["clang++"]) or _get_exec_path(["g++"]) llvm_toolchain = path.join(env['ANDROID_NDK'], "toolchains", "llvm", "prebuilt", host) gcc_toolchain = path.join(env['ANDROID_NDK'], "toolchains", android_toolchain_prefix + "-4.9", "prebuilt", host) gcc_libs = path.join(gcc_toolchain, "lib", "gcc", android_toolchain_name, "4.9.x") env['PATH'] = (path.join(llvm_toolchain, "bin") + ':' + path.join(gcc_toolchain, "bin") + ':' + env['PATH']) env['ANDROID_SYSROOT'] = path.join(env['ANDROID_NDK'], "platforms", android_platform, "arch-" + android_arch) support_include = path.join(env['ANDROID_NDK'], "sources", "android", "support", "include") cxx_include = path.join(env['ANDROID_NDK'], "sources", "cxx-stl", "llvm-libc++", "libcxx", "include") clang_include = path.join(llvm_toolchain, "lib64", "clang", "3.8", "include") sysroot_include = path.join(env['ANDROID_SYSROOT'], "usr", "include") env['HOST_CC'] = host_cc env['HOST_CXX'] = host_cxx env['HOST_CFLAGS'] = '' env['HOST_CXXFLAGS'] = '' env['CC'] = path.join(llvm_toolchain, "bin", "clang") env['CPP'] = path.join(llvm_toolchain, "bin", "clang") + " -E" env['CXX'] = path.join(llvm_toolchain, "bin", "clang++") env['ANDROID_TOOLCHAIN'] = gcc_toolchain env['GCC_TOOLCHAIN'] = gcc_toolchain gcc_toolchain_bin = path.join(gcc_toolchain, android_toolchain_name, "bin") env['AR'] = path.join(gcc_toolchain_bin, "ar") env['RANLIB'] = path.join(gcc_toolchain_bin, "ranlib") env['OBJCOPY'] = path.join(gcc_toolchain_bin, "objcopy") env['YASM'] = path.join(env['ANDROID_NDK'], 'prebuilt', host, 'bin', 'yasm') # A cheat-sheet for some of the build errors caused by getting the search path wrong... # # fatal error: 'limits' file not found # -- add -I cxx_include # unknown type name '__locale_t' (when running bindgen in mozjs_sys) # -- add -isystem sysroot_include # error: use of undeclared identifier 'UINTMAX_C' # -- add -D__STDC_CONSTANT_MACROS # # Also worth remembering: autoconf uses C for its configuration, # even for C++ builds, so the C flags need to line up with the C++ flags. env['CFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-isystem", sysroot_include, "-L" + gcc_libs ]) env['CXXFLAGS'] = ' '.join([ "--target=" + target, "--sysroot=" + env['ANDROID_SYSROOT'], "--gcc-toolchain=" + gcc_toolchain, "-I" + support_include, "-I" + cxx_include, "-I" + clang_include, "-isystem", sysroot_include, "-L" + gcc_libs, "-D__STDC_CONSTANT_MACROS", "-D__NDK_FPABI__=" ]) env["NDK_ANDROID_VERSION"] = android_platform.replace( "android-", "") env['CPPFLAGS'] = ' '.join(["--sysroot", env['ANDROID_SYSROOT']]) env["CMAKE_ANDROID_ARCH_ABI"] = android_lib env["CMAKE_TOOLCHAIN_FILE"] = path.join(self.android_support_dir(), "toolchain.cmake") # Set output dir for gradle aar files aar_out_dir = self.android_aar_dir() if not os.path.exists(aar_out_dir): os.makedirs(aar_out_dir) env["AAR_OUT_DIR"] = aar_out_dir # GStreamer and its dependencies use pkg-config and this flag is required # to make it work in a cross-compilation context. env["PKG_CONFIG_ALLOW_CROSS"] = '1' # Build the name of the package containing all GStreamer dependencies # according to the build target. gst_lib = "gst-build-{}".format(self.config["android"]["lib"]) gst_lib_zip = "%s.zip" % gst_lib gst_dir = os.path.join(base_path, "gstreamer") gst_lib_path = os.path.join(base_path, gst_dir, gst_lib) pkg_config_path = os.path.join(gst_lib_path, "pkgconfig") env["PKG_CONFIG_PATH"] = pkg_config_path if not os.path.exists(gst_lib_path): # Download GStreamer dependencies if they have not already been downloaded print("Downloading GStreamer dependencies") gst_url = "https://github.com/servo/libgstreamer_android_gen/blob/" \ "ebb0f0097fec985e0cef988c54a28c2ba06761aa/out/%s?raw=true" % gst_lib_zip print(gst_url) urllib.urlretrieve(gst_url, gst_lib_zip) zip_ref = zipfile.ZipFile(gst_lib_zip, "r") zip_ref.extractall(gst_dir) os.remove(gst_lib_zip) # Change pkgconfig info to make all GStreamer dependencies point # to the libgstreamer_android.so bundle. for each in os.listdir(pkg_config_path): if each.endswith('.pc'): print("Setting pkgconfig info for %s" % each) pc = os.path.join(pkg_config_path, each) expr = "s#libdir=.*#libdir=%s#g" % gst_lib_path subprocess.call(["perl", "-i", "-pe", expr, pc]) if very_verbose: print(["Calling", "cargo", "build"] + opts) for key in env: print((key, env[key])) status = self.call_rustup_run(["cargo", "build"] + opts, env=env, verbose=verbose) elapsed = time() - build_start # Do some additional things if the build succeeded if status == 0: if android and not no_package: rv = Registrar.dispatch("package", context=self.context, release=release, dev=dev, target=target) if rv: return rv if sys.platform == "win32": servo_exe_dir = path.join(base_path, "debug" if dev else "release") msvc_x64 = "64" if "x86_64" in (target or host_triple()) else "" # on msvc builds, use editbin to change the subsystem to windows, but only # on release builds -- on debug builds, it hides log output if not dev: call([ "editbin", "/nologo", "/subsystem:windows", path.join(servo_exe_dir, "servo.exe") ], verbose=verbose) # on msvc, we need to copy in some DLLs in to the servo.exe dir for ssl_lib in ["libcryptoMD.dll", "libsslMD.dll"]: shutil.copy( path.join(env['OPENSSL_LIB_DIR'], "../bin" + msvc_x64, ssl_lib), servo_exe_dir) elif sys.platform == "darwin": # On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools # like Instruments.app. try: import Cocoa icon_path = path.join(self.get_top_dir(), "resources", "servo.png") icon = Cocoa.NSImage.alloc().initWithContentsOfFile_( icon_path) if icon is not None: Cocoa.NSWorkspace.sharedWorkspace( ).setIcon_forFile_options_(icon, servo_path, 0) except ImportError: pass # Generate Desktop Notification if elapsed-time > some threshold value notify_build_done(self.config, elapsed, status == 0) print("Build %s in %s" % ("Completed" if status == 0 else "FAILED", format_duration(elapsed))) return status
type=int, default=DEFAULT_PORT, help='Port to run server on.') func = port(func) address = CommandArgument('--address', metavar='ADDRESS', default=DEFAULT_HOSTNAME, help='Hostname to bind server to.') func = address(func) return func Registrar.register_category(name='services', title='Services utilities', description='Commands for services development.') @CommandProvider class SyncTestCommands(MachCommandBase): def __init__(self, context): MachCommandBase.__init__(self, context) def run_server(self, js_file, hostname, port): topsrcdir = self.topsrcdir topobjdir = self.topobjdir unit_test_dir = mozpath.join(SRCDIR, 'unit') head_paths = [
def SyncStorageCommand(func): """Decorator that adds shared command arguments to services commands.""" port = CommandArgument('--port', metavar='PORT', type=int, default=DEFAULT_PORT, help='Port to run server on.') func = port(func) address = CommandArgument('--address', metavar='ADDRESS', default=DEFAULT_HOSTNAME, help='Hostname to bind server to.') func = address(func) return func Registrar.register_category(name='services', title='Services utilities', description='Commands for services development.') @CommandProvider class SyncTestCommands(MachCommandBase): def __init__(self, context): MachCommandBase.__init__(self, context) def run_server(self, js_file, hostname, port): topsrcdir = self.topsrcdir topobjdir = self.topobjdir unit_test_dir = mozpath.join(SRCDIR, 'unit') head_paths = [ 'head_global.js',
def install(self, release=False, dev=False, android=False, magicleap=False, emulator=False, usb=False, target=None): if target and android: print("Please specify either --target or --android.") sys.exit(1) if not android: android = self.handle_android_target(target) if target and magicleap: print("Please specify either --target or --magicleap.") sys.exit(1) if magicleap: target = "aarch64-linux-android" env = self.build_env(target=target) try: binary_path = self.get_binary_path(release, dev, android=android, magicleap=magicleap) except BuildNotFound: print("Servo build not found. Building servo...") result = Registrar.dispatch( "build", context=self.context, release=release, dev=dev, android=android, magicleap=magicleap, ) if result: return result try: binary_path = self.get_binary_path(release, dev, android=android, magicleap=magicleap) except BuildNotFound: print( "Rebuilding Servo did not solve the missing build problem." ) return 1 if magicleap: if not env.get("MAGICLEAP_SDK"): raise Exception( "Magic Leap installs need the MAGICLEAP_SDK environment variable" ) mldb = path.join(env.get("MAGICLEAP_SDK"), "tools", "mldb", "mldb") pkg_path = path.join(path.dirname(binary_path), "Servo.mpk") exec_command = [ mldb, "install", "-u", pkg_path, ] elif android: pkg_path = self.get_apk_path(release) exec_command = [self.android_adb_path(env)] if emulator and usb: print( "Cannot install to both emulator and USB at the same time." ) return 1 if emulator: exec_command += ["-e"] if usb: exec_command += ["-d"] exec_command += ["install", "-r", pkg_path] elif is_windows(): pkg_path = path.join(path.dirname(binary_path), 'msi', 'Servo.msi') exec_command = ["msiexec", "/i", pkg_path] if not path.exists(pkg_path): print("Servo package not found. Packaging servo...") result = Registrar.dispatch( "package", context=self.context, release=release, dev=dev, android=android, magicleap=magicleap, ) if result != 0: return result print(" ".join(exec_command)) return subprocess.call(exec_command, env=env)
def ensure_built_tests(self): if self.context.built_tests: return Registrar.dispatch('build-tests', context=self.context) self.context.built_tests = True