summaryrefslogtreecommitdiff
path: root/morphlib/source.py
blob: 53563a43e4ce4f48b05d9fde46f383f7fddc8026 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# Copyright (C) 2012-2015  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


import morphlib


class Source(object):

    '''Represent the source to be built.

    Has the following properties:

    * ``repo`` -- the git repository which contains the source
    * ``repo_name`` -- name of the git repository which contains the source
    * ``original_ref`` -- the git ref provided by the user or a morphology
    * ``sha1`` -- the absolute git commit id for the revision we use
    * ``tree`` -- the SHA1 of the tree corresponding to the commit
    * ``morphology`` -- the in-memory representation of the morphology we use
    * ``filename`` -- basename of the morphology filename
    * ``cache_id`` -- a dict describing the components of the cache key
    * ``cache_key`` -- a cache key to uniquely identify the artifact
    * ``dependencies`` -- list of Artifacts that need to be built beforehand
    * ``split_rules`` -- rules for splitting the source's produced artifacts
    * ``artifacts`` -- the set of artifacts this source produces.

    '''

    def __init__(self, name, repo_name, original_ref, sha1, tree, morphology,
            filename, split_rules):
        self.name = name
        self.repo = None
        self.repo_name = repo_name
        self.original_ref = original_ref
        self.sha1 = sha1
        self.tree = tree
        self.morphology = morphology
        self.filename = filename
        self.cache_id = None
        self.cache_key = None
        self.dependencies = []

        self.split_rules = split_rules
        self.artifacts = None

    def __str__(self):  # pragma: no cover
        return '%s|%s|%s|%s' % (self.repo_name,
                                self.original_ref,
                                self.filename,
                                self.name)

    def __repr__(self): # pragma: no cover
        return 'Source(%s)' % str(self)

    def basename(self):
        return '%s.%s' % (self.cache_key, str(self.morphology['kind']))

    def metadata_basename(self):
        return '%s.meta' % (self.cache_key)

    def build_log_basename(self):
        return '%s.build-log' % (self.cache_key)

    def files(self):
        '''Return the name of all built artifacts of this source.

        This includes every artifact and all associated metadata.

        It's usually a bad idea to have only some of the files for a given
        source available. Transfer all of them if you transfer any of them.

        '''
        files = {self.metadata_basename()}

        if self.morphology['kind'] == 'chunk':
            files.add(self.build_log_basename())

        for artifact in self.artifacts.values():
            files.add(artifact.basename())
            if self.morphology.needs_artifact_metadata_cached:
                files.add(artifact.metadata_basename())

        return files

    def add_dependency(self, artifact): # pragma: no cover
        if artifact not in self.dependencies:
            self.dependencies.append(artifact)
        if self not in artifact.dependents:
            artifact.dependents.append(self)

    def depends_on(self, artifact): # pragma: no cover
        '''Do we depend on ``artifact``?'''
        return artifact in self.dependencies


def make_sources(reponame, ref, filename, absref, tree, morphology):
    kind = morphology['kind']
    if kind in ('system', 'chunk'):
        unifier = getattr(morphlib.artifactsplitrule,
                          'unify_%s_matches' % kind)
        split_rules = unifier(morphology)
        # chunk and system sources are named after the morphology
        source_name = morphology['name']
        source = morphlib.source.Source(source_name, reponame, ref,
                                        absref, tree, morphology,
                                        filename, split_rules)
        source.artifacts = {name: morphlib.artifact.Artifact(source, name)
                     for name in split_rules.artifacts}
        yield source
    elif kind == 'stratum': # pragma: no cover
        unifier = morphlib.artifactsplitrule.unify_stratum_matches
        split_rules = unifier(morphology)
        for name in split_rules.artifacts:
            source = morphlib.source.Source(
                name, # stratum source name is artifact name
                reponame, ref, absref, tree, morphology, filename,
                # stratum sources need to match the unified
                # split rules, so they know to yield the match
                # to a different source
                split_rules)
            source.artifacts = {name: morphlib.artifact.Artifact(source, name)}
            yield source
    else:
        # cluster morphologies don't have sources
        pass