aboutsummaryrefslogtreecommitdiff
blob: d59bd4f303aa8e7f28d963e9d74a8b5509bb570f (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
"""
A package is a set of files, situated in a root directory.
A package can be installed into a repository.
A package is supposed to be created by a package source from a set of files.
A package is supposed to know, where it came from.
"""

from os import path, walk, makedirs
from shutil import copy2

import subprocess

from pomu.util.fs import strip_prefix
from pomu.util.result import Result

class Package():
    def __init__(self, backend, name, root, category=None, version=None, slot='0', d_path=None, files=None, filemap=None):
        """
        Parameters:
            backend - specific source module object/class
            name - name of the package
            root - root path of the repository (if applicable)
            d_path - a subdirectory of the root path, which would be sourced recursively.
                could be a relative or an absolute path
            files - a set of files to build a package from
            filemap - a mapping from destination files to files in the filesystem
            category, version, slot - self-descriptive
        """
        self.backend = backend
        self.name = name
        self.root = root
        self.category = category
        self.version = version
        self.slot = slot
        self.filemap = {}
        if d_path is None and files is None and filemap is None:
            self.d_path = None
            self.read_path(self.root)
        elif d_path:
            self.d_path = self.strip_root(d_path)
            self.read_path(path.join(self.root, self.d_path))
        elif files:
            for f in files:
                dst = self.strip_root(f)
                self.filemap[dst] = path.join(self.root, dst)
        elif filemap:
            self.filemap = filemap
        else:
            raise ValueError('You should specify either d_path, files or filemap')

    def strip_root(self, d_path):
        """Strip the root component of d_path"""
        # the path should be either relative, or a child of root
        if d_path.startswith('/'):
            if path.commonprefix(d_path, self.root) != self.root:
                raise ValueError('Path should be a subdirectory of root')
            return strip_prefix(strip_prefix(d_path, self.root), '/')
        return d_path

    def read_path(self, d_path):
        """Recursively add files from a subtree (specified by d_path)"""
        for wd, dirs, files in walk(d_path):
            wd = self.strip_root(wd)
            self.filemap.update({path.join(wd, f): path.join(self.root, wd, f) for f in files})

    def merge_into(self, dst):
        """Merges contents of the package into a specified directory (dst)"""
        for trg, src in self.filemap.items():
            wd, _ = path.split(trg)
            dest = path.join(dst, wd)
            try:
                makedirs(wd, exists_ok=True)
                copy2(src, dest)
            except PermissionError:
                return Result.Err('You do not have enough permissions')
        return Result.Ok()

    def gen_manifests(self, dst):
        """
        Generate manifests for the installed package (in the dst directory).
        TODO: use portage APIs instead of calling repoman.
        """
        dirs = [wd for wd, f in self.files if f.endswith('.ebuild')]
        dirs = list(set(dirs))
        res = []
        for d_ in dirs:
            d = path.join(dst, d_)
            ret = subprocess.run(['repoman', 'manifest'],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                    cwd=d)
            if ret.returncode != 0:
                return Result.Err('Failed to generate manifest at', d)
            if path.exists(path.join(d, 'Manifest')):
                res.append(path.join(d, 'Manifest'))
        return Result.Ok(res)


    def __str__(self):
        s = ''
        if self.category:
            s = self.category + '/'
        s += self.name
        if self.version:
            s += '-' + self.version
        if self.slot != '0':
            s += self.slot
        return s