aboutsummaryrefslogtreecommitdiff
blob: de9f7bd99dd1989e2856022403a0dcc40ea85585 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
#!/usr/bin/python -O
#
# $Header: $
# Authors:
# Eldad Zack <eldad@gentoo.org> - Original application
# Robin H. Johnson <robbat2@gentoo.org> - SLOT and masking support, and many
# other improvements.
#
# Present Maintainer: robbat2@gentoo.org
#
# earch: Gentoo last arch keyword checking tool, with SLOT and masking support
# version 0.9.5

import sys
import os
import re
import string
import readline
import getopt
from portage.output import *

# we import portage later
sys.path.insert(0, "/usr/lib/portage/pym")

version = '0.9'

def earch_main():

	# force to false
	opt_masking_reasons = False
	opt_hide_masked = False
	opt_category = False
	opt_nocolor = False
	opt_help = False
 	opt_remove_pkgs = False
 	opt_one_slot = False
	opt_ignore_redundant = False
	opt_follow_etc_portage = False
	opt_version = False
	opt_quiet = False
	opt_slot = []
	# process commandline
	try:
		(opts,args) = getopt.gnu_getopt(sys.argv[1:],'CcfhHims:orv',['nocolor','quiet','category','masking-reasons','help','hide-masked','version','slot=','remove-pkgs','ignore-redundant','follow-etc-portage'])
		for optkey,optvalue in opts:
			if optkey == '--nocolor':
				opt_nocolor = True
			if optkey == '--quiet':
				opt_quiet = True
			if optkey == '-c' or optkey == '--category':
				opt_category = True
			if optkey == '-f' or optkey == '--follow-etc-portage':
				opt_follow_etc_portage = True
			if optkey == '-m' or optkey == '--masking-reasons':
				opt_masking_reasons = True
			if optkey == '-h' or optkey == '--help':
				opt_help = True
			if optkey == '-H' or optkey == '--hide-masked':
				opt_hide_masked = True
			if optkey == '-i' or optkey == '--ignore-redundant':
				opt_ignore_redundant = True
			if optkey == '-o' or optkey == '--one-slot':
				opt_one_slot = True
			if optkey == '-r' or optkey == '--remove-pkgs':
				opt_remove_pkgs = True
			if optkey == '-s' or optkey == '--slot':
				opt_slot = re.split(',',optvalue)
			if optkey == '-v' or optkey == '--version':
				opt_version = True
	except getopt.GetoptError:
		opt_help = True

	# don't hide masked packages if we are printing reasons
	if opt_masking_reasons:
		opt_hide_masked = False
	# do help
	if opt_help:
		earch_help()
		return
	# version output
	if opt_version:
		earch_version()
		return

	if not opt_follow_etc_portage:
		# this is a cheat to get portage to ignore local user profiles
		# it must come before the portage module is imported
		os.environ["PORTAGE_CALLER"]="repoman"

	# generate
	(ebuildlist,ebuilddata,pkgkeywords) = earch_data_generate(args,slots=opt_slot,hide_masked=opt_hide_masked,include_category=opt_category,ignore_redundant=opt_ignore_redundant,one_slot=opt_one_slot,nocolor=opt_nocolor,quiet=opt_quiet)

	if opt_remove_pkgs:
		earch_remove_pkgs(ebuildlist,ebuilddata,pkgkeywords)
		return

	earch_data_output(ebuildlist,ebuilddata,pkgkeywords,show_masking_reason=opt_masking_reasons)

def earch_version():
	print 'earch %s' % (version)

