예제 #1
0
    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())
예제 #2
0
    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
예제 #3
0
    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())
예제 #4
0
    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.")
예제 #5
0
    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())
예제 #6
0
    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
예제 #7
0
    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())
예제 #8
0
    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)
예제 #9
0
    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
예제 #10
0
    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.")
예제 #11
0
    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
예제 #12
0
 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)
예제 #13
0
    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
예제 #14
0
    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)
예제 #15
0
    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)
예제 #16
0
    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
예제 #17
0
    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)
예제 #18
0
    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)
예제 #19
0
    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
예제 #20
0
    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
예제 #21
0
    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)
예제 #22
0
 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)
예제 #23
0
    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)
예제 #24
0
 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)
예제 #25
0
    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)
예제 #26
0
 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
예제 #27
0
 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
예제 #28
0
 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
예제 #29
0
    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)
예제 #30
0
    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)
예제 #31
0
    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
예제 #32
0
    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
예제 #33
0
    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())
예제 #34
0
    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())
예제 #35
0
    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
예제 #36
0
    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())
예제 #37
0
    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
예제 #38
0
    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())
예제 #39
0
    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
예제 #40
0
    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
예제 #41
0
    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
예제 #42
0
    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
예제 #43
0
    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
예제 #44
0
    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
예제 #45
0
                           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 = [
예제 #46
0
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',
예제 #47
0
    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)
예제 #48
0
 def ensure_built_tests(self):
     if self.context.built_tests:
         return
     Registrar.dispatch('build-tests', context=self.context)
     self.context.built_tests = True