Apply Justin Riley's ACCEPT_KEYWORDS patch.
[g-pypi.git] / g_pypi / portage_utils.py
1 #!/usr/bin/env python
2 # pylint: disable-msg=C0301,W0613,W0612,C0103,E0611,W0511
3
4 """
5
6 portage_utils.py
7 ================
8
9 Various functions dealing with portage
10
11 """
12
13 import sys
14 import os
15 import commands
16 import logging
17 #import fnmatch
18
19 from portage import config as portage_config
20 from portage import settings as portage_settings
21
22 try:
23     #portage >= 2.2
24     from portage import dep as portage_dep
25 except ImportError:
26     #portage <= 2.1
27     from portage import portage_dep
28
29 sys.path.insert(0, "/usr/lib/gentoolkit/pym")
30 import gentoolkit
31 import gentoolkit.query
32
33
34 __docformat__ = 'restructuredtext'
35
36 ENV = portage_config(clone=portage_settings)
37 LOGGER = logging.getLogger(__name__)
38 LOGGER.setLevel(logging.DEBUG)
39 LOGGER.addHandler(logging.StreamHandler())
40
41
42 def get_repo_names():
43     """
44     Return a dict of overlay names with their paths
45     e.g.
46     {'reponame': '/path/to/repo', ...}
47
48     @returns: dict with repoman/paths
49
50     """
51     porttrees = [ENV['PORTDIR']] + \
52         [os.path.realpath(t) for t in ENV["PORTDIR_OVERLAY"].split()]
53     treemap = {}
54     for path in porttrees:
55         repo_name_path = os.path.join(path, 'profiles/repo_name')
56         try:
57             repo_name = open(repo_name_path, 'r').readline().strip()
58             treemap[repo_name] = path
59         except (OSError,IOError):
60             LOGGER.warn("No repo_name in %s" % path)
61     return treemap
62
63 def get_installed_ver(cpn):
64     """
65     Return PV for installed version of package
66
67     @param cpn: cat/pkg-ver
68     @type cpn: string
69
70     @returns: string version or None if not pkg installed
71
72     """
73     try:
74         #Return first version installed
75         #XXX Log warning if more than one installed (SLOT)?
76         pkg = gentoolkit.find_installed_packages(cpn, masked=True)[0]
77         return pkg.get_version()
78     except:
79         return
80
81 def valid_cpn(cpn):
82     """
83     Return True if cpn is valid portage category/pn-pv
84
85     @param cpn: cat/pkg-ver
86     @type cpn: string
87
88     @returns: True if installed, False if not installed
89     """
90     if portage_dep.isvalidatom(cpn):
91         return True
92     else:
93         return False
94
95
96 def ebuild_exists(cat_pkg):
97     """
98
99     Checks if an ebuild exists in portage tree or overlay
100
101     @param cat_pkg: portage category/packagename
102     @type cat_pkg: string
103
104     @returns: True if ebuild exists, False if no ebuild exists
105     """
106
107     pkgs = gentoolkit.query.Query(cat_pkg).find()
108     if len(pkgs):
109         return True
110     else:
111         return False
112
113 #def run_tests(ebuild_path):
114 #    """
115 #    Use portage to run tests
116
117 #    Some day I'll figure out how to get portage to do this directly. Some day.
118
119 #    @param ebuild_path: full path to ebuild
120 #    @type ebuild_path: string
121 #    @returns: None if succeed, raises OSError if fails to unpack
122
123 #    """
124 #    cmd = "/usr/bin/python /usr/bin/ebuild %s test" % ebuild_path
125 #    print cmd
126 #    (status, output) = commands.getstatusoutput(cmd)
127 #    print output
128 #    print status
129
130 def unpack_ebuild(ebuild_path):
131     """
132     Use portage to unpack an ebuild
133
134     Some day I'll figure out how to get portage to do this directly. Some day.
135
136     @param ebuild_path: full path to ebuild
137     @type ebuild_path: string
138     @returns: None if succeed, raises OSError if fails to unpack
139
140     """
141     (status, output) = commands.getstatusoutput("ebuild %s digest setup clean unpack" % ebuild_path)
142     if status:
143         #Portage's error message, sometimes.
144         #Couldn't determine PN or PV so we misnamed ebuild
145         if 'does not follow correct package syntax' in output:
146             LOGGER.error(output)
147             LOGGER.error("Misnamed ebuild: %s" % ebuild_path)
148             LOGGER.error("Try using -n or -v to force PN or PV")
149             os.unlink(ebuild_path)
150         else:
151             LOGGER.error(output)
152             raise OSError
153   
154 def find_s_dir(p, cat):
155     """
156     Try to get ${S} by determining what directories were unpacked
157
158     @param p: portage ${P}
159     @type p: string
160
161     @param cat: valid portage category
162     @type cat: string
163
164     @returns: string with directory name if detected, empty string
165               if S=WORKDIR, None if couldn't find S
166     
167     
168     """
169
170     workdir = get_workdir(p, cat)
171     files = os.listdir(workdir)
172     dirs = []
173     for unpacked in files:
174         if os.path.isdir(os.path.join(workdir, unpacked)):
175             dirs.append(unpacked)
176     if len(dirs) == 1:
177         #Only one directory, must be it.
178         return dirs[0]
179     elif not len(dirs):
180         #Unpacked in cwd
181         return ""
182     else:
183         #XXX Need to search whole tree for setup.py
184         LOGGER.error("Can't determine ${S}")
185         LOGGER.error("Unpacked multiple directories: %s" % dirs)
186  
187 def get_workdir(p, cat):
188     """
189     Return WORKDIR
190
191     @param p: portage ${P}
192     @type p: string
193
194     @param cat: valid portage category
195     @type cat: string
196
197     @return: string of portage_tmpdir/cp
198     """
199
200     return '%s/portage/%s/%s/work' % (get_portage_tmpdir(), cat, p)
201
202 def get_portdir_overlay():
203     """Return PORTDIR_OVERLAY from /etc/make.conf"""
204     return ENV['PORTDIR_OVERLAY'].split(" ")[0]
205
206 def get_portage_tmpdir():
207     """Return PORTAGE_TMPDIR from /etc/make.conf"""
208     return ENV["PORTAGE_TMPDIR"]
209
210 def get_portdir():
211     """Return PORTDIR from /etc/make.conf"""
212     return ENV["PORTDIR"]
213     
214 def get_keyword():
215     """Return first ACCEPT_KEYWORDS from /etc/make.conf"""
216     #Choose the first arch they have, in case of multiples.
217     
218     try:
219         arch = ENV["ACCEPT_KEYWORDS"].split(' ')[0]
220     except KeyError:
221         LOGGER.error("No ACCEPT_KEYWORDS found, using ~x86")
222         arch = '~x86'
223
224     #New ebuilds must be ~arch
225
226     if not arch.startswith('~'):
227         arch = "~%s" % arch
228     return arch
229
230 def make_overlay_dir(category, pn, overlay):
231     """
232     Create directory(s) in overlay for ebuild
233     
234     @param category: valid portage category
235     @type category: string
236
237     @param pn: portage ${PN}
238     @type pn: string
239
240     @param overlay: portage overlay directory
241     @type overlay: string
242
243     @return: string of full directory name
244
245     """
246
247     ebuild_dir = os.path.join(overlay, category, pn)
248     if not os.path.isdir(ebuild_dir):
249         try:
250             os.makedirs(ebuild_dir)
251         except OSError, err:
252             #XXX Use logger
253             LOGGER.error(err)
254             sys.exit(2)
255     return ebuild_dir
256
257
258 def find_egg_info_dir(root):
259     """
260     Locate all files matching supplied filename pattern in and below
261     supplied root directory.
262     """
263     for path, dirs, files in os.walk(os.path.abspath(root)):
264         for this_dir in dirs:
265             if this_dir.endswith(".egg-info"):
266                 return os.path.normpath(os.path.join(path, this_dir, ".."))
267
268 #Unused as of now. Could be used to find setup.py
269 #def find_files(pattern, root):
270 #    """
271 #    Locate all files matching supplied filename pattern in and below
272 #    supplied root directory.
273 #    """
274 #    for path, dirs, files in os.walk(os.path.abspath(root)):
275 #        for filename in fnmatch.filter(dirs, pattern):
276 #            yield os.path.join(path, filename)
277 if __name__ == '__main__':
278     print get_repo_names()