def earch_help():
	earch_version
	#'chHmrs:v',['category','masking-reasons','help','hide-masked','version','slot=','remove-pkgs'])
	print 'Gentoo last arch keyword checking tool, with SLOT and masking support'
	print
	print 'Usage:'
	print 'earch [opts] [CP]'
	print 'If CP is omitted, the current directory is used.'
	print
	print 'Options:'
	print '-c|--category'
	print '  Include category in output.'
	print
	print '-f|--follow-etc-portage'
	print '  By default, earch acts like repoman and ignores /etc/portage.'
	print '  This option disables that behavior.'
	print
	print '-h|--help'
	print '  This help page.'
	print
	print '-H|--hide-masked'
	print '  Exclude all masked versions from output.'
	print
	print '-i|--ignore-redundant'
	print '  Exclude redudant versions from keyword output.'
	print '  Redundant versions are those output by -r.'
	print
	print '-m|--masking-reason'
	print '  For all masked versions, print masking reason. '
	print '  Disables other output.'
	print
	print '-o|--one-slot'
	print '  Consider SLOT values as if they were all equal.'
	print '  This effectively disables SLOTs from consideration.'
	print
	print '-r|--remove-pkgs'
	print '  Show all redundant versions to clean from the tree.' 
	print
	print '-s|--slot <SLOT,...>'
	print '  SLOT values to provide output for, seperated by commas.'
	print
	print '--nocolor'
	print '  Disable ANSI color output'
	print
	print '--quiet'
	print '  Squelch Portage python warnings'
	print
	print '-v|--version'
	print '  earch version output.'
	print
	print 'Explaination of output:'
	print '# earch [$CATEGORY/$PN]'
	print '$PF[$SLOT]: $KEYWORDS'
	print 'If a specific version is masked, a (M) will preceed the keywords.'

def earch_manual_getkeywords(pkg):
	file = open(pkg + ".ebuild")
	for line in file.readlines():
		line = string.rstrip(line)
		if re.match("^KEYWORDS=",line):
			keywords = re.split("\"",line)[1]
			file.close
			return re.split(" ",keywords)

