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