]> git.ipfire.org Git - pakfire.git/blame - pakfire/base.py
Remove pakfire-repo command.
[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
063606f6
MT
75 def create_request(self, builder=False):
76 request = satsolver.Request(self.pool)
77
78 # Add multiinstall information.
79 for solv in PAKFIRE_MULTIINSTALL:
80 request.noobsoletes(solv)
81
82 return request
7c8f2953 83
018127aa
MT
84 def create_relation(self, s):
85 assert s
86
87 if s.startswith("/"):
88 return satsolver.Relation(self.pool, s)
89
90 for pattern, type in self.RELATIONS:
91 if not pattern in s:
92 continue
93
94 name, version = s.split(pattern, 1)
95
96 return satsolver.Relation(self.pool, name, version, type)
97
98 return satsolver.Relation(self.pool, s)
99
7c8f2953
MT
100 def destroy(self):
101 if not self.path == "/":
102 util.rm(self.path)
103
104 @property
105 def supported_arches(self):
3ad4bb5a 106 return self.config.supported_arches
7c8f2953
MT
107
108 def check_root_user(self):
109 if not os.getuid() == 0 or not os.getgid() == 0:
110 raise Exception, "You must run pakfire as the root user."
111
112 def check_build_mode(self):
113 """
114 Check if we are running in build mode.
115 Otherwise, raise an exception.
116 """
6557ff4c 117 if not self.mode == "builder":
7c8f2953
MT
118 raise BuildError, "Cannot build when not in build mode."
119
120 def check_host_arch(self, arch):
121 """
122 Check if we can build for arch.
123 """
7c8f2953
MT
124 # If no arch was given on the command line we build for our
125 # own arch which should always work.
126 if not arch:
127 return True
128
3ad4bb5a 129 if not self.config.host_supports_arch(arch):
7c8f2953
MT
130 raise BuildError, "Cannot build for the target architecture: %s" % arch
131
132 raise BuildError, arch
18edfe75 133
7b1e25fd
MT
134 def check_is_ipfire(self):
135 ret = os.path.exists("/etc/ipfire-release")
136
137 if not ret:
138 raise NotAnIPFireSystemError, "You can run pakfire only on an IPFire system"
139
6557ff4c
MT
140 @property
141 def builder(self):
142 # XXX just backwards compatibility
143 return self.mode == "builder"
144
18edfe75 145 def install(self, requires):
ae20b05f 146 # Create a new request.
84680c15 147 request = self.create_request()
18edfe75 148 for req in requires:
b34c426d 149 req = self.create_relation(req)
ae20b05f 150 request.install(req)
18edfe75 151
ae20b05f 152 # Do the solving.
84680c15
MT
153 solver = self.create_solver()
154 t = solver.solve(request)
18edfe75 155
ae20b05f 156 if not t:
c9a51ed9 157 logging.info(_("Nothing to do"))
18edfe75
MT
158 return
159
c0fd807c
MT
160 # Ask if the user acknowledges the transaction.
161 if not t.cli_yesno():
162 return
163
164 # Run the transaction.
ae20b05f 165 t.run()
18edfe75 166
e0b99370 167 def localinstall(self, files):
de7fba8f 168 repo_name = repo_desc = "localinstall"
e0b99370
MT
169
170 # Create a new repository that holds all packages we passed on
171 # the commandline.
022c792a
MT
172 repo = repository.RepositoryDir(self, repo_name, repo_desc,
173 os.path.join(LOCAL_TMP_PATH, "repo_%s" % util.random_string()))
e0b99370 174
022c792a
MT
175 # Register the repository.
176 self.repos.add_repo(repo)
e0b99370 177
022c792a
MT
178 try:
179 # Add all packages to the repository index.
180 for file in files:
181 repo.collect_packages(file)
e0b99370 182
022c792a
MT
183 # Break if no packages were added at all.
184 if not len(repo):
185 logging.critical(_("There are no packages to install."))
186 return
e0b99370 187
022c792a
MT
188 # Create a new request that installs all solvables from the
189 # repository.
190 request = self.create_request()
191 for solv in [p.solvable for p in repo]:
192 request.install(solv)
e0b99370 193
022c792a
MT
194 solver = self.create_solver()
195 t = solver.solve(request)
e0b99370 196
022c792a
MT
197 # If solving was not possible, we exit here.
198 if not t:
c9a51ed9 199 logging.info(_("Nothing to do"))
022c792a 200 return
c0fd807c 201
022c792a
MT
202 # Ask the user if this is okay.
203 if not t.cli_yesno():
204 return
205
206 # If okay, run the transcation.
207 t.run()
208
209 finally:
210 # Remove the temporary copy of the repository we have created earlier.
211 repo.remove()
e0b99370 212
18edfe75 213 def update(self, pkgs):
84680c15 214 request = self.create_request()
0c1a80b8 215
6c395339
MT
216 # If there are given any packets on the command line, we will
217 # only update them. Otherwise, we update the whole system.
218 if pkgs:
219 update = False
220 for pkg in pkgs:
b34c426d 221 pkg = self.create_relation(pkg)
6c395339
MT
222 request.update(pkg)
223 else:
224 update = True
0c1a80b8 225
84680c15 226 solver = self.create_solver()
6c395339 227 t = solver.solve(request, update=update)
0c1a80b8
MT
228
229 if not t:
c9a51ed9 230 logging.info(_("Nothing to do"))
0c1a80b8
MT
231 return
232
c0fd807c
MT
233 # Ask the user if the transaction is okay.
234 if not t.cli_yesno():
235 return
0c1a80b8 236
c0fd807c 237 # Run the transaction.
0c1a80b8 238 t.run()
18edfe75 239
a39fd08b
MT
240 def remove(self, pkgs):
241 # Create a new request.
84680c15 242 request = self.create_request()
a39fd08b 243 for pkg in pkgs:
b34c426d 244 pkg = self.create_relation(pkg)
a39fd08b
MT
245 request.remove(pkg)
246
247 # Solve the request.
84680c15 248 solver = self.create_solver()
6eb8f338 249 t = solver.solve(request, uninstall=True)
a39fd08b
MT
250
251 if not t:
c9a51ed9 252 logging.info(_("Nothing to do"))
a39fd08b
MT
253 return
254
c0fd807c
MT
255 # Ask the user if okay.
256 if not t.cli_yesno():
257 return
258
259 # Process the transaction.
a39fd08b
MT
260 t.run()
261
c1e7f927 262 def info(self, patterns):
18edfe75
MT
263 pkgs = []
264
1f27e8fe
MT
265 # For all patterns we run a single search which returns us a bunch
266 # of solvables which are transformed into Package objects.
18edfe75 267 for pattern in patterns:
1f27e8fe
MT
268 solvs = self.pool.search(pattern, satsolver.SEARCH_GLOB, "solvable:name")
269
270 for solv in solvs:
e1972777
MT
271 pkg = packages.SolvPackage(self, solv)
272 if pkg in pkgs:
273 continue
274
275 pkgs.append(pkg)
18edfe75 276
e1972777 277 return sorted(pkgs)
18edfe75
MT
278
279 def search(self, pattern):
280 # Do the search.
c2e67297 281 pkgs = {}
884cd2fb 282 for solv in self.pool.search(pattern, satsolver.SEARCH_STRING|satsolver.SEARCH_FILES):
c2e67297 283 pkg = packages.SolvPackage(self, solv)
18edfe75 284
c2e67297
MT
285 # Check, if a package with the name is already in the resultset
286 # and always replace older ones by more recent ones.
287 if pkgs.has_key(pkg.name):
288 if pkgs[pkg.name] < pkg:
289 pkgs[pkg.name] = pkg
290 else:
291 pkgs[pkg.name] = pkg
292
293 # Return a list of the packages, alphabetically sorted.
294 return sorted(pkgs.values())
18edfe75
MT
295
296 def groupinstall(self, group):
297 pkgs = self.grouplist(group)
298
299 self.install(pkgs)
300
301 def grouplist(self, group):
fec9a917
MT
302 pkgs = []
303
304 for solv in self.pool.search(group, satsolver.SEARCH_SUBSTRING, "solvable:group"):
305 pkg = packages.SolvPackage(self, solv)
18edfe75 306
fec9a917
MT
307 if group in pkg.groups and not pkg.name in pkgs:
308 pkgs.append(pkg.name)
18edfe75 309
fec9a917 310 return sorted(pkgs)
18edfe75
MT
311
312 @staticmethod
dacaa18b 313 def build(pkg, resultdirs=None, shell=False, **kwargs):
18edfe75
MT
314 if not resultdirs:
315 resultdirs = []
316
317 b = builder.Builder(pkg, **kwargs)
318 p = b.pakfire
319
320 # Always include local repository.
321 resultdirs.append(p.repos.local_build.path)
322
323 try:
324 b.prepare()
325 b.extract()
326 b.build()
327 b.install_test()
328
329 # Copy-out all resultfiles
330 for resultdir in resultdirs:
331 if not resultdir:
332 continue
333
334 b.copy_result(resultdir)
335
336 except BuildError:
dacaa18b
MT
337 if shell:
338 b.shell()
339 else:
340 raise
18edfe75
MT
341
342 finally:
343 b.destroy()
344
345 @staticmethod
346 def shell(pkg, **kwargs):
347 b = builder.Builder(pkg, **kwargs)
348
349 try:
350 b.prepare()
351 b.extract()
352 b.shell()
353 finally:
354 b.destroy()
355
356 @staticmethod
50f96897
MT
357 def dist(pkgs, resultdirs=None, **pakfire_args):
358 # Create a builder with empty package.
359 b = builder.Builder(None, **pakfire_args)
18edfe75
MT
360 p = b.pakfire
361
362 if not resultdirs:
363 resultdirs = []
364
365 # Always include local repository
366 resultdirs.append(p.repos.local_build.path)
367
368 try:
369 b.prepare()
18edfe75 370
50f96897
MT
371 for pkg in pkgs:
372 b.pkg = pkg
18edfe75 373
50f96897 374 b.extract(build_deps=False)
18edfe75 375
50f96897
MT
376 # Run the actual dist.
377 b.dist()
378
379 # Copy-out all resultfiles
380 for resultdir in resultdirs:
381 if not resultdir:
382 continue
383
384 b.copy_result(resultdir)
385
386 # Cleanup the stuff that the package left.
387 b.cleanup()
18edfe75
MT
388 finally:
389 b.destroy()
390
391 def provides(self, patterns):
392 pkgs = []
393 for pattern in patterns:
e1972777
MT
394 for pkg in self.repos.whatprovides(pattern):
395 if pkg in pkgs:
396 continue
18edfe75 397
e1972777 398 pkgs.append(pkg)
18edfe75 399
e1972777 400 return sorted(pkgs)
18edfe75 401
8276111d
MT
402 def repo_create(self, path, input_paths, type="binary"):
403 assert type in ("binary", "source",)
404
c605d735 405 repo = repository.RepositoryDir(
18edfe75
MT
406 self,
407 name="new",
408 description="New repository.",
409 path=path,
8276111d 410 type=type,
18edfe75
MT
411 )
412
413 for input_path in input_paths:
c605d735 414 repo.collect_packages(input_path)
18edfe75
MT
415
416 repo.save()
417
8276111d
MT
418 return repo
419
18edfe75 420 def repo_list(self):
c605d735 421 return [r for r in self.repos]
31267a64
MT
422
423 def clean_all(self):
424 logging.debug("Cleaning up everything...")
425
426 # Clean up repository caches.
427 self.repos.clean()
35d89fd7
MT
428
429 def check(self, downgrade=True, uninstall=True):
430 """
431 Try to fix any errors in the system.
432 """
433 # Detect any errors in the dependency tree.
434 # For that we create an empty request and solver and try to solve
435 # something.
436 request = self.create_request()
437 solver = self.create_solver()
438
439 # XXX the solver does crash if we call it with fix_system=1,
440 # allow_downgrade=1 and uninstall=1. Need to fix this.
441 allow_downgrade = False
442 uninstall = False
443
444 t = solver.solve(request, fix_system=True, allow_downgrade=downgrade,
445 uninstall=uninstall)
446
447 if not t:
448 logging.info(_("Everything is fine."))
449 return
450
451 # Ask the user if okay.
452 if not t.cli_yesno():
453 return
454
455 # Process the transaction.
456 t.run()