summaryrefslogtreecommitdiff
path: root/src/buildstream/_artifact.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/buildstream/_artifact.py')
-rw-r--r--src/buildstream/_artifact.py173
1 files changed, 163 insertions, 10 deletions
diff --git a/src/buildstream/_artifact.py b/src/buildstream/_artifact.py
index c110e57f0..6e2bc9342 100644
--- a/src/buildstream/_artifact.py
+++ b/src/buildstream/_artifact.py
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2019 Codethink Limited
+# Copyright (C) 2020 Codethink Limited
# Copyright (C) 2019 Bloomberg Finance LP
#
# This program is free software; you can redistribute it and/or
@@ -29,13 +29,16 @@ artifact composite interaction away from Element class
"""
import os
+from typing import Dict, Tuple
from ._protos.buildstream.v2.artifact_pb2 import Artifact as ArtifactProto
from . import _yaml
from . import utils
+from .node import Node
from .types import _Scope
from .storage._casbaseddirectory import CasBasedDirectory
-
+from .sandbox._config import SandboxConfig
+from ._variables import Variables
# An Artifact class to abstract artifact operations
# from the Element class
@@ -44,28 +47,75 @@ from .storage._casbaseddirectory import CasBasedDirectory
# element (Element): The Element object
# context (Context): The BuildStream context
# strong_key (str): The elements strong cache key, dependent on context
+# strict_key (str): The elements strict cache key
# weak_key (str): The elements weak cache key
#
class Artifact:
- version = 0
+ version = 1
- def __init__(self, element, context, *, strong_key=None, weak_key=None):
+ def __init__(self, element, context, *, strong_key=None, strict_key=None, weak_key=None):
self._element = element
self._context = context
self._cache_key = strong_key
+ self._strict_key = strict_key
self._weak_cache_key = weak_key
self._artifactdir = context.artifactdir
self._cas = context.get_cascache()
self._tmpdir = context.tmpdir
self._proto = None
- self._metadata_keys = None # Strong and weak key tuple extracted from the artifact
+ self._metadata_keys = None # Strong, strict and weak key tuple extracted from the artifact
self._metadata_dependencies = None # Dictionary of dependency strong keys from the artifact
self._metadata_workspaced = None # Boolean of whether it's a workspaced artifact
self._metadata_workspaced_dependencies = None # List of which dependencies are workspaced from the artifact
self._cached = None # Boolean of whether the artifact is cached
+ # strong_key():
+ #
+ # A property which evaluates to the strong key, regardless of whether
+ # it was the strong key that the Artifact object was initialized with
+ # or whether it was the strong key loaded from artifact metadata.
+ #
+ @property
+ def strong_key(self) -> str:
+ if self.cached():
+ key, _, _ = self.get_metadata_keys()
+ else:
+ key = self._cache_key
+
+ return key
+
+ # strict_key():
+ #
+ # A property which evaluates to the strict key, regardless of whether
+ # it was the strict key that the Artifact object was initialized with
+ # or whether it was the strict key loaded from artifact metadata.
+ #
+ @property
+ def strict_key(self) -> str:
+ if self.cached():
+ _, key, _ = self.get_metadata_keys()
+ else:
+ key = self._strict_key
+
+ return key
+
+ # weak_key():
+ #
+ # A property which evaluates to the weak key, regardless of whether
+ # it was the weak key that the Artifact object was initialized with
+ # or whether it was the weak key loaded from artifact metadata.
+ #
+ @property
+ def weak_key(self) -> str:
+ if self.cached():
+ _, _, key = self.get_metadata_keys()
+ else:
+ key = self._weak_cache_key
+
+ return key
+
# get_files():
#
# Get a virtual directory for the artifact files content
@@ -137,11 +187,25 @@ class Artifact:
# sourcesvdir (Directory): Virtual Directoy object for the staged sources
# buildresult (tuple): bool, short desc and detailed desc of result
# publicdata (dict): dict of public data to commit to artifact metadata
+ # variables (Variables): The element's Variables
+ # environment (dict): dict of the element's environment variables
+ # sandboxconfig (SandboxConfig): The element's SandboxConfig
#
# Returns:
# (int): The size of the newly cached artifact
#
- def cache(self, sandbox_build_dir, collectvdir, sourcesvdir, buildresult, publicdata):
+ def cache(
+ self,
+ *,
+ sandbox_build_dir,
+ collectvdir,
+ sourcesvdir,
+ buildresult,
+ publicdata,
+ variables,
+ environment,
+ sandboxconfig,
+ ):
context = self._context
element = self._element
@@ -161,6 +225,7 @@ class Artifact:
# Store keys
artifact.strong_key = self._cache_key
+ artifact.strict_key = self._strict_key
artifact.weak_key = self._weak_cache_key
artifact.was_workspaced = bool(element._get_workspace())
@@ -180,6 +245,34 @@ class Artifact:
artifact.public_data.CopyFrom(public_data_digest)
size += public_data_digest.size_bytes
+ # Store low diversity metadata, this metadata must have a high
+ # probability of deduplication, such as environment variables
+ # and SandboxConfig.
+ #
+ with utils._tempnamedfile_name(dir=self._tmpdir) as tmpname:
+ sandbox_dict = sandboxconfig.to_dict()
+ low_diversity_dict = {"environment": environment, "sandbox-config": sandbox_dict}
+ low_diversity_node = Node.from_dict(low_diversity_dict)
+
+ _yaml.roundtrip_dump(low_diversity_node, tmpname)
+ low_diversity_meta_digest = self._cas.add_object(path=tmpname, link_directly=True)
+ artifact.low_diversity_meta.CopyFrom(low_diversity_meta_digest)
+ size += low_diversity_meta_digest.size_bytes
+
+ # Store high diversity metadata, this metadata is expected to diverge
+ # for every element and as such cannot be deduplicated.
+ #
+ with utils._tempnamedfile_name(dir=self._tmpdir) as tmpname:
+ # The Variables object supports being converted directly to a dictionary
+ variables_dict = dict(variables)
+ high_diversity_dict = {"variables": variables_dict}
+ high_diversity_node = Node.from_dict(high_diversity_dict)
+
+ _yaml.roundtrip_dump(high_diversity_node, tmpname)
+ high_diversity_meta_digest = self._cas.add_object(path=tmpname, link_directly=True)
+ artifact.high_diversity_meta.CopyFrom(high_diversity_meta_digest)
+ size += high_diversity_meta_digest.size_bytes
+
# store build dependencies
for e in element._dependencies(_Scope.BUILD):
new_build = artifact.build_deps.add()
@@ -282,6 +375,64 @@ class Artifact:
return data
+ # load_sandbox_config():
+ #
+ # Loads the sandbox configuration from the cached artifact
+ #
+ # Returns:
+ # The stored SandboxConfig object
+ #
+ def load_sandbox_config(self) -> SandboxConfig:
+
+ # Load the sandbox data from the artifact
+ artifact = self._get_proto()
+ meta_file = self._cas.objpath(artifact.low_diversity_meta)
+ data = _yaml.load(meta_file, shortname="low-diversity-meta.yaml")
+
+ # Extract the sandbox data
+ config = data.get_mapping("sandbox-config")
+
+ # Return a SandboxConfig
+ return SandboxConfig.new_from_node(config)
+
+ # load_environment():
+ #
+ # Loads the environment variables from the cached artifact
+ #
+ # Returns:
+ # The environment variables
+ #
+ def load_environment(self) -> Dict[str, str]:
+
+ # Load the sandbox data from the artifact
+ artifact = self._get_proto()
+ meta_file = self._cas.objpath(artifact.low_diversity_meta)
+ data = _yaml.load(meta_file, shortname="low-diversity-meta.yaml")
+
+ # Extract the environment
+ config = data.get_mapping("environment")
+
+ # Return the environment
+ return config.strip_node_info()
+
+ # load_variables():
+ #
+ # Loads the element variables from the cached artifact
+ #
+ # Returns:
+ # The element variables
+ #
+ def load_variables(self) -> Variables:
+
+ # Load the sandbox data from the artifact
+ artifact = self._get_proto()
+ meta_file = self._cas.objpath(artifact.high_diversity_meta)
+ data = _yaml.load(meta_file, shortname="high-diversity-meta.yaml")
+
+ # Extract the variables node and return the new Variables instance
+ variables_node = data.get_mapping("variables")
+ return Variables(variables_node)
+
# load_build_result():
#
# Load the build result from the cached artifact
@@ -303,10 +454,11 @@ class Artifact:
# Retrieve the strong and weak keys from the given artifact.
#
# Returns:
- # (str): The strong key
- # (str): The weak key
+ # The strong key
+ # The strict key
+ # The weak key
#
- def get_metadata_keys(self):
+ def get_metadata_keys(self) -> Tuple[str, str, str]:
if self._metadata_keys is not None:
return self._metadata_keys
@@ -315,9 +467,10 @@ class Artifact:
artifact = self._get_proto()
strong_key = artifact.strong_key
+ strict_key = artifact.strict_key
weak_key = artifact.weak_key
- self._metadata_keys = (strong_key, weak_key)
+ self._metadata_keys = (strong_key, strict_key, weak_key)
return self._metadata_keys