]> git.ipfire.org Git - pakfire.git/blame - pakfire/base.py
Make search returning always the most recent version of a package.
[pakfire.git] / pakfire / base.py
CommitLineData
47a4cb89
MT
1#!/usr/bin/python
2
7c8f2953
MT
3import logging
4import os
5import random
6import string
7
18edfe75 8import builder
53bb7960 9import config
7c8f2953
MT
10import distro
11import logger
53bb7960 12import repository
ae20b05f 13import packages
c605d735 14import satsolver
7c8f2953
MT
15import util
16
7c8f2953 17from constants import *
7c8f2953
MT
18from i18n import _
19
7c8f2953 20class Pakfire(object):
018127aa
MT
21 RELATIONS = (
22 (">=", satsolver.REL_GE,),
23 ("<=", satsolver.REL_LE,),
24 ("=" , satsolver.REL_EQ,),
25 ("<" , satsolver.REL_LT,),
26 (">" , satsolver.REL_GT,),
27 )
28
eb34496a 29 def __init__(self, mode=None, path="/", configs=[],
715d7009
MT
30 enable_repos=None, disable_repos=None,
31 distro_config=None):
6557ff4c
MT
32
33 # Set the mode.
34 assert mode in ("normal", "builder", "repo", "server", "master")
35 self.mode = mode
36
7c8f2953
MT
37 # Check if we are operating as the root user.
38 self.check_root_user()
39
40 # The path where we are operating in.
6557ff4c
MT
41 self.path = path
42
43 # Configure the instance of Pakfire we just started.
44 if mode == "builder":
7c8f2953 45 self.path = os.path.join(BUILD_ROOT, util.random_string())
7c8f2953 46
6557ff4c 47 elif mode == "normal":
7b1e25fd 48 # check if we are actually running on an ipfire system.
0891edd2
MT
49 if self.path == "/":
50 self.check_is_ipfire()
7c8f2953
MT
51
52 # Read configuration file(s)
6557ff4c 53 self.config = config.Config(type=mode)
7c8f2953
MT
54 for filename in configs:
55 self.config.read(filename)
56
57 # Setup the logger
58 logger.setup_logging(self.config)
59 self.config.dump()
60
61 # Get more information about the distribution we are running
62 # or building
53bb7960 63 self.distro = distro.Distribution(self, distro_config)
c605d735 64 self.pool = satsolver.Pool(self.distro.arch)
ae20b05f
MT
65 self.repos = repository.Repositories(self,
66 enable_repos=enable_repos, disable_repos=disable_repos)
7c8f2953 67
60845a36
MT
68 def __del__(self):
69 # Reset logging.
70 logger.setup_logging()
71
c605d735
MT
72 def create_solver(self):
73 return satsolver.Solver(self, self.pool)
74
75 def create_request(self):
76 return satsolver.Request(self.pool)
7c8f2953 77
018127aa
MT
78 def create_relation(self, s):
79 assert s
80
81 if s.startswith("/"):
82 return satsolver.Relation(self.pool, s)
83
84 for pattern, type in self.RELATIONS:
85 if not pattern in s:
86 continue
87
88 name, version = s.split(pattern, 1)
89
90 return satsolver.Relation(self.pool, name, version, type)
91
92 return satsolver.Relation(self.pool, s)
93
7c8f2953
MT
94 def destroy(self):
95 if not self.path == "/":
96 util.rm(self.path)
97
98 @property
99 def supported_arches(self):
3ad4bb5a 100 return self.config.supported_arches
7c8f2953
MT
101
102 def check_root_user(self):
103 if not os.getuid() == 0 or not os.getgid() == 0:
104 raise Exception, "You must run pakfire as the root user."
105
106 def check_build_mode(self):
107 """
108 Check if we are running in build mode.
109 Otherwise, raise an exception.
110 """
6557ff4c 111 if not self.mode == "builder":
7c8f2953
MT
112 raise BuildError, "Cannot build when not in build mode."
113
114 def check_host_arch(self, arch):
115 """
116 Check if we can build for arch.
117 """
7c8f2953
MT
118 # If no arch was given on the command line we build for our
119 # own arch which should always work.
120 if not arch:
121 return True
122
3ad4bb5a 123 if not self.config.host_supports_arch(arch):
7c8f2953
MT
124 raise BuildError, "Cannot build for the target architecture: %s" % arch
125
126 raise BuildError, arch
18edfe75 127
7b1e25fd
MT
128 def check_is_ipfire(self):
129 ret = os.path.exists("/etc/ipfire-release")
130
131 if not ret:
132 raise NotAnIPFireSystemError, "You can run pakfire only on an IPFire system"
133
6557ff4c
MT
134 @property
135 def builder(self):
136 # XXX just backwards compatibility
137 return self.mode == "builder"
138
18edfe75 139 def install(self, requires):
ae20b05f 140 # Create a new request.
84680c15 141 request = self.create_request()
18edfe75 142 for req in requires:
ae20b05f 143 request.install(req)
18edfe75 144
ae20b05f 145 # Do the solving.
84680c15
MT
146 solver = self.create_solver()
147 t = solver.solve(request)
18edfe75 148
ae20b05f 149 if not t:
18edfe75
MT
150 return
151
c0fd807c
MT
152 # Ask if the user acknowledges the transaction.
153 if not t.cli_yesno():
154 return
155
156 # Run the transaction.
ae20b05f 157 t.run()
18edfe75 158
e0b99370 159 def localinstall(self, files):
de7fba8f 160 repo_name = repo_desc = "localinstall"
e0b99370
MT
161
162 # Create a new repository that holds all packages we passed on
163 # the commandline.
022c792a
MT
164 repo = repository.RepositoryDir(self, repo_name, repo_desc,
165 os.path.join(LOCAL_TMP_PATH, "repo_%s" % util.random_string()))
e0b99370 166
022c792a
MT
167 # Register the repository.
168 self.repos.add_repo(repo)
e0b99370 169
022c792a
MT
170 try:
171 # Add all packages to the repository index.
172 for file in files:
173 repo.collect_packages(file)
e0b99370 174
022c792a
MT
175 # Break if no packages were added at all.
176 if not len(repo):
177 logging.critical(_("There are no packages to install."))
178 return
e0b99370 179
022c792a
MT
180 # Create a new request that installs all solvables from the
181 # repository.
182 request = self.create_request()
183 for solv in [p.solvable for p in repo]:
184 request.install(solv)
e0b99370 185
022c792a
MT
186 solver = self.create_solver()
187 t = solver.solve(request)
e0b99370 188
022c792a
MT
189 # If solving was not possible, we exit here.
190 if not t:
191 return
c0fd807c 192
022c792a
MT
193 # Ask the user if this is okay.
194 if not t.cli_yesno():
195 return
196
197 # If okay, run the transcation.
198 t.run()
199
200 finally:
201 # Remove the temporary copy of the repository we have created earlier.
202 repo.remove()
e0b99370 203
18edfe75 204 def update(self, pkgs):
84680c15 205 request = self.create_request()
0c1a80b8 206
6c395339
MT
207 # If there are given any packets on the command line, we will
208 # only update them. Otherwise, we update the whole system.
209 if pkgs:
210 update = False
211 for pkg in pkgs:
212 request.update(pkg)
213 else:
214 update = True
0c1a80b8 215
84680c15 216 solver = self.create_solver()
6c395339 217 t = solver.solve(request, update=update)
0c1a80b8
MT
218
219 if not t:
220 return
221
c0fd807c
MT
222 # Ask the user if the transaction is okay.
223 if not t.cli_yesno():
224 return
0c1a80b8 225
c0fd807c 226 # Run the transaction.
0c1a80b8 227 t.run()
18edfe75 228
a39fd08b
MT
229 def remove(self, pkgs):
230 # Create a new request.
84680c15 231 request = self.create_request()
a39fd08b
MT
232 for pkg in pkgs:
233 request.remove(pkg)
234
235 # Solve the request.
84680c15 236 solver = self.create_solver()
6eb8f338 237 t = solver.solve(request, uninstall=True)
a39fd08b
MT
238
239 if not t:
240 return
241
c0fd807c
MT
242 # Ask the user if okay.
243 if not t.cli_yesno():
244 return
245
246 # Process the transaction.
a39fd08b
MT
247 t.run()
248
c1e7f927 249 def info(self, patterns):
18edfe75
MT
250 pkgs = []
251
1f27e8fe
MT
252 # For all patterns we run a single search which returns us a bunch
253 # of solvables which are transformed into Package objects.
18edfe75 254 for pattern in patterns:
1f27e8fe
MT
255 solvs = self.pool.search(pattern, satsolver.SEARCH_GLOB, "solvable:name")
256
257 for solv in solvs:
258 pkgs.append(packages.SolvPackage(self, solv))
18edfe75
MT
259
260 return packages.PackageListing(pkgs)
261
262 def search(self, pattern):
263 # Do the search.
c2e67297 264 pkgs = {}
884cd2fb 265 for solv in self.pool.search(pattern, satsolver.SEARCH_STRING|satsolver.SEARCH_FILES):
c2e67297 266 pkg = packages.SolvPackage(self, solv)
18edfe75 267
c2e67297
MT
268 # Check, if a package with the name is already in the resultset
269 # and always replace older ones by more recent ones.
270 if pkgs.has_key(pkg.name):
271 if pkgs[pkg.name] < pkg:
272 pkgs[pkg.name] = pkg
273 else:
274 pkgs[pkg.name] = pkg
275
276 # Return a list of the packages, alphabetically sorted.
277 return sorted(pkgs.values())
18edfe75
MT
278
279 def groupinstall(self, group):
280 pkgs = self.grouplist(group)
281
282 self.install(pkgs)
283
284 def grouplist(self, group):
fec9a917
MT
285 pkgs = []
286
287 for solv in self.pool.search(group, satsolver.SEARCH_SUBSTRING, "solvable:group"):
288 pkg = packages.SolvPackage(self, solv)
18edfe75 289
fec9a917
MT
290 if group in pkg.groups and not pkg.name in pkgs:
291 pkgs.append(pkg.name)
18edfe75 292
fec9a917 293 return sorted(pkgs)
18edfe75
MT
294
295 @staticmethod
dacaa18b 296 def build(pkg, resultdirs=None, shell=False, **kwargs):
18edfe75
MT
297 if not resultdirs:
298 resultdirs = []
299
300 b = builder.Builder(pkg, **kwargs)
301 p = b.pakfire
302
303 # Always include local repository.
304 resultdirs.append(p.repos.local_build.path)
305
306 try:
307 b.prepare()
308 b.extract()
309 b.build()
310 b.install_test()
311
312 # Copy-out all resultfiles
313 for resultdir in resultdirs:
314 if not resultdir:
315 continue
316
317 b.copy_result(resultdir)
318
319 except BuildError:
dacaa18b
MT
320 if shell:
321 b.shell()
322 else:
323 raise
18edfe75
MT
324
325 finally:
326 b.destroy()
327
328 @staticmethod
329 def shell(pkg, **kwargs):
330 b = builder.Builder(pkg, **kwargs)
331
332 try:
333 b.prepare()
334 b.extract()
335 b.shell()
336 finally:
337 b.destroy()
338
339 @staticmethod
50f96897
MT
340 def dist(pkgs, resultdirs=None, **pakfire_args):
341 # Create a builder with empty package.
342 b = builder.Builder(None, **pakfire_args)
18edfe75
MT
343 p = b.pakfire
344
345 if not resultdirs:
346 resultdirs = []
347
348 # Always include local repository
349 resultdirs.append(p.repos.local_build.path)
350
351 try:
352 b.prepare()
18edfe75 353
50f96897
MT
354 for pkg in pkgs:
355 b.pkg = pkg
18edfe75 356
50f96897 357 b.extract(build_deps=False)
18edfe75 358
50f96897
MT
359 # Run the actual dist.
360 b.dist()
361
362 # Copy-out all resultfiles
363 for resultdir in resultdirs:
364 if not resultdir:
365 continue
366
367 b.copy_result(resultdir)
368
369 # Cleanup the stuff that the package left.
370 b.cleanup()
18edfe75
MT
371 finally:
372 b.destroy()
373
374 def provides(self, patterns):
375 pkgs = []
376 for pattern in patterns:
c605d735 377 pkgs += self.repos.whatprovides(pattern)
18edfe75
MT
378
379 pkgs = packages.PackageListing(pkgs)
380 #pkgs.unique()
381
382 return pkgs
383
384 def requires(self, patterns):
385 pkgs = []
386 for pattern in patterns:
ae20b05f 387 pkgs += self.repos.get_by_requires(pattern)
18edfe75
MT
388
389 pkgs = packages.PackageListing(pkgs)
390 #pkgs.unique()
391
392 return pkgs
393
8276111d
MT
394 def repo_create(self, path, input_paths, type="binary"):
395 assert type in ("binary", "source",)
396
c605d735 397 repo = repository.RepositoryDir(
18edfe75
MT
398 self,
399 name="new",
400 description="New repository.",
401 path=path,
8276111d 402 type=type,
18edfe75
MT
403 )
404
405 for input_path in input_paths:
c605d735 406 repo.collect_packages(input_path)
18edfe75
MT
407
408 repo.save()
409
8276111d
MT
410 return repo
411
18edfe75 412 def repo_list(self):
c605d735 413 return [r for r in self.repos]
31267a64
MT
414
415 def clean_all(self):
416 logging.debug("Cleaning up everything...")
417
418 # Clean up repository caches.
419 self.repos.clean()