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
|
"""
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 patch import PatchSet
from pomu.util.fs import strip_prefix
from pomu.util.misc import list_add
from pomu.util.result import Result
class Package():
def __init__(self, name, root, backend=None, category=None, version=None, slot='0', d_path=None, files=None, filemap=None, patches=[]):
"""
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 = {}
self.patches = patches
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')
@property
def files(self):
res = []
for k in self.filemap:
res.append(path.split(k))
return res
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(dest, exist_ok=True)
copy2(src, dest)
except PermissionError:
return Result.Err('You do not have enough permissions')
return Result.Ok().and_(self.apply_patches())
def patch(self, patch):
list_add(self.patches, patch)
def apply_patches(self):
"""Applies a sequence of patches at the root (after merging)"""
ps = PatchSet()
for p in self.patches:
ps.parse(open(p, 'r'))
ps.apply(root=self.root)
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
|