예제 #1
0
    def inputs(self, transitive: bool = False) -> Set[str]:
        """List the files tracked as inputs to the image.

        These files are used to compute the fingerprint for the image. See
        `ResolvedImage.fingerprint` for details.

        Returns:
            inputs: A list of input files, relative to the root of the
                repository.
        """
        paths = set(
            git.expand_globs(self.image.rd.root, f"{self.image.path}/**"))
        if not paths:
            # While we could find an `mzbuild.yml` file for this service, expland_globs didn't
            # return any files that matched this service. At the very least, the `mzbuild.yml`
            # file itself should have been returned. We have a bug if paths is empty.
            raise AssertionError(
                f"{self.image.name} mzbuild exists but its files are unknown to git"
            )
        if self.image.pre_image is not None:
            paths |= self.image.pre_image.inputs()
        if transitive:
            for dep in self.dependencies.values():
                paths |= dep.inputs(transitive)
        return paths
예제 #2
0
    def inputs(self) -> Set[str]:
        """Compute the files that can impact the compilation of this crate.

        Note that the returned list may have false positives (i.e., include
        files that do not in fact impact the compilation of this crate), but it
        is not believed to have false negatives.

        Returns:
            inputs: A list of input files, relative to the root of the
                Cargo workspace.
        """
        # NOTE(benesch): it would be nice to have fine-grained tracking of only
        # exactly the files that go into a Rust crate, but doing this properly
        # requires parsing Rust code, and we don't want to force a dependency on
        # a Rust toolchain for users running demos. Instead, we assume that all†
        # files in a crate's directory are inputs to that crate.
        #
        # † As a development convenience, we omit mzcompose and mzcompose.yml
        # files within a crate. This is technically incorrect if someone writes
        # `include!("mzcompose.yml")`, but that seems like a crazy thing to do.
        return git.expand_globs(
            self.root,
            f"{self.path}/**",
            f":(exclude){self.path}/mzcompose",
            f":(exclude){self.path}/mzcompose.yml",
        )
예제 #3
0
    def fingerprint(self) -> Fingerprint:
        """Fingerprint the inputs to the image.

        Compute the fingerprint of the image. Changing the contents of any of
        the files or adding or removing files to the image will change the
        fingerprint, as will modifying the inputs to any of its dependencies.

        The image considers all non-gitignored files in its mzbuild context to
        be inputs. If it has a pre-image action, that action may add additional
        inputs via `PreImage.inputs`.
        """
        self_hash = hashlib.sha1()
        for rel_path in sorted(
                set(git.expand_globs(self.image.rd.root, *self.inputs()))):
            abs_path = self.image.rd.root / rel_path
            file_hash = hashlib.sha1()
            raw_file_mode = os.lstat(abs_path).st_mode
            # Compute a simplified file mode using the same rules as Git.
            # https://github.com/git/git/blob/3bab5d562/Documentation/git-fast-import.txt#L610-L616
            if stat.S_ISLNK(raw_file_mode):
                file_mode = 0o120000
            elif raw_file_mode & stat.S_IXUSR:
                file_mode = 0o100755
            else:
                file_mode = 0o100644
            with open(abs_path, "rb") as f:
                file_hash.update(f.read())
            self_hash.update(file_mode.to_bytes(2, byteorder="big"))
            self_hash.update(rel_path.encode())
            self_hash.update(file_hash.digest())
            self_hash.update(b"\0")

        for pre_image in self.image.pre_images:
            self_hash.update(pre_image.extra().encode())
            self_hash.update(b"\0")

        self_hash.update(f"arch={self.image.rd.arch}".encode())
        self_hash.update(f"coverage={self.image.rd.coverage}".encode())

        full_hash = hashlib.sha1()
        full_hash.update(self_hash.digest())
        for dep in sorted(self.dependencies.values(), key=lambda d: d.name):
            full_hash.update(dep.name.encode())
            full_hash.update(dep.fingerprint())
            full_hash.update(b"\0")

        return Fingerprint(full_hash.digest())
예제 #4
0
    def inputs(self, transitive: bool = False) -> Set[str]:
        """List the files tracked as inputs to the image.

        These files are used to compute the fingerprint for the image. See
        `ResolvedImage.fingerprint` for details.

        Returns:
            inputs: A list of input files, relative to the root of the
                repository.
        """
        paths = set(git.expand_globs(self.image.rd.root, f"{self.image.path}/**"))
        if self.image.pre_image is not None:
            paths |= self.image.pre_image.inputs()
        if transitive:
            for dep in self.dependencies.values():
                paths |= dep.inputs(transitive)
        return paths
예제 #5
0
 def inputs(self) -> Set[str]:
     return set(
         git.expand_globs(self.rd.root, f"{self.source}/{self.matching}"))