def earch_data_generate(args,slots=[],hide_masked=False,include_category=False,ignore_redundant=False,one_slot=False,nocolor=False,quiet=False):
	# This makes portage really quiet	
	stderr = sys.stderr
	if quiet:
		sys.stderr = open('/dev/null', 'w')
	import portage
	# and reset it
	sys.stderr = stderr
		
	# disable color as needed
	# this is actually out of place, but still the best place to run it
	# to avoid importing portage twice
	if nocolor:
		portage.settings.unlock()
		portage.settings["NOCOLOR"] = '1'
		portage.settings.lock()
	if (not sys.stdout.isatty()) or (portage.settings["NOCOLOR"] in ["yes","true","1"]):
	        portage.output.nocolor()

	portdir = portage.settings["PORTDIR"]
	portdb = portage.portdbapi(None, portage.settings)
	archslotdict = {}
	ebuildlist = []
	ebuilddata = {}
	pkgkeywords = {}

	if len(args) < 1:
		workdir = "."
	else:
		workdir = portdir + "/" + args[0]
		try:
			pkg = portage.portdb.xmatch("match-all", args[0])
			catpkg = portage.pkgsplit(pkg[0])[0]
			workdir = portdir + "/" + catpkg
		except:
			pass

		try:
			os.chdir(workdir)
		except:
			print red("!!!" + " Can't find " + workdir)
			sys.exit(1)

	cp_path = os.path.abspath(workdir)
	cp = re.sub(portdir+'/?','', cp_path, count=1)
	#print cp
	cat = re.split('/',cp)[0]
	#print c

	for file in os.listdir(workdir):
		if re.search("\.ebuild$",file):
			s = re.split("\.ebuild$",file)[0]
			ebuildlist.append(s)

	ebuildlist.sort(lambda x,y: portage.pkgcmp(portage.pkgsplit(x),portage.pkgsplit(y)))

	ebuildlist2 = []
	for pkg in ebuildlist:
		cpv = cat+'/'+pkg
		aux = portdb.aux_get(cpv,['SLOT','KEYWORDS'])
		if one_slot:
			slot = '0'
		else:
			slot = aux[0]
		keywords = re.split(' ',aux[1])
		raw_keywords = earch_manual_getkeywords(pkg)
		masking = portage.getmaskingstatus(cpv)
		is_masked = len(masking) > 0
		if hide_masked and is_masked:
			continue
		if len(slots) > 0 and not slot in slots:
			continue
		if include_category:
			pkg = cpv
		effective_keywords = []
		if len(masking) > 0:
			prefix = 'M/'
		else:
			prefix = ''
		for key in keywords:
			if len(key) == 0:
				#print 'Bad key!',pkg
				continue
			effective_keywords.append(prefix+key)
			if key[0] != '-' and key[0] != '~':
				effective_keywords.append(prefix+'~'+key)
		ebuildlist2.append(pkg)
		# store this for usage later
		ebuilddata[pkg] = {'SLOT':slot, 'KEYWORDS':keywords, 'MASKING':masking, 'EFFECTIVE_KEYWORDS':effective_keywords,'RAW_KEYWORDS':raw_keywords}

	ebuildlist = ebuildlist2

	# build archslotdict
	for pkg in ebuildlist:
		slot = ebuilddata[pkg]['SLOT']
		keywords = ebuilddata[pkg]['EFFECTIVE_KEYWORDS']
		# ensure the second level tree exists
		try:
			archslotdict[slot]
		except KeyError:
			archslotdict[slot] = {}
		# now actually populate the second level tree
		for arch in keywords:
			archslotdict[slot][arch] = pkg
	

	# build pkgkeywords from archslotdict
	for pkg in ebuildlist:
		slot = ebuilddata[pkg]['SLOT']
		ek = ebuilddata[pkg]['EFFECTIVE_KEYWORDS']
		tpk = {}
		for value,key in archslotdict[slot].iteritems():
			#value = re.sub('M/','',value)
			if (key == pkg):
				tpk[re.sub('M/','',value)] = True
		# include all -arch and -* flags
		for k in ek:
			if k[0] == '-' or k[0:3] == 'M/-':
				tpk[re.sub('M/','',k)] = True
		pkgkeywords[pkg] = tpk.keys()

	# clean out items we are ignoring
	if ignore_redundant:
		ebuildlist2 = []
		for pkg in ebuildlist:
			if len(pkgkeywords[pkg]) > 0:
				ebuildlist2.append(pkg)
		ebuildlist = ebuildlist2

	# shorten the pkgkeywords to remove cases where a package is both ~arch
	# and arch.
	for pkg in ebuildlist:
		keywords =  pkgkeywords[pkg]
		newkeywords = []
		for k in keywords:
			if k[0] == '~' and k[1:] in keywords:
				continue
			else:
				newkeywords.append(k)
		pkgkeywords[pkg] = newkeywords
	
	# sort pkgkeywords to reflect the actual flags
	for pkg in ebuildlist:
		pk = pkgkeywords[pkg]
		npk = pk
		rk = ebuilddata[pkg]['RAW_KEYWORDS']
		if rk is not None:
			def earch_sort_cmp(x,y,src=rk):
				if x is None and y is None:
					return 0
				elif x is None and y is not None:
					return 1
				elif x is not None and y is None:
					return -1
				# nobody is None
				ix = src.index(x)
				iy = src.index(y)
				if(ix > iy):
					return 1
				elif(ix < iy):
					return -1
				else:
					return 0
			npk.sort(cmp=earch_sort_cmp)
		pkgkeywords[pkg] = npk
		
	return (ebuildlist,ebuilddata,pkgkeywords)


def earch_remove_pkgs(ebuildlist,ebuilddata,pkgkeywords):
	for pkg in pkgkeywords.keys():
		if len(pkgkeywords[pkg]) == 0:
			print pkg

def earch_data_output(ebuildlist,ebuilddata,pkgkeywords,show_masking_reason=False):

	for pkg in ebuildlist:
		slot = ebuilddata[pkg]['SLOT']
		masking = ebuilddata[pkg]['MASKING']
		is_masked = len(masking) > 0
		if is_masked:
			m = red(' (M)')
		else:
			m = ''
		print '%s[%s]:%s'%(white(pkg),yellow(slot),m),
		# print masking reason only
		if show_masking_reason:
			print '%s' % (string.join(masking,', '))
			continue
		earch_data_output_keywords(pkgkeywords[pkg])

def earch_data_output_keywords(keywords):
	# force -* to the first item
	if '-*' in keywords:
		earch_data_output_keyword('-*')
	for value in keywords:
		if value == '-*':
			continue
		earch_data_output_keyword(value)
	print

def earch_data_output_keyword(keyword):
	if keyword[0] == '-':
		print red(keyword),
	elif keyword[0] == '~':
		print blue(keyword),
	else:
		print green(keyword),

if __name__ == "__main__":
	earch_main()