Move pisi-1.1_beta3 tag

This commit is contained in:
Fatih Aşıcı
2009-10-27 10:13:29 +00:00
parent 6d7ec2f6a5
commit 07ea077113
224 changed files with 0 additions and 30772 deletions
-10
View File
@@ -1,10 +0,0 @@
Authors:
Eray Özkural <eray [at] pardus.org.tr>
Barış Metin <baris [at] pardus.org.tr>
A. Murat Eren <meren [at] pardus.org.tr>
S. Çağlar Onur <caglar [at] pardus.org.tr>
Gürer Özen <gurer [at] pardus.org.tr>
İsmail Dönmez <ismail [at] pardus.org.tr>
Faik Uygur <faik [at] pardus.org.tr>
Bahadır Kandemir <bahadir [at] pardus.org.tr>
-126
View File
@@ -1,126 +0,0 @@
Like every serious project, there are guidelines.
Oooooo. "Coding Standards".
Guidelines
----------
0. Before reading any further please observe
PEP 8: Style Guide for Python Code
http://www.python.org/peps/pep-0008.html
1. When using dirnames, don't expect the dir to end
with a trailing slash, and please use the dirnames
in pisiconfig. Use util.join_path instead of os.path.join
2. Python indentation is usually 4 spaces.
3. Follow python philosophy of 'batteries included'
4. Use exceptions, don't return error codes
5. Don't make the PISI code have runtime dependencies on
a particular distribution (as much as possible).
6. Don't assume narrow use cases. Allow for a mediocre
amount of generalization in your code, for pieces that
will be required later.
7. If you are changing something, check if that change
breaks anything and fix breakage. For instance a
name. Running the tests is not always enough!
8. A good design ensures separation of concerns. Every module
has a specific documented responsibility. Don't make the
horse clean your windows.
9. To ensure readability avoid nesting python constructs
more than 3 levels deep. Python is a good language (unlike C),
so you can define inner functions in a convenient way, use
such decomposition techniques to break down your code into
manageable chunks. The worst code you can write is one huge
procedure that goes on for 1000 (or more) lines.
10. Use a particular abstraction like a class or function only
if it makes sense. Don't just define things because they can
be defined. Define only things that will/may be used.
11. If you are doing an expensive task like searching through
10000 text chunks, please use an efficient data structure
and algorithm. We are not MS engineers who know no data
structure beyond doubly linked lists and no algorithm beyond
quicksort.
12. Resist the temptation to develop kludges and workarounds in
response to pressure. Take your time to solve the problems by
the book. The payoff comes later.
13. Same thing goes for premature optimizations. Knuth and Dijkstra
are watching over your shoulder. :)
Unit testing
------------
Unit tests are located in unittests directory. Running the tests is
trivial. But you must synchronize your code and data with the test
code, which can be a tedious work if you lose discipline.
Sample data files are located in the same directory with test modules.
For running the entire test suite, use the following command:
$ ./tests/run.py
The following command will run tests in specfiletests and archivetests
in unittests dir:
$ ./tests/run.py specfile archive
Do not depend on the output of unittests. Instead of producing an
output message/data in your tests, check the data internally. By
definition, unittest should just report succeeding and failing cases.
If you didn't, take a look at the links below for having an idea of
unit testing.
http://www.extremeprogramming.org/rules/unittests.html
http://www.extremeprogramming.org/rules/unittests2.html
Misc. Suggestions
-----------------
1. Demeter's Law
In OO programming, try to invoke Demeter's law.
One of the "rules" there is not directly accessing any
objects that are further than, 2/3 refs, away. So the
following code is OK.
destroy_system(a.system().name())
but the following isn't as robust
destroy_system(object_store.root().a.system.name())
As you can tell, this introduces too many implementation
dependencies. The rule of thumb is that, in these cases
this statement must have been elsewhere.... It may be a
good idea to not count the object scope in this case,
so in Python self.a means only one level of reference,
not two.
One quibble with this: it may be preferable not to insist
on this where it would be inefficient. So if everything
is neatly packed into one object contained in another
object, why replicate everything in the upper level? If
the semantics prevents dependency changes, then chains
of 3 or even 4 could be acceptable.
OTOH, in Python and C++, it's not always good to implement
accessor/modifier pairs for every property of an object.
It would be much simpler if you are not doing any special
processing on the property (e.g. if what the type system
does is sufficient).
The main rule of thumb in Demeter's Law is avoiding
putting more than, say, 10 methods in a class. That works
really well in practice, forcing refactoring every now
and then.
2. We all know, you're using LISP but didn't want to tell
us. Don't be scared, as a success story and for your encouragment
there are tens of people somewhere with LISP releated jobs.
3. If you are studying Data structures and Algorithms, and if
your first assignment is to implement a basic FIFO queue,
don't implement it. Just show your teacher the syntax of LISP,
tell him how beautiful it is, and show how an autistic person
can count lots of parenthesis with a "one second" look, you'll
probably get A+.
4. If you are interested in "Playstation 2 Linux Games Programming"
or "How to extend C programs with Guile", please don't exercise
your valuable skills in this project.
-346
View File
@@ -1,346 +0,0 @@
NOTE! The GPL below is copyrighted by the Free Software Foundation, but
the instance of code that it refers to (the kde programs) are copyrighted
by the authors who actually wrote it.
---------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
-16
View File
@@ -1,16 +0,0 @@
You can install PiSi on your system with a single command.
# ./setup.py install
If you are on a Pardus system, you should delete /usr/lib/pardus/pisi
when you are installing the SVN version
PiSi requires 4suite XML processing library, the newest version
bsd bindings called "bsddb3" and Pardus configuration manager COMAR
in order to run.
If you are upgrading from an old PiSi release you may have to
run the following command as root:
# pisi rebuild-db
-7
View File
@@ -1,7 +0,0 @@
recursive-include po *
recursive-include tests *.py
recursive-include tools *
recursive-include doc *
include . *.dtd
include . README NEWS CODING COPYING
-55
View File
@@ -1,55 +0,0 @@
PISI 1.0.9 25 April 2006:
* source index/database and remote builds by name
* "emerge" command, now we are just like gentoo :)
* performance and usability improvements. faster database access and
faster updates. index integrity, authenticity and compression.
smaller db files and simplification in db schema / less seeks.
correct use of berkeley DB cache.
* several other improvements and bugfixes
PISI 1.0.2 25 December 2005:
* some minor bugs were corrected
PISI 1.0.1 24 December 2005:
* bugfix release. one important improvement is preventing the accumulation
of inactive database logs (we handle catastrophic failure in
another way, by making all critical data available as xml)
PISI 1.0 21 December 2005:
* new command "check"
* a new tool "calc-build-order.py" to calculate build order
for the entire distro
* various bugfixes and improvements, no significant issues remain
PISI release candidate 2 12 December 2005:
* maintenance release for Pardus 1.0 RC1, various bugfixes
PISI release candidate 1 9 December 2005:
* a command to search package text
* better reliability through transaction support for berkeley db
* db version file
* loads of bugfixes
PISI Beta 1/2 release 17/18 November 2005:
* support for components, see the completed items in TODO
* a tool to find library dependencies
* various improvements and bugfixes
PISI Alpha 9 release 13 November 2005:
* new package: pisi.pxml
* first release of autoxml, a metaclass for high-level xml processing
* uses 4suite.org for fast xml processing
* supports multiple parsers: minidom, 4suite and in the future: piksemel
* all xml structures in PISI have been updated to use autoxml
* improvements in the ui interface
* significant fixes for Turkish translations
previous PISI releases had too many things to mention :)
-16
View File
@@ -1,16 +0,0 @@
PISI - Packages Installed Succesfully as Intended
PISI is a new package manager for the PARDUS
distribution. In Turkish PISI means "kitty", and
like a kitty, it is featureful and small.
Some of its distinctive features:
- Implemented in python
- Efficient and small
- Package sources are written in XML and python
- Fast database access implemented with berkeley DB
- Integrates low-level and high-level package operations (dependency resolution)
- Framework approach to build applications and tools upon
- Comprehensive CLI and a user-friendly qt GUI (distributed separately)
- Extremely simple package construction
-5
View File
@@ -1,5 +0,0 @@
For upgrading from pisi 1.0 and before
You have to move the packages in /var/lib/pisi under /var/lib/pisi/package and remove /var/db/pisi/ directory.
db version has changed, so a rebuild-db will be unfortunately necessary.
-247
View File
@@ -1,247 +0,0 @@
PiSi TODO List
==============
A list of tasks to accomplish, organized into priority sections
Legend:
- Todo
? Not determined if/how we have to do
/ In progress
+ Accomplished
1. Pre-Alpha
+ reading spec file
+ writing
+ files
+ metadata
+ Actions API framework
+ sample api modules (autotools)
+ unit test
+ source building
+ package module
+ extraction
+ add file / compression
+ package creation
+ install database
+ package database
+ command line interface
+ pisi-build
+ pisi-install
+ pisi-index
+ pisi-updatedb
+ single repository index
+ simple dependency checking
+ Package/Files
2. Alpha
+ multi-package dependency analysis (eray)
+ design a package operation planner
+ install/remove operations (eray)
+ command line interface:
+ pisi remove
+ svn-like CLI
+ configuration file
+ define the format of the configuration file
+ extend Config module (config.py) accordingly
+ define configuration keys (baris - meren)
+ query
+ list of installed packages (eray)
+ refactor actionsAPI (caglar)
+ fix xml indentation (meren)
+ define file types (doc, executable, conf, etc...) (baris - meren)
+ write specfile (eray)
+ file locking for database access [not tested!] (eray)
+ implement file uri
+ extend the modules dealing with files accordingly
+ ui module improvements
+ methods for interaction (yes, no questions, etc.)
+ implement source database (eray)
+ easy package preparation
+ convert ebuild to pisi
+ COMAR interface
+ what do we need to specify in a package.
+ package install: register config script
+ package remove: unregister config script
+ internet installation
+ support URI's whereever a filename is supported
+ http server
+ pisi updatedb over internet
3. Beta
+ build cmd: installl build deps
+ improve url support, it's a little dumb at the moment
+ devise a File class that works with URLs
+ add URL support to autoxml
+ info cmd: reverse dependencies of a package
+ a helper script for finding dependencies (eray)
+ autoxml: automated xml processing (eray)
+ design
+ basic types
+ list type
+ class type
+ localtext/text types
+ revised interface
+ improved error handling, check routines, hooks, format/print routines
+ xmlfile support (read/write)
+ string member, in addition to tag/attribute members
+ replace specfile with new specfile using autoxml
+ autoxmlize: metadata, index, dependency and related code paths
+ inheritance support
+ revise writing, error reporting, tests
+ components (eray)
+ requirements
+ xml format, add to PSPEC
+ index component.xml's and make use of them in build
+ a database of components:
+ query components by name
+ query list of packages in a component
+ index: update component database(s)
+ bug fixing
+ install/remove/upgrade components
+ safety switch: preserve system.base, install system.devel for build, etc.
+ info <component>
+ started documentation
+ component dependencies
+ multiple package repository (eray)
+ decide how to implement
+ support repo order
+ refactor xml support and implement multiple backends
+ 4suite support (initial patch by faik, completed by eray)
+ piksemel support (eray)
+ fix autoxml implementation to use 4suite by default
+ partial caching and automatic resume for file download (meren)
+ use a '.part' extension
+ search-file command (eray)
+ exception handling (eray)
+ uniform exception hierarchy
+ proper handling
+ API: we even have application users (eray)
+ a facade module pisi.api (eray)
+ a library init command (eray)
+ high-level package ops, index (eray)
+ info/util commands (eray)
+ build commands (baris)
+ documentation
+ actionsAPI documentation, unittests (caglar - meren)
+ versioning information document (eray - baris)
+ UI
+ PISIMAT
+ pykde GUI (cartman)
+ improve interface (eray)
+ refactor UI, reintroduce base class
+ add an ack interface, start cleaning up a bit
+ cli (eray)
+ ask alternatives to choose from
+ eliminate \n's from infos, what's the point?
+ metaclass coolness for CLI command framework (eray)
+ i18n support
+ translations and correct language for summary, description (packagers)
+ make pisi messages translatable (eray)
+ .pot support for dist script (gurer)
+ translate messages mesajların çevirisinin yapılması (pisi tr.po) (eray)
+ proper unicode handling in code - tons of fixes (eray - baris)
+ configure-pending (eray)
+ proper commar connection (baris - gurer)
+ ignore-deps (eray)
+ more robust upgrade (eray)
+ query (eray)
+ comar OM information (Provides)
+ find package by name, display summary/description
+ info --files option (like rpm -ql, but better of course)
+ implement missing unit tests
+ sourcedb
+ package (baris)
+ configuration file (baris)
+ upgrade (eray)
+ upgrade operation
+ test upgrade op
+ system-wide upgrade (upgrade-all) komutu
+ incremental build (eray)
+ generate binary release number by comparing MD5s (eray)
+ keep track of successfully completed configure, make, install
steps (necessary for large-scale builds?) (meren)
+ database locking bugs (eray)
4. Release
* high priority
+ transaction stuff for database (eray)
+ database version file
* low priority
+ search command: (eray)
+ implement an inverted index
+ search in summary and description
5. Post Release
* high priority
- package signing
- a flag to sign package after building
- code to check if the package has a valid signature
- cli improvements
- non interactive mode (similar to --auto in urpmi)
/ high-level build commands
+ an emerge command, just for fun
+ make use of binary repo to satisfy dependencies in emerge
/ build command enhancements:
+ aware of repo sources
? full dependency analysis like install/upgrade etc.
? build-all (or build-distro) command, or as a separate tool
+ make a package and sourcedb for each repo
+ packagedb
+ sourceb
? improve url support: consider adding "other" protocols
- removable media: media://
* medium priority
- UI impl: non-interactive use that selects defaults and logs output
- UI impl: a general purpose Qt UI
/ multi-architecture support
+ design decisions
+ extend XML specs to support that
- cross-platform building support
- incremental build/fetch for repository index (pisi-index.xml)
/ xdelta support (caglar)
- diffsets
- a command (check-repo) to check if repos are consistent wrt dep relations
- component improvements (optional)
- maintain a list of components each component has
- maintain a list of packages
- more support for categories:
- put categories into a database
- fast search for categories
* low priority
/ overhaul installdb (try to merge its use with packagedb) (eray)
/ verify metodlari
+ SpecFile
+ MetaData
+ Files
- Index (somebody should write one :} )
- autoxml:
- revise error checking, printing and tests
- uri type
- date type
- enum type
- make autoxml dependent on only xmlext and xmlfile interfaces
- search command: (eray)
- search components
- english stemming
- turkish stemming
- phonetic/syntactic fault tolerance (whichever works!)
- in particular evaluate soundex and metaphone
- research if there are language independent phonetic encodings
- implement as an option (or make it possible to turn it off if it works well :))
- fault tolerant search in package names (somehow)
-79
View File
@@ -1,79 +0,0 @@
% ALGORITHM STYLE -- Released 8 April 1996
% for LaTeX-2e
% Copyright -- 1994 Peter Williams
% E-mail Peter.Williams@dsto.defence.gov.au
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{algorithm}
\typeout{Document Style `algorithm' - floating environment}
\RequirePackage{float}
\RequirePackage{ifthen}
\newcommand{\ALG@within}{nothing}
\newboolean{ALG@within}
\setboolean{ALG@within}{false}
\newcommand{\ALG@floatstyle}{ruled}
\newcommand{\ALG@name}{Algorithm}
\newcommand{\listalgorithmname}{List of \ALG@name s}
% Declare Options
% first appearance
\DeclareOption{plain}{
\renewcommand{\ALG@floatstyle}{plain}
}
\DeclareOption{ruled}{
\renewcommand{\ALG@floatstyle}{ruled}
}
\DeclareOption{boxed}{
\renewcommand{\ALG@floatstyle}{boxed}
}
% then numbering convention
\DeclareOption{part}{
\renewcommand{\ALG@within}{part}
\setboolean{ALG@within}{true}
}
\DeclareOption{chapter}{
\renewcommand{\ALG@within}{chapter}
\setboolean{ALG@within}{true}
}
\DeclareOption{section}{
\renewcommand{\ALG@within}{section}
\setboolean{ALG@within}{true}
}
\DeclareOption{subsection}{
\renewcommand{\ALG@within}{subsection}
\setboolean{ALG@within}{true}
}
\DeclareOption{subsubsection}{
\renewcommand{\ALG@within}{subsubsection}
\setboolean{ALG@within}{true}
}
\DeclareOption{nothing}{
\renewcommand{\ALG@within}{nothing}
\setboolean{ALG@within}{true}
}
\DeclareOption*{\edef\ALG@name{\CurrentOption}}
% ALGORITHM
%
\ProcessOptions
\floatstyle{\ALG@floatstyle}
\ifthenelse{\boolean{ALG@within}}{
\ifthenelse{\equal{\ALG@within}{part}}
{\newfloat{algorithm}{htbp}{loa}[part]}{}
\ifthenelse{\equal{\ALG@within}{chapter}}
{\newfloat{algorithm}{htbp}{loa}[chapter]}{}
\ifthenelse{\equal{\ALG@within}{section}}
{\newfloat{algorithm}{htbp}{loa}[section]}{}
\ifthenelse{\equal{\ALG@within}{subsection}}
{\newfloat{algorithm}{htbp}{loa}[subsection]}{}
\ifthenelse{\equal{\ALG@within}{subsubsection}}
{\newfloat{algorithm}{htbp}{loa}[subsubsection]}{}
\ifthenelse{\equal{\ALG@within}{nothing}}
{\newfloat{algorithm}{htbp}{loa}}{}
}{
\newfloat{algorithm}{htbp}{loa}
}
\floatname{algorithm}{\ALG@name}
\newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}}
-158
View File
@@ -1,158 +0,0 @@
% ALGORITHMIC STYLE -- Released 8 APRIL 1996
% for LaTeX version 2e
% Copyright -- 1994 Peter Williams
% E-mail PeterWilliams@dsto.defence.gov.au
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{algorithmic}
\typeout{Document Style `algorithmic' - environment}
%
\RequirePackage{ifthen}
\RequirePackage{calc}
\newboolean{ALC@noend}
\setboolean{ALC@noend}{false}
\newcounter{ALC@line}
\newcounter{ALC@rem}
\newlength{\ALC@tlm}
%
\DeclareOption{noend}{\setboolean{ALC@noend}{true}}
%
\ProcessOptions
%
% ALGORITHMIC
\newcommand{\algorithmicrequire}{\textbf{Require:}}
\newcommand{\algorithmicensure}{\textbf{Ensure:}}
\newcommand{\algorithmiccomment}[1]{\{#1\}}
\newcommand{\algorithmicend}{\textbf{end}}
\newcommand{\algorithmicif}{\textbf{if}}
\newcommand{\algorithmicthen}{\textbf{then}}
\newcommand{\algorithmicelse}{\textbf{else}}
\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif}
\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif}
\newcommand{\algorithmicfor}{\textbf{for}}
\newcommand{\algorithmicforall}{\textbf{for all}}
\newcommand{\algorithmicdo}{\textbf{do}}
\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor}
\newcommand{\algorithmicwhile}{\textbf{while}}
\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile}
\newcommand{\algorithmicloop}{\textbf{loop}}
\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop}
\newcommand{\algorithmicrepeat}{\textbf{repeat}}
\newcommand{\algorithmicuntil}{\textbf{until}}
\def\ALC@item[#1]{%
\if@noparitem \@donoparitem
\else \if@inlabel \indent \par \fi
\ifhmode \unskip\unskip \par \fi
\if@newlist \if@nobreak \@nbitem \else
\addpenalty\@beginparpenalty
\addvspace\@topsep \addvspace{-\parskip}\fi
\else \addpenalty\@itempenalty \addvspace\itemsep
\fi
\global\@inlabeltrue
\fi
\everypar{\global\@minipagefalse\global\@newlistfalse
\if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels
\penalty\z@ \fi
\everypar{}}\global\@nobreakfalse
\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi
\sbox\@tempboxa{\makelabel{#1}}%
\global\setbox\@labels
\hbox{\unhbox\@labels \hskip \itemindent
\hskip -\labelwidth \hskip -\ALC@tlm
\ifdim \wd\@tempboxa >\labelwidth
\box\@tempboxa
\else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
\hskip \ALC@tlm}\ignorespaces}
%
\newenvironment{algorithmic}[1][0]{
\let\@item\ALC@item
\newcommand{\ALC@lno}{%
\ifthenelse{\equal{\arabic{ALC@rem}}{0}}
{{\footnotesize \arabic{ALC@line}:}}{}%
}
\let\@listii\@listi
\let\@listiii\@listi
\let\@listiv\@listi
\let\@listv\@listi
\let\@listvi\@listi
\let\@listvii\@listi
\newenvironment{ALC@g}{
\begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@
\listparindent\z@ \rightmargin\z@
\topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@
\leftmargin 1em
\addtolength{\ALC@tlm}{\leftmargin}
}
}
{\end{list}}
\newcommand{\ALC@it}{\addtocounter{ALC@line}{1}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item}
\newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}%
{}{\ \algorithmiccomment{##1}}}
\newcommand{\REQUIRE}{\item[\algorithmicrequire]}
\newcommand{\ENSURE}{\item[\algorithmicensure]}
\newcommand{\STATE}{\ALC@it}
\newcommand{\COMMENT}[1]{\algorithmiccomment{##1}}
\newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}}
\newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}}
\renewcommand{\\}{\@centercr}
\newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen%
\ALC@com{##1}\begin{ALC@if}}
\newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse%
\ALC@com{##1}\begin{ALC@if}}
\newcommand{\ELSIF}[2][default]%
{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen%
\ALC@com{##1}\begin{ALC@if}}
\newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo%
\ALC@com{##1}\begin{ALC@for}}
\newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ %
\algorithmicdo%
\ALC@com{##1}\begin{ALC@for}}
\newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ %
\algorithmicdo%
\ALC@com{##1}\begin{ALC@whl}}
\newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop%
\ALC@com{##1}\begin{ALC@loop}}
\newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat%
\ALC@com{##1}\begin{ALC@rpt}}
\newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1}
\ifthenelse{\boolean{ALC@noend}}{
\newcommand{\ENDIF}{\end{ALC@if}}
\newcommand{\ENDFOR}{\end{ALC@for}}
\newcommand{\ENDWHILE}{\end{ALC@whl}}
\newcommand{\ENDLOOP}{\end{ALC@loop}}
}{
\newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif}
\newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor}
\newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile}
\newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop}
}
\renewcommand{\@toodeep}{}
\begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}%
\itemsep\z@ \itemindent\z@ \listparindent\z@%
\partopsep\z@ \parskip\z@ \parsep\z@%
\labelsep 0.5em \topsep 0.2em%
\ifthenelse{\equal{#1}{0}}
{\labelwidth 0.5em }
{\labelwidth 1.2em }
\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep}
\ALC@tlm\labelsep
}
}
{\end{list}}
Binary file not shown.
-381
View File
@@ -1,381 +0,0 @@
%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End:
\documentclass[a4paper,11pt]{article}
\usepackage{graphicx}
\usepackage{algorithm}
\usepackage{algorithmic}
\usepackage{amsmath}
\usepackage{amstext}
\usepackage{amsfonts}
\usepackage{amsbsy}
\usepackage{amsthm}
\usepackage{prettyref}
%\newrefformat{alg}{Algorithm~\ref{#1}}
%\newrefformat{eq}{Equation~\ref{#1}}
%\newrefformat{lem}{Lemma~\ref{#1}}
%\newrefformat{thm}{Theorem~\ref{#1}}
%\newrefformat{chp}{Chapter~\ref{#1}}
%\newrefformat{sec}{Section~\ref{#1}}
%\newrefformat{apx}{Appendix~\ref{#1}}
%\newrefformat{tab}{Table~\ref{#1}}
%\newrefformat{fig}{Figure~\ref{#1}}
%usepackage[active]{srcltx}
\title{ Dependency Resolution in PISI}
\author{Eray \"{O}zkural}
\date{\today}
\begin{document}
\maketitle
\section{Introduction}
Dependency resolution in package management systems have a
significance in that they are the key to providing system stability
and internet upgrades. The scale of package databases requires the
dependency resolution mechanism to be efficient and correct,
motivating a closer look at the theory.
\section{Review}
Dependency resolution has been taken in the most general setting as
the famous SAT problem of propositional logic. If we consider a system
$D$ of dependency statements $D_i$, each statement can be taken as a
proposition in propositional logic which states, for instance:
$D_i$: if package $a$ is installed or package $b$ is installed, then
package $i$ is installable.\\
...
The system is thus understood as the conjunction of such facts, giving
us a logical programming formulation to determine installation
conditions. Note that for simplicity we do not consider the nuances in
upgrade and remove operations at the moment.
However, using a SAT solver for this operation may be shooting a fly
with a bazooka. We observe that only certain forms of propositions
will be necessary for a dependency system. Furthermore, as we shall
see further constraints and optimizations may be required of the
system that are not modelled well with the SAT problem.
We use a graph theoretic approach instead. A directed graph (digraph)
$G=(V,E)$ is formally a set of vertices $V$ and a set of edges $E$
where each edge $(u,v)$ represents an edge from a vertex to another.
Accessor functions $V(G)$ and $E(G)$ yield the vertex and edge set of
the graph $G$. Topological sort of a graph gives a total ordering of
the vertices in which there are only forward edges. A vertex induced
subgraph of $G$ by vertex set $A$ contains only the vertex set $A$ and
edges incident to members of $A$.
\section{Package operation planning}
The dependency resolution problem may be viewed as a simple forward
chaining problem, where we would like to begin from an initial state
$S_0$ and by following allowable system transitions $t_i: S \to S$,
arrive at a desired system state $S_f$ (where $S$ is the set of all states).
A system state $S_i$ is defined as the set of installed packages on
the system together with their versions, i.e. $S_i = \{ (x,v) : x
\text{ is installed}, v=version(x)\} $. An atomic system transition
$t_i$ chains one system state into another, making one ACID change on
the system. The usual atomic transitions are the single package
install, remove and reinstall (upgrade or downgrade) operations found
in low-level package management code of PISI. Note that in PISI, an
upgrade operation is identical to a remove operation followed by an
install operation (which sets it apart from some other packaging
systems).
A package operation plan is thus naturally conceived of as a sequence
of atomic system transitions. Given an initial state and a final
state, the job of the package operation planner is to determine
whether there is a plan, and if so find the "best" one.
Where there are no versions involved (e.g. upgrade/downgrade), we will
replace the pair $(x,v)$ with $x$ in the definitions for simplicity.
\subsection{System consistency}
It is worth mentioning here the concept of system consistency. As in a
database transaction, it is not acceptable that the system violates an
invariant afterwards. In the context of PISI, system consistency is
composed of two conditions for the current set of installed packages.
\begin{enumerate}
\item All package dependencies are satisfied (we may call this a
closed system)
\item No package conflicts are present.
\end{enumerate}
Therefore, by atomic transition we also mean one that does not corrupt
system consistency. The system is thus never in an inconsistent
state. We will explain the conflicts later, for the present let us
look at the dependency condition.
\subsection{Solving the simplest case with topological sorting}
We will now concentrate on a simple form of the problem which can be
solved with topological sorting. This form is not concerned with
versions. Neither do we consider remote repositories. From an initial
set of packages $S_0$, we would like to install in addition a new set
$A$ of packages obtaining $S_f = S_0 \cup A$, for a static set of package
relations.
The only relations considered are of the form: $a$ Depends on $b$, or
more briefly $aDb$. The graph of all such simple dependency relations
is a digraph $G$. For each dependency relation $aDb$, there is an
edge $a \to b$ in $G$. Accessing graph $G$ usually requires a database
operation and is therefore expensive.
We now consider the digraph $G_A$ of the minimal set of simple
dependency relations which contains all information required to
construct a plan to install packages $A$. $G_A$ is a vertex induced
graph such that the fringe of $A$, e.g. vertices with out-degree $0$
depend only on packages that are already installed (or none). Vertices
of $G_A$ are taken from $S_f$. First, let us explain the labelling
scheme. Already installed vertices are labelled with 'i'. Packages to
be added are labelled with 'a', and packages to be installed due to
dependencies are labelled with 'd'. We construct the graph as follows
\begin{algorithm}
\caption{$\textsc{Make-}G_A(G, A)$}
\label{alg:cons-graph}
\begin{algorithmic}[1]
\STATE $G_A \gets$ vertex induced subgraph of $G$ by $A$ labelled with 'a'
\REPEAT
\STATE done $\gets$ true
\FOR{each $u \in V(G_A)$ with out-degree $0$}
\FOR{ $v \in adj(u) $ of $G$}
\IF{$v \notin V(G_A)$}
\STATE done $\gets$ false
\IF{$v$ is installed}
\STATE label $v$ with 'i'
\ELSE
\STATE label $v$ with 'd'
\ENDIF
\STATE add $(u,v)$ to $G_A$
\ENDIF
\ENDFOR
\ENDFOR
\UNTIL{done}
\end{algorithmic}
\end{algorithm}
By this iterative expansion, we do a minimum number of database
accesses to $G$ and construct a dependency graph in memory. If the
$G_A$'s fringe has vertices with non 'i'-labels, then $A$ cannot be
installed. Otherwise, we find a topological sort $L$ of $G_A$, and in
the reverse order, install packages for vertices labelled with
'a' or 'd'. Observe that, by definition of a topological sort,
installing packages in the reverse order of a topological sort
guarantees that no package is installed before all of its dependencies
are installed. Thus, this yields a consistency-preserving plan.
\subsection{Dependency conditions}
In the PISI specification, we allow a dependency to specify a local
condition, for instance a program may require a dependency on
\texttt{libx} with pardus source release $3$ or greater. Another
program may require a dependency on a particular source release. These
conditions are local because they can be computed over the elements of
system state $S_i$, e.g. package (name, version) pairs. Let us denote this
condition by a predicate $P(b)$ such that $aDb$ iff $P(b)$. The
predicate $P$ for the dependency $aDb$ can be stored as edge data for
$(u,v)$ on the graph.
% thus when we say $P(u,v)$, this means the predicate stored on
%$(u,v)$ edge for vertex $v$.
In this case, the vertices of the package dependency graph $G$ and the
planning graph $G_A$ retain the version information along with the
package name. The dependency relation thus holds between two pairs
$(p_1,v_1)$ and $(p_2,v_2)$, satisfying a given predicate
$P(p_2,v_2)$. When constructing the graph, we therefore take this
predicate into account and admit a new edge $(u,v)$, and thus a new
vertex $v$ into $G_A$ if and only if the target vertex satisfies
$P(v)$.
\subsection{Conflicts and COMAR dependencies}
The tags \texttt{Conflicts} and \texttt{Provides} in PISI are
inherited from Debian distribution. A conflict between two packages
($a$ conflicts with $b$) is a symmetric relation that prevents the
packages $a,b$ from being installed simultaneously (It is
sufficient that only one direction of the relation is declared, the
other direction is inferred). Provision in the form of $a$ provides $A$
denotes that $a$ implements a virtual package abstraction $A$.
In PISI, a package can provide an object of a COMAR Object Model (OM),
and is currently the only model of a ``virtual package''. In the
following example, let $a_1,a_2,\ldots,a_n$ provide the OM $A$. A package
can depend on another package's OM, for instance $b$ comar-depends on
$A$ (or in short form $bDA$) (Currently, conditions on virtual
dependencies are not supported). In this case, it is sufficient that
only one of the $a_i$ are installed. To resolve this, the user is
asked to choose from a list of alternatives immediately, since
otherwise there is unavoidable combinatorial explosion (in the form of
having to consider $\Pi_{bDA}num(A)$ graphs in the worst case where
$num(A)$ is the number of alternatives for comar OM $A$; the problem
is that there seems to be no simple solution to solve satisfiability
with arbitrary disjunctions in package dependency, short of a $SAT$
solver).
The resolution of conflicts to maintain system consistency condition
$2$ is easier to achieve. This can be satisfied by disallowing
installation of a package that would violate the condition, or
removing currently installed packages which conflict with the newly
installed package and its dependencies. In most package managers, the
second option is confirmed by the user for making it easier. In the
install operation, after constructing the partial dependency graph
$G_A$, we merely have to check whether any conflict appears among the
vertices of $G_A$. If so, then the operation is untenable, since $G_A$
shows the future state of the installed system. Since a conflict is
symmetric, it is represented as a bidirectional edge $a \leftrightarrow b$. To
distinguish dependencies from conflicts, the edges would have to be
labelled in this case, for instance with 'd' and 'c'. The removal
option can be implemented by invoking a multi-package remove operation
on the packages in conflict.
\subsection{Remove operation}
Dependency resolution for remove operation is similar to install. The
only significant difference is that we remove the packages in the
topological order rather than installing packages in the reverse
topological order.
\section{Remote repositories and upgrade operation}
The upgrade operation is more complicated. First of all, the system
has to distinguish between the current relation graph (e.g.
dependencies and conflicts), and the future relation graph which may
be different in rather important aspects. In theory, we allow any
dependency and conflict to change. Therefore, we have a $G_0$ which
represents the current relations (among installed packages) in the
system, and a $G_f$ which is probably taken from a remote package
repository. We begin by noting that $G_0$ and $G_f$ have to be
compatible. That is, to say, if a package $(p_1,v_1)$ is shared across
two graphs, then the declarations made by the package are one and the
same.
$G_0$ can be calculated from the package information (e.g. metadata)
of the installed packages and is stored by PISI in a dedicated
database. $G_f$ is most likely constructed from a PISI Index file
corresponding to a particular package repository. Accessing both of
these entities is expensive and we should take care to minimize access
as in the previous section.
To preserve consistency during individual transitions, the planner can
choose to remove a minimum number of packages from the system to bring
it to a clean state, and then install the new versions of these
packages in the correct order. Let us assume that it is indeed
possible to achieve this ``clean state''. Apparently, this is not
always possible because other packages may depend on the package(s) to
upgrade. At any rate, to achieve this, first we need to
calculate subgraphs of $G_0$ and $G_f$. We can calculate alternative
plans from these subgraphs if need be.
Let $A$ be the set of packages to be upgraded from a given repository.
$G_{A,0}$ is the subgraph of $G_{0}$ induced by the ``upgrade
closure'' of $A$. The ``upgrade closure'' of a set $A$ of packages is
defined as a minimal set of packages $B \supseteq A$ such that there is no
package in $B$ that requires an upgrade for $A$ to be upgraded. This
is found by assuming that the current system state $S_0$ is
consistent, and by constructing a relation graph of the future state
of the system to detect the dependencies that have changed.
Obviously, to make a plan, we must first know the goal state. In a
multi-package upgrade, the exact details of the goal state depend on
the graph $G_f$ of the repository. Thus, we construct a graph
$G_{A,f}$ that is a vertex-induced subgraph of $G_f$ such that it
contains all information relevant to upgrading packages $A$. We begin
by a vertex induced subgraph of $G_f$ by $A$. These are the packages
that will be upgraded in any case. Then, we make a pass on the
vertices, and look at all the outgoing edges, we compare whether this
edge has changed in any substantial way from the previous version. In
particular, we are interested in whether the predicate of the edge is
valid for the version of the same package in our current system. Every
compared vertex in this manner is marked done, and the edges not valid
for the current system pull new unmarked vertices into $G_f$, this
continues until there are no unmarked vertices left. Hence, the
vertices of $G_f$ are the packages that must be upgraded.
To actually carry out the upgrade a strategy is to upgrade all the
packages in $G_{A,f}$ in some order. A good order is again the reverse
topological order order, in fact, the upgrade operation is merely a
special case of a multi-package installation code that can install
from a remote repository, since a multi-package installation can
contain upgrades in addition to new packages. However, in case no
package depends on the packages to be upgraded, then we can carry out
a completely consistency-preserving plan as discussed above. The
conflicts are resolved in the usual fashion, by removing those
packages in conflict with new packages that are installed. This can be
accomplished by invoking a remove operation prior to the upgrade
operation.
\section{Examples}
\subsection{A single package upgrade}
goal: upgrade $(a,1)$ to $(a,2)$\\
\\
rules:\\
$(a,1)$ depends on $(b,1), (c,1)$ \\
$(a,1)$ conflicts with $(d,1)$\\
$(a,2)$ depends on $(c,3), (d,2)$\\
$(a,2)$ conflicts with $(b,1)$\\
\\
initial state:\\
$(a,1), (b,1), (c,1)$ installed \\
In this case, we can find a consistency-preserving plan in terms of
install and remove operations.
\\
plan:\\
remove $(a,1)$\\
remove $(b,1)$\\
remove $(c,1)$\\
install $(c,3)$\\
install $(d,2)$\\
install $(a,2)$\\
\subsection{Another upgrade}
goal: upgrade $(b,1) \to (b,2)$\\
\\
current dep: $(a,1) \to[=1] (b,1) \to[=1] (c,1) \to[=1] (d,1)$\\
repo dep: \nobreakspace{} \nobreakspace$(a,1) \to[=2] (b,2) \to[=2] (c,2) \to[=1] (d,1)$\\
In this case, we cannot remove $(b,1)$ because it's locked in the
chain. In fact, here there is no consistency-preserving plan in terms
of atomic single package transitions: install, remove, upgrade. In
these cases, it seems best to resort to upgrade in place, and in the
reverse topological order of dependencies.
\\
plan:\\
upgrade $(c,1) \to (c,2)$\\
upgrade $(b,1) \to (b,2)$\\
\subsection{A multi package remove}
goal: remove $(a,2), (b,3), (c,2)$\\
\\
rules:\\
$(c,2)$ depends $(a,2)$\\
$(d,2)$ depends on $(b,3), (c,2)$\\
$(e,1)$ depends on $(a,2)$\\
$(f,2)$ depends on $(e,1)$\\
$(g,2)$ depends on $(e,1)$\\\\
plan:\\
remove $(f,2)$\\
remove $(g,2)$\\
remove $(e,1)$\\
remove $(d,2)$\\
remove $(b,3)$\\
remove $(c,2)$\\
remove $(a,2)$\\
\end{document}
@@ -1,249 +0,0 @@
From eray@uludag.org.tr Wed Sep 7 20:15:04 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id BE4C284D16;
Wed, 7 Sep 2005 20:15:15 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id 128A11878DE;
Wed, 7 Sep 2005 20:09:27 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id 8A1AD1878D6
for <cekirdek@liste.uludag.org.tr>;
Wed, 7 Sep 2005 20:09:13 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 56F5B908AE; Wed, 7 Sep 2005 20:15:02 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from [192.168.1.5] (unknown [85.97.87.38])
by uludag.org.tr (Postfix) with ESMTP id 0057B84D16
for <cekirdek@uludag.org.tr>; Wed, 7 Sep 2005 20:15:01 +0300 (EEST)
From: Eray Ozkural <eray@uludag.org.tr>
Organization: Pardus
To: cekirdek@uludag.org.tr
Date: Wed, 7 Sep 2005 20:15:04 +0300
User-Agent: KMail/1.7.2
MIME-Version: 1.0
Message-Id: <200509072015.04792.eray@uludag.org.tr>
Cc:
Subject: [Cekirdek] [PISI Design] Dandik Fedora'nin component treesi
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============1420878072=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 3643
X-Length: 4136
--===============1420878072==
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Bu aslinda mandrake'ye gore biraz daha akli basinda ve daha
onemlisi component mantigina uygun bir liste, bir iki degisiklik
disinda bu listeyle baslamakta fayda var. Bir de tabii gentoo'nun
kendi listesi var, onu da gentoo'cular gonderebilir mi listeye lutfen?
Bu dedigim gibi iki seviyeli bir tree, oldukca iyi aslinda.... sadece
dedigim gibi component/category karisimi biraz onun disinda gayet
guzel... Bir de baktim "group" demisler, group da iyi bir sozcuk aslinda
degistirmeye gerek yok degil mi component lafini?
Saygilar,
Desktop
X11
GNOME
KDE
Applications
Editors
Engineering and Scientific
Graphical Internet
Text-based Internet
Office/Productivity
Sound and Video
Authoring and Publishing
Graphics
Games and Entertainment
Servers
Server Configuration Tools
Web Server
Mail Server
Windows File Server
DNS Name Server
FTP Server
PostgreSQL Database
MySQL Database
News Server
Network Servers
Legacy Network Server
Development
Development Tools
X Software Development
GNOME ""
KDE ""
Legacy ""
Java Development
Eclipse
System
Language Support (includes fonts and input methods)
Administration Tools
System Tools
Printing Support
--
Eray Ozkural (exa), Uludag Gelistiricisi
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
--===============1420878072==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============1420878072==--
From gurer@uludag.org.tr Wed Sep 7 22:04:45 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id 00FBF380AA;
Wed, 7 Sep 2005 22:04:23 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id 4BEDB1878F8;
Wed, 7 Sep 2005 21:58:34 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id CBC0E159028
for <cekirdek@liste.uludag.org.tr>;
Wed, 7 Sep 2005 21:58:32 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 5EB758654F; Wed, 7 Sep 2005 22:04:21 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from cable73-96.anadolu.kablonet.com.tr (unknown [195.174.73.96])
(using SSLv3 with cipher RC4-MD5 (128/128 bits))
(No client certificate requested)
by uludag.org.tr (Postfix) with ESMTP id D92BD380AA
for <cekirdek@uludag.org.tr>; Wed, 7 Sep 2005 22:04:20 +0300 (EEST)
From: =?ISO-8859-1?Q?G=FCrer_=D6zen?= <gurer@uludag.org.tr>
To: cekirdek@uludag.org.tr
In-Reply-To: <200509072015.04792.eray@uludag.org.tr>
References: <200509072015.04792.eray@uludag.org.tr>
Organization: TUBITAK - UEKAE
Date: Wed, 07 Sep 2005 22:04:45 +0300
Message-Id: <1126119885.10981.13.camel@localhost>
Mime-Version: 1.0
X-Mailer: Evolution 2.2.3
Cc:
Subject: [Cekirdek] component hadisesi
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============1294830247=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 3648
X-Length: 4903
--===============1294830247==
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Kanalda meren, exa, çağlar, ben biraz fikir teatisinde bulunduk. Teati
dedim de çay çekti canım. Özetlen:
Component bir görev için bir araya gelmiş ve görevin ayrı parçalarını
yapan paketler dedik. Kullanım amacı kullanıcının bir alt sistemi (kde,
tex) bir hamlede kurabilmesini sağlamak, gerektiğinde dependency olarak
kullanılmak.
Aynı işi yapan farklı programların bir grubu (library, editor, archiver,
vs) kesinlikle component olmuyor.
Temel olarak iki tipi var:
KDE (kicker, kwm, kdm, kopete, vs)
burda aynı koddan türemiş ve bize masaüstü sağlayan paketlerin bir
componenti var.
Base (kernel, grub, libc, bash, vs)
burda ayrı kodlardan gelen ve bize açılıp çalışabilen bir sistem
sağlayan paketlerin bir componenti var.
Aynı paket birden fazla componente dahil olmuyor, bu component değil
bağımlılık ilişkisi. Örnek:
libxml2 is a part of Gnome
kopete (a partof KDE) depends on libxml2 (bunu uydurdum, kopete
libxml2 kullanmıyor olabilir)
Yani componenti *asıl* olarak kod ilişkisi belirliyor, ama örneğin base
paketinde direk kod ilişkisi yoksa da, birbirleriyle çok içiçe iş yapan
programları bir component altında grupladık. İkincil kriter birlikte
çalışma.
Karma bi örnek:
Tex componenti içinde aynı koddan gelen kpathsea, tetex,vs paketleri var
diyelim, ama farklı kodu olan hevea da buraya girebilir, çünkü html
generate edebilen bir tex sistemi için gerekli.
Hiçbir componente ait olmayan, sadece dependency ve category ilişkileri
olan paketler olabilir. Bunlar mesela other diye bi dizinde
(componentler aynı zamanda paket deposundaki dizinler, bu şart değil ama
kolaylık) tutulur.
Componentleri hiyerarşiye koymak kötü bi fikir değil, fedoranın ana
grupları bu konuda fena değil.
Kafa karışıklığı kalmadı sanırım. Kategorileri konuşmadık daha :)
Bu vesileyle yavaş yavaş pisi depomuzu a/ b/ c/ lerden kurtarıp
component bazlı dizinlere geçirmeye başlayalım dedik.
--===============1294830247==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============1294830247==--
@@ -1,167 +0,0 @@
component'larla ilgili notlar
1. Temel feature'lar (Eray)
--------------------
Iki yerde component tag'i tanimladik simdiye kadar, bir
source'larda bir de binary'lerde. Binary'de tanimlanan component default
olarak source'daki tanimi inherit ediyor ve tanimlanan component'i override
edebiliyor.
Bir component temel olarak bir meta-package, icerisinde paketler olan
bir paket. Bir component'in icerisinde bir takim source'lar ve bir takim
binary'ler bulunuyor diye bakabiliriz. Sanirim query'leri bu sekilde yapmak
mumkun olmali.
$ pisi list-components
$ pisi info system.base
Source packages in system.base:
....
....
Binary packages in system.base:
....
....
gibi ozellikler eklemeyi umit ediyorum.
Source'larin component tag'leri de, gene sadece bir senaryo konusuyorum,
direkt olarak directory structure'indan inherit alinacak. Ayni zamanda o pspec
icin de bir tane component tanimlananack default olarak, ve bu component o
scope'da tanimlanmis olan butun binary package'lari icerecek.
Ornegin diyelim ki a/b/c/pspec.xml var ve c1 c2 c3 seklinde uc tane paket
tanimliyor. Hic bir component tanimi yapilmadigi zaman otomatik olarak bir
a.b.c component'i olusturulacak, ve bu component'in icerisinde c1 c2 c3
bulunacak.
$ pisi info a.b.c
Source packages:
c - oldur beni yarim sen olmazsan biterim
Binary packages:
c1 - bu aksam demlenmemek sonum olur benim
c2 - sincaplarla konustum butun gece
c3 - her gul gordugumde icim kan aglar
Bu varsayilan davranis, ama bunu degistirmek mumkun olacak. Burada yamuk
gozukebilecek bir sey var, o da tek bir paket oldugunda sanki biraz
redundancy olmasi, o takdirde bir optimizasyon olarak, diyelim ki
a/b/c/pspec.xml'in icerisinde tek bir paket tanimli c1
$ pisi info a.b
....
c1 - dil dil dillerdeyim
olabilir bu durumda, bu genel agac mantiginda bir sorun yaratmayacaktir.
2. Temel tanım (Barış)
fiziksel aitlik: kdebase'den çıkan kcontrol gibi; grup aitliği: pdf
göstericileri gibi...
3. Database XML ayrımı (Barış)
Component database'i ile component.xml ayrı olmalı. Component database,
pisi'nin pisi-index.xml dosyasını okuyarak oluşturacağı bir veritabanı. Hangi
paketler hangi componentlere dahil, vs. sorguları bu veritabanından
yapılacak.
Pspec dosyasını hatırlayalım. İçerisinde bir <PartOf> diye bir tag var.
Oluşturulacak paketin hangi component'e ait olduğunu belirtiyor. Bu bilgi
pisi-index.xml dosyasına da koyulmalı.
4. PL modüllerine benzerlik (Eray)
component tag'leri Java ya da python'daki gibi
directory yapisindan cikiyor. Yani pisi bir programlama dili olsaydi
component'lar module'ler ya da package'larla es anlamli olacakti.
5. Mereology ve Eray'ın açıklama çabaları (Eray)
PartOf iliskisi hakkında su kadarini soylemek yeter: eger a b'nin bir parcasiysa,
a'nin fonksiyonu b'nin fonksiyonunun bir parcasidir. Bu da fiziksel sistemler
icin bir principle of compositionality'nin varligindan hareket eder [*]
Genel olarak da software engineering ve AI camiasinda module'un tanimi gayet
iyi bilinir. Bir modul icerisindeki bagimliliklar yogundur. Moduller arasindaki
bagimliliklar zayiftir.
Bu tanim sadece software engineering'de degil, nesneler arasindaki
benzerliklerin incelendigi bir cok disiplinde kullanilan informal bir tanim,
ama tabii ki formule dokulmus bir ton hali var.Sırf bu tanımı taban alarak
yazılmış başarılı kümeleme (clustering) algoritmaları var.
Modulerlik tanimi verilen *fiziksel* modul ve parcasi olma iliskisiyle
birlesince birlikte install edilip remove edilme yahut ortak bağımlılıklara
sahip olma tanımlarına götürür.
Bu tanımı analiz edebilmek için parcasi olma iliskisininin anlamini
korumamiz yeterli. Temel olarak
  kol insanin parcasidir
iliskisi burada yer aliyor.  Eger
  a partof b
turu iliskilerde a paket ya da component, ve b component ise, a ve b'nin
iliskisinin kol ve insan iliskisi gibi olmasini bekleriz. Eger bu parca-butun
iliskisini ihlal ediyorsa o zaman muhtemelen yanlis bir iliski bulunmus
demektir.
Bunun turlu sonuclari da onem sirasina gore soyle dizilebilir:
1. Fonksiyonlarin bolunebilmesi prensibinden: (basit bir sonuç)
   a's function is part-of b's function
orneğin kolun fonksiyonu insanın total fonksiyonunun bir parçasıdır.
paket ornegi: pisi'nin fonksiyonu olan paket yükleme/çıkarma system.base'in fonksiyonunun, yani temel pardus sisteminin fonksiyonunun bir parçasıdır.
2. Karmasik sistemlerde birbirine dayanan ufak parcalarin kararli yapilar
meydana getirmesi prensibinden: (evrimsel sonuç)
   if a is a part-of c, and b is a part-of c, then it follows that a and b
may:
     a. have many interdependencies
     b. share in their origins
  (which are about the same thing)
2b. sonucunun bizim durumumuza uygulanması, source code'un aynı kaynaktan
çıkması, birlikte inşa edilmeleri gibi şartları getirir. Bunun oldukça
olasi, en azindan insa metodlarinin ve kaynaklarının birbirine benzemesini
bekleriz. Yalniz, 2.a'daki bagimliliklar sadece insa ile degil ayni zamanda
calisma ile de alakalidir. Kaynakları farklı parçaların birbirine bağımlı
hale gelebilecegini unutmamak gerekir.
Fedora'nin yaklasiminin hos tarafi, boyle teknik ayrintilara girmeden "package
group" mantigini kullanmasiydi, ama bizim belli bir anlami olan parca-butun
iliskisini korumamiz daha mantikli bir yazilim ontology'si ortaya
cikaracaktir. Onlarin yaklasimi ise "anything goes", grup ile kategori'nin
temel bir farki yok cunku, herhangi bir bakis acisi olabilir "grup".
6. Modulleri test etmek (!!) (Eray)
------------------------------------
PISI'deki seçilen implementation detaylari bir tanima cok fazla commitment
yapmiyor, implementation'ın getirdiği tek şey paketleri bir ağaca koymak.
Component'ların seçiminin ne kadar kaliteli olduğunu belirleyemiyor.
Paketlerin ve componentlarin secimi yapiyor, ki kritik olan o.
Yalnız modulerlik tanimindan ve part-of iliskisinin
  if a is part of b, and b is part of c, then a is part of c.
  if a is part of b, and b is not part of c, then a is not part of c.
gibi sonuclari getirmesinden hareketle (ki bunlar klasik computational
ontology) kismen test edebilecegimiz bir sekil aliyor ornegin bagimlilik
graph'ini cluster ederek, ya da her modul icin bir modulerlik sayisi
hesaplayarak. Daha az formal olarak da bu sonuçları kafamızda yürüterek
yaptığımız componentların ne kadar akla yatkın olduğunu bulabiliriz.
7. Cagların frugalware önerisi
------------------------------
http://ftp.frugalware.org/pub/frugalware/frugalware-current/source/
adresindeki yerleşimin hem source hem de binary depo için uygulanmasını ve
kategori, componentların da bundan çıkartılmasını öneriyorum.
@@ -1,879 +0,0 @@
From eray@uludag.org.tr Thu Jul 21 15:54:55 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id 351D885A97;
Thu, 21 Jul 2005 15:55:08 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id 0742810A659;
Thu, 21 Jul 2005 15:52:59 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id BABCC10A654
for <cekirdek@liste.uludag.org.tr>;
Thu, 21 Jul 2005 15:52:56 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 91D0C86099; Thu, 21 Jul 2005 15:55:05 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from [192.168.1.5] (unknown [85.99.19.56])
by uludag.org.tr (Postfix) with ESMTP id 0143385A97
for <cekirdek@uludag.org.tr>; Thu, 21 Jul 2005 15:55:04 +0300 (EEST)
From: Eray Ozkural <eray@uludag.org.tr>
Organization: Pardus
To: cekirdek@uludag.org.tr
Date: Thu, 21 Jul 2005 15:54:55 +0300
User-Agent: KMail/1.7.2
MIME-Version: 1.0
Message-Id: <200507211554.55602.eray@uludag.org.tr>
Cc:
Subject: [Cekirdek] [PISI design] mandrake component / group tree
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============2130999829=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 1651
X-Length: 4961
--===============2130999829==
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan. Bu
component lafi akil mi karistiriyor diye killaniyorum hala.
Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
Development, Networking fena degil ama bizim daha anlamli birseyler yapmamiz
gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice bir
component olacak. Ya bizim componentlari en iyi python module'leri gibi
dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
apps.office partof apps
apps.office.openoffice partof apps.office
openoffice-libs partof apps.office.openoffice
openoffice-bin partof apps.office.openoffice
ornegin
buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle, bu
da guzel birsey!
Altlarindaki paketleri yazmiyorum.
Mandrake Group Tree
--------------------------------
Accessibility
Archiving
Backup
Cd burning
Compression
Other
Books
Computer books
Howtos
Literature
Other
Cluster
Message Passing
Queueing Services
Communications
Configuration
Databases
Development
C
C++
Databases
Debug
Gnome and GTK+
Java
KDE and Qt
Kernel
Libraries
Other
Perl
Python
Documentation
Editors
Education
Emulators
File Tools
Games
Adventure
Arcade
Boards
Cards
Other
Puzzles
Sports
Strategy
Graphical desktop
Enlightenment
FVWM based
GNOME
Icewm
KDE
Other
Sawfish
WindowMakes
Xfce
Graphics
Monitoring
Networking
Chat
File transfer
Instant messaging
Instant Messaging
IRC
Mail
News
Other
Remote access
WWW
Office
Publishing
Sciences
Astronomy
Biology
Chemistry
Computer science
Geosciences
Mathematics
Other
Physics
System
Base
Configuration
Cryptography
Deployment
Fonts
Internationalization
Kernel and hardware
Libraries
Servers
X11
XFree86
Shells
Sounds
System
Terminals
Text tools
Toys
Video
--
Eray
--===============2130999829==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============2130999829==--
From serdar@uludag.org.tr Thu Jul 21 16:10:37 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id 6010F85F66;
Thu, 21 Jul 2005 16:12:48 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id 7E52910A661;
Thu, 21 Jul 2005 16:10:39 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id 2FAA4950E8
for <cekirdek@liste.uludag.org.tr>;
Thu, 21 Jul 2005 16:10:37 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 5ECC486095; Thu, 21 Jul 2005 16:12:46 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from webmail.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id 5801F86094
for <cekirdek@uludag.org.tr>; Thu, 21 Jul 2005 16:12:46 +0300 (EEST)
Received: from 193.140.74.2 (SquirrelMail authenticated user serdar)
by webmail.uludag.org.tr with HTTP;
Thu, 21 Jul 2005 16:10:37 +0300 (EEST)
Message-ID: <51672.193.140.74.2.1121951437.squirrel@webmail.uludag.org.tr>
In-Reply-To: <200507211554.55602.eray@uludag.org.tr>
References: <200507211554.55602.eray@uludag.org.tr>
Date: Thu, 21 Jul 2005 16:10:37 +0300 (EEST)
Subject: Re: [Cekirdek] [PISI design] mandrake component / group tree
From: serdar@uludag.org.tr
To: cekirdek@uludag.org.tr
User-Agent: SquirrelMail/1.4.4
MIME-Version: 1.0
X-Priority: 3 (Normal)
Importance: Normal
Cc:
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============1295726599=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 1655
X-Length: 7560
--===============1295726599==
Content-Type: text/plain;charset=UTF-8
Content-Transfer-Encoding: 8bit
Selamlar..
Benim görüşüm, kullanıcının karşısına ilk anda bu kadar çok
kategori çıkarsa, çabucak refüze olacağıdır. Kapsamları daraltıp,
işi 2 kademe yerine 3-4 kademeye yaymak daha kullanışlı olur
sanıyorum.
İyi bir örnek değil ama, freshmeat.net'te ki kategorilere bir bakabiliriz.
# Adaptive Technologies (51 projects)
# Artistic Software (165 projects)
# Communications (5684 projects)
# Database (2022 projects)
# Desktop Environment (2373 projects)
# Documentation (172 projects)
# Education (896 projects)
# freshmeat.net (28 projects)
# Games/Entertainment (2720 projects)
# Home Automation (80 projects)
# Information Management (1259 projects)
# Internet (10220 projects)
# Multimedia (6251 projects)
# Office/Business (2066 projects)
# Other/Nonlisted Topic (261 projects)
# Printing (227 projects)
# Religion (50 projects)
# Scientific/Engineering (2705 projects)
# Security (1591 projects)
# Software Development (10758 projects)
# System (10314 projects)
# Terminals (321 projects)
# Text Editors (647 projects)
# Text Processing (2416 projects)
# Utilities (3056 projects)
Bu listeden çıkarılabilecek bir sürü şey olabilir. Hatta minor
şeyler birleştirilip bir alt seviyede detaylandırılabilir. Freshmeat
bunları upuzun uzatıyor, böylece aradığını bulmak daha kolay
oluyor.
Yukarıdaki liste çok uygun değil. Örneğin, Text Editors ile Office
ayrımı çoğu için anlam ifade etmez. Database ile
development/business'in ayrık durması da anlamsız bence, Databases
development ve belki business altında listlenebilir.
Liste ne kadar uzun olursa, o kadar bıkkınlık verici olacaktır
sanıyorum. Bu yüzden de laf gelimi, her kademede en fazla 10 girdi
olacak şekilde daha derin listeler hazırlanmasının faydalı
olacağını düşünüyorum..
Saygı ve sevgiler..
> Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan.
> Bu
> component lafi akil mi karistiriyor diye killaniyorum hala.
>
> Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
> bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
> debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
> Development, Networking fena degil ama bizim daha anlamli birseyler
> yapmamiz
> gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice bir
> component olacak. Ya bizim componentlari en iyi python module'leri gibi
> dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
>
> apps.office partof apps
> apps.office.openoffice partof apps.office
>
> openoffice-libs partof apps.office.openoffice
> openoffice-bin partof apps.office.openoffice
>
> ornegin
>
> buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
> edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle,
> bu
> da guzel birsey!
>
> Altlarindaki paketleri yazmiyorum.
>
> Mandrake Group Tree
> --------------------------------
>
> Accessibility
> Archiving
> Backup
> Cd burning
> Compression
> Other
> Books
> Computer books
> Howtos
> Literature
> Other
> Cluster
> Message Passing
> Queueing Services
> Communications
> Configuration
> Databases
> Development
> C
> C++
> Databases
> Debug
> Gnome and GTK+
> Java
> KDE and Qt
> Kernel
> Libraries
> Other
> Perl
> Python
> Documentation
> Editors
> Education
> Emulators
> File Tools
> Games
> Adventure
> Arcade
> Boards
> Cards
> Other
> Puzzles
> Sports
> Strategy
> Graphical desktop
> Enlightenment
> FVWM based
> GNOME
> Icewm
> KDE
> Other
> Sawfish
> WindowMakes
> Xfce
> Graphics
> Monitoring
> Networking
> Chat
> File transfer
> Instant messaging
> Instant Messaging
> IRC
> Mail
> News
> Other
> Remote access
> WWW
> Office
> Publishing
> Sciences
> Astronomy
> Biology
> Chemistry
> Computer science
> Geosciences
> Mathematics
> Other
> Physics
> System
> Base
> Configuration
> Cryptography
> Deployment
> Fonts
> Internationalization
> Kernel and hardware
> Libraries
> Servers
> X11
> XFree86
> Shells
> Sounds
> System
> Terminals
> Text tools
> Toys
> Video
>
> --
> Eray
> _______________________________________________
> Cekirdek mailing list
> Cekirdek@uludag.org.tr
> http://liste.uludag.org.tr/mailman/listinfo/cekirdek
>
--===============1295726599==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============1295726599==--
From eray@uludag.org.tr Wed Jul 27 13:02:17 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id AABE08584B;
Wed, 27 Jul 2005 13:02:30 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id EAF71E558D;
Wed, 27 Jul 2005 12:59:53 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id CF155E558D
for <cekirdek@liste.uludag.org.tr>;
Wed, 27 Jul 2005 12:59:52 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 0B4998584E; Wed, 27 Jul 2005 13:02:29 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from [192.168.1.5] (unknown [85.99.19.56])
by uludag.org.tr (Postfix) with ESMTP id 719658584B
for <cekirdek@uludag.org.tr>; Wed, 27 Jul 2005 13:02:28 +0300 (EEST)
From: Eray Ozkural <eray@uludag.org.tr>
Organization: Pardus
To: cekirdek@uludag.org.tr
Subject: Re: [Cekirdek] [PISI design] mandrake component / group tree
Date: Wed, 27 Jul 2005 13:02:17 +0300
User-Agent: KMail/1.7.2
References: <200507211554.55602.eray@uludag.org.tr>
In-Reply-To: <200507211554.55602.eray@uludag.org.tr>
MIME-Version: 1.0
Message-Id: <200507271302.17641.eray@uludag.org.tr>
Cc:
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============2058226043=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 1835
X-Length: 5701
--===============2058226043==
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Buna bakip dusundunuz mu? Bakiniz, mandrake'nin daha cok kategorik
bir ayrim, cok component mantigina uymuyor. Fedora daha uygun aslinda ama o da
cok basit yararli olmak icin.
On Thursday 21 July 2005 03:54 pm, Eray Ozkural wrote:
> Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan. Bu
> component lafi akil mi karistiriyor diye killaniyorum hala.
>
> Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
> bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
> debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
> Development, Networking fena degil ama bizim daha anlamli birseyler
> yapmamiz gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice
> bir component olacak. Ya bizim componentlari en iyi python module'leri gibi
> dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
>
> apps.office partof apps
> apps.office.openoffice partof apps.office
>
> openoffice-libs partof apps.office.openoffice
> openoffice-bin partof apps.office.openoffice
>
> ornegin
>
> buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
> edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle,
> bu da guzel birsey!
>
> Altlarindaki paketleri yazmiyorum.
>
> Mandrake Group Tree
> --------------------------------
>
> Accessibility
> Archiving
> Backup
> Cd burning
> Compression
> Other
> Books
> Computer books
> Howtos
> Literature
> Other
> Cluster
> Message Passing
> Queueing Services
> Communications
> Configuration
> Databases
> Development
> C
> C++
> Databases
> Debug
> Gnome and GTK+
> Java
> KDE and Qt
> Kernel
> Libraries
> Other
> Perl
> Python
> Documentation
> Editors
> Education
> Emulators
> File Tools
> Games
> Adventure
> Arcade
> Boards
> Cards
> Other
> Puzzles
> Sports
> Strategy
> Graphical desktop
> Enlightenment
> FVWM based
> GNOME
> Icewm
> KDE
> Other
> Sawfish
> WindowMakes
> Xfce
> Graphics
> Monitoring
> Networking
> Chat
> File transfer
> Instant messaging
> Instant Messaging
> IRC
> Mail
> News
> Other
> Remote access
> WWW
> Office
> Publishing
> Sciences
> Astronomy
> Biology
> Chemistry
> Computer science
> Geosciences
> Mathematics
> Other
> Physics
> System
> Base
> Configuration
> Cryptography
> Deployment
> Fonts
> Internationalization
> Kernel and hardware
> Libraries
> Servers
> X11
> XFree86
> Shells
> Sounds
> System
> Terminals
> Text tools
> Toys
> Video
--
Eray Ozkural (exa), Uludag Gelistiricisi
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
--===============2058226043==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============2058226043==--
From eray@uludag.org.tr Wed Sep 7 20:05:53 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id 8D2A084F4B;
Wed, 7 Sep 2005 20:06:04 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id 086A718789A;
Wed, 7 Sep 2005 20:00:16 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id F039818789A
for <cekirdek@liste.uludag.org.tr>;
Wed, 7 Sep 2005 20:00:02 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 401CE8F9B9; Wed, 7 Sep 2005 20:05:51 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from [192.168.1.5] (unknown [85.97.87.38])
by uludag.org.tr (Postfix) with ESMTP id A4EC084F4B
for <cekirdek@uludag.org.tr>; Wed, 7 Sep 2005 20:05:50 +0300 (EEST)
From: Eray Ozkural <eray@uludag.org.tr>
Organization: Pardus
To: cekirdek@uludag.org.tr
Subject: Re: [Cekirdek] [PISI design] mandrake component / group tree
Date: Wed, 7 Sep 2005 20:05:53 +0300
User-Agent: KMail/1.7.2
References: <200507211554.55602.eray@uludag.org.tr>
In-Reply-To: <200507211554.55602.eray@uludag.org.tr>
MIME-Version: 1.0
Message-Id: <200509072005.53482.eray@uludag.org.tr>
Cc:
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============1083705676=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 3642
X-Length: 5765
--===============1083705676==
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
bunu gormus muydunuz?
genelde iki seviye ana component'lari belirtmek icin yeterli
mandrake'nin kafasinin karismasi icin kategorilerle componentlari
karismasi yeterli olmus, yani asagidaki bircok sey aslinda component
*degil* kategori.
On Thursday 21 July 2005 03:54 pm, Eray Ozkural wrote:
> Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan. Bu
> component lafi akil mi karistiriyor diye killaniyorum hala.
>
> Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
> bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
> debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
> Development, Networking fena degil ama bizim daha anlamli birseyler
> yapmamiz gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice
> bir component olacak. Ya bizim componentlari en iyi python module'leri gibi
> dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
>
> apps.office partof apps
> apps.office.openoffice partof apps.office
>
> openoffice-libs partof apps.office.openoffice
> openoffice-bin partof apps.office.openoffice
>
> ornegin
>
> buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
> edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle,
> bu da guzel birsey!
>
> Altlarindaki paketleri yazmiyorum.
>
> Mandrake Group Tree
> --------------------------------
>
> Accessibility
> Archiving
> Backup
> Cd burning
> Compression
> Other
> Books
> Computer books
> Howtos
> Literature
> Other
> Cluster
> Message Passing
> Queueing Services
> Communications
> Configuration
> Databases
> Development
> C
> C++
> Databases
> Debug
> Gnome and GTK+
> Java
> KDE and Qt
> Kernel
> Libraries
> Other
> Perl
> Python
> Documentation
> Editors
> Education
> Emulators
> File Tools
> Games
> Adventure
> Arcade
> Boards
> Cards
> Other
> Puzzles
> Sports
> Strategy
> Graphical desktop
> Enlightenment
> FVWM based
> GNOME
> Icewm
> KDE
> Other
> Sawfish
> WindowMakes
> Xfce
> Graphics
> Monitoring
> Networking
> Chat
> File transfer
> Instant messaging
> Instant Messaging
> IRC
> Mail
> News
> Other
> Remote access
> WWW
> Office
> Publishing
> Sciences
> Astronomy
> Biology
> Chemistry
> Computer science
> Geosciences
> Mathematics
> Other
> Physics
> System
> Base
> Configuration
> Cryptography
> Deployment
> Fonts
> Internationalization
> Kernel and hardware
> Libraries
> Servers
> X11
> XFree86
> Shells
> Sounds
> System
> Terminals
> Text tools
> Toys
> Video
--
Eray Ozkural (exa), Uludag Gelistiricisi
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
--===============1083705676==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============1083705676==--
@@ -1,110 +0,0 @@
From eray@uludag.org.tr Thu Jul 28 16:42:47 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
by uludag.org.tr (Postfix) with ESMTP id 0CE2C75758;
Thu, 28 Jul 2005 16:43:24 +0300 (EEST)
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
by liste.uludag.org.tr (Postfix) with ESMTP id 2FD2D109DF1;
Thu, 28 Jul 2005 16:40:43 +0300 (EEST)
X-Original-To: cekirdek@liste.uludag.org.tr
Delivered-To: cekirdek@liste.uludag.org.tr
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
by liste.uludag.org.tr (Postfix) with ESMTP id B4AAA109DDB
for <cekirdek@liste.uludag.org.tr>;
Thu, 28 Jul 2005 16:40:40 +0300 (EEST)
Received: by uludag.org.tr (Postfix)
id 5D432858B5; Thu, 28 Jul 2005 16:43:21 +0300 (EEST)
Delivered-To: cekirdek@uludag.org.tr
Received: from [192.168.1.5] (unknown [85.99.19.56])
by uludag.org.tr (Postfix) with ESMTP id CC4ED75758
for <cekirdek@uludag.org.tr>; Thu, 28 Jul 2005 16:43:19 +0300 (EEST)
From: Eray Ozkural <eray@uludag.org.tr>
Organization: Pardus
To: cekirdek@uludag.org.tr
Date: Thu, 28 Jul 2005 16:42:47 +0300
User-Agent: KMail/1.7.2
MIME-Version: 1.0
Message-Id: <200507281642.47792.eray@uludag.org.tr>
Cc:
Subject: [Cekirdek] [PISI design] release hikayesi
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============1945860385=="
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 1875
X-Length: 4330
--===============1945860385==
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
selamlar,
dun konustuklarimiz uzerine biraz yatip dusundum. bir ara haci
kuslugu yapmisim ben de ve bir durumu gozden kacirmisim.
dun caglar ve murat'in getirdigi 2. oneriyle karisik bir sey yapmamiz
gerekebilir. benim kafam mi dagilmis ne kusura bakmayin. ama
o kadar onemli degil.
soyle bir sorun var.
simdi bir source var x diye, 5. release'de
icindeki sadece bir paketi ornegin x2'yi etkileyecek bir degisiklik yaptik.
bu durumda x1 ve x3'un build numaralari artmiyor. ama source release
numaralari mi artiyor? tamam buraya kadar ok. ama build sistemi
bu paketlerden yeni bir paket dosyasi yapip repository'e koymuyor bu
durumda, yani source release'i arttigi halde build nosu degismedigi icin
yeni paket koyulmayacak ve indekslenmeyecek bu mantikli mi? mantikli
degilse nasil bir cozum bulunur?
bunun yani sira, simdi bu source release degistiginde, eger paketi build
*edersek*, bir yerde metadata degismek zorunda degil mi? hangi source'un
hangi release'inden build edildigini kaydetmek zorundayiz yanlis mi
dusunuyorum. hemen kizmayin butun bu olaylari basimiza ben sarmadim, bir
sekilde evrimlesti :)
not: galiba olay suna geliyor, tamam build no degismediyse ozunde bu paket
bir onceki source release'inde kalmistir deriz, yani yeni bir dosya kesinlikle
koymayiz repository'e cunku install dosyalari ve metadata, release numarasi
disinda ayni kalacak. offffff.
su anda multiple repository olayinin ilkel versiyonunu kodluyorum bu arada.
gorusuruz,
--
Eray Ozkural (exa), Uludag Gelistiricisi
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
--===============1945860385==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
--===============1945860385==--
@@ -1,125 +0,0 @@
From eray@uludag.org.tr Tue Jun 28 18:49:13 2005
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
X-Original-To: eray@uludag.org.tr
Delivered-To: eray@uludag.org.tr
Received: from uludag.org.tr (localhost [127.0.0.1])
by uludag.org.tr (Postfix) with ESMTP id 80D2653C00C;
Tue, 28 Jun 2005 16:07:58 +0300 (EEST)
X-Original-To: cekirdek@uludag.org.tr
Delivered-To: cekirdek@uludag.org.tr
Received: from [192.168.3.131] (unknown [193.140.73.11])
by uludag.org.tr (Postfix) with ESMTP id 0B6B753C00C
for <cekirdek@uludag.org.tr>; Tue, 28 Jun 2005 16:07:57 +0300 (EEST)
From: Eray Ozkural <eray@uludag.org.tr>
To: cekirdek@uludag.org.tr
Date: Tue, 28 Jun 2005 15:49:13 +0000
User-Agent: KMail/1.8.1
MIME-Version: 1.0
Message-Id: <200506281549.13525.eray@uludag.org.tr>
Subject: [Cekirdek] source release ve binary build numaralari
X-BeenThere: cekirdek@liste.uludag.org.tr
X-Mailman-Version: 2.1.5
Precedence: list
Reply-To: cekirdek@uludag.org.tr
List-Id: cekirdek.liste.uludag.org.tr
List-Unsubscribe: <http://liste.uludag.org.tr/cgi-bin/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
List-Archive: <http://liste.uludag.org.tr/cgi-bin/mailman/private/cekirdek>
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
List-Subscribe: <http://liste.uludag.org.tr/cgi-bin/mailman/listinfo/cekirdek>,
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
Content-Type: multipart/mixed;
boundary="===============2126904290=="
Mime-version: 1.0
Sender: cekirdek-bounces@liste.uludag.org.tr
Errors-To: cekirdek-bounces@liste.uludag.org.tr
X-UID: 865
X-Length: 4347
--===============2126904290==
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
yigitler,
bu post'da dun tartistigimiz bir konuyu ilgilendiren
paket versiyonu numaralarina tam bir aciklik getirmeye
calisacagim.
Bir PISI source'u uc tane sayiyla belirlenir:
name, version, release
Burada version "convenience" icin bulunur. name + release
ise her zaman *ayni* PISI source build islemine sebep olur.
Bundan kasit su. version ve release tamamen bagimsiz.
release 0 ile baslar. ornegin kdevelop
ilk basta
name: kdevelop, version: 3.4.1, release: 0
dir
Bu paketin yeni versiyonlariyla biz ugrastikca, yaptigimiz
her update, en minikleri dahi, release'i arttirir, ve her zaman
bir arttirir (daha aciklayacagim), ama hic bir zaman geriye
gitmez (0'lanmaz ornegin).
ileride:
name: kdevelop, version: 3.4.2, release: 3
olabilir ornegin.
Eger paketin gelistirildigi *distribution* source'u dahilinde
bir versiyon dallanmasi varsa, ancak bu durumda release'e
branch'ler eklenir.
Diyelim ki yukaridaki paket pardus 1.0'in parcasiydi. simdi
pardus 1.0.1 diye bir branch olusturulmak istendi, ornegin
bu UEKAE overlay'ine karsilik gelsin. Bu durumda, versiyon
iliskisini eksiksiz bicimde belirtmek icin:
name: kdevelop, version: 3.4.2, release: 3.1
demek yeterli olacaktir. Ekledigimiz '.1' suffix'i bize butun
dal bilgisini verir. Boylece eksik hicbirsey olmaz.
Binary package'lara gelince. Bir binary package dort
stringle her zaman bulunabilir.
name, version, build, architecture
burada name + build, *bir binary distribution* dahilinde,
ornegin Pardus 1.0 x86 icin yapilmis tek ve tek bir paketi
her zaman gosterir.
Build, o architecture icin, o paketin kacinci kere build
edildigini gosterir. Her paket release'inde en fazla bir
artar.
Bunu daha once tartistigimiz gibi tamamen incremental
build script'inin buldugu md5'larla hesapliyoruz.
Binary package'larin build numarasi icin bir dallanma/budaklanma
semasi henuz onermiyorum, ama bana su anda ayni
source package'lar gibi birsey yapilabilir geliyor. Bana buradaki
en kritik mesele source ve binary package'larini, ve upstream
version string'lerle kendi, SVN-tarzi version string'lerimizi
karistirmamamiz.
Kaplan gunler,
--
Eray
--===============2126904290==
Content-Type: text/plain; charset="iso-8859-9"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
_______________________________________________
Cekirdek mailing list
Cekirdek@uludag.org.tr
http://liste.uludag.org.tr/cgi-bin/mailman/listinfo/cekirdek
--===============2126904290==--
Binary file not shown.
@@ -1,187 +0,0 @@
\documentclass[a4paper,11pt]{article}
\title{P\.IS\.I Packages: Version Policy v0.2}
\date{\today}
\author{Eray \"Ozkural and T. Bar\i{}\c s Metin}
\begin{document}
\maketitle
\section*{Revision History}
\begin{itemize}
\item v0.1: Bar\i\c s Metin wrote the first version preparing the outline,
detailed Source Version Section, and started the Section on Release Number.
\item v0.2: Eray \"Ozkural wrote a detailed introduction, added
explanations of release and build numbers, reorganized a bit.
\end{itemize}
\section{Introduction}
This document explains the \emph{version policy} that applies to
P\.IS\.I packages. Classically, the issue of distinguishing source and
binary distributions unambiguously has not received a rigorous
treatment in the context of LINUX distributions. We have identified
several shortcomings of the usual practices of extending the original
version with suffixes and prefixes, colorfully illustrated in the
following common problems.
\begin{description}
\item[The problem of future downgrades]
The distribution chooses to use a previous version of the package in
the next release. There is no way to indicate this, so ad-hoc
solutions such as version prefixes are used. It is
impossible to denote a future dependency that requires at least this
distribution source release in this case, either.
\item[The problem of redundant distributions]
A trivial patch has been applied to the source. While few binary
packages have been affected by this change, all binary packages
built from the source are redistributed.
\item[The problem of underdetermined rebuilds]
There have been rapid changes in the system, and although no
changes have been made to the package source, a new binary
distribution must be prepared.
\end{description}
We have devised a slightly new approach in order to alleviate these
problems. Our solution consists of encoding the history of source and
binary package developments in separate version strings we call release and
build numbers.
Since the source version is usually used by the users and developers
to identify software, we retain the notion of a source version in
P\.IS\.I as a convenience.
In the following sections, we explain the components of our
versioning scheme.
\subsection{Source Version}
Source version is the version number provided by the
upstream maintainer of the source archive used in package. It must
always be the same as the upstream version used.
\textbf{Example}: If the upstream archive name is
\emph{bash-3.0.tar.gz} the version number of the package is \emph{3.0}
\subsubsection{Version Suffixes}
There is a pre-defined list of suffixes a package version can
take.
\begin{itemize}
\item \textbf{alpha} Source/Package is in alpha state
\item \textbf{beta} Source/Package is in beta state
\item \textbf{pre} Source/Pacgage passed the beta state but stable
version is not relased yet.
\item \textbf{rc} Source/Package is a release-candidate.
\item \textbf{m} Source/Package is a milestone before stable version.
\item \textbf{p} Source/Package is released and some patches are
applied after the release. This is the patch level.
\end{itemize}
The suffix should be written after the special separator
character \textbf{\_}. And there must allways be a number after a
suffix. \textbf{Example}: packagename-1.0\_beta1
The basic order of the priorities for suffixes is:\newline
\emph{p $>$ (no suffix) $>$ m $>$ rc $>$ pre $>$ beta $>$ alpha}.
The scope of a source version string is global in the literal
sense. It shall not vary from repository to repository.
The support for these special suffixes as well as usual alphanumeric
version string ordering has been implemented in P\.IS\.I.
\section{Identifying Package Sources}
A P\.IS\.I source has three identity elements written under
\texttt{SOURCE} tag: name, source version, and source release number.
We usually say just version and release number/release instead of
source version and source release number, respectively. Name is available in
the \texttt{<Name>} tag. Version and release are available in the last
\texttt{<Update>} element of \texttt{<History>} tag of a \texttt{PSPEC}.
The name of a source package is constant throughout its revision
history. The version is the original version, given by its
programmers. Release is a positive integer. Name and release
is sufficient to uniquely identify a particular PISI source revision.
That is, version and release are independent.
\subsection{Release Number}
Release number is the number of the changes that are made to the
package source since the initial version in the distribution source. A
change can be a patch applied to the source archive, modification in
the actions.py, pspec.xml or any file in the source package
directory. This change is indicated in \texttt{<Update>} tags manually
by the package maintainer.
The initial release of a package is by default \texttt{1}. The release
number always increments by $1$ in each revision in the
\texttt{History}, even the slightest ones, but it never decrements.
The scope of the release number is a given distribution, regardless of
its version, e.g. Pardus.
In the future, PISI will have strict checks for release numbers.
\subsection{Dependency Specifications}
We allow a package to use both source version and release to identify
a particular version or a range of package versions.
\section{Identifying Binary packages}
A PISI binary package is produced from a PISI source package. It has a
name that is constant throughout the history of the source package,
and it inherits the source version and release number from the source
package. However, a binary package has in addition a binary build
number. Shortly, build number or just build. For each of the
architecture targets, e.g. particular binaries, it also has an
architecture tag.
A binary package is uniquely identified by its name, build number, and
architecture regardless of the source version.
\section{Build Number}
Similarly to source release number, binary build number is the number
of changes that are made to a binary package. By change, we mean any
bit change. The existence of a change is tested by comparing the
cryptographic checksums in files.xml with those of the previous build, and the
build number is automatically determined by the P\.IS\.I build system.
The build number starts from $1$ as in release number, and increments
by one with each binary change.
The user never interferes with the build number himself. However, if
the user fails to provide the previous build, then a package without
a build number is built. A package without a build number is evaluated
on the basis of release number, which is guaranteed to exist.
The scope of a build number is a given distribution build environment
for a particular architecture, which may vary from repository to
repository. Therefore, it is not used in dependency
specifications. However, the system does assume that a build of a
given package and architecture is unique in a given repository.
\section{Package File Names}
A P\.IS\.I binary package file name contains all the components relevant
to its identification, separated by dashes:
\begin{verbatim}
<binary name>-<source version>-<source release>-<binary build>.pisi
\end{verbatim}
\section{Future Work}
In the future, it may be necessary to extend the notion of release
number and build number to support branches and forks of a
distribution. A proposal was to have CVS-like branching, but it
was dismissed as unnecessary.
\end{document}
-37
View File
@@ -1,37 +0,0 @@
%%
%% This is file `prettyref.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% prettyref.dtx (with options: `style')
%%
%% Copyright (c) 1995 Kevin Ruland
%%
%%
%% prettyref v3.0
%%
%% Copyright 1995,1998. by Kevin Ruland kevin@rodin.wustl.edu
%%
\ProvidesPackage{prettyref}[1998/07/09 v3.0]
\def\newrefformat#1#2{%
\@namedef{pr@#1}##1{#2}}
\newrefformat{eq}{\textup{(\ref{#1})}}
\newrefformat{lem}{Lemma \ref{#1}}
\newrefformat{thm}{Theorem \ref{#1}}
\newrefformat{cha}{Chapter \ref{#1}}
\newrefformat{sec}{Section \ref{#1}}
\newrefformat{tab}{Table \ref{#1} on page \pageref{#1}}
\newrefformat{fig}{Figure \ref{#1} on page \pageref{#1}}
\def\prettyref#1{\@prettyref#1:}
\def\@prettyref#1:#2:{%
\expandafter\ifx\csname pr@#1\endcsname\relax%
\PackageWarning{prettyref}{Reference format #1\space undefined}%
\ref{#1:#2}%
\else%
\csname pr@#1\endcsname{#1:#2}%
\fi%
}
\endinput
%%
%% End of file `prettyref.sty'.
-83
View File
@@ -1,83 +0,0 @@
#!/usr/bin/python
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import locale
import traceback
import exceptions
import signal
import bsddb3.db as db
import pisi.ui
import pisi.context as ctx
from pisi.cli.pisicli import PisiCLI
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
def exit():
pisi.api.finalize()
sys.exit(1)
def handle_exception(exception, value, tb):
signal.signal(signal.SIGINT, signal.SIG_IGN) # disable further interrupts
ui = pisi.cli.CLI() # make a temporary UI
show_traceback = False
if exception == exceptions.KeyboardInterrupt:
ui.error(_("Keyboard Interrupt: Exiting..."))
exit()
elif isinstance(value, pisi.Error):
ui.error(_("Program Terminated."))
show_traceback = ctx.get_option('debug')
elif isinstance(value, db.DBRunRecoveryError):
ui.error(_("""A database operation has been aborted. You should run pisi
again for normal DB recovery procedure. Make sure you have free disk space.
You have to run rebuild-db only when there is file corruption and database upgrades."""))
elif isinstance(value, pisi.Exception):
show_traceback = True
ui.error(_("""Unhandled internal exception.
Please file a bug report. (http://bugs.uludag.org.tr)"""))
else:
# For any other exception (possibly Python exceptions) show
# the traceback!
show_traceback = ctx.get_option('debug')
ui.error(_("System Error. Program Terminated."))
if ctx.get_option('debug'):
ui.error(u"%s: %s" % (exception, value))
else:
ui.error(unicode(value))
ui.info(_("Please use 'pisi help' for general help."))
if show_traceback:
ui.info(_("Traceback:"))
traceback.print_tb(tb)
else:
ui.info(_("Use --debug to see a traceback."))
exit()
if __name__ == "__main__":
try:
import psyco
psyco.full()
except ImportError:
pass
sys.excepthook = handle_exception
locale.setlocale(locale.LC_ALL, '')
cli = PisiCLI()
cli.run_command()
-117
View File
@@ -1,117 +0,0 @@
<!-- Document Type Declaration file for PISI pspec.xml files -->
<!-- http://www.uludag.org.tr/projeler/pisi/ -->
<!ELEMENT PISI (Source,Package+,History)>
<!-- Source Section -->
<!ELEMENT Source (
#PCDATA |
Name |
Homepage |
Icon |
Packager |
License |
IsA |
PartOf |
Summary |
Description |
Archive |
Patches |
BuildDependencies
)*>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Homepage (#PCDATA)>
<!ELEMENT Icon (#PCDATA)>
<!ELEMENT Packager (Name, Email)>
<!ELEMENT Email (#PCDATA)>
<!ELEMENT License (#PCDATA)>
<!ELEMENT IsA (#PCDATA)>
<!ELEMENT PartOf (#PCDATA)>
<!ELEMENT Summary (#PCDATA)>
<!ATTLIST Summary xml:lang CDATA #IMPLIED>
<!ELEMENT Description (#PCDATA)>
<!ATTLIST Description xml:lang CDATA #IMPLIED>
<!ELEMENT Archive (#PCDATA)>
<!ATTLIST Archive sha1sum CDATA #REQUIRED>
<!-- NOTE: we can limit possible types here to the ones that pisi know -->
<!ATTLIST Archive type CDATA #REQUIRED>
<!ELEMENT BuildDependencies (Dependency*)>
<!ELEMENT Dependency (#PCDATA)>
<!ATTLIST Dependency versionFrom CDATA #IMPLIED>
<!ATTLIST Dependency versionTo CDATA #IMPLIED>
<!ATTLIST Dependency version CDATA #IMPLIED>
<!-- NOTE: release number dependencies? -->
<!ELEMENT Patches (Patch*)>
<!ELEMENT Patch (#PCDATA)>
<!ATTLIST Patch compressionType CDATA #IMPLIED>
<!ATTLIST Patch level CDATA #IMPLIED>
<!ATTLIST Patch target CDATA #IMPLIED>
<!-- Package Section -->
<!ELEMENT Package (
#PCDATA |
Name |
License |
IsA |
PartOf |
Summary |
Description |
RuntimeDependencies |
Files |
Conflicts |
AdditionalFiles |
Provides
)*>
<!ELEMENT Provides (COMAR)+>
<!ELEMENT COMAR (#PCDATA)>
<!ATTLIST COMAR script CDATA #IMPLIED>
<!ELEMENT Conflicts (#PCDATA)>
<!ELEMENT AdditionalFiles (AdditionalFile+)>
<!ELEMENT AdditionalFile (#PCDATA)>
<!ATTLIST AdditionalFile target CDATA #REQUIRED>
<!ATTLIST AdditionalFile permission CDATA #IMPLIED>
<!ATTLIST AdditionalFile owner CDATA #IMPLIED>
<!ELEMENT RuntimeDependencies (Dependency*)>
<!ELEMENT Files (Path)+>
<!ELEMENT Path (#PCDATA)>
<!-- NOTE: is this list correct? -->
<!ATTLIST Path fileType
(executable|library|data|config|doc|man|info|localedata|header|all)
#REQUIRED>
<!-- History Section -->
<!ELEMENT History (Update+)>
<!ELEMENT Update (#PCDATA | Date | Version | Comment | Name | Email)*>
<!ATTLIST Update release CDATA #REQUIRED>
<!-- FIXME: valid types must be used instead of CDATA -->
<!ATTLIST Update type CDATA #IMPLIED>
<!ELEMENT Date (#PCDATA)>
<!ELEMENT Version (#PCDATA)>
<!ELEMENT Comment (#PCDATA)>
-556
View File
@@ -1,556 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
<!-- Project file for project pisi -->
<!-- Saved: 2006-05-08, 09:53:27 -->
<!-- Copyright (C) 2006 PiSi Development Team, -->
<Project version="3.8">
<ProgLanguage mixed="0">Python</ProgLanguage>
<UIType>Console</UIType>
<Description>The package management software of Pardus distribution.
</Description>
<Author>PiSi Development Team</Author>
<EmaIl></EmaIl>
<Sources>
<Source>
<Dir>tools</Dir>
<Name>cat-db.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Dir>popt</Dir>
<Name>actions.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Dir>zip</Dir>
<Name>actions.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Dir>unzip</Dir>
<Name>actions.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>configfiletests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>sourcedbtests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>utiltests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>installdbtests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>run.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>metadatatests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>constantstests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>fetchertests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>archivetests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>versiontests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>actionsapitests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>specfiletests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>packagetests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>packagedbtests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>graphtests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>get.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>shelltools.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>variables.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>autotools.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>pisitools.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>kde.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>libtools.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>coreutils.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>cli</Dir>
<Name>pisicli.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>cli</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>cli</Dir>
<Name>commands.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>lockeddbshelve.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>specfile.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>package.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>build.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>packagedb.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>files.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>config.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>index.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>repodb.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>ui.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>util.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>dependency.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>metadata.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>pgraph.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>operations.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>context.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>graph.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>sourcearchive.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>configfile.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>sourcedb.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>installdb.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>constants.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>fetcher.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>archive.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>version.py</Name>
</Source>
<Source>
<Name>setup.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>pisitoolsfunctions.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>repostats.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>uri.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>api.py</Name>
</Source>
<Source>
<Name>TODO</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>fetchAll.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>pythonmodules.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>scons.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>cli</Dir>
<Name>colors.py</Name>
</Source>
<Source>
<Name>pisi-cli</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>actionsapi</Dir>
<Name>perlmodules.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>comariface.py</Name>
</Source>
<Source>
<Dir>po</Dir>
<Name>tr.po</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>oo.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>update-environ.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>convert_history.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>dependencytests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>atomicoperations.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>ootests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>testcase.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>fix_old_metadata_files.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>xmlexttests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>filestests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>component.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>autoxmltests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlextpiks.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>autoxml.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlfilemdom.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlextmdom.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlfile.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlext.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlfilecdom.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlextcdom.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>pxml</Dir>
<Name>xmlfilepiks.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>find-lib-deps.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>svndist.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>rmcomp-source-repo.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>check-source-repo.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>filetests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>file.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>search</Dir>
<Name>invertedindex.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>search</Dir>
<Name>tokenize.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>search</Dir>
<Name>__init__.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Dir>search</Dir>
<Name>preprocess.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>searchtests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>dbshelve.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>calc-build-order.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>find_unowned_files.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>repodbtests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>itembyrepodb.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>buildtests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>zipfileext.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>pygettext.py</Name>
</Source>
<Source>
<Dir>tools</Dir>
<Name>repo-diff.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>conflicttests.py</Name>
</Source>
<Source>
<Dir>tests</Dir>
<Name>zipfileexttests.py</Name>
</Source>
<Source>
<Dir>pisi</Dir>
<Name>environment.py</Name>
</Source>
</Sources>
<Forms>
</Forms>
<Translations>
</Translations>
<Interfaces>
</Interfaces>
<Others>
<Other>
<Name>pisi-cli</Name>
</Other>
<Other>
<Dir>tests</Dir>
<Name>beta-light.sh</Name>
</Other>
<Other>
<Dir>tests</Dir>
<Name>beta-upgrade.sh</Name>
</Other>
<Other>
<Dir>tests</Dir>
<Name>beta.sh</Name>
</Other>
<Other>
<Dir>tests</Dir>
<Name>sandbox</Name>
</Other>
<Other>
<Dir>tests</Dir>
<Name>beta-remoterepo.sh</Name>
</Other>
</Others>
<MainScript>
<Name>pisi-cli</Name>
</MainScript>
<Vcs>
<VcsType>Subversion</VcsType>
<VcsOptions>{'status': [], 'log': [], 'global': [], 'update': [], 'remove': [], 'add': [], 'tag': [], 'export': [], 'diff': [], 'commit': [], 'checkout': [], 'history': []}</VcsOptions>
<VcsOtherData>{'standardLayout': 1}</VcsOtherData>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.py" type="SOURCES" />
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
</FiletypeAssociations>
</Project>
-39
View File
@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# PiSi version
__version__ = "1.1_beta3"
__dbversion__ = "1.1_beta3"
__filesdbversion__ = "1.0.5" # yes, this is the real bottleneck
__all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ]
class Exception(Exception):
"""Class of exceptions that must be caught and handled within PISI"""
def __str__(self):
s = u''
for x in self.args:
if s != '':
s += '\n'
s += unicode(x)
return s
class Error(Exception):
"""Class of exceptions that lead to program termination"""
pass
import pisi.api
# FIXME: can't do this due to name clashes in config and other singletons booo
#pisi.api import *
@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import pisi
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
import pisi.context as ctx
def error(msg):
if ctx.config.get_option('ignore_action_errors'):
ctx.ui.error(msg)
else:
raise Error(msg)
@@ -1,154 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
from pisi.actionsapi.shelltools import can_access_file
from pisi.actionsapi.libtools import gnuconfig_update
class ConfigureError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
if can_access_file('config.log'):
ctx.ui.error(_('Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class RunTimeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def configure(parameters = ''):
'''configure source with given parameters = "--with-nls --with-libusb --with-something-usefull"'''
if can_access_file('configure'):
gnuconfig_update()
args = './configure \
--prefix=/%s \
--host=%s \
--mandir=/%s \
--infodir=/%s \
--datadir=/%s \
--sysconfdir=/%s \
--localstatedir=/%s \
%s' % (get.defaultprefixDIR(), \
get.HOST(), get.manDIR(), \
get.infoDIR(), get.dataDIR(), \
get.confDIR(), get.localstateDIR(), parameters)
if system(args):
raise ConfigureError(_('Configure failed.'))
else:
raise ConfigureError(_('No configure script found.'))
def rawConfigure(parameters = ''):
'''configure source with given parameters = "--prefix=/usr --libdir=/usr/lib --with-nls"'''
if can_access_file('configure'):
gnuconfig_update()
if system('./configure %s' % parameters):
raise ConfigureError(_('Configure failed.'))
else:
raise ConfigureError(_('No configure script found.'))
def compile(parameters = ''):
#FIXME: Only one package uses this until now, hmmm
system('%s %s %s' % (get.CC(), get.CFLAGS(), parameters))
def make(parameters = ''):
'''make source with given parameters = "all" || "doc" etc.'''
if system('make %s %s' % (get.makeJOBS(), parameters)):
raise MakeError(_('Make failed.'))
def install(parameters = '', argument = 'install'):
'''install source into install directory with given parameters'''
if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'):
args = 'make prefix=%(prefix)s/%(defaultprefix)s \
datadir=%(prefix)s/%(data)s \
infodir=%(prefix)s/%(info)s \
localstatedir=%(prefix)s/%(localstate)s \
mandir=%(prefix)s/%(man)s \
sysconfdir=%(prefix)s/%(conf)s \
%(parameters)s \
%(argument)s' % {'prefix': get.installDIR(),
'defaultprefix': get.defaultprefixDIR(),
'man': get.manDIR(),
'info': get.infoDIR(),
'localstate': get.localstateDIR(),
'conf': get.confDIR(),
'data': get.dataDIR(),
'parameters': parameters,
'argument':argument}
if system(args):
raise InstallError(_('Install failed.'))
else:
raise InstallError(_('No Makefile found.'))
def rawInstall(parameters = '', argument = 'install'):
'''install source into install directory with given parameters = PREFIX=%s % get.installDIR()'''
if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'):
if system('make %s %s' % (parameters, argument)):
raise InstallError(_('Install failed.'))
else:
raise InstallError(_('No Makefile found.'))
def aclocal(parameters = ''):
'''generates an aclocal.m4 based on the contents of configure.in.'''
if system('aclocal %s' % parameters):
raise RunTimeError(_('Running aclocal failed.'))
def autoconf(parameters = ''):
'''generates a configure script'''
if system('autoconf %s' % parameters):
raise RunTimeError(_('Running autoconf failed.'))
def autoreconf(parameters = ''):
'''re-generates a configure script'''
if system('autoreconf %s' % parameters):
raise RunTimeError(_('Running autoconf failed.'))
def automake(parameters = ''):
'''generates a makefile'''
if system('automake %s' % parameters):
raise RunTimeError(_('Running automake failed.'))
def autoheader(parameters = ''):
'''generates templates for configure'''
if system('autoheader %s' % parameters):
raise RunTimeError(_('Running autoheader failed.'))
@@ -1,81 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import re
import sys
from itertools import izip
from itertools import imap
from itertools import count
from itertools import ifilter
from itertools import ifilterfalse
# ActionsAPI
import pisi.actionsapi
def cat(filename):
return file(filename).xreadlines()
class grep:
'''keep only lines that match the regexp'''
def __init__(self, pat, flags = 0):
self.fun = re.compile(pat, flags).match
def __ror__(self, input):
return ifilter(self.fun, input)
class tr:
'''apply arbitrary transform to each sequence element'''
def __init__(self, transform):
self.tr = transform
def __ror__(self, input):
return imap(self.tr, input)
class printto:
'''print sequence elements one per line'''
def __init__(self, out = sys.stdout):
self.out = out
def __ror__(self,input):
for line in input:
print >> self.out, line
printlines = printto(sys.stdout)
class terminator:
def __init__(self,method):
self.process = method
def __ror__(self,input):
return self.process(input)
aslist = terminator(list)
asdict = terminator(dict)
astuple = terminator(tuple)
join = terminator(''.join)
enum = terminator(enumerate)
class sort:
def __ror__(self,input):
ll = list(input)
ll.sort()
return ll
sort = sort()
class uniq:
def __ror__(self,input):
for i in input:
try:
if i == prev:
continue
except NameError:
pass
prev = i
yield i
uniq = uniq()
-193
View File
@@ -1,193 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standart Python Modules
import os
import sys
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PISI Modules
import pisi.actionsapi
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi.variables
class BinutilsError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
# Globals
env = pisi.actionsapi.variables.glb.env
dirs = pisi.actionsapi.variables.glb.dirs
def curDIR():
'''returns current work directory's path'''
return os.getcwd()
def curKERNEL():
'''returns currently running kernel's version'''
return os.uname()[2]
def curPYTHON():
''' returns currently used python's version'''
(a, b, c, x, y) = sys.version_info
return 'python%s.%s' % (a, b)
def ENV(environ):
'''returns any given environ variable'''
try:
return os.environ[environ];
except KeyError:
return None
# PİSİ Related Functions
def pkgDIR():
'''returns the path of binary packages'''
'''Default: /var/cache/pisi/packages'''
return env.pkg_dir
def workDIR():
return env.work_dir
def installDIR():
'''returns the path of binary packages'''
return env.install_dir
# PSPEC Related Functions
def srcNAME():
return env.src_name
def srcVERSION():
return env.src_version
def srcRELEASE():
return env.src_release
def srcTAG():
return '%s-%s-%s' % (env.src_name, env.src_version, env.src_release)
def srcDIR():
return '%s-%s' % (env.src_name, env.src_version)
# Build Related Functions
def HOST():
return env.host
def CHOST():
# FIXME: Currently it behave same as HOST,
# but will be used for cross-compiling when PİSİ ready...
return env.host
def CFLAGS():
return env.cflags
def CXXFLAGS():
return env.cxxflags
def LDFLAGS():
return env.ldflags
def makeJOBS():
return env.jobs
# Directory Related Functions
def docDIR():
return dirs.doc
def sbinDIR():
return dirs.sbin
def infoDIR():
return dirs.info
def manDIR():
return dirs.man
def dataDIR():
return dirs.data
def confDIR():
return dirs.conf
def localstateDIR():
return dirs.localstate
def defaultprefixDIR():
return dirs.defaultprefix
def kdeDIR():
return dirs.kde
def qtDIR():
return dirs.qt
def qtLIBDIR():
return '%s/lib/' % qtDIR()
# Binutils Variables
def existBinary(bin):
# determine if path has binary
path = os.environ['PATH'].split(':')
for directory in path:
if os.path.exists(os.path.join(directory, bin)):
return True
return False
def getBinutilsInfo(util):
cross_build_name = '%s-%s' % (HOST(), util)
if not existBinary(cross_build_name):
if not existBinary(util):
raise BinutilsError(_('Util %s cannot be found') % util)
else:
ctx.ui.debug(_('Warning: %s does not exist, using plain name %s') \
% (cross_build_name, util))
return util
else:
return cross_build_name
def AR():
return getBinutilsInfo('ar')
def AS():
return getBinutilsInfo('as')
def CC():
return getBinutilsInfo('gcc')
def CXX():
return getBinutilsInfo('g++')
def LD():
return getBinutilsInfo('ld')
def NM():
return getBinutilsInfo('nm')
def RANLIB():
return getBinutilsInfo('ranlib')
def F77():
return getBinutilsInfo('f77')
def GCJ():
return getBinutilsInfo('gcj')
@@ -1,82 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# standard python modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
from pisi.actionsapi.shelltools import can_access_file
class ConfigureError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
if can_access_file('config.log'):
ctx.ui.error(_('\n!!! Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def configure(parameters = ''):
''' parameters = '--with-nls --with-libusb --with-something-usefull '''
if can_access_file('configure'):
args = './configure \
--prefix=%s \
--host=%s \
--with-x \
--enable-mitshm \
--with-xinerama \
--with-qt-dir=%s \
--enable-mt \
--with-qt-libraries=%s \
--disable-dependency-tracking \
--disable-debug \
%s' % (get.kdeDIR(), get.HOST(), get.qtDIR(), get.qtLIBDIR(), parameters)
if system(args):
raise ConfigureError(_('Configure failed.'))
else:
raise ConfigureError(_('No configure script found.'))
def make(parameters = ''):
'''make source with given parameters = "all" || "doc" etc.'''
if system('make %s %s' % (get.makeJOBS(), parameters)):
raise MakeError(_('Make failed.'))
def install(parameters = 'install'):
if can_access_file('Makefile'):
args = 'make DESTDIR=%s destdir=%s %s' % (get.installDIR(), get.installDIR(), parameters)
if system(args):
raise InstallError(_('Install failed.'))
else:
raise InstallError(_('No Makefile found.'))
@@ -1,73 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi-Core Modules
import pisi.context as ctx
from pisi.util import join_path
# ActionsAPI Modules
import pisi.actionsapi
from pisi.actionsapi.shelltools import *
import pisi.actionsapi.get as get
class RunTimeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def preplib(sourceDirectory = '/usr/lib'):
sourceDirectory = join_path(get.installDIR(), sourceDirectory)
if can_access_directory(sourceDirectory):
if system('/sbin/ldconfig -n -N %s' % sourceDirectory):
raise RunTimeError(_('Running ldconfig failed.'))
def gnuconfig_update():
''' copy newest config.* onto source\'s '''
for root, dirs, files in os.walk(os.getcwd()):
for fileName in files:
if fileName in ['config.sub', 'config.guess']:
targetFile = os.path.join(root, fileName)
if os.path.islink(targetFile):
unlink(targetFile)
copy('/usr/share/gnuconfig/%s' % fileName, join_path(root, fileName))
ctx.ui.info(_('GNU Config Update Finished.'))
def libtoolize(parameters = ''):
if system('/usr/bin/libtoolize %s' % parameters):
raise RunTimeError(_('Running libtoolize failed.'))
def gen_usr_ldscript(dynamicLib):
makedirs('%s/usr/lib' % get.installDIR())
destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w')
content = '''
/* GNU ld script
Since Pardus has critical dynamic libraries
in /lib, and the static versions in /usr/lib,
we need to have a "fake" dynamic lib in /usr/lib,
otherwise we run into linking problems.
*/
GROUP ( /lib/%s )
''' % dynamicLib
destinationFile.write(content)
destinationFile.close()
chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib))
@@ -1,76 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# standard python modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
from pisi.actionsapi.shelltools import can_access_file
from pisi.actionsapi.shelltools import export
class ConfigureError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def configure(parameters = ''):
'''configure source with given parameters.'''
export('PERL_MM_USE_DEFAULT', '1')
if can_access_file('Build.PL'):
if system('perl Build.PL installdirs=vendor destdir=%s' % get.installDIR()):
raise CompileError, _('Configure failed.')
else:
if system('perl Makefile.PL %s PREFIX=/usr INSTALLDIRS=vendor DESTDIR=%s' % (parameters, get.installDIR())):
raise CompileError, _('Configure failed.')
def make(parameters = ''):
'''make source with given parameters.'''
if can_access_file('Makefile'):
if system('make %s' % parameters):
raise MakeError, _('Make failed.')
else:
if system('perl Build build'):
raise MakeError, _('perl build failed.')
def install(parameters = 'install'):
'''install source with given parameters.'''
if can_access_file('Makefile'):
if system('make %s' % parameters):
raise InstallError, _('Make failed.')
else:
if system('perl Build install'):
raise MakeError, _('perl install failed.')
# FIXME: fix_local_pod % update_pod
@@ -1,235 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
'''supports globs in sourceFile arguments'''
# Standart Python Modules
import os
import glob
import sys
import fileinput
import re
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
from pisi.util import join_path
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.pisitoolsfunctions import *
from pisi.actionsapi.shelltools import *
from pisi.actionsapi import error
def dobin(sourceFile, destinationDirectory = '/usr/bin'):
'''insert a executable file into /bin or /usr/bin'''
''' example call: pisitools.dobin("bin/xloadimage", "/bin", "xload") '''
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
def dodir(destinationDirectory):
'''creates a directory tree'''
makedirs(join_path(get.installDIR(), destinationDirectory))
def dodoc(*sourceFiles):
'''inserts the files in the list of files into /usr/share/doc/PACKAGE'''
readable_insinto(join_path(get.installDIR(), join_path('/usr/share/doc', get.srcTAG())), *sourceFiles)
def doexe(sourceFile, destinationDirectory):
'''insert a executable file into destination directory'''
''' example call: pisitools.doexe("kde-3.4.sh", "/etc/X11/Sessions")'''
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
def dohtml(*sourceFiles):
'''inserts the files in the list of files into /usr/share/doc/PACKAGE/html'''
''' example call: pisitools.dohtml("doc/doxygen/html/*")'''
destionationDirectory = join_path(get.installDIR(), 'usr/share/doc' ,get.srcTAG(), 'html')
if not can_access_directory(destionationDirectory):
makedirs(destionationDirectory)
allowed_extensions = ['.png', '.gif', '.html', '.htm', '.jpg', '.css', '.js']
disallowed_directories = ['CVS']
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
if os.path.isfile(source) and os.path.splitext(source)[1] in allowed_extensions:
system('install -m0644 "%s" %s' % (source, destionationDirectory))
if os.path.isdir(source) and os.path.basename(source) not in disallowed_directories:
for root, dirs, files in os.walk(source):
for source in files:
if os.path.splitext(source)[1] in allowed_extensions:
makedirs(destionationDirectory)
system('install -m0644 %s %s' % (join_path(root, source), destionationDirectory))
def doinfo(*sourceFiles):
'''inserts the into files in the list of files into /usr/share/info'''
readable_insinto(join_path(get.installDIR(), get.infoDIR()), *sourceFiles)
def dolib(sourceFile, destinationDirectory = '/usr/lib'):
'''insert the library into /usr/lib'''
'''example call: pisitools.dolib_a("libz.a")'''
'''example call: pisitools.dolib_a("libz.so")'''
sourceFile = join_path(os.getcwd(), sourceFile)
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
lib_insinto(sourceFile, destinationDirectory, 755)
def dolib_a(sourceFile, destinationDirectory = '/usr/lib'):
'''insert the static library into /usr/lib with permission 0644'''
'''example call: pisitools.dolib_a("staticlib/libvga.a")'''
sourceFile = join_path(os.getcwd(), sourceFile)
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
lib_insinto(sourceFile, destinationDirectory, 644)
def dolib_so(sourceFile, destinationDirectory = '/usr/lib'):
'''insert the static library into /usr/lib with permission 0755'''
'''example call: pisitools.dolib_so("pppd/plugins/minconn.so")'''
sourceFile = join_path(os.getcwd(), sourceFile)
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
lib_insinto(sourceFile, destinationDirectory, 755)
def doman(*sourceFiles):
'''inserts the man pages in the list of files into /usr/share/man/'''
'''example call: pisitools.doman("man.1", "pardus.*")'''
manDIR = join_path(get.installDIR(), get.manDIR())
if not can_access_directory(manDIR):
makedirs(manDIR)
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
try:
pageName, pageDirectory = source[:source.rindex('.')], \
source[source.rindex('.')+1:]
except ValueError:
error(_('ActionsAPI [doman]: Wrong man page file: %s') % (source))
makedirs(join_path(manDIR, '/man%s' % pageDirectory))
system('install -m0644 %s %s' % (source, join_path(manDIR, '/man%s' % pageDirectory)))
def domo(sourceFile, locale, destinationFile ):
'''inserts the mo files in the list of files into /usr/share/locale/LOCALE/LC_MESSAGES'''
'''example call: pisitools.domo("po/tr.po", "tr", "pam_login.mo")'''
system('msgfmt %s' % sourceFile)
makedirs('%s/usr/share/locale/%s/LC_MESSAGES/' % (get.installDIR(), locale))
move('messages.mo', '%s/usr/share/locale/%s/LC_MESSAGES/%s' % (get.installDIR(), locale, destinationFile))
def domove(sourceFile, destination, destinationFile = ''):
'''moves sourceFile/Directory into destinationFile/Directory'''
''' example call: pisitools.domove("/usr/bin/bash", "/bin/bash")'''
''' example call: pisitools.domove("/usr/bin/", "/usr/sbin")'''
makedirs(join_path(get.installDIR(), destination))
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
if not destinationFile:
move(filePath, join_path(get.installDIR(), join_path(destination, os.path.basename(filePath))))
else:
move(filePath, join_path(get.installDIR(), join_path(destination, destinationFile)))
def rename(sourceFile, destinationFile):
''' renames sourceFile as destinationFile'''
''' example call: pisitools.rename("/usr/bin/bash", "bash.old") '''
''' the result of the previous example would be "/usr/bin/bash.old" '''
baseDir = os.path.dirname(sourceFile)
try:
os.rename(join_path(get.installDIR(), sourceFile), join_path(get.installDIR(), baseDir, destinationFile))
except OSError:
error(_('ActionsAPI [rename]: No such file or directory: %s') % (sourceFile))
def dosed(sourceFiles, findPattern, replacePattern = ''):
'''replaces patterns in sourceFiles'''
''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")'''
''' example call: pisitools.dosed("/etc/passwd", "caglar")'''
''' example call: pisitools.dosed("/etc/pass*", "caglar")'''
''' example call: pisitools.dosed("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")'''
for sourceFile in glob.glob(sourceFiles):
if can_access_file(sourceFile):
for line in fileinput.input(sourceFile, inplace = 1):
#FIXME: In-place filtering is disabled when standard input is read
line = re.sub(findPattern, replacePattern, line)
sys.stdout.write(line)
else:
raise FileError(_('File does not exist or permission denied: %s') % sourceFile)
def dosbin(sourceFile, destinationDirectory = '/usr/sbin'):
'''insert a executable file into /sbin or /usr/sbin'''
''' example call: pisitools.dobin("bin/xloadimage", "/sbin") '''
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
def dosym(sourceFile, destinationFile):
'''creates soft link between sourceFile and destinationFile'''
''' example call: pisitools.dosym("/usr/bin/bash", "/bin/bash")'''
makedirs(join_path(get.installDIR(), os.path.dirname(destinationFile)))
try:
os.symlink(sourceFile, join_path(get.installDIR() ,destinationFile))
except OSError:
error(_('ActionsAPI [dosym]: File exists: %s') % (sourceFile))
def insinto(destinationDirectory, sourceFile, destinationFile = '', sym = True):
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
makedirs(join_path(get.installDIR(), destinationDirectory))
if not destinationFile:
for filePath in glob.glob(sourceFile):
if can_access_file(filePath):
copy(filePath, join_path(get.installDIR(), join_path(destinationDirectory, os.path.basename(filePath))), sym)
else:
copy(sourceFile, join_path(get.installDIR(), join_path(destinationDirectory, destinationFile)), sym)
def newdoc(sourceFile, destinationFile):
'''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile'''
destinationDirectory = '' #490
destinationDirectory = os.path.dirname(destinationFile)
destinationFile = os.path.basename(destinationFile)
# Use copy instead of move or let build-install scream like file not found!
copy(sourceFile, destinationFile)
readable_insinto(join_path(get.installDIR(), 'usr/share/doc', get.srcTAG(), destinationDirectory), destinationFile)
def newman(sourceFile, destinationFile):
'''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile'''
# Use copy instead of move or let build-install scream like file not found!
copy(sourceFile, destinationFile)
doman(destinationFile)
def remove(sourceFile):
'''removes sourceFile'''
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
unlink(filePath)
def removeDir(destinationDirectory):
'''removes destinationDirectory and its subtrees'''
for directory in glob.glob(join_path(get.installDIR(), destinationDirectory)):
unlinkDir(directory)
@@ -1,81 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Generic functions for common usage of pisitools #
# Standart Python Modules
import os
import glob
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
from pisi.actionsapi.shelltools import *
class FileError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class ArgumentError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def executable_insinto(destinationDirectory, *sourceFiles):
'''insert a executable file into destinationDirectory'''
if not sourceFiles or not destinationDirectory:
raise ArgumentError(_('Insufficient arguments.'))
if not can_access_directory(destinationDirectory):
makedirs(destinationDirectory)
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
# FIXME: use an internal install routine for these
system('install -m0755 -o root -g root %s %s' % (source, destinationDirectory))
def readable_insinto(destinationDirectory, *sourceFiles):
'''inserts file list into destinationDirectory'''
if not sourceFiles or not destinationDirectory:
raise ArgumentError(_('Insufficient arguments.'))
if not can_access_directory(destinationDirectory):
makedirs(destinationDirectory)
for sourceFile in sourceFiles:
for source in glob.glob(sourceFile):
system('install -m0644 "%s" %s' % (source, destinationDirectory))
def lib_insinto(sourceFile, destinationDirectory, permission = 0644):
'''inserts a library fileinto destinationDirectory with given permission'''
if not sourceFile or not destinationDirectory:
raise ArgumentError(_('Insufficient arguments.'))
if not can_access_directory(destinationDirectory):
makedirs(destinationDirectory)
if os.path.islink(sourceFile):
os.symlink(os.path.realpath(sourceFile), os.path.join(destinationDirectory, sourceFile))
else:
system('install -m%s %s %s' % (permission, sourceFile, destinationDirectory))
@@ -1,67 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# standard python modules
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system, can_access_file
from pisi.actionsapi.pisitools import dodoc
class CompileError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class RunTimeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def compile(parameters = ''):
'''compile source with given parameters.'''
if system('python setup.py build %s' % (parameters)):
raise CompileError, _('Make failed.')
def install(parameters = ''):
'''does python setup.py install'''
if system('python setup.py install --root=%s --no-compile %s' % (get.installDIR(), parameters)):
raise InstallError, _('Install failed.')
DDOCS = 'CHANGELOG COPYRIGHT KNOWN_BUGS MAINTAINERS PKG-INFO \
CONTRIBUTORS LICENSE COPYING* Change* MANIFEST* README*'
for doc in DDOCS:
if can_access_file(doc):
pisitools.dodoc(doc)
def run(parameters = ''):
'''executes parameters with python'''
if system('python %s' % (parameters)):
raise RunTimeError, _('Running %s failed.') % parameters
@@ -1,44 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Pisi Modules
import pisi.context as ctx
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get as get
from pisi.actionsapi.shelltools import system
class MakeError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
class InstallError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
def make(parameters = ''):
if system('scons %s' % parameters):
raise MakeError(_('Make failed.'))
def install(parameters = 'install', prefix = get.installDIR()):
if system('scons prefix=%s %s' % (prefix, parameters)):
raise InstallError(_('Install failed.'))
@@ -1,225 +0,0 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standart Python Modules
import os
import glob
import shutil
import string
import pwd
import grp
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# Pisi Modules
import pisi.context as ctx
# ActionsAPI Modules
import pisi.actionsapi
import pisi.actionsapi.get
from pisi.actionsapi import error
from pisi.util import run_batch
from pisi.util import join_path
def can_access_file(filePath):
'''test the existence of file'''
return os.access(filePath, os.F_OK)
def can_access_directory(destinationDirectory):
'''test readability, writability and executablility of directory'''
return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK)
def makedirs(destinationDirectory):
'''recursive directory creation function'''
try:
if not os.access(destinationDirectory, os.F_OK):
os.makedirs(destinationDirectory)
except OSError:
error(_('Cannot create directory %s') % destinationDirectory)
def echo(destionationFile, content):
try:
f = open(destionationFile, 'a')
f.write('%s\n' % content)
f.close()
except IOError:
error(_('ActionsAPI [echo]: Can\'t append to file %s.') % (destionationFile))
def chmod(filePath, mode = 0755):
'''change the mode of filePath to the mode'''
for fileName in glob.glob(filePath):
if can_access_file(fileName):
try:
os.chmod(fileName, mode)
except OSError:
ctx.ui.error(_('ActionsAPI [chmod]: Operation not permitted: %s (mode: %s)') \
% (fileName, mode))
else:
ctx.ui.error(_('ActionsAPI [chmod]: File %s doesn\'t exists.') % (fileName))
def chown(filePath, uid = "root", gid = "root"):
'''change the owner and group id of filePath to uid and gid'''
if can_access_file(filePath):
try:
os.chown(filePath, pwd.getpwnam(uid)[2], grp.getgrnam(gid)[2])
except OSError:
ctx.ui.error(_('ActionsAPI [chown]: Operation not permitted: %s (uid: %s, gid: %s)') \
% (filePath, uid, gid))
else:
ctx.ui.error(_('ActionsAPI [chown]: File %s doesn\'t exists.') % filePath)
def sym(source, destination):
'''creates symbolic link'''
try:
os.symlink(source, destination)
except OSError:
ctx.ui.error(_('ActionsAPI [sym]: Permission denied: %s to %s') % (source, destination))
def unlink(filePath):
'''remove the file path'''
if isFile(filePath) or isLink(filePath):
try:
os.unlink(filePath)
except OSError:
ctx.ui.error(_('ActionsAPI [unlink]: Permission denied: %s.') % (filePath))
elif isDirectory(filePath):
pass
else:
ctx.ui.error(_('ActionsAPI [unlink]: File %s doesn\'t exists.') % (filePath))
def unlinkDir(sourceDirectory):
'''delete an entire directory tree'''
if isDirectory(sourceDirectory) or isLink(sourceDirectory):
try:
shutil.rmtree(sourceDirectory)
except OSError:
error(_('ActionsAPI [unlinkDir]: Operation not permitted: %s') % (sourceDirectory))
elif isFile(sourceDirectory):
pass
else:
error(_('ActionsAPI [unlinkDir]: Directory %s doesn\'t exists.') % (sourceDirectory))
def move(source, destination):
'''recursively move a "source" file or directory to "destination"'''
for filePath in glob.glob(source):
if isFile(filePath) or isLink(filePath) or isDirectory(filePath):
try:
shutil.move(filePath, destination)
except OSError:
error(_('ActionsAPI [move]: Permission denied: %s to %s') % (filePath, destination))
else:
error(_('ActionsAPI [move]: File %s doesn\'t exists.') % (filePath))
# FIXME: instead of passing a sym parameter, split copy and copytree into 4 different function
def copy(source, destination, sym = True):
'''recursively copy a "source" file or directory to "destination"'''
for filePath in glob.glob(source):
if isFile(filePath) and not isLink(filePath):
try:
shutil.copy(filePath, destination)
except IOError:
error(_('ActionsAPI [copy]: Permission denied: %s to %s') % (filePath, destination))
elif isLink(filePath) and sym:
if isDirectory(destination):
os.symlink(os.readlink(filePath), join_path(destination, os.path.basename(filePath)))
else:
if isFile(destination):
os.remove(destination)
os.symlink(os.readlink(filePath), destination)
elif isLink(filePath) and not sym:
if isDirectory(filePath):
copytree(filePath, destination)
else:
shutil.copy(filePath, destination)
elif isDirectory(filePath):
copytree(filePath, destination, sym)
else:
error(_('ActionsAPI [copy]: File %s does not exist.') % filePath)
def copytree(source, destination, sym = True):
'''recursively copy an entire directory tree rooted at source'''
if isDirectory(source):
if os.path.exists(destination):
if isDirectory(destination):
copytree(source, join_path(destination, os.path.basename(source.strip('/'))))
return
else:
copytree(source, join_path(destination, os.path.basename(source)))
return
try:
shutil.copytree(source, destination, sym)
except OSError, e:
error(_('ActionsAPI [copytree] %s to %s: %s') % (source, destination, e))
else:
error(_('ActionsAPI [copytree]: Directory %s doesn\'t exists.') % (source))
def touch(filePath):
'''changes the access time of the 'filePath', or creates it if it is not exist'''
if glob.glob(filePath):
for f in glob.glob(filePath):
os.utime(f, None)
else:
try:
f = open(filePath, 'w')
f.close()
except IOError:
error(_('ActionsAPI [touch]: Permission denied: %s') % (filePath))
def cd(directoryName = ''):
'''change directory'''
current = os.getcwd()
if directoryName:
os.chdir(directoryName)
else:
os.chdir(os.path.dirname(current))
def ls(source):
'''listdir'''
if os.path.isdir(source):
return os.listdir(source)
else:
return glob.glob(source)
def export(key, value):
'''export environ variable'''
os.environ[key] = value
def isLink(filePath):
'''return True if filePath refers to a symbolic link'''
return os.path.islink(filePath)
def isFile(filePath):
'''return True if filePath is an existing regular file'''
return os.path.isfile(filePath)
def isDirectory(filePath):
'''Return True if filePath is an existing directory'''
return os.path.isdir(filePath)
def realPath(filePath):
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
return os.path.realpath(filePath)
def baseName(filePath):
'''return the base name of pathname filePath'''
return os.path.basename(filePath)
def dirName(filePath):
'''return the directory name of pathname path'''
return os.path.dirname(filePath)
def system(command):
command = string.join(string.split(command))
return run_batch(command, True)[0]
@@ -1,98 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# Standard Python Modules
from os import getenv
from os import environ
# Pisi-Core Modules
import pisi.context as ctx
# Set individual information, that are generally needed for ActionsAPI
def exportFlags():
'''General flags used in actions API.'''
# Build systems depend on these environment variables. That is why
# we export them instead of using as (instance) variables.
values = ctx.config.values
environ['HOST'] = values.build.host
environ['CFLAGS'] = values.build.cflags
environ['CXXFLAGS'] = values.build.cxxflags
environ['LDFLAGS'] = values.build.ldflags
environ['USER_LDFLAGS'] = values.build.ldflags
environ['JOBS'] = values.build.jobs
class Env(object):
'''General environment variables used in actions API'''
def __init__(self):
exportFlags()
self.__vars = {
'pkg_dir': 'PKG_DIR',
'work_dir': 'WORK_DIR',
'install_dir': 'INSTALL_DIR',
'src_name': 'SRC_NAME',
'src_version': 'SRC_VERSION',
'src_release': 'SRC_RELEASE',
'host': 'HOST',
'cflags': 'CFLAGS',
'cxxflags': 'CXXFLAGS',
'ldflags': 'LDFLAGS',
'jobs': 'JOBS'
}
def __getattr__(self, attr):
# Using environment variables is somewhat tricky. Each time
# you need them you need to check for their value.
if self.__vars.has_key(attr):
return getenv(self.__vars[attr])
else:
return None
class Dirs:
'''General directories used in actions API.'''
# TODO: Eventually we should consider getting these from a/the
# configuration file
doc = 'usr/share/doc'
sbin = 'usr/sbin'
man = 'usr/share/man'
info = 'usr/share/info'
data = 'usr/share'
conf = 'etc'
localstate = 'var/lib'
defaultprefix = 'usr'
# These should be owned by object not the class. Or else Python
# will bug us with NoneType errors because of uninitialized
# context (ctx) because of the import in build.py.
def __init__(self):
self.values = ctx.config.values
self.kde = self.values.dirs.kde_dir
self.qt = self.values.dirs.qt_dir
# As we import this module from build.py, we can't init glb as a
# singleton here. Or else Python will bug us with NoneType errors
# because of uninitialized context (ctx) because of exportFlags().
#
# We import this modue from build.py becase we need to reset/init glb
# for each build. # See bug #2575
glb = None
def initVariables():
global glb
ctx.env = Env()
ctx.dirs = Dirs()
glb = ctx
-482
View File
@@ -1,482 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray at pardus.org.tr>
# Baris Metin <baris at pardus.org.tr>
"""Top level PISI interfaces. a facade to the entire PISI system"""
import os
import sys
import logging
import logging.handlers
from os.path import exists
import bsddb3.db as db
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.uri import URI
import pisi.util as util
import pisi.dependency as dependency
import pisi.pgraph as pgraph
import pisi.operations as operations
import pisi.packagedb as packagedb
import pisi.repodb
import pisi.installdb
import pisi.sourcedb
import pisi.component as component
from pisi.index import Index
import pisi.cli
from pisi.operations import install, remove, upgrade, emerge
from pisi.build import build_until
from pisi.atomicoperations import resurrect_package, build
from pisi.metadata import MetaData
from pisi.files import Files
from pisi.file import File
import pisi.search
import pisi.lockeddbshelve as shelve
from pisi.version import Version
class Error(pisi.Error):
pass
def init(database = True, write = True,
options = None, ui = None, comar = True,
stdout = None, stderr = None,
comar_sockname = None):
"""Initialize PiSi subsystem"""
# UI comes first
if ui is None:
from pisi.cli import CLI
if options:
ctx.ui = CLI(options.debug)
else:
ctx.ui = CLI()
else:
ctx.ui = ui
if os.access('/var/log', os.W_OK):
handler = logging.handlers.RotatingFileHandler('/var/log/pisi.log')
#handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)-12s: %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
ctx.log = logging.getLogger('pisi')
ctx.log.addHandler(handler)
ctx.loghandler = handler
ctx.log.setLevel(logging.DEBUG)
else:
ctx.log = None
# If given define stdout and stderr. Needed by buildfarm currently
# but others can benefit from this too.
if stdout:
ctx.stdout = stdout
if stderr:
ctx.stderr = stderr
import pisi.config
ctx.config = pisi.config.Config(options)
# TODO: this is definitely not dynamic beyond this point!
ctx.comar = comar and not ctx.config.get_option('ignore_comar')
# This is for YALI, used in comariface.py:make_com()
ctx.comar_sockname = comar_sockname
# initialize repository databases
ctx.database = database
if database:
shelve.init_dbenv(write=write)
ctx.repodb = pisi.repodb.init()
ctx.installdb = pisi.installdb.init()
ctx.filesdb = pisi.files.FilesDB()
ctx.componentdb = pisi.component.ComponentDB()
ctx.packagedb = packagedb.init_db()
ctx.sourcedb = pisi.sourcedb.init()
pisi.search.init(['summary', 'description'], ['en', 'tr'])
else:
ctx.repodb = None
ctx.installdb = None
ctx.filesdb = None
ctx.componentdb = None
ctx.packagedb = None
ctx.sourcedb = None
ctx.ui.debug('PISI API initialized')
ctx.initialized = True
def finalize():
if ctx.initialized:
if ctx.log:
ctx.loghandler.flush()
ctx.log.removeHandler(ctx.loghandler)
pisi.repodb.finalize()
pisi.installdb.finalize()
if ctx.filesdb != None:
ctx.filesdb.close()
ctx.filesdb = None
if ctx.componentdb != None:
ctx.componentdb.close()
ctx.componentdb = None
if ctx.packagedb:
packagedb.finalize_db()
ctx.packagedb = None
if ctx.sourcedb:
pisi.sourcedb.finalize()
ctx.sourcedb = None
pisi.search.finalize()
if ctx.dbenv:
ctx.dbenv.close()
ctx.dbenv_lock.close()
ctx.ui.debug('PISI API finalized')
ctx.ui.close()
ctx.initialized = False
def list_available(repo = None):
'''returns a set of available package names'''
return set(ctx.packagedb.list_packages(repo = repo))
def list_upgradable():
ignore_build = ctx.get_option('ignore_build_no')
A = ctx.installdb.list_installed()
# filter packages that are not upgradable
Ap = []
for x in A:
(version, release, build) = ctx.installdb.get_version(x)
pkg = ctx.packagedb.get_package(x)
if ignore_build or (not build) or (not pkg.build):
if Version(release) < Version(pkg.release):
Ap.append(x)
elif build < pkg.build:
Ap.append(x)
else:
pass
return Ap
def package_graph(A, ignore_installed = False):
"""Construct a package relations graph, containing
all dependencies of packages A, if ignore_installed
option is True, then only uninstalled deps will
be added."""
ctx.ui.debug('A = %s' % str(A))
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
#state = {}
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x)
#print pkg
for dep in pkg.runtimeDependencies():
if ignore_installed:
if dependency.installed_satisfies_dep(dep):
continue
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
B = Bp
return G_f
def configure_pending():
# start with pending packages
# configure them in reverse topological order of dependency
A = ctx.installdb.list_pending()
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
for x in A.keys():
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B.keys():
pkg = ctx.packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
if dep.package in G_f.vertices():
G_f.add_dep(x, dep)
B = Bp
if ctx.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
try:
import pisi.comariface as comariface
for x in order:
if ctx.installdb.is_installed(x):
pkginfo = A[x]
pkgname = util.package_name(x, pkginfo.version,
pkginfo.release,
False,
False)
pkg_path = util.join_path(ctx.config.lib_dir(),
'package', pkgname)
m = MetaData()
metadata_path = util.join_path(pkg_path, ctx.const.metadata_xml)
m.read(metadata_path)
for pcomar in m.package.providesComar:
scriptPath = util.join_path(pkg_path,
ctx.const.comar_dir,
pcomar.script)
comariface.register(pcomar, x, scriptPath)
# FIXME: we need a full package info here!
# Eray, please fix this
# your wish is a command, darling -- eray
pkginfo.name = x
ctx.ui.notify(pisi.ui.configuring, package = pkginfo, files = None)
comariface.run_postinstall(x)
ctx.ui.notify(pisi.ui.configured, package = pkginfo, files = None)
ctx.installdb.clear_pending(x)
except ImportError:
raise Error(_("COMAR: comard not fully installed"))
def info(package, installed = False):
if package.endswith(ctx.const.package_suffix):
return info_file(package)
else:
return info_name(package, installed)
def info_file(package_fn):
from package import Package
if not os.path.exists(package_fn):
raise Error (_('File %s not found') % package_fn)
package = Package(package_fn)
package.read()
return package.metadata, package.files
def info_name(package_name, installed=False):
"""fetch package information for a package"""
if ctx.packagedb.has_package(package_name, pisi.itembyrepodb.all):
if installed:
package = ctx.packagedb.get_package(package_name, pisi.itembyrepodb.installed)
else:
package, repo = ctx.packagedb.get_package_repo(package_name)
if repo==pisi.itembyrepodb.installed:
raise Error(_('Package %s not found') % package_name)
from pisi.metadata import MetaData
metadata = MetaData()
metadata.package = package
#FIXME: get it from sourcedb if available
metadata.source = None
#TODO: fetch the files from server if possible (wow, you maniac -- future exa)
if installed and ctx.installdb.is_installed(package.name):
files = ctx.installdb.files(package.name)
else:
files = None
return metadata, files
else:
raise Error(_('Package %s not found') % package_name)
def search_package_names(query):
r = set()
packages = ctx.packagedb.list_packages()
for pkgname in packages:
if query in pkgname:
r.add(pkgname)
return r
def search_package_terms(terms, lang = None, search_names = True):
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
r1 = pisi.search.query_terms('summary', lang, terms)
r2 = pisi.search.query_terms('description', lang, terms)
r = r1.union(r2)
if search_names:
for term in terms:
r |= search_package_names(term)
return r
def search_package(query, lang = None, search_names = True):
if not lang:
lang = pisi.pxml.autoxml.LocalText.get_lang()
r1 = pisi.search.query('summary', lang, query)
r2 = pisi.search.query('description', lang, query)
r = r1.union(r2)
if search_names:
r |= search_package_names(query)
return r
def check(package):
md, files = info(package, True)
corrupt = []
for file in files.list:
if file.hash and file.type != "config" \
and not os.path.islink('/' + file.path):
ctx.ui.info(_("Checking %s...") % file.path, False, True)
if file.hash != util.sha1_file('/' + file.path):
corrupt.append(file)
ctx.ui.info("Corrupt file: %s" % file)
else:
ctx.ui.info("OK", False)
return corrupt
def index(dirs=None, output='pisi-index.xml', skip_sources=False, skip_signing=False,
non_recursive=False):
"""accumulate PISI XML files in a directory"""
index = Index()
if not dirs:
dirs = ['.']
for repo_dir in dirs:
repo_dir = str(repo_dir)
ctx.ui.info(_('* Building index of PISI files under %s') % repo_dir)
index.index(repo_dir, skip_sources, non_recursive)
if skip_signing:
index.write(output, sha1sum=True, compress=File.bz2, sign=None)
else:
index.write(output, sha1sum=True, compress=File.bz2, sign=File.detached)
ctx.ui.info(_('* Index file written'))
def add_repo(name, indexuri):
if ctx.repodb.has_repo(name):
raise Error(_('Repo %s already present.') % name)
else:
repo = pisi.repodb.Repo(URI(indexuri))
ctx.repodb.add_repo(name, repo)
ctx.ui.info(_('Repo %s added to system.') % name)
def remove_repo(name):
if ctx.repodb.has_repo(name):
ctx.repodb.remove_repo(name)
pisi.util.clean_dir(os.path.join(ctx.config.index_dir(), name))
ctx.ui.info(_('Repo %s removed from system.') % name)
else:
ctx.ui.error(_('Repository %s does not exist. Cannot remove.')
% name)
def update_repo(repo, force=False):
ctx.ui.info(_('* Updating repository: %s') % repo)
index = Index()
if ctx.repodb.has_repo(repo):
repouri = ctx.repodb.get_repo(repo).indexuri.get_uri()
try:
index.read_uri(repouri, repo)
except pisi.file.AlreadyHaveException, e:
ctx.ui.info(_('No updates available for repository %s.') % repo)
if force:
ctx.ui.info(_('Updating database at any rate as requested'))
index.read_uri(repouri, repo, force = force)
else:
return
try:
index.check_signature(repouri, repo)
except pisi.file.NoSignatureFound, e:
ctx.ui.warning(e)
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
ctx.ui.info(_('* Package database updated.'))
else:
raise Error(_('No repository named %s found.') % repo)
def rebuild_repo(repo):
ctx.ui.info(_('* Rebuilding \'%s\' named repo... ') % repo, noln=True)
index = Index()
if ctx.repodb.has_repo(repo):
repouri = ctx.repodb.get_repo(repo).indexuri.get_uri()
indexname = os.path.basename(repouri)
indexpath = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, indexname)
if os.path.exists(indexpath):
repouri = indexpath
try:
index.read_uri(repouri, repo, force = True)
except IOError:
ctx.ui.warning(_("Repo index file \'%s\' not found.") % repouri)
return
else:
raise Error(_('No repository named %s found.') % repo)
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
ctx.ui.info(_('OK.'))
def delete_cache():
util.clean_dir(ctx.config.packages_dir())
util.clean_dir(ctx.config.archives_dir())
util.clean_dir(ctx.config.tmp_dir())
def rebuild_db(files=False):
assert ctx.database == False
def destroy(files):
#from pisi.lockeddbshelve import LockedDBShelf
pisi.lockeddbshelve.init_dbenv(write=True, writeversion=True)
#TODO: either don't delete version files here, or remove force flag...
import bsddb3.db
for db in os.listdir(ctx.config.db_dir()):
if db.endswith('.bdb'):# or db.startswith('log'): # delete only db files
if db.startswith('files') or db.startswith('filesdbversion'):
clean = files
else:
clean = True
if clean:
fn = pisi.util.join_path(ctx.config.db_dir(), db)
#FIXME: there is a bug with bsddb3
#ctx.dbenv.dbremove(fn, "", None, bsddb3.db.DB_AUTO_COMMIT)
ctx.dbenv.dbremove(file=fn, flags=bsddb3.db.DB_AUTO_COMMIT)
#os.unlink(fn)
ctx.dbenv.close()
def reload_packages(files, txn):
for package_fn in os.listdir( pisi.util.join_path( ctx.config.lib_dir(),
'package' ) ):
if not package_fn == "scripts":
ctx.ui.debug('Resurrecting %s' % package_fn)
pisi.api.resurrect_package(package_fn, files, txn)
def reload_indexes(txn):
index_dir = ctx.config.index_dir()
if os.path.exists(index_dir): # it may have been erased, or we may be upgrading from a previous version -- exa
for repo in os.listdir(index_dir):
indexuri = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, 'uri')
indexuri = open(indexuri, 'r').readline()
pisi.api.add_repo(repo, indexuri)
pisi.api.rebuild_repo(repo)
# check db schema versions
try:
pisi.lockeddbshelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=False)
except:
files = True # exception means the files db version was wrong
destroy(files) # bye bye
pisi.lockeddbshelve.check_dbversion('dbversion', pisi.__dbversion__, write=True, update=True)
pisi.lockeddbshelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=True, update=True)
# save parameters and shutdown pisi
options = ctx.config.options
ui = ctx.ui
comar = ctx.comar
finalize()
# construct new database version
init(database=True, options=options, ui=ui, comar=comar)
#ctx.txn_proc(reload)
reload_packages(files, None)
reload_indexes(None)
-259
View File
@@ -1,259 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Archive module provides access to regular archive file types.
# maintainer baris and meren
# standard library modules
import os
import stat
import shutil
import tarfile
import zipfileext
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi modules
import pisi
import pisi.util as util
import pisi.context as ctx
class ArchiveError(pisi.Error):
pass
class ArchiveBase(object):
"""Base class for Archive classes."""
def __init__(self, file_path, atype):
self.file_path = file_path
self.type = atype
def unpack(self, target_dir, clean_dir = False):
self.target_dir = target_dir
# first we check if we need to clean-up our working env.
if os.path.exists(self.target_dir) and clean_dir:
util.clean_dir(self.target_dir)
os.makedirs(self.target_dir)
class ArchiveBinary(ArchiveBase):
"""ArchiveBinary handles binary archive files (usually distrubuted as
.bin files)"""
def __init__(self, file_path, arch_type = "binary"):
super(ArchiveBinary, self).__init__(file_path, arch_type)
def unpack(self, target_dir, clean_dir = False):
super(ArchiveBinary, self).unpack(target_dir, clean_dir)
# we can't unpack .bin files. we'll just move them to target
# directory and leave the dirty job to actions.py ;)
import shutil
target_file = os.path.join(target_dir, os.path.basename(self.file_path))
shutil.copyfile(self.file_path, target_file)
class ArchiveTar(ArchiveBase):
"""ArchiveTar handles tar archives depending on the compression
type. Provides access to tar, tar.gz and tar.bz2 files.
This class provides the unpack magic for tar archives."""
def __init__(self, file_path, arch_type = "tar"):
super(ArchiveTar, self).__init__(file_path, arch_type)
def unpack(self, target_dir, clean_dir = False):
"""Unpack tar archive to a given target directory(target_dir)."""
super(ArchiveTar, self).unpack(target_dir, clean_dir)
rmode = ""
if self.type == 'tar':
rmode = 'r:'
elif self.type == 'targz':
rmode = 'r:gz'
elif self.type == 'tarbz2':
rmode = 'r:bz2'
else:
raise ArchiveError(_("Archive type not recognized"))
tar = tarfile.open(self.file_path, rmode)
oldwd = os.getcwd()
os.chdir(self.target_dir)
for tarinfo in tar:
tar.extract(tarinfo)
os.chdir(oldwd)
tar.close()
class ArchiveZip(ArchiveBase):
"""ArchiveZip handles zip archives.
Being a zip archive PISI packages also use this class
extensively. This class provides unpacking and packing magic for
zip archives."""
symmagic = 2716663808 #long of hex val '0xA1ED0000L'
comp_method = {'lzma': zipfileext.ZIP_LZMA, 'deflated': zipfileext.ZIP_DEFLATED}
def __init__(self, file_path, arch_type = "zip", mode = 'r'):
super(ArchiveZip, self).__init__(file_path, arch_type)
self.zip_obj = zipfileext.ZipFileExt(self.file_path, mode)
def close(self):
"""Close the zip archive."""
self.zip_obj.close()
def add_to_archive(self, file_name, arc_name=None):
"""Add file or directory path to the zip file"""
# It's a pity that zipfile can't handle unicode strings. Grrr!
file_name = str(file_name)
if os.path.isdir(file_name) and not os.path.islink(file_name):
self.zip_obj.writestr(file_name + '/', '')
attr_obj = self.zip_obj.getinfo(file_name + '/')
attr_obj.external_attr = stat.S_IMODE(os.stat(file_name)[0]) << 16L
for f in os.listdir(file_name):
self.add_to_archive(os.path.join(file_name, f))
else:
if os.path.islink(file_name):
dest = os.readlink(file_name)
attr = zipfileext.ZipInfo()
attr.filename = file_name
attr.create_system = 3
attr.external_attr = self.symmagic
self.zip_obj.writestr(attr, dest)
else:
method = ctx.get_option('compression_method')
if not method:
self.zip_obj.write(file_name, arc_name, zipfileext.ZIP_LZMA)
else:
self.zip_obj.write(file_name, arc_name, self.comp_method[method])
if not arc_name:
zinfo = self.zip_obj.getinfo(file_name)
else:
zinfo = self.zip_obj.getinfo(arc_name)
zinfo.create_system = 3
def add_basename_to_archive(self, file_name):
"""Add only the basepath to the zip file. For example; if the given
file_name parameter is /usr/local/bin/somedir, this function
will create only the base directory/file somedir in the
archive."""
cwd = os.getcwd()
path_name = os.path.dirname(file_name)
file_name = os.path.basename(file_name)
if path_name:
os.chdir(path_name)
self.add_to_archive(file_name)
os.chdir(cwd)
def unpack_file_cond(self, pred, target_dir, archive_root = ''):
"""Unpack/Extract files according to predicate function
pred: filename -> bool
unpacks stuff into target_dir and only extracts files
from archive_root, treating it as the archive root"""
zip_obj = self.zip_obj
for info in zip_obj.infolist():
if pred(info.filename): # check if condition holds
# below code removes that, so we find it here
is_dir = info.filename.endswith('/')
# calculate output file name
if archive_root == '':
outpath = info.filename
else:
# change archive_root
if util.subpath(archive_root, info.filename):
outpath = util.removepathprefix(archive_root,
info.filename)
else:
continue # don't extract if not under
ofile = os.path.join(target_dir, outpath)
if is_dir: # this is a directory
d = os.path.join(target_dir, outpath)
if not os.path.isdir(d):
os.makedirs(d)
perm = info.external_attr
perm &= 0xFFFF0000
perm >>= 16
perm |= 0x00000100
os.chmod(d, perm)
continue
# check that output dir is present
util.check_dir(os.path.dirname(ofile))
# remove output file we might be overwriting.
# (also check for islink? for broken symlinks...)
if os.path.isfile(ofile) or os.path.islink(ofile):
os.remove(ofile)
if info.external_attr == self.symmagic:
if os.path.isdir(ofile):
shutil.rmtree(ofile) # a rare case, the file used to be a dir, now it is a symlink!
target = zip_obj.read(info.filename)
os.symlink(target, ofile)
else:
perm = info.external_attr
perm &= 0x08FF0000
perm >>= 16
perm |= 0x00000100
buff = open (ofile, 'wb')
file_content = zip_obj.read(info.filename)
buff.write(file_content)
buff.close()
os.chmod(ofile, perm)
def unpack_files(self, paths, target_dir):
self.unpack_file_cond(lambda f:f in paths, target_dir)
def unpack_dir(self, path, target_dir):
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir)
def unpack_dir_flat(self, path, target_dir):
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir, path)
def unpack(self, target_dir, clean_dir=False):
super(ArchiveZip, self).unpack(target_dir, clean_dir)
self.unpack_file_cond(lambda f: True, target_dir)
self.close()
return
class Archive:
"""Archive is the main factory for ArchiveClasses, regarding the
Abstract Factory Pattern :)."""
def __init__(self, file_path, arch_type):
"""accepted archive types:
targz, tarbz2, zip, tar"""
handlers = {
'targz': ArchiveTar,
'tarbz2': ArchiveTar,
'tar': ArchiveTar,
'zip': ArchiveZip,
'binary': ArchiveBinary
}
self.archive = handlers.get(arch_type)(file_path, arch_type)
def unpack(self, target_dir, clean_dir = False):
self.archive.unpack(target_dir, clean_dir)
def unpack_files(self, files, target_dir):
self.archive.unpack_files(files, target_dir)
@@ -1,512 +0,0 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
"Atomic package operations such as install/remove/upgrade"
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import sys
import os
import bsddb3.db as db
import pisi
import pisi.context as ctx
import pisi.packagedb as packagedb
import pisi.dependency as dependency
import pisi.util as util
from pisi.specfile import *
#from pisi.package import Package
from pisi.metadata import MetaData
from pisi.files import Files
from pisi.uri import URI
import pisi.ui
from pisi.version import Version
#import conflicts
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
pass
# single package operations
class AtomicOperation(object):
def __init__(self, ignore_dep = None):
#self.package = package
if ignore_dep==None:
self.ignore_dep = ctx.config.get_option('ignore_dependency')
else:
self.ignore_dep = ignore_dep
def run(self, package):
"perform an atomic package operation"
pass
class Install(AtomicOperation):
"Install class, provides install routines for pisi packages"
@staticmethod
def from_name(name, ignore_dep = None):
# download package and return an installer object
# find package in repository
repo = ctx.packagedb.which_repo(name)
if repo:
repo = ctx.repodb.get_repo(repo)
pkg = ctx.packagedb.get_package(name)
# FIXME: let pkg.packageURI be stored as URI type rather than string
pkg_uri = URI(pkg.packageURI)
if pkg_uri.is_absolute_path():
pkg_path = str(pkg.packageURI)
else:
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
str(pkg_uri.path()))
ctx.ui.debug(_("Package URI: %s") % pkg_path)
return Install(pkg_path, ignore_dep)
else:
raise Error(_("Package %s not found in any active repository.") % name)
def __init__(self, package_fname, ignore_dep = None):
"initialize from a file name"
super(Install, self).__init__(ignore_dep)
self.package_fname = package_fname
self.package = pisi.package.Package(package_fname)
self.package.read()
self.metadata = self.package.metadata
self.files = self.package.files
self.pkginfo = self.metadata.package
def install(self, ask_reinstall = True):
"entry point"
ctx.ui.status(_('Installing %s, version %s, release %s, build %s') %
(self.pkginfo.name, self.pkginfo.version,
self.pkginfo.release, self.pkginfo.build))
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
self.ask_reinstall = ask_reinstall
self.check_requirements()
self.check_relations()
self.check_reinstall()
self.extract_install()
self.store_pisi_files()
self.register_comar()
self.postinstall()
txn = ctx.dbenv.txn_begin()
try:
self.update_databases(txn)
txn.commit()
except db.DBError, e:
txn.abort()
raise e
self.update_environment()
ctx.ui.status()
if self.upgrade:
event = pisi.ui.upgraded
else:
event = pisi.ui.installed
ctx.ui.notify(event, package = self.pkginfo, files = self.files)
def check_requirements(self):
"""check system requirements"""
#TODO: IS THERE ENOUGH SPACE?
# what to do if / is split into /usr, /var, etc.
# check comar
if self.metadata.package.providesComar and ctx.comar:
import pisi.comariface as comariface
com = comariface.make_com()
def check_relations(self):
# check conflicts
for pkg in self.metadata.package.conflicts:
if ctx.installdb.is_installed(self.pkginfo):
raise Error(_("Package conflicts %s") % pkg)
# check dependencies
if not ctx.config.get_option('ignore_dependency'):
if not self.pkginfo.installable():
ctx.ui.error(_('Dependencies for %s not satisfied') %
self.pkginfo.name)
raise Error(_("Package not installable"))
# check if package is in database
# If it is not, put it into 3rd party packagedb
if not ctx.packagedb.has_package(self.pkginfo.name):
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.thirdparty)
# check file conflicts
for file in self.files.list:
if ctx.filesdb.has_file(file.path):
pkg, existing_file = ctx.filesdb.get_file(file.path)
if pkg != self.pkginfo.name:
#raise Error(_('Trying to overwrite an existing file: %s') % file.path)
ctx.ui.warning(_('Trying to overwrite an existing file: %s') % file.path)
def check_reinstall(self):
"check reinstall, confirm action, and schedule reinstall"
pkg = self.pkginfo
self.reinstall = False
self.upgrade = False
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
#FIXME: consider REPOSITORY instead of DISTRIBUTION -- exa
#ipackage = ctx.packagedb.get_package(pkg.name, pisi.itembyrepodb.installed)
ipkg = ctx.installdb.get_info(pkg.name)
repomismatch = ipkg.distribution != pkg.distribution
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
# determine if same version
self.same_ver = False
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
if repomismatch or (not ibuild) or (not pkg.build) or ignore_build:
# we don't look at builds to compare two package versions
if Version(pkg.release) == Version(irelease):
self.same_ver = True
else:
if pkg.build == ibuild:
self.same_ver = True
if self.same_ver:
if self.ask_reinstall:
if not ctx.ui.confirm(_('Re-install same version package?')):
raise Error(_('Package re-install declined'))
else:
upgrade = False
# is this an upgrade?
# determine and report the kind of upgrade: version, release, build
if Version(pkg.version) > Version(iversion):
ctx.ui.info(_('Upgrading to new upstream version'))
upgrade = True
elif Version(pkg.release) > Version(irelease):
ctx.ui.info(_('Upgrading to new distribution release'))
upgrade = True
elif ((not ignore_build) and ibuild and pkg.build
and pkg.build > ibuild):
ctx.ui.info(_('Upgrading to new distribution build'))
upgrade = True
self.upgrade = upgrade
# is this a downgrade? confirm this action.
if self.ask_reinstall and (not upgrade):
if Version(pkg.version) < Version(iversion):
x = _('Downgrade to old upstream version?')
elif Version(pkg.release) < Version(irelease):
x = _('Downgrade to old distribution release?')
else:
x = _('Downgrade to old distribution build?')
if not ctx.ui.confirm(x):
raise Error(_('Package downgrade declined'))
# schedule for reinstall
self.old_files = ctx.installdb.files(pkg.name)
self.old_path = ctx.installdb.pkg_dir(pkg.name, iversion, irelease)
self.reinstall = True
self.remove_old = Remove(pkg.name)
self.remove_old.run_preremove()
def register_comar(self):
self.config_later = False
if self.metadata.package.providesComar:
if ctx.comar:
import pisi.comariface as comariface
self.register_comar_scripts()
else:
self.config_later = True # configure-pending will register scripts later
def postinstall(self):
if 'System.Package' in [x.om for x in self.metadata.package.providesComar]:
if ctx.comar:
import pisi.comariface as comariface
ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files)
comariface.run_postinstall(self.pkginfo.name)
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files)
else:
self.config_later = True
def extract_install(self):
"unzip package in place"
ctx.ui.notify(pisi.ui.extracting, package = self.pkginfo, files = self.files)
if self.reinstall:
# remove left over files
new = set(map(lambda x: str(x.path), self.files.list))
old = set(map(lambda x: str(x.path), self.old_files.list))
leftover = old - new
old_fileinfo = {}
for fileinfo in self.old_files.list:
old_fileinfo[str(fileinfo.path)] = fileinfo
for path in old:
if path in leftover or old_fileinfo[path].type == ctx.const.conf:
Remove.remove_file( old_fileinfo[path] )
self.package.extract_dir_flat('install', ctx.config.dest_dir())
def store_pisi_files(self):
"""put files.xml, metadata.xml, actions.py and COMAR scripts
somewhere in the file system. We'll need these in future..."""
if self.reinstall:
util.clean_dir(self.old_path)
ctx.ui.info(_('Storing %s, ') % ctx.const.files_xml)
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
ctx.ui.info(_('Storing %s.') % ctx.const.metadata_xml)
self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
for pcomar in self.metadata.package.providesComar:
fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
# comar prefix is added to the pkg_dir while extracting comar
# script file. so we'll use pkg_dir as destination.
ctx.ui.info(_('Storing %s') % fpath)
self.package.extract_file(fpath, self.package.pkg_dir())
def register_comar_scripts(self):
"register COMAR scripts"
for pcomar in self.metadata.package.providesComar:
scriptPath = os.path.join(self.package.comar_dir(),pcomar.script)
import pisi.comariface
pisi.comariface.register(pcomar, self.metadata.package.name,
scriptPath)
def update_databases(self, txn):
"update databases"
if self.reinstall:
self.remove_old.remove_db(txn)
# installdb
ctx.installdb.install(self.metadata.package.name,
self.metadata.package.version,
self.metadata.package.release,
self.metadata.package.build,
self.metadata.package.distribution,
config_later = self.config_later,
txn = txn)
# filesdb
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn=txn)
# installed packages
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.installed, txn=txn)
def update_environment(self):
# check if we have any shared objects or anything under
# /etc/env.d
shared = False
for x in self.files.list:
if x.path.endswith('.so') or x.path.startswith('/etc/env.d'):
shared = True
break
if not ctx.get_option('bypass_ldconfig'):
if shared:
ctx.ui.info(_("Regenerating /etc/ld.so.cache..."))
util.env_update()
else:
ctx.ui.warning(_("Bypassing ldconfig"))
def install_single(pkg, upgrade = False):
"""install a single package from URI or ID"""
url = URI(pkg)
# Check if we are dealing with a remote file or a real path of
# package filename. Otherwise we'll try installing a package from
# the package repository.
if url.is_remote_file() or os.path.exists(url.uri):
install_single_file(pkg, upgrade)
else:
install_single_name(pkg, upgrade)
# FIXME: Here and elsewhere pkg_location must be a URI
def install_single_file(pkg_location, upgrade = False):
"""install a package file"""
Install(pkg_location).install(not upgrade)
def install_single_name(name, upgrade = False):
"""install a single package from ID"""
install = Install.from_name(name)
install.install(not upgrade)
class Remove(AtomicOperation):
def __init__(self, package_name, ignore_dep = None):
super(Remove, self).__init__(ignore_dep)
self.package_name = package_name
self.package = ctx.packagedb.get_package(self.package_name, pisi.itembyrepodb.installed)
try:
self.files = ctx.installdb.files(self.package_name)
except pisi.Error, e:
# for some reason file was deleted, we still allow removes!
ctx.ui.error(unicode(e))
ctx.ui.warning(_('File list could not be read for package %s, continuing removal.') % package_name)
self.files = Files()
def run(self):
"""Remove a single package"""
ctx.ui.status(_('Removing package %s') % self.package_name)
ctx.ui.notify(pisi.ui.removing, package = self.package, files = self.files)
if not ctx.installdb.is_installed(self.package_name):
raise Exception(_('Trying to remove nonexistent package ')
+ self.package_name)
self.check_dependencies()
self.run_preremove()
for fileinfo in self.files.list:
self.remove_file(fileinfo)
txn = ctx.dbenv.txn_begin()
try:
self.remove_db(txn)
txn.commit()
except db.DBError, e:
txn.abort()
raise e
self.remove_pisi_files()
ctx.ui.status()
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files)
def check_dependencies(self):
#we only have to check the dependencies to ensure the
#system will be consistent after this removal
pass
# is there any package who depends on this package?
def remove_file(fileinfo):
fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path)
# TODO: We have to store configuration files for futher
# usage. Currently we'are doing it like rpm does, saving
# with a prefix and leaving the user to edit it. In the future
# we'll have a plan for these configuration files.
if fileinfo.permanent:
# do not remove precious files :)
# just write anything permanent="true" for instance
return
if fileinfo.type == ctx.const.conf:
if os.path.isfile(fpath):
os.rename(fpath, fpath + ".oldconfig")
else:
if os.path.isfile(fpath) or os.path.islink(fpath):
os.unlink(fpath)
elif os.path.isdir(fpath) and not os.listdir(fpath):
os.rmdir(fpath)
else:
ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath, True)
return
# remove emptied directories
dpath = os.path.dirname(fpath)
while dpath != '/' and not os.listdir(dpath):
os.rmdir(dpath)
dpath = os.path.dirname(dpath)
remove_file = staticmethod(remove_file)
def run_preremove(self):
if ctx.comar and self.package.providesComar:
import pisi.comariface as comariface
comariface.run_preremove(self.package_name)
else:
# TODO: store this somewhere
pass
def remove_pisi_files(self):
util.clean_dir(self.package.pkg_dir())
def remove_db(self, txn):
ctx.installdb.remove(self.package_name, txn)
ctx.filesdb.remove_files(self.files, txn)
pisi.packagedb.remove_tracking_package(self.package_name, txn)
def remove_single(package_name):
Remove(package_name).run()
def build(package, authinfo=None):
# wrapper for build op
import pisi.build
return pisi.build.build(package, authinfo)
def virtual_install(metadata, files, txn):
"""Recreate the package info for rebuilddb command"""
# installdb
ctx.installdb.install(metadata.package.name,
metadata.package.version,
metadata.package.release,
metadata.package.build,
metadata.package.distribution,
rebuild=True,
txn=txn)
# filesdb
if files:
ctx.filesdb.add_files(metadata.package.name, files, txn=txn)
# installed packages
ctx.packagedb.add_package(metadata.package, pisi.itembyrepodb.installed, txn=txn)
def resurrect_package(package_fn, write_files, txn = None):
"""Resurrect the package from xml files"""
from os.path import exists
metadata_xml = util.join_path(ctx.config.lib_dir(), 'package',
package_fn, ctx.const.metadata_xml)
if not exists(metadata_xml):
raise Error, _("Metadata XML '%s' cannot be found") % metadata_xml
metadata = MetaData()
metadata.read(metadata_xml)
errs = metadata.errors()
if errs:
util.Checks.print_errors(errs)
raise Error, _("MetaData format wrong (%s)") % package_fn
ctx.ui.info(_('* Adding \'%s\' to db... ') % (metadata.package.name), noln=True)
if write_files:
files_xml = util.join_path(ctx.config.lib_dir(), 'package',
package_fn, ctx.const.files_xml)
if not exists(files_xml):
raise Error, _("Files XML '%s' cannot be found") % files_xml
files = Files()
files.read(files_xml)
if files.errors():
raise Error, _("Invalid %s") % ctx.const.files_xml
else:
files = None
#import pisi.atomicoperations
def f(t):
pisi.atomicoperations.virtual_install(metadata, files, t)
ctx.txn_proc(f, txn)
ctx.ui.info(_('OK.'))
-910
View File
@@ -1,910 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# package bulding stuff
# maintainer: baris and meren
# python standard library
import os
import sys
import glob
from copy import deepcopy
from os.path import basename, dirname
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
from pisi.specfile import SpecFile
import pisi.util as util
from pisi.util import join_path as join, parenturi
from pisi.file import File
import pisi.context as ctx
import pisi.dependency as dependency
import pisi.operations as operations
from pisi.sourcearchive import SourceArchive
from pisi.files import Files, FileInfo
from pisi.fetcher import fetch_url
from pisi.uri import URI
from pisi.metadata import MetaData
from pisi.package import Package
import pisi.component as component
import pisi.actionsapi.variables
class Error(pisi.Error):
pass
# Helper Functions
def get_file_type(path, pinfo_list, install_dir):
"""Return the file type of a path according to the given PathInfo
list"""
Match = lambda x: [match for match in glob.glob(install_dir + x) if join(install_dir, path).find(match) > -1]
def Sort(x):
x.sort(reverse=True)
return x
best_matched_path = Sort([pinfo.path for pinfo in pinfo_list if Match(pinfo.path)])[0]
info = [pinfo for pinfo in pinfo_list if best_matched_path == pinfo.path][0]
return info.fileType, info.permanent
def check_path_collision(package, pkgList):
"""This function will check for collision of paths in a package with
the paths of packages in pkgList. The return value will be the
list containing the paths that collide."""
collisions = []
for pinfo in package.files:
for pkg in pkgList:
if pkg is package:
continue
for path in pkg.files:
# if pinfo.path is a subpath of path.path like
# the example below. path.path is marked as a
# collide. Exp:
# pinfo.path: /usr/share
# path.path: /usr/share/doc
if (path.path.endswith(ctx.const.ar_file_suffix) and ctx.get_option('create_static')) or \
(path.path.endswith(ctx.const.debug_file_suffix) and not ctx.get_option('no_debug')):
# don't throw collision error for these files.
# we'll handle this in gen_files_xml..
continue
if util.subpath(pinfo.path, path.path):
collisions.append(path.path)
ctx.ui.error(_('Path %s belongs in multiple packages') %
path.path)
return collisions
class Builder:
"""Provides the package build and creation routines"""
#FIXME: this class and every other class must use URLs as paths!
@staticmethod
def from_name(name, authinfo = None):
# download package and return an installer object
# find package in repository
sf, reponame = ctx.sourcedb.get_spec_repo(name)
src = sf.source
if src:
src_uri = URI(src.sourceURI)
if src_uri.is_absolute_path():
pkg_path = str(src_uri)
else:
repo = ctx.repodb.get_repo(reponame)
src_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
str(src_uri.path()))
ctx.ui.debug(_("Source URI: %s") % src_path)
return Builder(src_path, authinfo)
else:
raise Error(_("Source %s not found in any active repository.") % name)
def __init__(self, specuri, authinfo = None):
# process args
if not isinstance(specuri, URI):
specuri = URI(specuri)
if authinfo:
specuri.set_auth_info(authinfo)
self.authinfo = authinfo
# read spec file, we'll need it :)
self.set_spec_file(specuri)
if specuri.is_remote_file():
#make local here and fuck up
self.specdir = self.fetch_files()
else:
self.specdir = dirname(self.specuri.get_uri())
self.sourceArchive = SourceArchive(self.spec, self.pkg_work_dir())
self.set_environment_vars()
self.actionLocals = None
self.actionGlobals = None
self.srcDir = None
def set_spec_file(self, specuri):
if not specuri.is_remote_file():
specuri = URI(os.path.realpath(specuri.get_uri())) # FIXME: doesn't work for file://
self.specuri = specuri
spec = SpecFile()
spec.read(specuri, ctx.config.tmp_dir())
self.spec = spec
# directory accessor functions
# pkg_x_dir: per package directory for storing info type x
def pkg_dir(self):
"package build directory"
packageDir = self.spec.source.name + '-' + \
self.spec.source.version + '-' + self.spec.source.release
return util.join_path(ctx.config.dest_dir(), ctx.config.values.dirs.tmp_dir,
packageDir)
def pkg_work_dir(self):
return self.pkg_dir() + ctx.const.work_dir_suffix
def pkg_debug_dir(self):
return self.pkg_dir() + ctx.const.debug_dir_suffix
def pkg_install_dir(self):
return self.pkg_dir() + ctx.const.install_dir_suffix
def set_state(self, state):
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
open(stateFile, "w").write(state)
def get_state(self):
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
if not os.path.exists(stateFile): # no state
return None
return open(stateFile, "r").read()
def build(self):
"""Build the package in one shot."""
ctx.ui.status(_("Building PISI source package: %s") % self.spec.source.name)
self.compile_action_script()
# check if all patch files exists, if there are missing no need to unpack!
self.patch_exists()
self.check_build_dependencies()
self.fetch_component()
self.fetch_source_archive()
self.unpack_source_archive()
# apply the patches and prepare a source directory for build.
self.apply_patches()
self.run_setup_action()
self.run_build_action()
self.run_install_action()
# after all, we are ready to build/prepare the packages
return self.build_packages()
def set_environment_vars(self):
"""Sets the environment variables for actions API to use"""
# Each time a builder is created we must reset
# environment. See bug #2575
pisi.actionsapi.variables.initVariables()
env = {
"PKG_DIR": self.pkg_dir(),
"WORK_DIR": self.pkg_work_dir(),
"INSTALL_DIR": self.pkg_install_dir(),
"SRC_NAME": self.spec.source.name,
"SRC_VERSION": self.spec.source.version,
"SRC_RELEASE": self.spec.source.release
}
os.environ.update(env)
# First check icecream, if not found use ccache, no need to use both
# together (according to kde-wiki it cause performance loss)
if ctx.config.values.general.buildhelper == "icecream":
if os.path.exists("/opt/icecream/bin/gcc"):
# Add icecream directory for support distributed compiling :)
os.environ["PATH"] = "/opt/icecream/bin/:%s" % os.environ["PATH"]
ctx.ui.info(_("IceCream detected. Make sure your daemon is up and running..."))
elif ctx.config.values.general.buildhelper == "ccache":
if os.path.exists("/usr/lib/ccache/bin/gcc"):
# Add ccache directory for support Compiler Cache :)
os.environ["PATH"] = "/usr/lib/ccache/bin/:%s" % os.environ["PATH"]
ctx.ui.info(_("CCache detected..."))
def fetch_files(self):
self.specdiruri = dirname(self.specuri.get_uri())
pkgname = basename(self.specdiruri)
self.destdir = join(ctx.config.tmp_dir(), pkgname)
#self.location = dirname(self.url.uri)
self.fetch_actionsfile()
self.fetch_patches()
self.fetch_comarfiles()
self.fetch_additionalFiles()
return self.destdir
def fetch_actionsfile(self):
actionsuri = join(self.specdiruri, ctx.const.actions_file)
self.download(actionsuri, self.destdir)
def fetch_patches(self):
spec = self.spec
for patch in spec.source.patches:
file_name = basename(patch.filename)
dir_name = dirname(patch.filename)
patchuri = join(self.specdiruri,
ctx.const.files_dir, dir_name, file_name)
self.download(patchuri, join(self.destdir, ctx.const.files_dir, dir_name))
def fetch_comarfiles(self):
spec = self.spec
for package in spec.packages:
for pcomar in package.providesComar:
comaruri = join(self.specdiruri,
ctx.const.comar_dir, pcomar.script)
self.download(comaruri, join(self.destdir, ctx.const.comar_dir))
def fetch_additionalFiles(self):
spec = self.spec
for pkg in spec.packages:
for afile in pkg.additionalFiles:
file_name = basename(afile.filename)
dir_name = dirname(afile.filename)
afileuri = join(self.specdiruri,
ctx.const.files_dir, dir_name, file_name)
self.download(afileuri, join(self.destdir, ctx.const.files_dir, dir_name))
def download(self, uri, transferdir):
# fix auth info and download
uri = File.make_uri(uri)
if self.authinfo:
uri.set_auth_info(self.authinfo)
File.download(uri, transferdir)
def fetch_component(self):
if not self.spec.source.partOf:
ctx.ui.warning(_('PartOf tag not defined, looking for component'))
diruri = parenturi(self.specuri.get_uri())
parentdir = parenturi(diruri)
url = util.join_path(parentdir, 'component.xml')
progress = ctx.ui.Progress
if URI(url).is_remote_file():
fetch_url(url, self.pkg_work_dir(), progress)
path = util.join_path(self.pkg_work_dir(), 'component.xml')
else:
if not os.path.exists(url):
raise Exception(_('Cannot find component.xml in upper directory'))
path = url
comp = component.Component()
comp.read(path)
ctx.ui.info(_('Source is part of %s component') % comp.name)
self.spec.source.partOf = comp.name
self.spec.override_tags()
def fetch_source_archive(self):
ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archive.uri)
self.sourceArchive.fetch()
ctx.ui.info(_("Source archive is stored: %s/%s")
%(ctx.config.archives_dir(), self.spec.source.archive.name))
def unpack_source_archive(self):
ctx.ui.info(_("Unpacking archive..."), noln = True)
self.sourceArchive.unpack()
ctx.ui.info(_(" unpacked (%s)") % self.pkg_work_dir())
self.set_state("unpack")
def run_setup_action(self):
# Run configure, build and install phase
ctx.ui.action(_("Setting up source..."))
self.run_action_function(ctx.const.setup_func)
self.set_state("setupaction")
def run_build_action(self):
ctx.ui.action(_("Building source..."))
self.run_action_function(ctx.const.build_func)
self.set_state("buildaction")
def run_install_action(self):
ctx.ui.action(_("Installing..."))
# Before install make sure install_dir is clean
if os.path.exists(self.pkg_install_dir()):
util.clean_dir(self.pkg_install_dir())
# install function is mandatory!
self.run_action_function(ctx.const.install_func, True)
self.set_state("installaction")
def get_abandoned_files(self):
# return the files those are not collected from the install dir
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
abandoned_files = []
all_paths_in_packages = []
for package in self.spec.packages:
for path in package.files:
map(lambda p: all_paths_in_packages.append(p), [p for p in glob.glob(install_dir + path.path)])
for root, dirs, files in os.walk(install_dir):
for file_ in files:
already_in_package = False
fpath = util.join_path(root, file_)
for path in all_paths_in_packages:
if not fpath.find(path):
already_in_package = True
if not already_in_package:
abandoned_files.append(fpath)
return abandoned_files
def compile_action_script(self):
"""Compiles actions.py and sets the actionLocals and actionGlobals"""
scriptfile = util.join_path(self.specdir, ctx.const.actions_file)
try:
localSymbols = globalSymbols = {}
buf = open(scriptfile).read()
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
except IOError, e:
raise Error(_("Unable to read Action Script (%s): %s") %(scriptfile,e))
except SyntaxError, e:
raise Error(_("SyntaxError in Action Script (%s): %s") %(scriptfile,e))
self.actionLocals = localSymbols
self.actionGlobals = globalSymbols
self.srcDir = self.pkg_src_dir()
def pkg_src_dir(self):
"""Returns the real path of WorkDir for an unpacked archive."""
try:
workdir = self.actionGlobals['WorkDir']
except KeyError:
workdir = self.spec.source.name + "-" + self.spec.source.version
return util.join_path(self.pkg_work_dir(), workdir)
def run_action_function(self, func, mandatory=False):
"""Calls the corresponding function in actions.py.
If mandatory parameter is True, and function is not present in
actionLocals pisi.build.Error will be raised."""
# we'll need our working directory after actionscript
# finished its work in the archive source directory.
curDir = os.getcwd()
os.chdir(self.srcDir)
if func in self.actionLocals:
self.actionLocals[func]()
else:
if mandatory:
Error, _("unable to call function from actions: %s") %func
os.chdir(curDir)
def check_build_dependencies(self):
"""check and try to install build dependencies, otherwise fail."""
build_deps = self.spec.source.buildDependencies
if not ctx.get_option('bypass_safety'):
if ctx.componentdb.has_component('system.devel'):
build_deps_names = set([x.package for x in build_deps])
devel_deps_names = set(ctx.componentdb.get_component('system.devel').packages)
extra_names = devel_deps_names - build_deps_names
extra_names = filter(lambda x: not ctx.installdb.is_installed(x), extra_names)
if extra_names:
ctx.ui.warning(_('Safety switch: following extra packages in system.devel will be installed: ') +
util.strlist(extra_names))
extra_deps = [dependency.Dependency(package = x) for x in extra_names]
build_deps.extend(extra_deps)
else:
ctx.ui.warning(_('Safety switch: system.devel is already installed'))
else:
ctx.ui.warning(_('Safety switch: the component system.devel cannot be found'))
# find out the build dependencies that are not satisfied...
dep_unsatis = []
for dep in build_deps:
if not dependency.installed_satisfies_dep(dep):
dep_unsatis.append(dep)
if dep_unsatis:
ctx.ui.info(_("Unsatisfied Build Dependencies:") + ' '
+ util.strlist([str(x) for x in dep_unsatis]) )
def fail():
raise Error(_('Cannot build package due to unsatisfied build dependencies'))
if ctx.config.get_option('no_install'):
fail()
if not ctx.config.get_option('ignore_dependency'):
for dep in dep_unsatis:
if not dependency.repo_satisfies_dep(dep):
raise Error(_('Build dependency %s cannot be satisfied') % str(dep))
if ctx.ui.confirm(
_('Do you want to install the unsatisfied build dependencies')):
ctx.ui.info(_('Installing build dependencies.'))
operations.install([dep.package for dep in dep_unsatis])
else:
fail()
else:
ctx.ui.warning(_('Ignoring build dependencies.'))
def patch_exists(self):
"""check existence of patch files declared in PSPEC"""
files_dir = os.path.abspath(util.join_path(self.specdir,
ctx.const.files_dir))
for patch in self.spec.source.patches:
patchFile = util.join_path(files_dir, patch.filename)
if not os.access(patchFile, os.F_OK):
raise Error(_("Patch file is missing: %s\n") % patch.filename)
def apply_patches(self):
files_dir = os.path.abspath(util.join_path(self.specdir,
ctx.const.files_dir))
for patch in self.spec.source.patches:
patchFile = util.join_path(files_dir, patch.filename)
if patch.compressionType:
patchFile = util.uncompress(patchFile,
compressType=patch.compressionType,
targetDir=ctx.config.tmp_dir())
ctx.ui.action(_("* Applying patch: %s") % patch.filename)
util.do_patch(self.srcDir, patchFile, level=patch.level, target=patch.target)
def generate_static_package_object(self):
ar_files = []
for root, dirs, files in os.walk(self.pkg_install_dir()):
for f in files:
if f.endswith(ctx.const.ar_file_suffix) and util.is_ar_file(util.join_path(root, f)):
ar_files.append(util.join_path(root, f))
if not len(ar_files):
return None
static_package_obj = pisi.specfile.Package()
static_package_obj.name = self.spec.source.name + ctx.const.static_name_suffix
# FIXME: find a better way to deal with the summary and description constants.
static_package_obj.summary['en'] = u'Ar files for %s' % (self.spec.source.name)
static_package_obj.description['en'] = u'Ar files for %s' % (self.spec.source.name)
static_package_obj.partOf = self.spec.source.partOf
for f in ar_files:
static_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_install_dir()):], fileType = "library"))
# append all generated packages to dependencies
for p in self.spec.packages:
static_package_obj.packageDependencies.append(
pisi.dependency.Dependency(package = p.name))
return static_package_obj
def generate_debug_package_object(self):
debug_files = []
for root, dirs, files in os.walk(self.pkg_debug_dir()):
for f in files:
if f.endswith(ctx.const.debug_file_suffix):
debug_files.append(util.join_path(root, f))
if not len(debug_files):
return None
static_package_obj = pisi.specfile.Package()
static_package_obj.debug_package = True
static_package_obj.name = self.spec.source.name + ctx.const.debug_name_suffix
# FIXME: find a better way to deal with the summary and description constants.
static_package_obj.summary['en'] = u'Debug files for %s' % (self.spec.source.name)
static_package_obj.description['en'] = u'Debug files for %s' % (self.spec.source.name)
static_package_obj.partOf = self.spec.source.partOf
for f in debug_files:
static_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_debug_dir()):], fileType = "debug"))
# append all generated packages to dependencies
for p in self.spec.packages:
static_package_obj.packageDependencies.append(
pisi.dependency.Dependency(package = p.name))
return static_package_obj
def strip_install_dir(self):
"""strip install directory"""
ctx.ui.action(_("Stripping files.."))
install_dir = self.pkg_install_dir()
try:
nostrip = self.actionGlobals['NoStrip']
util.strip_directory(install_dir, nostrip)
except KeyError:
util.strip_directory(install_dir)
def gen_metadata_xml(self, package):
"""Generate the metadata.xml file for build source.
metadata.xml is composed of the information from specfile plus
some additional information."""
metadata = MetaData()
metadata.from_spec(self.spec.source, package)
metadata.package.distribution = ctx.config.values.general.distribution
metadata.package.distributionRelease = ctx.config.values.general.distribution_release
metadata.package.architecture = "Any"
size = 0
if package.debug_package:
d = self.pkg_debug_dir()
else:
d = self.pkg_install_dir()
for path in package.files:
for p in glob.glob(util.join_path(d, path.path)):
size += util.dir_size(p)
metadata.package.installedSize = size
old_build = None
# build no
if ctx.config.options.ignore_build_no:
metadata.package.build = None
ctx.ui.warning(_('Build number is not available due to --ignore-build'))
elif (not ctx.config.values.build.buildno):
metadata.package.build = None
ctx.ui.warning(_('Build number is not available. For repo builds you must enable buildno in pisi.conf.'))
else:
metadata.package.build, old_build = self.calc_build_no(metadata.package.name)
metadata_xml_path = util.join_path(self.pkg_dir(), ctx.const.metadata_xml)
metadata.write(metadata_xml_path)
self.metadata = metadata
return metadata.package.build, old_build
def gen_files_xml(self, package):
"""Generates files.xml using the path definitions in specfile and
the files produced by the build system."""
files = Files()
if package.debug_package:
install_dir = self.pkg_debug_dir()
else:
install_dir = self.pkg_install_dir()
# FIXME: We need to expand globs before trying to calculate hashes
# Not on the fly like now.
# we'll exclude collisions in get_file_hashes. Having a
# collisions list is not wrong, we must just handle it :).
collisions = check_path_collision(package, self.spec.packages)
# FIXME: material collisions after expanding globs could be
# reported as errors
d = {}
def add_path(path):
# add the files under material path
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
if ctx.get_option('create_static') \
and fpath.endswith(ctx.const.ar_file_suffix) \
and not package.name.endswith(ctx.const.static_name_suffix) \
and util.is_ar_file(fpath):
# if this is an ar file, and this package is not a static package,
# don't include this file into the package.
continue
frpath = util.removepathprefix(install_dir, fpath) # relative path
ftype, permanent = get_file_type(frpath, package.files, install_dir)
fsize = util.dir_size(fpath)
d[frpath] = FileInfo(path=frpath, type=ftype, permanent=permanent,
size=fsize, hash=fhash)
for pinfo in package.files:
wildcard_path = util.join_path(install_dir, pinfo.path)
for path in glob.glob(wildcard_path):
add_path(path)
for (p, fileinfo) in d.iteritems():
files.append(fileinfo)
files_xml_path = util.join_path(self.pkg_dir(), ctx.const.files_xml)
files.write(files_xml_path)
self.files = files
def calc_build_no(self, package_name):
"""Calculate build number"""
# find previous build in packages dir
found = []
def locate_old_package(old_package_fn):
if util.is_package_name(os.path.basename(old_package_fn), package_name):
try:
old_pkg = Package(old_package_fn, 'r')
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
ctx.ui.info(_('(found old version %s)') % old_package_fn)
if str(old_pkg.metadata.package.name) != package_name:
ctx.ui.warning(_('Skipping %s with wrong pkg name ') %
old_package_fn)
return
old_build = old_pkg.metadata.package.build
found.append( (old_package_fn, old_build) )
except:
ctx.ui.warning('Package file %s may be corrupt. Skipping.' % old_package_fn)
for root, dirs, files in os.walk(ctx.config.packages_dir()):
for file in files:
locate_old_package(join(root,file))
outdir=ctx.get_option('output_dir')
if not outdir:
outdir = '.'
for file in [join(outdir,entry) for entry in os.listdir(outdir)]:
if os.path.isfile(file):
locate_old_package(file)
if not found:
return (1, None)
ctx.ui.warning(_('(no previous build found, setting build no to 1.)'))
else:
a = filter(lambda (x,y): y != None, found)
ctx.ui.debug(str(a))
if a:
# sort in order of increasing build number
a.sort(lambda x,y : cmp(x[1],y[1]))
old_package_fn = a[-1][0] # get the last one
old_build = a[-1][1]
# compare old files.xml with the new one..
old_pkg = Package(old_package_fn, 'r')
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
# FIXME: TAKE INTO ACCOUNT MINOR CHANGES IN METADATA
changed = False
fnew = self.files.list
fold = old_pkg.files.list
fold.sort(lambda x,y : cmp(x.path,y.path))
fnew.sort(lambda x,y : cmp(x.path,y.path))
if len(fnew) != len(fold):
changed = True
else:
for i in range(len(fold)):
fo = fold.pop(0)
fn = fnew.pop(0)
if fo.path != fn.path:
changed = True
break
else:
if fo.hash != fn.hash:
changed = True
break
else: # no old build had a build number
old_build = None
ctx.ui.debug('old build number: %s' % old_build)
# set build number
if old_build is None:
ctx.ui.warning(_('(old package lacks a build no, setting build no to 1.)'))
return (1, None)
elif changed:
ctx.ui.info(_('There are changes, incrementing build no to %d') % (old_build + 1))
return (old_build + 1, old_build)
else:
ctx.ui.info(_('There is no change from previous build %d') % old_build)
return (old_build, old_build)
def build_packages(self):
"""Build each package defined in PSPEC file. After this process there
will be .pisi files hanging around, AS INTENDED ;)"""
self.fetch_component() # bug 856
# Strip install directory before building .pisi packages.
self.strip_install_dir()
if ctx.get_option('create_static'):
obj = self.generate_static_package_object()
if obj:
self.spec.packages.append(obj)
if not ctx.get_option('no_debug'):
obj = self.generate_debug_package_object()
if obj:
self.spec.packages.append(obj)
package_names = []
old_package_names = []
for package in self.spec.packages:
old_package_name = None
# store additional files
c = os.getcwd()
os.chdir(self.specdir)
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
for afile in package.additionalFiles:
src = os.path.join(ctx.const.files_dir, afile.filename)
dest = os.path.join(install_dir + os.path.dirname(afile.target), os.path.basename(afile.target))
util.copy_file(src, dest)
if afile.permission:
# mode is octal!
os.chmod(dest, int(afile.permission, 8))
os.chdir(c)
ctx.ui.action(_("** Building package %s") % package.name);
ctx.ui.info(_("Generating %s,") % ctx.const.files_xml)
self.gen_files_xml(package)
ctx.ui.info(_("Generating %s,") % ctx.const.metadata_xml)
build_number, old_build_number = self.gen_metadata_xml(package)
name = util.package_name(package.name,
self.spec.source.version,
self.spec.source.release,
self.metadata.package.build)
outdir = ctx.get_option('output_dir')
if outdir:
name = pisi.util.join_path(outdir, name)
ctx.ui.info(_("Creating PISI package %s.") % name)
# somebody explain to me why this is done here -- exa
if old_build_number:
old_package_name = util.package_name(package.name,
self.spec.source.version,
self.spec.source.release,
old_build_number)
pkg = Package(name, 'w')
package_names.append(name)
old_package_names.append(old_package_name)
# add comar files to package
os.chdir(self.specdir)
for pcomar in package.providesComar:
fname = util.join_path(ctx.const.comar_dir,
pcomar.script)
pkg.add_to_package(fname)
# add xmls and files
os.chdir(self.pkg_dir())
pkg.add_to_package(ctx.const.metadata_xml)
pkg.add_to_package(ctx.const.files_xml)
# Now it is time to add files to the packages using newly
# created files.xml
files = Files()
files.read(ctx.const.files_xml)
for finfo in files.list:
orgname = arcname = join("install", finfo.path)
if package.debug_package:
orgname = join("debug", finfo.path)
pkg.add_to_package(orgname, arcname)
pkg.close()
os.chdir(c)
self.set_state("buildpackages")
ctx.ui.info(_("Done."))
#show the files those are not collected from the install dir
if ctx.get_option('show_abandoned_files') or ctx.get_option('debug'):
abandoned_files = self.get_abandoned_files()
if abandoned_files:
ctx.ui.warning(_('Abandoned files under the install dir (%s):') % (install_dir))
for f in abandoned_files:
ctx.ui.info(' - %s' % (f))
else:
ctx.ui.warning(_('All of the files under the install dir (%s) has been collected by package(s)')
% (install_dir))
if ctx.config.values.general.autoclean is True:
ctx.ui.info(_("Cleaning Build Directory..."))
util.clean_dir(self.pkg_dir())
else:
ctx.ui.info(_("Keeping Build Directory"))
return package_names, old_package_names
# build functions...
def build(pspec, authinfo=None):
if pspec.endswith('.xml'):
pb = Builder(pspec, authinfo)
else:
pb = Builder.from_name(pspec, authinfo)
return pb.build()
order = {"none": 0,
"unpack": 1,
"setupaction": 2,
"buildaction": 3,
"installaction": 4,
"buildpackages": 5}
def __buildState_unpack(pb):
# unpack is the first state to run.
pb.fetch_source_archive()
pb.unpack_source_archive()
pb.apply_patches()
def __buildState_setupaction(pb, last):
if order[last] < order["unpack"]:
__buildState_unpack(pb)
pb.run_setup_action()
def __buildState_buildaction(pb, last):
if order[last] < order["setupaction"]:
__buildState_setupaction(pb, last)
pb.run_build_action()
def __buildState_installaction(pb, last):
if order[last] < order["buildaction"]:
__buildState_buildaction(pb, last)
pb.run_install_action()
def __buildState_buildpackages(pb, last):
if order[last] < order["installaction"]:
__buildState_installaction(pb, last)
pb.build_packages()
def build_until(pspec, state, authinfo=None):
if pspec.endswith('.xml'):
pb = Builder(pspec, authinfo)
else:
pb = Builder.from_name(pspec, authinfo)
pb.compile_action_script()
last = pb.get_state()
ctx.ui.info("Last state was %s"%last)
if not last: last = "none"
if state == "unpack":
__buildState_unpack(pb)
return
if state == "setup":
__buildState_setupaction(pb, last)
return
if state == "build":
__buildState_buildaction(pb, last)
return
if state == "install":
__buildState_installaction(pb, last)
return
__buildState_buildpackages(pb, last)
-23
View File
@@ -1,23 +0,0 @@
Writing commands:
-----------------
TODO: this is a *bit* out of date now.
Subclass from Command.
Be careful not to import PiSi modules before Command is run, e.g.
do it in run() method. This is necessary to prevent an obscure
initialization error.
We used to import pisi.operations in pisi.cli
This prevented pisi.operations from having the current global
pisi.ui.ui because it's loaded before the UI is set! So, despite
what you might understand from the python FAQ 1.2.3
http://www.python.org/doc/faq/programming.html#how-do-i-share-global-variables-across-modules
you have to be careful about the initialization order when using
singleton modules.
Another way to solve this is to ensure that the global module is
always imported again in every function it is called (see
ui.CLI.confirm() ) but this is more expensive.
-160
View File
@@ -1,160 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import logging
import locale
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.ui import UI
import pisi.ui as ui
import pisi.util as util
from pisi.cli.colors import colorize
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
def printu(obj, err = False):
if not isinstance(obj, unicode):
obj = unicode(obj)
if err:
out = sys.stderr
else:
out = sys.stdout
out.write(obj.encode('utf-8'))
out.flush()
class CLI(UI):
"Command Line Interface"
def __init__(self, show_debug = False, show_verbose = False):
super(CLI, self).__init__(show_debug, show_verbose)
def close(self):
util.xterm_title_reset()
def output(self, msg, err = False, verbose = False):
if (verbose and self.show_verbose) or (not verbose):
if type(msg)==type(unicode()):
msg = msg.encode('utf-8')
if err:
out = sys.stderr
else:
out = sys.stdout
out.write(msg)
out.flush()
def info(self, msg, verbose = False, noln = False):
# TODO: need to look at more kinds of info messages
# let's cheat from KDE :)
if noln:
msgend = ''
else:
msgend = '\n'
self.output( unicode(msg) + msgend, verbose=verbose)
def warning(self, msg, verbose = False):
msg = unicode(msg)
if ctx.log:
ctx.log.warning(msg)
if ctx.get_option('no_color'):
self.output(_('Warning: ') + msg + '\n', err=True, verbose=verbose)
else:
self.output(colorize(msg + '\n', 'purple'), err=True, verbose=verbose)
def error(self, msg):
msg = unicode(msg)
if ctx.log:
ctx.log.error(msg)
if ctx.get_option('no_color'):
self.output(_('Error: ') + msg + '\n', err=True)
else:
self.output(colorize(msg + '\n', 'red'), err=True)
def action(self, msg, verbose = False):
#TODO: this seems quite redundant?
msg = unicode(msg)
if ctx.log:
ctx.log.info(msg)
self.output(colorize(msg + '\n', 'green'))
def choose(self, msg, opts):
print msg
for i in range(0,len(opts)):
print i + 1, opts(i)
while True:
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
try:
opt = int(s)
if 1 <= opt and opt <= len(opts):
return opts(opt-1)
except (Exception,e):
pass
def confirm(self, msg):
msg = unicode(msg)
if ctx.config.options and ctx.config.options.yes_all:
return True
while True:
import re
yesexpr = re.compile(locale.nl_langinfo(locale.YESEXPR))
prompt = msg + colorize(_(' (yes/no)'), 'red')
s = raw_input(prompt.encode('utf-8'))
if yesexpr.search(s):
return True
return False
def display_progress(self, **ka):
totalsize = '%.1f %s' % pisi.util.human_readable_size(ka['total_size'])
out = '\r%-30.30s (%s)%3d%% %9.2f %s [%s]' % \
(ka['filename'], totalsize, ka['percent'],
ka['rate'], ka['symbol'], ka['eta'])
self.output(out)
if ka['percent'] == 100:
self.output(colorize(_(' [complete]\n'), 'gray'))
def status(self, msg = None):
if msg:
msg = unicode(msg)
self.output(colorize(msg + '\n', 'purple'))
util.xterm_title(msg)
def notify(self, event, **keywords):
if event == ui.installed:
msg = _('Installed %s') % keywords['package'].name
elif event == ui.removed:
msg = _('Removed %s') % keywords['package'].name
elif event == ui.upgraded:
msg = _('Upgraded %s') % keywords['package'].name
elif event == ui.configured:
msg = _('Configured %s') % keywords['package'].name
elif event == ui.extracting:
msg = _('Extracting the files of %s') % keywords['package'].name
else:
msg = None
if msg:
self.output(colorize(msg + '\n', 'cyan'))
if ctx.log:
ctx.log.info(msg)
-64
View File
@@ -1,64 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Colors module provides some color codes for console output.
import pisi.context as ctx
colors = {'black' : "\033[30m",
'red' : "\033[31m",
'green' : "\033[32m",
'yellow' : "\033[33m",
'blue' : "\033[34m",
'purple' : "\033[35m",
'cyan' : "\033[36m",
'white' : "\033[37m",
'brightblack' : "\033[01;30m",
'brightred' : "\033[01;31m",
'brightgreen' : "\033[01;32m",
'brightyellow' : "\033[01;33m",
'brightblue' : "\033[01;34m",
'brightmagenta' : "\033[01;35m",
'brightcyan' : "\033[01;36m",
'brightwhite' : "\033[01;37m",
'underlineblack' : "\033[04;30m",
'underlinered' : "\033[04;31m",
'underlinegreen' : "\033[04;32m",
'underlineyellow' : "\033[04;33m",
'underlineblue' : "\033[04;34m",
'underlinemagenta' : "\033[04;35m",
'underlinecyan' : "\033[04;36m",
'underlinewhite' : "\033[04;37m",
'blinkingblack' : "\033[05;30m",
'blinkingred' : "\033[05;31m",
'blinkinggreen' : "\033[05;32m",
'blinkingyellow' : "\033[05;33m",
'blinkingblue' : "\033[05;34m",
'blinkingmagenta' : "\033[05;35m",
'blinkingcyan' : "\033[05;36m",
'blinkingwhite' : "\033[05;37m",
'backgroundblack' : "\033[07;30m",
'backgroundred' : "\033[07;31m",
'backgroundgreen' : "\033[07;32m",
'backgroundyellow' : "\033[07;33m",
'backgroundblue' : "\033[07;34m",
'backgroundmagenta' : "\033[07;35m",
'backgroundcyan' : "\033[07;36m",
'backgroundwhite' : "\033[07;37m",
'default' : "\033[0m" }
def colorize(msg, color):
"""Colorize the given message for console output"""
if colors.has_key(color) and not ctx.get_option('no_color'):
return colors[color] + msg + colors['default']
else:
return msg
File diff suppressed because it is too large Load Diff
-112
View File
@@ -1,112 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
from optparse import OptionParser
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.cli
from pisi.cli import printu
from pisi.uri import URI
from pisi.cli.commands import *
class ParserError(pisi.Exception):
pass
class Error(pisi.Error):
pass
class PreParser(OptionParser):
"""consumes any options, and finds arguments from command line"""
def __init__(self, version):
OptionParser.__init__(self, usage=usage_text, version=version)
def error(self, msg):
raise ParserError, msg
def parse_args(self, args=None):
self.opts = []
self.rargs = self._get_args(args)
self._process_args()
return (self.opts, self.args)
def _process_args(self):
args = []
rargs = self.rargs
if not self.allow_interspersed_args:
first_arg = False
while rargs:
arg = rargs[0]
def option():
if not self.allow_interspersed_args and first_arg:
self.error(_('Options must precede non-option arguments'))
arg = rargs[0]
if arg.startswith('--'):
self.opts.append(arg[2:])
else:
self.opts.append(arg[1:])
del rargs[0]
return
# We handle bare "--" explicitly, and bare "-" is handled by the
# standard arg handler since the short arg case ensures that the
# len of the opt string is greater than 1.
if arg == "--":
del rargs[0]
break
elif arg[0:2] == "--":
# process a single long option (possibly with value(s))
option()
elif arg[:1] == "-" and len(arg) > 1:
# process a cluster of short options (possibly with
# value(s) for the last one only)
option()
else: # then it must be an argument
args.append(arg)
del rargs[0]
self.args = args
class PisiCLI(object):
def __init__(self):
# first construct a parser for common options
# this is really dummy
self.parser = PreParser(version="%prog " + pisi.__version__)
try:
opts, args = self.parser.parse_args()
if len(args)==0: # more explicit than using IndexError
if 'version' in opts:
self.parser.print_version()
sys.exit(0)
elif 'help' in opts or 'h' in opts:
self.die()
raise Error(_('No command given'))
cmd_name = args[0]
except ParserError:
raise Error(_('Command line parsing error'))
self.command = Command.get_command(cmd_name)
if not self.command:
raise Error(_("Unrecognized command: %s") % cmd_name)
def die(self):
printu('\n' + self.parser.format_help())
sys.exit(1)
def run_command(self):
self.command.run()
-129
View File
@@ -1,129 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Baris Metin <baris@pardus.org.tr>
# Gurer Ozen <gurer@pardus.org.tr>
import comar
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
class Error(pisi.Error):
pass
def make_com():
try:
import comar
if ctx.comar_sockname:
comard = comar.Link(sockname=ctx.comar_sockname)
else:
comard = comar.Link()
return comard
except ImportError:
raise Error(_("COMAR: comard not fully installed"))
except comar.Error:
raise Error(_("COMAR: comard not running or defunct"))
def register(pcomar, name, path):
ctx.ui.info(_("Registering COMAR script %s") % pcomar.script)
com = make_com()
assert(com)
com.register(pcomar.om, name, path)
while 1:
reply = com.read_cmd()
if reply[0] == com.RESULT:
break
else:
raise Error, _("COMAR.register ERROR!")
def wait_comar():
import socket, time
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
timeout = 5
while timeout > 0:
try:
if ctx.comar_sockname:
sock.connect(sockname=ctx.comar_sockname)
else:
sock.connect("/var/run/comar.socket")
return True
except:
timeout -= 0.2
time.sleep(0.2)
return False
def run_postinstall(package_name):
"run postinstall scripts trough COMAR"
com = make_com()
assert(com)
ctx.ui.info(_("Running post-install script for %s") % package_name)
com.call_package("System.Package.postInstall", package_name)
while 1:
try:
reply = com.read_cmd()
except:
# Comar postInstall does a "service comar restart" which cuts
# our precious communication link, so we waitsss
if package_name == "comar":
if not wait_comar():
raise Error, _("Could not restart Comar")
return
else:
raise Error, _("Comar daemon closed the connection")
if reply[0] == com.RESULT:
break
elif reply[0] == com.NONE: # package has no postInstall script
break
elif reply[0] == com.FAIL:
e = _("COMAR.call_package(System.Package.postInstall, %s) failed!: %s") % (
package_name, reply[2])
raise Error, e
else:
raise Error, _("COMAR.call_package ERROR: %d") % reply[0]
def run_preremove(package_name):
com = make_com()
assert(com)
# First, call preRemove script!
ctx.ui.info(_("Running pre-remove script for %s") % package_name)
com.call_package("System.Package.preRemove", package_name)
while 1:
reply = com.read_cmd()
if reply[0] == com.RESULT:
break
elif reply[0] == com.NONE: # package has no preRemove script
break
elif reply[0] == com.FAIL:
e = _("COMAR.call_package(System.Package.preRemove, %s) failed!: %s") % (
package_name, reply[2])
raise Error, e
else:
raise Error, _("COMAR.call_package ERROR: %d") % reply[0]
# and then, remove package's Comar Scripts...
ctx.ui.info(_("Unregistering COMAR scripts for %s") % package_name)
com.remove(package_name)
while 1:
reply = com.read_cmd()
if reply[0] == com.RESULT:
break
elif reply[1] == com.ERROR:
raise Error, "COMAR.remove failed!"
-180
View File
@@ -1,180 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.pxml.xmlfile as xmlfile
import pisi.pxml.autoxml as autoxml
import pisi.lockeddbshelve as shelve
from pisi.itembyrepodb import ItemByRepoDB
import pisi.itembyrepodb as itembyrepodb
class Error(pisi.Error):
pass
__metaclass__ = autoxml.autoxml
class Distribution(xmlfile.XmlFile):
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_SourceName = [autoxml.Text, autoxml.mandatory] # name of distribution (source)
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_Version = [autoxml.Text, autoxml.optional]
t_Type = [autoxml.Text, autoxml.mandatory]
t_Dependencies = [ [autoxml.Text], autoxml.optional, "Dependencies/Distribution"]
t_BinaryName = [autoxml.Text, autoxml.optional] # name of repository (binary distro)
t_Architecture = [autoxml.Text, autoxml.optional] # architecture identifier
class Component(xmlfile.XmlFile):
"representation for component declarations"
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Name = [autoxml.String, autoxml.mandatory] # fully qualified name
# component name in other languages, for instance in Turkish
# LocalName for system.base could be sistem.taban or "Taban Sistem",
# this could be useful for GUIs
t_LocalName = [autoxml.LocalText, autoxml.mandatory]
# Information about the component
t_Summary = [autoxml.LocalText, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
#t_Icon = [autoxml.Binary, autoxml.mandatory]
# Dependencies to other components
t_Dependencies = [ [autoxml.String], autoxml.optional, "Dependencies/Component"]
# the parts of this component.
# to be filled by the component database, thus it is optional.
t_Packages = [ [autoxml.String], autoxml.optional, "Parts/Package"]
# TODO: this is probably not necessary since we use fully qualified
# module names (like in Java)
#t_PartOf = [autoxml.Text, autoxml.mandatory]
#FIXME: recursive declarations do not work!
#class ComponentTree(xmlfile.XmlFile):
# "index representation for the component structure"
#
# __metaclass__ = autoxml.autoxml
#
# tag = "Component"
#
# t_Name = [autoxml.Text, autoxml.mandatory] # fully qualified name
# #t_Icon = [autoxml.Binary, autoxml.mandatory]
# t_Dependencies = [ [autoxml.Text], autoxml.optional, "Component"]
# #t_Parts = [ [pisi.component.ComponentTree], autoxml.optional, "Component"]
class ComponentDB(object):
"""a database of components"""
#FIXME: we might need a database per repo in the future
def __init__(self):
self.d = ItemByRepoDB('component')
def close(self):
self.d.close()
def destroy(self):
self.d.destroy()
def has_component(self, name, repo = None, txn = None):
#name = shelve.LockedDBShelf.encodekey(name)
name = str(name)
return self.d.has_key(name, repo, txn)
def get_component(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn=txn)
except pisi.itembyrepodb.NotfoundError, e:
raise Error(_('Component %s not found') % name)
def get_component_repo(self, name, repo=None, txn = None):
#name = shelve.LockedDBShelf.encodekey(name)
try:
return self.d.get_item_repo(name, repo, txn=txn)
except pisi.itembyrepodb.NotfoundError, e:
raise Error(_('Component %s not found') % name)
def get_union_comp(self, name, txn = None, repo = pisi.itembyrepodb.repos ):
"""get a union of all repository components packages, not just the first repo in order.
get only basic repo info from the first repo"""
def proc(txn):
s = self.d.d.get(name, txn=txn)
pkgs = set()
for repostr in self.d.order(repo = repo):
if s.has_key(repostr):
pkgs |= set(s[repostr].packages)
comp = self.get_component(name)
comp.packages = list(pkgs)
return comp
return self.d.txn_proc(proc, txn)
def list_components(self, repo=None):
return self.d.list(repo)
def update_component(self, component, repo, txn = None):
def proc(txn):
if self.has_component(component.name, repo, txn):
# preserve the list of packages
component.packages = self.d.get_item(component.name, repo, txn).packages
self.d.add_item(component.name, component, repo, txn)
self.d.txn_proc(proc, txn)
def add_package(self, component_name, package, repo, txn = None):
def proc(txn):
if self.has_component(component_name, repo, txn):
component = self.get_component(component_name, repo, txn)
else:
component = Component( name = component_name )
if not package in component.packages:
component.packages.append(package)
self.d.add_item(component_name, component, repo, txn) # update
self.d.txn_proc(proc, txn)
def remove_package(self, component_name, package, repo = None, txn = None):
def proc(txn, repo):
if not self.has_component(component_name, repo, txn):
raise Error(_('Information for component %s not available') % component_name)
if not repo:
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
component = self.get_component(component_name, repo, txn)
if package in component.packages:
component.packages.remove(package)
self.d.add_item(component_name, component, repo, txn) # update
ctx.txn_proc(lambda x: proc(txn, repo), txn)
def clear(self, txn = None):
self.d.clear(txn)
def remove_component(self, name, repo = None, txn = None):
name = str(name)
self.d.remove_item(name, repo, txn)
def remove_repo(self, repo, txn = None):
self.d.remove_repo(repo, txn=txn)
-113
View File
@@ -1,113 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Baris Metin <baris@pardus.org.tr>
# Eray Ozkural <eray@pardus.org.tr>
"""
PISI Configuration module is used for gathering and providing
regular PISI configurations.
"""
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.configfile import ConfigurationFile
import pisi.util
from pisi.util import join_path as join
class Error(pisi.Error):
pass
class Options(object):
def __getattr__(self, name):
if not self.__dict__.has_key(name):
return None
else:
return self.__dict__[name]
class Config(object):
"""Config Singleton"""
def __init__(self, options = Options()):
self.options = options
self.values = ConfigurationFile("/etc/pisi/pisi.conf")
destdir = self.get_option('destdir')
if destdir:
if destdir.strip().startswith('/'):
self.destdir = destdir
else:
self.destdir = join(os.getcwd(), destdir)
else:
self.destdir = self.values.general.destinationdirectory
if not os.path.exists(self.destdir):
ctx.ui.warning( _('Destination directory %s does not exist. Creating it.') % self.destdir)
os.makedirs(self.destdir)
def get_option(self, opt):
if self.options:
if hasattr(self.options, opt):
return getattr(self.options, opt)
return None
# directory accessor functions
# here is how it goes
# x_dir: system wide directory for storing info type x
# pkg_x_dir: per package directory for storing info type x
def dest_dir(self):
return self.destdir
def subdir(self, path):
dir = join(self.dest_dir(), path)
pisi.util.check_dir(dir)
return dir
def lib_dir(self):
return self.subdir(self.values.dirs.lib_dir)
def db_dir(self):
return self.subdir(self.values.dirs.db_dir)
def archives_dir(self):
return self.subdir(self.values.dirs.archives_dir)
def packages_dir(self):
return self.subdir(self.values.dirs.packages_dir)
def index_dir(self):
return self.subdir(self.values.dirs.index_dir)
def tmp_dir(self):
sysdir = self.subdir(self.values.dirs.tmp_dir)
userdir = '/tmp/pisi-' + os.environ['USER'] # FIXME: too unix specific?
# check write access
if os.access(sysdir, os.W_OK):
return sysdir
else:
return userdir
# bu dizini neden kullanıyoruz? Yalnızca index.py içerisinde
# kullanılıyor ama /var/tmp/pisi/install gibi bir dizine niye
# ihtiyacımız var? (baris)
def install_dir(self):
return self.tmp_dir() + ctx.const.install_dir_suffix
#TODO: remove this
config = Config()
-153
View File
@@ -1,153 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# PISI Configuration File module, obviously, is used to read from the
# configuration file. Module also defines default values for
# configuration parameters.
#
# Configuration file is located in /etc/pisi/pisi.conf by default,
# having an INI like format like below.
#
#[general]
#destinationdirectory = /
#buildhelper = None / ccache / icecream
#autoclean = False
#
#[build]
#host = i686-pc-linux-gnu
#debug = False
#jobs = "-j1"
#CFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
#CXXFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
#LDFLAGS=
#buildno=True # necessary for generating build nos
#
#[directories]
#lib_dir = /var/lib/pisi
#db_dir = /var/db/pisi
#archives_dir = /var/cache/pisi/archives
#packages_dir = /var/cache/pisi/packages
#index_dir = /var/cache/pisi/index
#tmp_dir = /var/tmp/pisi
#icon_theme_dir = /usr/share/icons/Tulliana-1.0
#kde_dir = /usr/kde/3.5
#qt_dir = /usr/qt/3
import os
from ConfigParser import ConfigParser, NoSectionError
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class Error(pisi.Error):
pass
class GeneralDefaults:
"""Default values for [general] section"""
destinationdirectory = "/"
buildhelper = None
autoclean = False
distribution = "Pardus"
distribution_release = "1.1"
http_proxy = os.getenv("HTTP_PROXY") or None
https_proxy = os.getenv("HTTPS_PROXY") or None
ftp_proxy = os.getenv("FTP_PROXY") or None
class BuildDefaults:
"""Default values for [build] section"""
host = "i686-pc-linux-gnu"
jobs = "-j1"
debug = False
cflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
cxxflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
ldflags = ""
buildno = False
class DirsDefaults:
"Default values for [directories] section"
lib_dir = "/var/lib/pisi"
db_dir = "/var/db/pisi"
archives_dir = "/var/cache/pisi/archives"
packages_dir = "/var/cache/pisi/packages"
index_dir = "/var/lib/pisi/index"
tmp_dir = "/var/tmp/pisi"
icon_theme_dir = "/usr/share/icons/Tulliana-1.0"
kde_dir = "/usr/kde/3.5"
qt_dir = "/usr/qt/3"
class ConfigurationSection(object):
"""ConfigurationSection class defines a section in the configuration
file, using defaults (above) as a fallback."""
def __init__(self, section, items=[]):
self.items = items
if section == "general":
self.defaults = GeneralDefaults
elif section == "build":
self.defaults = BuildDefaults
elif section == "directories":
self.defaults = DirsDefaults
else:
e = _("No section by name '%s'") % section
raise Error, e
self.section = section
def __getattr__(self, attr):
# first search for attribute in the items provided in the
# configuration file.
if self.items:
for item in self.items:
if item[0] == attr:
return item[1]
# then fall back to defaults
if hasattr(self.defaults, attr):
return getattr(self.defaults, attr)
return ""
# We'll need to access configuration keys by their names as a
# string. Like; ["default"]...
def __getitem__(self, key):
return self.__getattr__(key)
class ConfigurationFile(object):
"""Parse and get configuration values from the configuration file"""
def __init__(self, filePath):
parser = ConfigParser()
self.filePath = filePath
parser.read(self.filePath)
try:
generalitems = parser.items("general")
except NoSectionError:
generalitems = []
self.general = ConfigurationSection("general", generalitems)
try:
builditems = parser.items("build")
except NoSectionError:
builditems = []
self.build = ConfigurationSection("build", builditems)
try:
dirsitems = parser.items("directories")
except NoSectionError:
dirsitems = []
self.dirs = ConfigurationSection("directories", dirsitems)
-102
View File
@@ -1,102 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""PISI constants.
If you have a "magic" constant value this is where it should be
defined."""
# Author: Baris Metin <baris@pardus.org.tr
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class _constant:
"Constant members implementation"
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, _("Can't rebind constant: %s") % name
# Binding an attribute once to a const is available
self.__dict__[name] = value
def __delattr__(self, name):
if self.__dict__.has_key(name):
raise self.ConstError, _("Can't unbind constant: %s") % name
# we don't have an attribute by this name
raise NameError, name
class Constants:
"Pisi Constants Singleton"
__c = _constant()
def __init__(self):
# suffix for package names
self.__c.package_suffix = ".pisi"
self.__c.xdelta_suffix = ".xdelta"
# suffix for auto generated debug packages
self.__c.debug_name_suffix = "-debug"
self.__c.debug_file_suffix = ".debug"
# suffix for auto generated ar packages
self.__c.static_name_suffix = "-static" # an admissible use of constant
self.__c.ar_file_suffix = ".a"
# directory suffixes for build
self.__c.work_dir_suffix = "/work" # these, too, because we might wanna change 'em
self.__c.install_dir_suffix = "/install"
self.__c.debug_dir_suffix = "/debug"
# file/directory names
#note: these don't seem very well, constants are used
#when it is easier/more meaningful to write the constant name, or
#when the constant is bound to change later on.
#in some places literals are just as good, for instance
#when constant is the same as string. readability is important...
self.__c.actions_file = "actions.py"
self.__c.files_dir = "files"
self.__c.metadata_dir = "metadata"
self.__c.comar_dir = "comar"
self.__c.files_xml = "files.xml"
self.__c.metadata_xml = "metadata.xml"
# functions in actions_file
self.__c.setup_func = "setup"
self.__c.build_func = "build"
self.__c.install_func = "install"
# file types
# FIXME: these seem redundant
self.__c.doc = "doc"
self.__c.man = "man"
self.__c.info = "info"
self.__c.conf = "config"
self.__c.header = "header"
self.__c.library = "library"
self.__c.executable = "executable"
self.__c.data = "data"
self.__c.localedata = "localedata"
def __getattr__(self, attr):
return getattr(self.__c, attr)
def __setattr__(self, attr, value):
setattr(self.__c, attr, value)
def __delattr__(self, attr):
delattr(self.__c, attr)
-77
View File
@@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Context module.
# Authors: Baris Metin <baris@pardus.org.tr
# Eray Ozkural <eray@pardus.org.tr>
# global variables here
import pisi.constants
const = pisi.constants.Constants()
config = None
log = None
use_mdom = False
def get_option(opt):
return config and config.get_option(opt)
# default UI is CLI
ui = None # not now
# stdout, stderr for PiSi API
stdout = None
stderr = None
dbenv = None
installdb = None
packagedb = None
repodb = None
invidx = None
comar = None
comar_sockname = None
initialized = False
#def register(_impl):
# """ Register a UI implementation"""
# ui = _impl
import bsddb3.db as db
# copy of DBShelve.txn_proc, the only difference is it doesn't need a shelf object
#FIXME: remove this redundancy, and move all this stuff to database.py
def txn_proc(proc, txn = None):
# can be used to txn protect a method automatically
if not txn:
if dbenv:
autotxn = dbenv.txn_begin()
try:
retval = proc(autotxn)
except db.DBError, e:
autotxn.abort()
raise e
except Exception, e:
autotxn.abort()
raise e
autotxn.commit()
else:
retval = proc(None)
return retval
else:
return proc(txn)
-322
View File
@@ -1,322 +0,0 @@
#!/bin/env python
#------------------------------------------------------------------------
# Copyright (c) 1997-2001 by Total Control Software
# All Rights Reserved
#------------------------------------------------------------------------
#
# Module Name: dbShelve.py
#
# Description: A reimplementation of the standard shelve.py that
# forces the use of cPickle, and DB.
#
# Creation Date: 11/3/97 3:39:04PM
#
# License: This is free software. You may use this software for any
# purpose including modification/redistribution, so long as
# this header remains intact and that you do not claim any
# rights of ownership or authorship of this software. This
# software has been tested, but no warranty is expressed or
# implied.
#
# 13-Dec-2000: Updated to be used with the new bsddb3 package.
# Added DBShelfCursor class.
#
# 13-Dec-2005: Minor hacking by exa to make it work better with PISI
#------------------------------------------------------------------------
"""Manage shelves of pickled objects using bsddb database files for the
storage.
Add transaction processing by default to dictionary ops also
Also other minor improvements -- exa
Now added support for overriding the marshalling method
"""
#------------------------------------------------------------------------
import cPickle
import bsddb3.db as db
import bsddb3.dbobj as dbobj
import string
import sys
import traceback
import pisi
class CodingError(pisi.Error):
pass
class DBShelf:
"""A shelf to hold pickled objects, built upon a bsddb DB object. It
automatically pickles/unpickles data objects going to/from the DB.
"""
def __init__(self, dbenv = None):
self.dbenv = dbenv
# how lame is bsddb3?
if self.dbenv:
self.db = dbobj.DB(dbenv)
else:
self.db = db.DB(None)
# it is better to explicitly close a shelf
#def __del__(self):
# self.close()
allowed_chars = string.letters + string.digits + '-'
def check_key(key):
return pisi.util.all(lambda x: x in allowed_chars, key)
def has_key(self, key, txn = None):
if txn:
return self.db.has_key(key, txn)
else:
return self.db.has_key(key)
def txn_proc(self, proc, txn):
# can be used to txn protect a method automatically
if not txn:
if self.dbenv:
autotxn = self.dbenv.txn_begin()
try:
retval = proc(autotxn)
except db.DBError, e:
autotxn.abort()
raise e
except Exception, e:
autotxn.abort()
#e.args += tuple(traceback.format_tb(sys.exc_traceback))
raise e
autotxn.commit()
else: # execute without transactions
retval = proc(None)
return retval
else:
return proc(txn)
def decode(self, data):
try:
return cPickle.loads(data)
except:
raise CodingError()
def encode(self, obj):
return cPickle.dumps(obj, 1)
def clear(self, txn = None):
def proc(txn):
for x in self.keys(txn):
self.db.delete(x, txn)
self.txn_proc(proc, txn)
def delete(self, x, txn):
def proc(txn):
self.db.delete(x, txn)
self.txn_proc(proc, txn)
# another lame pythonic implementation method:
#def __getattr__(self, name):
# """Many methods we can just pass through to the DB object.
# (See below)
# """
# print 'aptal bsddb3', name
# return getattr(self.db, name)
#-----------------------------------
# Dictionary access methods
def __len__(self):
return len(self.db)
def __getitem__(self, key):
def proc(txn):
data = self.db.get(key)
return self.decode(data)
return self.txn_proc(proc, None)
def __setitem__(self, key, value):
# hyperdandik transactions
def proc(txn):
data = self.encode(value)
self.db.put(key,data,txn)
return self.txn_proc(proc, None)
def __delitem__(self, key):
txn = self.dbenv.txn_begin()
try:
self.db.delete(key, txn)
except db.DBError, e:
txn.abort()
raise e
txn.commit()
def keys(self, txn=None):
if txn != None:
return self.db.keys(txn)
else:
return self.db.keys()
def items(self, txn=None):
if txn != None:
items = self.db.items(txn)
else:
items = self.db.items()
newitems = []
for k, v in items:
newitems.append( (k, self.decode(v) ) )
return newitems
def values(self, txn=None):
if txn != None:
values = self.db.values(txn)
else:
values = self.db.values()
return map(lambda x : self.decode(x), values)
#-----------------------------------
# Other methods
def __append(self, value, txn=None):
data = self.encode(value)
return self.db.append(data, txn)
def append(self, value, txn=None):
if self.get_type() != db.DB_RECNO:
self.append = self.__append
return self.append(value, txn=txn)
raise db.DBError, "append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO"
def associate(self, secondaryDB, callback, flags=0):
def _shelf_callback(priKey, priData, realCallback=callback):
data = self.decode(priData)
return realCallback(priKey, data)
return self.db.associate(secondaryDB, _shelf_callback, flags)
#def get(self, key, default=None, txn=None, flags=0):
def get(self, *args, **kw):
# We do it with *args and **kw so if the default value wasn't
# given nothing is passed to the extension module. That way
# an exception can be raised if set_get_returns_none is turned
# off.
data = apply(self.db.get, args, kw)
try:
return self.decode(data)
except (TypeError, CodingError):
return data # we may be getting the default value, or None,
# so it doesn't need unpickled.
def get_both(self, key, value, txn=None, flags=0):
data = self.encode(value)
data = self.db.get(key, data, txn, flags)
return self.decode(data)
def cursor(self, txn=None, flags=0):
c = DBShelfCursor(self.db.cursor(txn, flags))
c.binary = self.binary
return c
def put(self, key, value, txn=None, flags=0):
data = self.encode(value)
return self.db.put(key, data, txn, flags)
def join(self, cursorList, flags=0):
raise NotImplementedError
#----------------------------------------------
# Methods allowed to pass-through to self.db
#
# close, delete, fd, get_byteswapped, get_type, has_key,
# key_range, open, remove, rename, stat, sync,
# upgrade, verify, and all set_* methods.
#---------------------------------------------------------------------------
class DBShelfCursor:
"""
"""
def __init__(self, cursor):
self.dbc = cursor
def __del__(self):
self.close()
def __getattr__(self, name):
"""Some methods we can just pass through to the cursor object. (See below)"""
return getattr(self.dbc, name)
#----------------------------------------------
def dup(self, flags=0):
return DBShelfCursor(self.dbc.dup(flags))
def put(self, key, value, flags=0):
data = self.encode(value)
return self.dbc.put(key, data, flags)
def get(self, *args):
count = len(args) # a method overloading hack
method = getattr(self, 'get_%d' % count)
apply(method, args)
def get_1(self, flags):
rec = self.dbc.get(flags)
return self._extract(rec)
def get_2(self, key, flags):
rec = self.dbc.get(key, flags)
return self._extract(rec)
def get_3(self, key, value, flags):
data = self.encode(value)
rec = self.dbc.get(key, flags)
return self._extract(rec)
def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT)
def first(self, flags=0): return self.get_1(flags|db.DB_FIRST)
def last(self, flags=0): return self.get_1(flags|db.DB_LAST)
def next(self, flags=0): return self.get_1(flags|db.DB_NEXT)
def prev(self, flags=0): return self.get_1(flags|db.DB_PREV)
def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME)
def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP)
def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP)
def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP)
def get_both(self, key, value, flags=0):
data = self.encode(value)
rec = self.dbc.get_both(key, flags)
return self._extract(rec)
def set(self, key, flags=0):
rec = self.dbc.set(key, flags)
return self._extract(rec)
def set_range(self, key, flags=0):
rec = self.dbc.set_range(key, flags)
return self._extract(rec)
def set_recno(self, recno, flags=0):
rec = self.dbc.set_recno(recno, flags)
return self._extract(rec)
set_both = get_both
def _extract(self, rec):
if rec is None:
return None
else:
key, data = rec
return key, self.decode(data)
#----------------------------------------------
# Methods allowed to pass-through to self.dbc
#
# close, count, delete, get_recno, join_item
#---------------------------------------------------------------------------
-123
View File
@@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
"""dependency analyzer"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.context as ctx
#import pisi.packagedb as packagedb
from pisi.version import Version
import pisi.pxml.autoxml as autoxml
from pisi.util import Checks
import pisi.itembyrepodb
class Dependency:
__metaclass__ = autoxml.autoxml
s_Package = [autoxml.String, autoxml.mandatory]
a_version = [autoxml.String, autoxml.optional]
a_versionFrom = [autoxml.String, autoxml.optional]
a_versionTo = [autoxml.String, autoxml.optional]
a_release = [autoxml.String, autoxml.optional]
a_releaseFrom = [autoxml.String, autoxml.optional]
a_releaseTo = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.package
if self.versionFrom:
s += _(" version >= ") + self.versionFrom
if self.versionTo:
s += _(" version <= ") + self.versionTo
if self.releaseFrom:
s += _(" release >= ") + self.releaseFrom
if self.releaseTo:
s += _(" release <= ") + self.releaseTo
return s
def satisfies(self, pkg_name, version, release):
"""determine if a package ver. satisfies given dependency spec"""
ret = True
v = Version(version)
if self.version:
ret &= v == Version(self.version)
if self.versionFrom:
ret &= v >= Version(self.versionFrom)
if self.versionTo:
ret &= v <= Version(self.versionTo)
r = Version(release)
if self.release:
ret &= r == Version(self.release)
if self.releaseFrom:
ret &= r >= Version(self.releaseFrom)
if self.releaseTo:
ret &= r <= Version(self.releaseTo)
return ret
def dict_satisfies_dep(dict, depinfo):
"""determine if a package in a dictionary satisfies given dependency spec"""
pkg_name = depinfo.package
if not dict.has_key(pkg_name):
return False
else:
pkg = dict[pkg_name]
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies(pkg_name, version, release)
def installed_satisfies_dep(depinfo):
"""determine if a package in *repository* satisfies given
dependency spec"""
pkg_name = depinfo.package
if not ctx.installdb.is_installed(pkg_name):
return False
else:
pkg = ctx.packagedb.get_package(pkg_name, pisi.itembyrepodb.installed)
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies(pkg_name, version, release)
def repo_satisfies_dep(depinfo):
"""determine if a package in *repository* satisfies given
dependency spec"""
pkg_name = depinfo.package
if not ctx.packagedb.has_package(pkg_name):
return False
else:
pkg = ctx.packagedb.get_package(pkg_name)
(version, release) = (pkg.version, pkg.release)
return depinfo.satisfies(pkg_name, version, release)
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
for dep in deps:
if not sat(dep):
ctx.ui.error(_('Package %s does not satisfy dependency %s') %
(pkg,dep))
return False
return True
def satisfies_runtime_deps(pkg):
deps = ctx.packagedb.get_package(pkg).runtimeDependencies()
return satisfies_dependencies(pkg, deps)
def installable(pkg):
"""calculate if pkg name is installable currently
which means it has to satisfy both install and runtime dependencies"""
if not ctx.packagedb.has_package(pkg):
ctx.ui.info(_("Package %s is not present in the package database") % pkg);
return False
elif satisfies_runtime_deps(pkg):
return True
else:
return False
-126
View File
@@ -1,126 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version. Please read the COPYING file.
#
import sys
import os
import subprocess
import getopt
header = "### This file is automatically generated by update-environment"
header_note = """
#
# Please do not edit this file directly. If you want to change
# anything, please take a look at the files in /etc/env.d
# and read the Pardus initialization system documentation.
#
# Bu otomatik olarak oluşturulmuş bir dosyadır.
# Lütfen bu dosyayı elle değiştirmeyin. Değiştirmek istediğiniz
# şeyler varsa, /etc/env.d dizinindeki dosyalara ve Pardus
# açılış sistemi belgesine bakın.
#
"""
specials = (
"KDEDIRS",
"PATH",
"CLASSPATH",
"LDPATH",
"MANPATH",
"INFOPATH",
"ROOTPATH",
"CONFIG_PROTECT",
"CONFIG_PROTECT_MASK",
"PRELINK_PATH",
"PRELINK_PATH_MASK",
"PYTHONPATH",
"ADA_INCLUDE_PATH",
"ADA_OBJECTS_PATH",
)
def read_env_d(envdir):
dict = {}
paths = []
for name in os.listdir(envdir):
path = os.path.join(envdir, name)
# skip dirs (.svn, .cvs, etc)
if os.path.isdir(path):
continue
# skip backup and version control files
if name.endswith("~") or name.endswith(".bak") or name.endswith(",v"):
continue
# skip pisi's .oldconfig files
if name.endswith(".oldconfig"):
continue
paths.append(path)
paths.sort()
for path in paths:
for line in file(path):
if line == "" or line.startswith("#"):
continue
line = line.rstrip("\n")
if "=" in line:
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
if value.startswith('"') or value.startswith("'"):
value = value[1:-1]
# Merge for special variables, override for others
if key in specials:
if dict.has_key(key):
dict[key].extend(value.split(":"))
else:
dict[key] = value.split(":")
else:
dict[key] = value
return dict
def generate_profile_env(envdict, format='export %s="%s"\n'):
profile = ""
keys = envdict.keys()
keys.sort()
for key in keys:
if key != "LDPATH":
tmp = envdict[key]
if isinstance(tmp, list):
tmp = ":".join(tmp)
profile += format % (key, tmp)
return header + header_note + profile
def generate_ld_so_conf(envdict):
ldpaths = envdict["LDPATH"]
tmp = "\n".join(ldpaths)
return tmp + "\n"
def update_file(path, content):
if os.path.exists(path):
data = file(path).read()
if data == content:
return False
f = file(path, "w")
f.write(content)
f.close()
return True
def update_environment(prefix):
join = os.path.join
env = read_env_d(join(prefix, "etc/env.d"))
update_file(join(prefix, "etc/profile.env"), generate_profile_env(env))
update_file(join(prefix, "etc/csh.env"), generate_profile_env(env, 'setenv %s %s\n'))
if env.has_key("LDPATH"):
if update_file(join(prefix, "etc/ld.so.conf"), generate_ld_so_conf(env)):
subprocess.call(["/sbin/ldconfig", "-X", "-r", prefix])
-369
View File
@@ -1,369 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""Yet another Pisi module for fetching files from various sources. Of
course, this is not limited to just fetching source files. We fetch
all kinds of things: source tarballs, index files, packages, and God
knows what."""
# python standard library modules
import urllib2
import urllib
import ftplib
import os
import socket
import sys
from mimetypes import guess_type
from mimetools import Message
from base64 import encodestring
from shutil import move
from time import time
from time import gmtime
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# pisi modules
import pisi
import pisi.util as util
import pisi.context as ctx
from pisi.uri import URI
class FetchError(pisi.Error):
pass
class RangeError(pisi.Error):
pass
# helper functions
def fetch_url(url, destdir, progress=None, resume=True):
fetch = Fetcher(url, destdir)
if not resume:
fetch.resume = False
fetch.progress = progress
fetch.fetch()
if progress:
pass
class Fetcher:
"""Fetcher can fetch a file from various sources using various
protocols."""
def __init__(self, url, destdir, resume = True):
if not isinstance(url, URI):
url = URI(url)
self.resume = resume
self.scheme = url.scheme()
self.url = url
self.destdir = destdir
util.check_dir(self.destdir)
self.eta = '??:??:??'
self.percent = 0
self.rate = 0.0
self.progress = None
self.exist_size = 0
def fetch (self):
"""Return value: Fetched file's full path.."""
if not self.url.filename():
self.err(_('Filename error'))
if not os.access(self.destdir, os.W_OK):
self.err(_('Access denied to write to destination directory: "%s"') % (self.destdir))
archive_file = os.path.join(self.destdir, self.url.filename())
if os.path.exists(archive_file) and not os.access(archive_file, os.W_OK):
self.err(_('Access denied to destination file: "%s"') % (archive_file))
partial_file = archive_file + '.part'
if self.url.is_local_file():
self.fetchLocalFile(partial_file)
else:
self.fetchRemoteFile(partial_file)
if os.stat(partial_file).st_size == 0:
os.remove(partial_file)
self.err(_('A problem occured. Please check the archive address and/or permissions again.'))
move(partial_file, archive_file)
return archive_file
def _do_grab(self, fileURI, dest, total_size):
ctx.ui.notify(pisi.ui.downloading, url = fileURI)
bs, tt, = 1024, int(time())
s_time = time()
Tdiff = lambda: time() - s_time
downloaded_size = exist_size = self.exist_size
symbol = 'B/s'
st = time()
chunk = fileURI.read(bs)
downloaded_size += len(chunk)
if self.progress:
p = self.progress(total_size, exist_size)
self.percent = p.update(downloaded_size)
self.complete = False
while chunk:
dest.write(chunk)
chunk = fileURI.read(bs)
downloaded_size += len(chunk)
ct = time()
if int(tt) != int(ct):
self.rate = (downloaded_size - exist_size) / (ct - st)
if self.percent:
self.eta = '%02d:%02d:%02d' %\
tuple([i for i in gmtime((Tdiff() * (100 - self.percent)) / self.percent)[3:6]])
self.rate, symbol = util.human_readable_rate(self.rate)
tt = time()
if self.progress:
if p.update(downloaded_size):
self.percent = p.percent
if not self.complete:
ctx.ui.display_progress(filename = self.url.filename(),
percent = self.percent,
total_size = total_size,
downloaded_size = downloaded_size,
rate = self.rate,
eta = self.eta,
symbol = symbol)
if self.percent == 100: #FIXME: will be superseded by a
self.complete = True # working progress interface
dest.close()
def fetchLocalFile (self, archive_file):
url = self.url
if not os.access(url.path(), os.F_OK):
self.err(_('No such file or no permission to read'))
dest = open(archive_file, 'w')
total_size = os.path.getsize(url.path())
fileObj = open(url.path())
self._do_grab(fileObj, dest, total_size)
def fetchRemoteFile (self, archive_file):
from httplib import HTTPException
if os.path.exists(archive_file) and self.resume:
if self.scheme == 'http' or self.scheme == 'https' or self.scheme == 'ftp':
self.exist_size = os.path.getsize(archive_file)
dest = open(archive_file, 'ab')
else:
dest = open(archive_file, 'wb')
uri = self.url.get_uri()
flag = 1
try:
try:
try:
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
except RangeError:
ctx.ui.info(_('Requested range not satisfiable, starting again.'))
dest = open(archive_file, 'wb')
self.exist_size = 0
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
headers = fileObj.info()
flag = 0
except ValueError, e:
self.err(_('Cannot fetch %s; value error: %s') % (uri, e))
except urllib2.HTTPError, e:
self.err(_('Cannot fetch %s; %s') % (uri, e))
except urllib2.URLError, e:
self.err(_('Cannot fetch %s; %s') % (uri, e[-1][-1]))
except OSError, e:
self.err(_('Cannot fetch %s; %s') % (uri, e))
except HTTPException, e:
self.err(_('Cannot fetch %s; (%s): %s') % (uri, e.__class__.__name__, e))
finally:
if flag:
if os.stat(archive_file).st_size == 0:
os.remove(archive_file)
try:
total_size = int(headers['Content-Length']) + self.exist_size
except:
total_size = 0
self._do_grab(fileObj, dest, total_size)
def formatRequest(self, request):
if self.url.auth_info():
enc = encodestring('%s:%s' % self.url.auth_info())
request.add_header('Authorization', 'Basic %s' % enc)
range_handlers = {
'http' : HTTPRangeHandler,
'https': HTTPRangeHandler,
'ftp' : FTPRangeHandler
}
if self.exist_size and range_handlers.has_key(self.scheme):
opener = urllib2.build_opener(range_handlers.get(self.scheme)())
urllib2.install_opener(opener)
request.add_header('Range', 'bytes=%d-' % self.exist_size)
proxy_handler = None
if ctx.config.values.general.http_proxy and self.url.scheme() == "http":
http_proxy = ctx.config.values.general.http_proxy
proxy_handler = urllib2.ProxyHandler({URI(http_proxy).scheme(): http_proxy})
elif ctx.config.values.general.https_proxy and self.url.scheme() == "https":
https_proxy = ctx.config.values.general.https_proxy
proxy_handler = urllib2.ProxyHandler({URI(https_proxy): https_proxy})
elif ctx.config.values.general.ftp_proxy and self.url.scheme() == "ftp":
ftp_proxy = ctx.config.values.general.ftp_proxy
proxy_handler = urllib2.ProxyHandler({URI(http_proxy): ftp_proxy})
if proxy_handler:
ctx.ui.info(_("Proxy configuration has been found for '%s' protocol") % self.url.scheme())
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)
return request
def err (self, error):
raise FetchError(error)
class HTTPRangeHandler(urllib2.BaseHandler):
"""
to override the urllib2 error: 'Error 206: Partial Content'
this reponse from the HTTP server is already what we expected to get.
Don't give up, resume downloading..
"""
def http_error_206(self, request, fp, errcode, msg, headers):
return urllib.addinfourl(fp, headers, request.get_full_url())
def http_error_416(self, request, fp, errcode, msg, headers):
# HTTP 1.1's 'Range Not Satisfiable' error..
raise RangeError
class FTPRangeHandler(urllib2.FTPHandler):
"""
FTP Range support..
"""
def ftp_open(self, req):
host = req.get_host()
host, port = urllib.splitport(host)
if port is None:
port = ftplib.FTP_PORT
try:
host = socket.gethostbyname(host)
except socket.error, msg:
raise FetchError(msg)
path, attrs = urllib.splitattr(req.get_selector())
dirs = path.split('/')
dirs = map(urllib.unquote, dirs)
dirs, file = dirs[:-1], dirs[-1]
if dirs and not dirs[0]:
dirs = dirs[1:]
try:
fw = self.connect_ftp('', '', host, port, dirs)
type = file and 'I' or 'D'
for attr in attrs:
attr, value = urllib.splitattr(attr)
if attr.lower() == 'type' and \
value in ('a', 'A', 'i', 'I', 'd', 'D'):
type = value.upper()
rawr = req.headers.get('Range', None)
if rawr:
rest = int(rawr.split("=")[1].rstrip("-"))
else:
rest = 0
fp, retrlen = fw.retrfile(file, type, rest)
fb, lb = rest, retrlen
if retrlen is None or retrlen == 0:
raise RangeError
retrlen = lb - fb
if retrlen < 0:
# beginning of range is larger than file
raise RangeError
headers = ''
mtype = guess_type(req.get_full_url())[0]
if mtype:
headers += 'Content-Type: %s\n' % mtype
if retrlen is not None and retrlen >= 0:
headers += 'Content-Length: %d\n' % retrlen
try:
from cStringIO import StringIO
except ImportError, msg:
from StringIO import StringIO
return urllib.addinfourl(fp, Message(StringIO(headers)), req.get_full_url())
except ftplib.all_errors, msg:
raise IOError, (_('ftp error'), msg), sys.exc_info()[2]
def connect_ftp(self, user, passwd, host, port, dirs):
fw = ftpwrapper('', '', host, port, dirs)
return fw
class ftpwrapper(urllib.ftpwrapper):
def retrfile(self, file, type, rest=None):
self.endtransfer()
if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
else: cmd = 'TYPE ' + type; isdir = 0
try:
self.ftp.voidcmd(cmd)
except ftplib.all_errors:
self.init()
self.ftp.voidcmd(cmd)
conn = None
if file and not isdir:
try:
self.ftp.nlst(file)
except ftplib.error_perm, reason:
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
# Restore the transfer mode!
self.ftp.voidcmd(cmd)
try:
cmd = 'RETR ' + file
conn = self.ftp.ntransfercmd(cmd, rest)
except ftplib.error_perm, reason:
if str(reason)[:3] == '501':
# workaround for REST not suported error
fp, retrlen = self.retrfile(file, type)
fp = RangeableFileObject(fp, (rest,''))
return (fp, retrlen)
elif str(reason)[:3] != '550':
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
if not conn:
self.ftp.voidcmd('TYPE A')
if file: cmd = 'LIST ' + file
else: cmd = 'LIST'
conn = self.ftp.ntransfercmd(cmd)
self.busy = 1
return (urllib.addclosehook(conn[0].makefile('rb'),
self.endtransfer), conn[1])
-248
View File
@@ -1,248 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
"""
generic file abstraction that allows us to use URIs for everything
we support only the simple read case ATM
we are just encapsulating a common pattern in our program, nothing big.
like all pisi classes, it has been programmed in a non-restricting way
"""
import os
import types
import bz2
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
from pisi.uri import URI
from pisi.util import join_path as join
from pisi.fetcher import fetch_url
import pisi.context as ctx
class AlreadyHaveException(pisi.Exception):
def __init__(self, url, localfile):
pisi.Exception.__init__(self, "URL %s already downloaded as %s" % (url, localfile))
self.url = url
self.localfile = localfile
class NoSignatureFound(pisi.Exception):
def __init__(self, url):
pisi.Exception.__init__(self, "No signature found for %s" % url)
self.url = url
class Error(pisi.Error):
pass
class InvalidSignature(pisi.Error):
def __init__(self, url):
pisi.Exception.__init__(self, " invalid for %s" % url)
self.url = url
class File:
(read, write) = range(2) # modes
(bz2, lzma) = range(2) # compress enums
(detached, whatelse) = range(2)
@staticmethod
def make_uri(uri):
"handle URI arg"
if type(uri) == types.StringType or type(uri) == types.UnicodeType:
uri = URI(uri)
elif not isinstance(uri, URI):
raise Error(_("uri must have type either URI or string"))
return uri
@staticmethod
def decompress(localfile, compress):
if compress == File.bz2:
if not localfile.endswith(".bz2"):
raise Error(_("bz2 compressed filename must end with '.bz2'"))
open(localfile[:-4], "w").write(bz2.BZ2File(localfile).read())
localfile = localfile[:-4]
elif compress == File.lzma:
raise Error(_("lzma compression not supported yet"))
return localfile
@staticmethod
def download(uri, transfer_dir = "/tmp",
sha1sum = False, compress = None, sign = None):
assert isinstance(uri, URI)
if sha1sum:
sha1filename = File.download(URI(uri.get_uri() + '.sha1sum'), transfer_dir)
sha1f = file(sha1filename)
newsha1 = sha1f.readlines()[0]
if uri.is_remote_file():
localfile = join(transfer_dir, uri.filename())
# TODO: code to use old .sha1sum file, is this a necessary optimization?
#oldsha1fn = localfile + '.sha1sum'
#if os.exists(oldsha1fn):
#oldsha1 = file(oldsha1fn).readlines()[0]
if sha1sum and os.path.exists(localfile):
oldsha1 = pisi.util.sha1_file(localfile)
if (newsha1 == oldsha1):
# early terminate, we already got it ;)
raise AlreadyHaveException(uri, localfile)
ctx.ui.info(_("Fetching %s") % uri.get_uri())
fetch_url(uri, transfer_dir)
else:
localfile = uri.get_uri() #TODO: use a special function here?
if not os.path.exists(localfile):
raise IOError(_("File \'%s\' not found.") % localfile)
if sha1sum:
if (pisi.util.sha1_file(localfile) != newsha1):
raise Error(_("File integrity of %s compromised.") % uri)
localfile = File.decompress(localfile, compress)
return localfile
def __init__(self, uri, mode, transfer_dir = "/tmp",
sha1sum = False, compress = None, sign = None):
"it is pointless to open a file without a URI and a mode"
self.transfer_dir = transfer_dir
self.sha1sum = sha1sum
self.compress = compress
self.sign = sign
uri = File.make_uri(uri)
if mode==File.read or mode==File.write:
self.mode = mode
else:
raise Error(_("File mode must be either File.read or File.write"))
if uri.is_remote_file():
if self.mode == File.read:
localfile = File.download(uri, transfer_dir, sha1sum, compress, sign)
else:
raise Error(_("Remote write not implemented"))
else:
localfile = uri.get_uri()
if self.mode == File.read:
localfile = File.decompress(localfile, self.compress)
if self.mode == File.read:
access = 'r'
else:
access = 'w'
self.__file__ = file(localfile, access)
self.localfile = localfile
def local_file(self):
"returns the underlying file object"
return self.__file__
def close(self, delete_transfer = False):
"this method must be called at the end of operation"
self.__file__.close()
if self.mode == File.write:
compressed_file = None
if self.compress == File.bz2:
compressed_file = self.localfile + ".bz2"
bz2.BZ2File(compressed_file, "w").write(open(self.localfile, "r").read())
elif self.compress == File.lzma:
raise Error(_("lzma compression not supported yet"))
if self.sha1sum:
sha1 = pisi.util.sha1_file(self.localfile)
cs = file(self.localfile + '.sha1sum', 'w')
cs.write(sha1)
cs.close()
if compressed_file:
sha1 = pisi.util.sha1_file(compressed_file)
cs = file(compressed_file + '.sha1sum', 'w')
cs.write(sha1)
cs.close()
if self.sign==File.detached:
if pisi.util.run_batch('gpg --detach-sig ' + self.localfile)[0]:
raise Error(_("ERROR: gpg --detach-sig %s failed") % self.localfile)
if compressed_file:
if pisi.util.run_batch('gpg --detach-sig ' + compressed_file)[0]:
raise Error(_("ERROR: gpg --detach-sig %s failed") % compressed_file)
@staticmethod
def check_signature(uri, transfer_dir, sign=detached):
if sign==File.detached:
try:
sigfilename = File.download(URI(uri + '.sig'), transfer_dir)
except:
raise NoSignatureFound(uri)
if os.system('gpg --verify ' + sigfilename) != 0:
raise InvalidSignature(uri)
# everything is all right here
def flush(self):
self.__file__.flush()
def fileno(self):
return self.__file__.fileno()
def isatty(self):
return self.__file__.isatty()
def next(self):
return self.__file__.next()
def read(self, size = None):
if size:
return self.__file__.read(size)
else:
return self.__file__.read()
def readline(self, size = None):
if size:
return self.__file__.readline(size)
else:
return self.__file__.readline()
def readlines(self, size = None):
if size:
return self.__file__.readlines(size)
else:
return self.__file__.readlines()
def xreadlines(self):
return self.__file__.xreadlines()
def seek(self, offset, whence=0):
self.__file__.seek(offset, whence)
def tell(self):
return self.__file__.tell()
def truncate(self):
self.__file__.truncate()
def write(self, str):
self.__file__.write(str)
def writelines(self, sequence):
self.__file__.writelines(sequence)
-95
View File
@@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
'''Files module provides access to files.xml. files.xml is generated
during the build process of a package and used in installation.'''
import pisi.pxml.autoxml as autoxml
from pisi.util import Checks
import pisi.lockeddbshelve as shelve
class FileInfo:
"""File holds the information for a File node/tag in files.xml"""
__metaclass__ = autoxml.autoxml
t_Path = [ autoxml.String, autoxml.mandatory ]
t_Type = [ autoxml.String, autoxml.mandatory ]
t_Size = [ autoxml.Long, autoxml.optional ]
t_Hash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
t_Permanent = [ autoxml.String, autoxml.optional ]
def __str__(self):
s = "%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type,
self.size, self.hash)
return s
class Files(autoxml.XmlFile):
__metaclass__ = autoxml.autoxml
tag = "Files"
t_List = [ [FileInfo], autoxml.optional, "File"]
def append(self, fileinfo):
self.list.append(fileinfo)
class FilesDB(shelve.LockedDBShelf):
def __init__(self):
shelve.LockedDBShelf.__init__(self, 'files')
def add_files(self, pkg_name, files, txn = None):
def proc(txn):
for x in files.list:
path = x.path
del x.path # don't store redundant attribute in db
self.put(path, (pkg_name, x), txn)
x.path = path # store it back in
self.txn_proc(proc, txn)
def remove_files(self, files, txn = None):
def proc(txn):
for x in files.list:
if self.has_key(x.path):
self.delete(x.path, txn)
self.txn_proc(proc, txn)
def has_file(self, path, txn = None):
return self.has_key(str(path), txn)
def get_file(self, path, txn = None):
path = str(path)
def proc(txn):
if not self.has_key(path, txn):
return None
else:
(name, fileinfo) = self.get(path, txn)
fileinfo.path = path
return (name, fileinfo)
return self.txn_proc(proc, txn)
def match_files(self, glob):
# NB: avoid using, this reads the entire db
import fnmatch
glob = str(glob)
infos = []
for key in self.keys():
if fnmatch.fnmatch(key, glob):
infos.append(self[key])
return infos
-169
View File
@@ -1,169 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
# the most simple minded digraph class ever
import sys
import pisi
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# not an error!
class CycleException(pisi.Exception):
def __init__(self, cycle):
self.cycle = cycle
def __str__(self):
return _('Encountered cycle %s') % self.cycle
class Digraph(object):
def __init__(self):
self.__v = set()
self.__adj = {}
self.__vdata = {}
self.__edata = {}
def vertices(self):
"return set of vertex descriptors"
return self.__v
def edges(self):
"return a list of edge descriptors"
list = []
for u in self.__v:
for v in self.__u:
list.append( (u,v) )
return list
def from_list(self, el):
"convert a list of edges (u,v) to graph"
for (u,v) in el:
self.add_edge(u,v)
def add_vertex(self, u, data = None):
"add vertex u, optionally with data"
assert not u in self.__v
self.__v.add(u)
self.__adj[u] = set()
if data:
self.__vdata[u] = data
self.__edata[u] = {}
def add_edge(self, u, v, edata = None, udata = None, vdata = None):
"add edge u -> v"
if not u in self.__v:
self.add_vertex(u, udata)
if not v in self.__v:
self.add_vertex(v, vdata)
self.__adj[u].add(v)
if edata != None:
self.__edata[u][v] = edata
def add_biedge(self, u, v, edata = None):
self.add_edge(u, v, edata)
self.add_edge(v, u, edata)
def set_vertex_data(self, u, data):
self.__vdata[u] = data
def vertex_data(self, u):
return self.__vdata[u]
def edge_data(self, u, v):
return self.__edata[u][v]
def has_vertex(self, u):
return u in self.__v
def has_edge(self, u,v):
if u in self.__v:
return v in self.__adj[u]
else:
return False
def adj(self, u):
return self.__adj[u]
def dfs(self, finish_hook = None):
self.color = {}
self.p = {}
self.d = {}
self.f = {}
for u in self.__v:
self.color[u] = 'w' # mark white (unexplored)
self.p[u] = None
self.time = 0
for u in self.__v:
if self.color[u] == 'w':
self.dfs_visit(u, finish_hook)
def dfs_visit(self, u, finish_hook):
self.color[u] = 'g' # mark green (discovered)
self.d[u] = self.time = self.time + 1
for v in self.adj(u):
if self.color[v] == 'w': # explore unexplored vertices
self.p[v] = u
self.dfs_visit(v, finish_hook)
elif self.color[v] == 'g': # cycle detected
cycle = [u]
while self.p[u]:
u = self.p[u]
cycle.append(u)
cycle.reverse()
raise CycleException(cycle)
self.color[u] = 'b' # mark black (completed)
if finish_hook:
finish_hook(u)
self.f[u] = self.time = self.time + 1
def cycle_free(self):
try:
self.dfs()
return True
except CycleException:
return False
def topological_sort(self):
list = []
self.dfs(lambda u: list.append(u))
list.reverse()
return list
def id_str(self, u):
s = str(u)
return s.replace('-', '_')
def write_graphviz(self, f):
f.write('digraph G {\n')
for u in self.vertices():
f.write(self.id_str(u))
self.write_graphviz_vlabel(f, u)
f.write(';\n')
f.write('\n')
for u in self.vertices():
for v in self.adj(u):
f.write( self.id_str(u) + ' -> ' + self.id_str(v))
self.write_graphviz_elabel(f, u, v)
f.write(';\n')
f.write('\n')
f.write('}\n')
def write_graphviz_vlabel(self, f, u):
pass
def write_graphviz_elabel(self, f, u, v):
pass
-172
View File
@@ -1,172 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
"""PISI source/package index"""
import os
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.specfile as specfile
import pisi.metadata as metadata
import pisi.packagedb as packagedb
import pisi.sourcedb as sourcedb
import pisi.util as util
from pisi.package import Package
from pisi.pxml.xmlfile import XmlFile
from pisi.file import File
import pisi.pxml.autoxml as autoxml
from pisi.uri import URI
import pisi.component as component
import pisi.specfile as specfile
class Error(pisi.Error):
pass
class Index(XmlFile):
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Distribution = [ component.Distribution, autoxml.optional ]
t_Specs = [ [specfile.SpecFile], autoxml.optional, "SpecFile"]
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
#t_Metadatas = [ [metadata.MetaData], autoxml.optional, "MetaData"]
t_Components = [ [component.Component], autoxml.optional, "Component"]
def name():
return self.distribution.name + self.distribution.repositoryname
# read index for a given repo, force means download even if remote not updated
def read_uri(self, filename, repo = None, force = False):
"""Read PSPEC file"""
if repo:
tmpdir = os.path.join(ctx.config.index_dir(), repo)
else:
tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
pisi.util.clean_dir(tmpdir)
pisi.util.check_dir(tmpdir)
if not force:
urlfile = file(pisi.util.join_path(tmpdir, 'uri'), 'w')
urlfile.write(filename) # uri
# FIXME: This is a mess, we have a compress flag and we have to
# use it with filename extention. Because File.decompress also
# checks the extension. We really have to go all over the code
# and simplify it. -- baris
if filename.endswith(".bz2"):
self.read(filename, tmpDir=tmpdir, sha1sum=not force,
compress=File.bz2, sign=File.detached)
else:
self.read(filename, tmpDir=tmpdir, sha1sum=not force,
compress=None, sign=File.detached)
if not repo:
repo = self.distribution.name()
def check_signature(self, filename, repo):
tmpdir = os.path.join(ctx.config.index_dir(), repo)
File.check_signature(filename, tmpdir)
def index(self, repo_uri, skip_sources=False, non_recursive=False):
self.repo_dir = repo_uri
for root, dirs, files in os.walk(repo_uri):
for fn in files:
if fn.endswith(ctx.const.package_suffix):
ctx.ui.info(_('Adding %s to package index') % fn)
self.add_package(os.path.join(root, fn), repo_uri)
if fn == 'component.xml':
ctx.ui.info(_('Adding %s to component index') % fn)
self.add_component(os.path.join(root, fn))
if fn == 'pspec.xml' and not skip_sources:
self.add_spec(os.path.join(root, fn), repo_uri)
if fn == 'distribution.xml':
self.add_distro(os.path.join(root, fn))
if non_recursive:
del dirs[0:]
def update_db(self, repo, txn = None):
for comp in self.components:
ctx.componentdb.update_component(comp, repo, txn)
ctx.packagedb.remove_repo(repo, txn=txn)
for pkg in self.packages:
ctx.packagedb.add_package(pkg, repo, txn=txn)
ctx.sourcedb.remove_repo(repo, txn=txn)
for sf in self.specs:
ctx.sourcedb.add_spec(sf, repo, txn=txn)
def add_package(self, path, repo_uri):
package = Package(path, 'r')
# extract control files
util.clean_dir(ctx.config.install_dir())
package.extract_PISI_files(ctx.config.install_dir())
md = metadata.MetaData()
md.read(os.path.join(ctx.config.install_dir(), ctx.const.metadata_xml))
md.package.packageSize = os.path.getsize(path)
if ctx.config.options and ctx.config.options.absolute_uris:
# FIXME: the name "absolute_uris" does not seem to fit below :/
md.package.packageURI = os.path.realpath(path)
else: # create relative path by default
# TODO: in the future well do all of this with purl/pfile/&helpers
# really? heheh -- future exa
md.package.packageURI = util.removepathprefix(repo_uri, path)
# check package semantics
errs = md.errors()
if md.errors():
ctx.ui.error(_('Package %s: metadata corrupt, skipping...') % md.package.name)
ctx.ui.error(unicode(Error(*errs)))
else:
self.packages.append(md.package)
def add_component(self, path):
comp = component.Component()
#try:
comp.read(path)
self.components.append(comp)
#except:
# raise Error(_('Component in %s is corrupt') % path)
#ctx.ui.error(str(Error(*errs)))
def add_distro(self, path):
distro = component.Distribution()
#try:
distro.read(path)
self.distribution = distro
#except:
# raise Error(_('Distribution in %s is corrupt') % path)
#ctx.ui.error(str(Error(*errs)))
def add_spec(self, path, repo_uri):
ctx.ui.info(_('Adding %s to source index') % path)
sf = specfile.SpecFile()
sf.read(path)
errs = sf.errors()
if sf.errors():
ctx.ui.error(_('SpecFile in %s is corrupt, skipping...') % path)
ctx.ui.error(str(Error(*errs)))
else:
if ctx.config.options and ctx.config.options.absolute_uris:
sf.source.sourceURI = os.path.realpath(path)
else: # create relative path by default
sf.source.sourceURI = util.removepathprefix(repo_uri, path)
self.specs.append(sf)
-216
View File
@@ -1,216 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
#
# installation database
#
# Author: Eray Ozkural <eray@pardus.org.tr>
# System
import os
import fcntl
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi
import pisi
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
from pisi.files import Files
import pisi.util as util
from pisi.util import join_path as join
class InstallDBError(pisi.Error):
pass
class InstallInfo:
# some data is replicated from packagedb
# we store as an object, hey, we can waste O(1) space.
# this is also easier to modify in the future, without
# requiring database upgrades! wow!
def __init__(self, state, version, release, build, distribution, time):
self.state = state
self.version = version
self.release = release
self.build = build
self.distribution = distribution
self.time = time
def one_liner(self):
import time
time_str = time.strftime("%d %b %Y %H:%M", self.time)
s = '%2s|%10s|%6s|%6s|%8s|%12s' % (self.state, self.version, self.release,
self.build, self.distribution,
time_str)
return s
state_map = { 'i': _('installed'), 'ip':_('installed-pending'),
'r':_('removed'), 'p': _('purged') }
def __str__(self):
s = _("State: %s\nVersion: %s, Release: %s, Build: %s\n") % \
(InstallInfo.state_map[self.state], self.version,
self.release, self.build)
import time
time_str = time.strftime("%d %b %Y %H:%M", self.time)
s += _('Distribution: %s, Install Time: %s\n') % (self.distribution,
time_str)
return s
class InstallDB:
def __init__(self):
self.d = shelve.LockedDBShelf('install')
self.dp = shelve.LockedDBShelf('configpending')
self.files_dir = join(ctx.config.db_dir(), 'files')
def close(self):
self.d.close()
self.dp.close()
def files_name(self, pkg, version, release):
pkg_dir = self.pkg_dir(pkg, version, release)
return join(pkg_dir, ctx.const.files_xml)
def files(self, pkg):
pkg = str(pkg)
pkginfo = self.d[pkg]
files = Files()
files.read(self.files_name(pkg,pkginfo.version,pkginfo.release))
return files
def pkg_dir(self, pkg, version, release):
return join(ctx.config.lib_dir(), 'package',
pkg + '-' + version + '-' + release)
def is_recorded(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
return self.d.has_key(pkg)
return self.d.txn_proc(proc, txn)
def is_installed(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
if self.is_recorded(pkg, txn):
info = self.d.get(pkg, txn)
return info.state=='i' or info.state=='ip'
else:
return False
return self.d.txn_proc(proc, txn)
def list_installed(self, txn = None):
def proc(txn):
list = []
for (pkg, info) in self.d.items(txn):
if info.state=='i' or info.state=='ip':
list.append(pkg)
return list
return self.d.txn_proc(proc, txn)
def list_pending(self):
# warning: reads the entire db
dict = {}
for (pkg, x) in self.dp.items():
pkginfo = self.d[pkg]
dict[pkg] = pkginfo
return dict
def get_info(self, pkg):
pkg = str(pkg)
return self.d[pkg]
def get_version(self, pkg):
pkg = str(pkg)
info = self.d[pkg]
return (info.version, info.release, info.build)
def is_removed(self, pkg):
pkg = str(pkg)
if self.is_recorded(pkg):
info = self.d[pkg]
return info.state=='r'
else:
return False
def install(self, pkg, version, release, build, distro = "",
config_later = False, rebuild=False, txn = None):
"""install package with specific version, release, build"""
pkg = str(pkg)
def proc(txn):
if self.is_installed(pkg, txn):
raise InstallDBError(_("Already installed"))
if config_later:
state = 'ip'
self.dp.put(pkg, True, txn)
else:
state = 'i'
# FIXME: it might be more appropriate to pass date
# as an argument, or installation data afterwards
# to do this -- exa
if not rebuild:
import time
ctime = time.localtime()
else:
files_xml = self.files_name(pkg, version, release)
ctime = util.creation_time(files_xml)
self.d.put(pkg, InstallInfo(state, version, release, build, distro, ctime), txn)
self.d.txn_proc(proc,txn)
def clear_pending(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
info = self.d.get(pkg, txn)
if self.is_installed(pkg, txn):
assert info.state == 'ip'
info.state = 'i'
self.d.put(pkg, info, txn)
self.dp.delete(pkg, txn)
self.d.txn_proc(proc,txn)
def remove(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
info = self.d.get(pkg, txn)
info.state = 'r'
self.d.put(pkg, info, txn)
self.d.txn_proc(proc, txn)
def purge(self, pkg, txn = None):
pkg = str(pkg)
def proc(txn):
if self.d.has_key(pkg, txn):
self.d.delete(pkg, txn)
self.d.txn_proc(proc, txn)
db = None
def init():
global db
if db:
return db
db = InstallDB()
return db
def finalize():
global db
if db:
db.close()
db = None
-221
View File
@@ -1,221 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
import os
import fcntl
import types
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.util as util
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
installed, thirdparty, repos, all = range(1, 5)
"""installed and thirdparty are special databases to keep track
of already installed stuff and third party stuff not in any real repository.
repos means search in repositories only, and all means search in
repositories and special databases (called tracking databases)
"""
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
pass
class ItemByRepoDB(object):
def __init__(self, name):
self.d = shelve.LockedDBShelf(name)
#self.dbyrepo = shelve.LockedDBShelf(name + '-byrepo')
def close(self):
self.d.close()
def clear(self, txn = None):
self.d.clear(txn=txn)
def txn_proc(self, proc, txn):
return self.d.txn_proc(proc, txn)
def items(self):
return self.d.items()
@staticmethod
def not_just_tracking(data):
keys = data.keys()
if len(keys)==1:
if 'trdparty' in keys or 'inst' in keys:
return False
elif len(keys)==2:
if 'trdparty' in keys and 'inst' in keys:
return False
return True
#below is a slower way
#for x in data.keys():
# if x.startsWith('repo-'):
# return True
#return False
def list_if(self, pred):
return [ k for k,data in self.d.items() if pred(k, data)]
def list(self, repo = None):
if repo == None:
repo = repos
if repo not in [repos, all]:
return [ k for k,data in self.d.items() if data.has_key(self.repo_str(repo))]
else:
if repo == all:
return [ pkg for pkg in self.d.keys() ]
else:
return self.list_if( lambda k,data: ItemByRepoDB.not_just_tracking(data) )
# TODO: carry this to repodb, really :/
def order(self, repo = None):
if repo == None:
repo = repos
assert repo in [all, repos]
import pisi.repodb
order = [ 'repo-' + x for x in ctx.repodb.list() ]
if repo == all:
order += ['trdparty', 'inst']
return order
# def list_repo(self, repo):
# return self.dbyrepo[repo]
def repo_str(self, repo):
if repo==thirdparty:
repo='trdparty'
elif repo==installed:
repo='inst'
else:
assert type(repo) == type("")
repo='repo-'+repo
return repo
def str_repo(self, str):
if str.startswith('repo-'):
return str[5:]
elif str=='trdparty':
return thirdparty
elif str=='inst':
return installed
else:
raise Error(_('Invalid repository string'))
def has_key(self, name, repo = None, txn = None):
name = str(name)
if repo == None:
repo = repos
haskey = self.d.has_key(name, txn)
if repo == all:
return haskey
elif repo == repos:
data = self.d.get(name, txn)
return haskey and ItemByRepoDB.not_just_tracking(data)
else:
repostr = self.repo_str(repo)
return haskey and self.d.get(name, txn).has_key(repostr)
def get_item_repo(self, name, repo = None, txn = None):
name = str(name)
if repo == None:
repo = repos
def proc(txn):
if not self.d.has_key(name, txn=txn):
raise NotfoundError(_('Key %s not found') % name)
s = self.d.get(name, txn=txn)
if repo in [repos, all]:
for repostr in self.order(repo):
if s.has_key(repostr):
return (s[repostr], self.str_repo(repostr))
else:
repostr = self.repo_str(repo)
if s.has_key(repostr):
return (s[repostr], repo)
raise NotfoundError(_('Key %s in repo %s not found') % (name, repo))
#return None
return self.d.txn_proc(proc, txn)
def get_item(self, name, repo = None, txn = None):
if repo == None:
repo = repos
x = self.get_item_repo(name, repo, txn)
if x:
item, repo = x
# discard repo, not always needed
return item
else:
return None
def which_repo(self, name, txn = None):
x = self.get_item_repo(name, txn=txn)
if x:
item, repo = x
return repo
else:
return None
def add_item(self, name, obj, repo, txn = None):
assert not repo in [all, repos]
repostr = self.repo_str(repo)
def proc(txn):
if not self.d.has_key(name):
s = dict()
else:
s = self.d.get(name, txn)
s[ repostr ] = obj
self.d.put(name, s, txn)
self.d.txn_proc(proc, txn)
def remove_item_repo(self, name, repo, txn = None):
assert not repo in [all, repos]
name = str(name)
def p(txn):
s = self.d.get(name, txn)
repostr = self.repo_str(repo)
if s.has_key(repostr):
del s[repostr]
if (len(s)==0):
self.d.delete(name, txn)
else:
self.d.put(name, s, txn)
self.d.txn_proc(p, txn)
def remove_item_only(self, name, txn = None):
def p(txn):
repo = self.which_repo(name, txn=txn)
self.remove_item_repo(name, repo, txn=txn)
self.d.txn_proc(p, txn)
def remove_item(self, name, repo=None, txn=None):
if repo == None:
repo = repos
if not repo in [all, repos]:
self.remove_item_repo(name, repo,txn=txn)
else:
self.remove_item_only(name,txn=txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
for key in self.d.keys():
self.remove_item_repo(key, repo, txn=txn)
self.d.txn_proc(proc, txn)
-195
View File
@@ -1,195 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
import os
import fcntl
import types
import cPickle
import bsddb3.db as db
import bsddb3.dbobj as dbobj
#import bsddb3.dbshelve as shelve
import pisi.dbshelve as shelve
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.util import join_path
from pisi.version import Version
class Error(pisi.Error):
pass
# check database version
# if write is given it knows it has write access
# if force is given it updates the specified db version
def check_dbversion(versionfile, ver, write=False, update=False):
verfn = join_path(pisi.context.config.db_dir(), versionfile)
firsttime = False
if os.path.exists(verfn):
verfile = file(verfn, 'r')
ls = verfile.readlines()
currver = Version(ls[0])
dbver = Version(ver)
if currver < dbver:
if not update:
raise Error(_('Database version for %s insufficient. Please run rebuild-db command.') % versionfile)
else:
pass # continue to update, then
elif currver > dbver:
raise Error(_('Database version for %s greater than PiSi version. You need a newer PiSi.') % versionfile)
elif not update:
return True # db version is OK
else:
firsttime = True
if write and (update or firsttime):
if os.access(pisi.context.config.db_dir(), os.W_OK):
ctx.ui.warning(_('Writing current database version for %s') % versionfile)
verfile = file(verfn, 'w')
verfile.write(ver)
verfile.close()
else:
raise Error(_('Cannot attain write access to database environment'))
else:
raise Error(_('Database version %s not present.') % versionfile)
def lock_dbenv():
ctx.dbenv_lock = file(join_path(pisi.context.config.db_dir(), 'dbenv.lock'), 'w')
try:
fcntl.flock(ctx.dbenv_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
raise Error(_("Another instance of PISI is running. Only one instance is allowed to modify the PISI database at a time."))
# write: write access to database environment
# writeversion: would you like to be able
def init_dbenv(write=False, writeversion=False):
if os.access(pisi.context.config.db_dir(), os.R_OK):
# try to read version
check_dbversion('dbversion', pisi.__dbversion__, write=write, update=writeversion)
check_dbversion('filesdbversion', pisi.__filesdbversion__, write=write, update=writeversion)
else:
raise Error(_('Cannot attain read access to database environment'))
if write and os.access(pisi.context.config.db_dir(), os.W_OK):
lock_dbenv()
ctx.dbenv = dbobj.DBEnv()
flags = (db.DB_INIT_MPOOL | # cache
db.DB_INIT_TXN | # transaction subsystem
db.DB_INIT_LOG | # logging subsystem
db.DB_RECOVER | # run normal recovery
db.DB_CREATE) # allow db to create files
ctx.dbenv.set_cachesize(0, 4*1024*1024)
ctx.dbenv.open(pisi.context.config.db_dir(), flags)
ctx.dbenv.set_flags(db.DB_LOG_AUTOREMOVE, 1) # clear inactive logs automatically
else:
# Warn if not on purpose
if write:
ctx.ui.warning(_("Opening PISI database in read-only mode. Operations that require write access will fail."))
ctx.dbenv = None
#def open(filename, flags='r', mode = 0644, filetype = db.DB_BTREE):
# db = LockedDBShelf(None, mode, filetype, None, True)
# db.open(filename, filename, filetype, flags, mode)
# return db
class LockedDBShelf(shelve.DBShelf):
"""A simple wrapper to implement locking for bsddb's dbshelf"""
# def decode(self, data):
# try:
# return cPickle.loads(pylzma.decompress(data))
# except:
# raise shelve.CodingError()
# def encode(self, obj):
# return pylzma.compress(cPickle.dumps(obj, 1), algorithm=0)
def __init__(self, dbname, mode=0644,
filetype=db.DB_BTREE, dbenv = None):
if dbenv == None:
dbenv = ctx.dbenv
shelve.DBShelf.__init__(self, dbenv)
filename = join_path(pisi.context.config.db_dir(), dbname + '.bdb')
if dbenv and os.access(os.path.dirname(filename), os.W_OK):
flags = 'w'
elif os.access(filename, os.R_OK):
flags = 'r'
else:
raise Error(_('Cannot attain read or write access to database %s') % dbname)
self.open(filename, dbname, filetype, flags, mode)
def destroy(self):
os.unlink(self.filename)
#self.close()
#self.db.remove(self.filename)
def __del__(self):
# superclass does something funky, we don't need that
pass
def open(self, filename, dbname, filetype, flags=db.DB_CREATE, mode=0644):
self.filename = filename
self.closed = False
if type(flags) == type(''):
sflag = flags
if sflag == 'r':
flags = db.DB_RDONLY
elif sflag == 'rw':
flags = 0
elif sflag == 'w':
flags = db.DB_CREATE
elif sflag == 'c':
flags = db.DB_CREATE
elif sflag == 'n':
flags = db.DB_TRUNCATE | db.DB_CREATE
else:
raise Error, _("Flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags")
self.flags = flags
if self.flags & db.DB_RDONLY == 0:
flags |= db.DB_AUTO_COMMIT # use txn subsystem in write mode
self.lock()
filename = os.path.realpath(filename) # we give absolute path due to dbenv
#print 'opening', filename, filetype, flags, mode
return self.db.open(filename, None, filetype, flags, mode)
def lock(self):
self.lockfile = file(self.filename + '.lock', 'w')
try:
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
raise Error(_("Another instance of PISI is running. Only one instance is allowed to modify the PISI database at a time."))
def close(self):
if self.closed:
return
self.db.close()
if self.flags & db.DB_RDONLY == 0:
self.unlock()
self.closed = True
def unlock(self):
self.lockfile.close()
os.unlink(self.filename + '.lock')
@staticmethod
def encodekey(key):
'''utility method for dbs that must store unicodes in keys'''
if type(key)==types.UnicodeType:
return key.encode('utf-8')
elif type(key)==types.StringType:
return key
else:
raise Error('Key must be either string or unicode')
-106
View File
@@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr
"""
Metadata module provides access to metadata.xml. metadata.xml is
generated during the build process of a package and used in the
installation. Package repository also uses metadata.xml for building
a package index.
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.context as ctx
import pisi.specfile as specfile
import pisi.pxml.xmlfile as xmlfile
import pisi.pxml.autoxml as autoxml
import pisi.util as util
class Source:
__metaclass__ = autoxml.autoxml
t_Name = [autoxml.String, autoxml.mandatory]
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [specfile.Packager, autoxml.mandatory]
class Package(specfile.Package):
__metaclass__ = autoxml.autoxml
# if this is working autoxml r0Xx0rz
t_PartOf = [autoxml.String, autoxml.mandatory]
t_Build = [ autoxml.Integer, autoxml.optional]
t_Distribution = [ autoxml.String, autoxml.mandatory]
t_DistributionRelease = [ autoxml.String, autoxml.mandatory]
t_Architecture = [ autoxml.String, autoxml.mandatory]
t_InstalledSize = [ autoxml.Long, autoxml.mandatory]
t_PackageSize = [ autoxml.Long, autoxml.optional]
t_PackageURI = [ autoxml.String, autoxml.optional]
t_Source = [ Source, autoxml.optional]
def decode_hook(self, node, errs, where):
self.version = self.history[0].version
self.release = self.history[0].release
def __str__(self):
s = specfile.Package.__str__(self)
s += _('Build: % s, Distribution: %s, Dist. Release: %s\n') % \
(self.build, self.distribution, self.distributionRelease)
s += _('Architecture: %s, Installed Size: %s\n') % \
(self.architecture, self.installedSize)
return s
class MetaData(xmlfile.XmlFile):
"""Package metadata. Metadata is composed of Specfile and various
other information. A metadata has two parts, Source and Package."""
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Source = [ Source, autoxml.mandatory]
t_Package = [ Package, autoxml.mandatory]
#t_History = [ [Update], autoxml.mandatory]
def from_spec(self, src, pkg):
# this just copies fields, it doesn't fix every necessary field
self.source.name = src.name
self.source.homepage = src.homepage
self.source.packager = src.packager
self.package.source = self.source # FIXME: I know that replication sucks here, but this is the easiest for now-- exa
self.package.name = pkg.name
self.package.summary = pkg.summary
self.package.description = pkg.description
self.package.icon = pkg.icon
self.package.isA = pkg.isA
self.package.partOf = pkg.partOf
self.package.license = pkg.license
self.package.packageDependencies = pkg.packageDependencies
self.package.componentDependencies = pkg.componentDependencies
self.package.files = pkg.files
# FIXME: no need to copy full history with comments
self.package.history = src.history
self.package.conflicts = pkg.conflicts
self.package.providesComar = pkg.providesComar
#self.package.requiresComar = pkg.requiresComar
self.package.additionalFiles = pkg.additionalFiles
# FIXME: right way to do it?
self.source.version = src.version
self.source.release = src.release
self.package.version = src.version
self.package.release = src.release
-39
View File
@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
#
# Guido's cool metaclass examples. fair use. ahahah.
# I find these quite handy. Use them :)
class autoprop(type):
def __init__(cls, name, bases, dict):
super(autoprop, cls).__init__(name, bases, dict)
props = {}
for name in dict.keys():
if name.startswith("_get_") or name.startswith("_set_"):
props[name[5:]] = 1
for name in props.keys():
fget = getattr(cls, "_get_%s" % name, None)
fset = getattr(cls, "_set_%s" % name, None)
setattr(cls, name, property(fget, fset))
class autosuper(type):
def __init__(cls, name, bases, dict):
super(autosuper, cls).__init__(name, bases, dict)
setattr(cls, "_%s__super" % name, super(cls))
class autosuprop(autosuper, autoprop):
pass
class autoeq(type):
"useful for structures"
def __init__(cls, name, bases, dict):
super(autoeq, cls).__init__(name, bases, dict)
def equal(self, other):
return self.__dict__ == other.__dict__
cls.__eq__ = equal
class Struct:
__metaclass__ = autoeq
def __init__(self, **entries):
self.__dict__.update(entries)
-762
View File
@@ -1,762 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray at pardus.org.tr>
"Package Operations: install/remove/upgrade"
import os
import sys
ver = sys.version_info
if ver[0] <= 2 and ver[1] < 4:
from sets import Set as set
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
from pisi.uri import URI
import pisi.util as util
import pisi.dependency as dependency
import pisi.pgraph as pgraph
import pisi.packagedb as packagedb
import pisi.repodb
import pisi.installdb
from pisi.index import Index
import pisi.cli
import pisi.atomicoperations as atomicoperations
import pisi.ui as ui
from pisi.version import Version
class Error(pisi.Error):
pass
# high level operations
def install(packages, reinstall = False):
"""install a list of packages (either files/urls, or names)"""
# FIXME: this function name "install" makes impossible to import
# and use install module directly.
from pisi.atomicoperations import Error as InstallError
# determine if this is a list of files/urls or names
if packages[0].endswith(ctx.const.package_suffix): # they all have to!
return install_pkg_files(packages)
else:
return install_pkg_names(packages, reinstall)
def install_pkg_files(package_URIs):
"""install a number of pisi package files"""
from package import Package
ctx.ui.debug('A = %s' % str(package_URIs))
for x in package_URIs:
if not x.endswith(ctx.const.package_suffix):
raise Error(_('Mixing file names and package names not supported yet.'))
if ctx.config.get_option('ignore_dependency'):
# simple code path then
for x in package_URIs:
atomicoperations.install_single_file(x)
return # short circuit
# read the package information into memory first
# regardless of which distribution they come from
d_t = {}
dfn = {}
for x in package_URIs:
package = Package(x)
package.read()
name = str(package.metadata.package.name)
d_t[name] = package.metadata.package
dfn[name] = x
def satisfiesDep(dep):
# is dependency satisfied among available packages
# or packages to be installed?
return dependency.installed_satisfies_dep(dep) \
or dependency.dict_satisfies_dep(d_t, dep)
# for this case, we have to determine the dependencies
# that aren't already satisfied and try to install them
# from the repository
dep_unsatis = []
for name in d_t.keys():
pkg = d_t[name]
deps = pkg.runtimeDependencies()
for dep in deps:
if not satisfiesDep(dep):
dep_unsatis.append(dep)
# now determine if these unsatisfied dependencies could
# be satisfied by installing packages from the repo
# if so, then invoke install_pkg_names
extra_packages = [x.package for x in dep_unsatis]
if extra_packages:
ctx.ui.info(_("""The following packages will be installed
in the respective order to satisfy extra dependencies:
""") + util.strlist(extra_packages))
if not ctx.ui.confirm(_('Do you want to continue?')):
raise Error(_('External dependencies not satisfied'))
install_pkg_names(extra_packages)
class PackageDB:
def get_package(self, key):
return d_t[str(key)]
packagedb = PackageDB()
A = d_t.keys()
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
if dependency.dict_satisfies_dep(d_t, dep):
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
check_conflicts(order, packagedb)
order.reverse()
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
atomicoperations.install_single_file(dfn[x])
def check_conflict(pkg):
conflicts = []
for c in pkg.conflicts:
if ctx.installdb.is_installed(c):
conflicts.append(c)
return conflicts
def calculate_conflicts(order, packagedb):
B_0 = set(order)
C = D = set()
pkg_conflicts = {}
for x in order:
pkg = packagedb.get_package(x)
B_p = set(check_conflict(pkg))
if B_p:
pkg_conflicts[x] = B_p
C = C.union(B_p)
B_i = B_0.intersection(set(pkg.conflicts))
# check if there are any conflicts within the packages that are
# going to be installed
if B_i:
D = D.union(B_i)
D.add(pkg.name)
return (C, D, pkg_conflicts)
def check_conflicts(order, packagedb):
"""check if upgrading to the latest versions will cause havoc
done in a simple minded way without regard for dependencies of
conflicts, etc."""
(C, D, pkg_conflicts) = calculate_conflicts(order, packagedb)
if D:
raise Error(_("Selected packages [%s] are in conflict with each other.") %
util.strlist(list(D)))
if pkg_conflicts:
conflicts = ""
for pkg in pkg_conflicts.keys():
conflicts += _("[%s conflicts with: %s]") % (pkg, util.strlist(pkg_conflicts[pkg]))
ctx.ui.info(_("The following packages have conflicts: %s") %
conflicts)
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
raise Error(_("Conflicts remain"))
if remove(list(C)) == False:
raise Error(_("Conflicts remain"))
def expand_components(A):
Ap = set()
for x in A:
if ctx.componentdb.has_component(x):
Ap = Ap.union(ctx.componentdb.get_component(x).packages)
else:
Ap.add(x)
return Ap
def is_upgradable(name, ignore_build = False):
if not ctx.installdb.is_installed(name):
return False
(version, release, build) = ctx.installdb.get_version(name)
pkg = ctx.packagedb.get_package(name)
if ignore_build or (not build) or (not pkg.build):
return Version(release) < Version(pkg.release)
else:
return build < pkg.build
def upgrade_base(A = set()):
ignore_build = ctx.get_option('ignore_build_no')
if not ctx.get_option('bypass_safety'):
if ctx.componentdb.has_component('system.base'):
extra_packages = set(ctx.componentdb.get_component('system.base').packages) - A
extra_installs = filter(lambda x: not ctx.installdb.is_installed(x), extra_packages)
if extra_installs:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be installed: ') +
util.strlist(extra_installs))
install_pkg_names(extra_installs, bypass_safety=True)
#A |= extra_installs
extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), extra_packages)
if extra_upgrades:
ctx.ui.warning(_('Safety switch: Following packages in system.base will be upgraded: ') +
util.strlist(extra_upgrades))
upgrade_pkg_names(extra_upgrades, bypass_safety=True)
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
def install_pkg_names(A, reinstall = False, bypass_safety = False):
"""This is the real thing. It installs packages from
the repository, trying to perform a minimum number of
installs"""
A = [str(x) for x in A] #FIXME: why do we still get unicode input here? :/ -- exa
# A was a list, remove duplicates and expand components
A_0 = A = expand_components(set(A))
ctx.ui.debug('A = %s' % str(A))
if not bypass_safety:
upgrade_base(A)
# filter packages that are already installed
if not reinstall:
Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A))
d = A - Ap
if len(d) > 0:
ctx.ui.warning(_('Not re-installing the following packages: ') +
util.strlist(d))
A = Ap
if len(A)==0:
ctx.ui.info(_('No packages to install.'))
return
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_install_pkg_names(A)
else:
G_f = None
order = A
ctx.ui.info(_("""Following packages will be installed
in the respective order to satisfy dependencies:
""") + util.strlist(order))
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of packages: %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if len(order) > len(A_0):
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
atomicoperations.install_single_name(x)
def plan_install_pkg_names(A):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
ctx.ui.debug('checking %s' % str(dep))
# we don't deal with already *satisfied* dependencies
if not dependency.installed_satisfies_dep(dep):
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
check_conflicts(order, ctx.packagedb)
return G_f, order
def upgrade(A):
upgrade_pkg_names(A)
def upgrade_pkg_names(A = [], bypass_safety = False):
"""Re-installs packages from the repository, trying to perform
a minimum or maximum number of upgrades according to options."""
ignore_build = ctx.get_option('ignore_build_no')
if not A:
# if A is empty, then upgrade all packages
A = ctx.installdb.list_installed()
# filter packages that are not upgradable
A_0 = A = expand_components(set(A))
if not bypass_safety:
upgrade_base(A)
Ap = []
for x in A:
if x.endswith(ctx.const.package_suffix):
ctx.ui.debug(_("Warning: package *name* ends with '.pisi'"))
if not ctx.installdb.is_installed(x):
ctx.ui.info(_('Package %s is not installed.') % x, True)
continue
(version, release, build) = ctx.installdb.get_version(x)
if ctx.packagedb.has_package(x):
pkg = ctx.packagedb.get_package(x)
else:
ctx.ui.info(_('Package %s is not available in repositories.') % x, True)
continue
if ignore_build or (not build) or (not pkg.build):
if Version(release) < Version(pkg.release):
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest release %s.')
% (pkg.name, pkg.release), True)
else:
if build < pkg.build:
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest build %s.')
% (pkg.name, pkg.build), True)
A = set(Ap)
if len(A)==0:
ctx.ui.info(_('No packages to upgrade.'))
return True
ctx.ui.debug('A = %s' % str(A))
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_upgrade(A, ignore_build)
else:
G_f = None
order = A
ctx.ui.info(_('The following packages will be upgraded: ') +
util.strlist(order))
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
total_size, symbol = util.human_readable_size(total_size)
ctx.ui.info(_('Total size of packages: %.2f %s') % (total_size, symbol))
if ctx.get_option('dry_run'):
return
if len(order) > len(A_0):
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False
ctx.ui.notify(ui.packagestogo, order = order)
paths = []
for x in order:
install_op = atomicoperations.Install.from_name(x)
paths.append(install_op.package_fname)
for path in paths:
install_op = atomicoperations.Install(path)
install_op.install(True)
def plan_upgrade(A, ignore_build = False):
# try to construct a pisi graph of packages to
# install / reinstall
packagedb = ctx.packagedb
G_f = pgraph.PGraph(packagedb) # construct G_f
# find the "install closure" graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
# TODO: conflicts
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
for dep in pkg.runtimeDependencies():
# add packages that can be upgraded
if dependency.repo_satisfies_dep(dep):
if ctx.installdb.is_installed(dep.package):
if ctx.get_option('eager'):
if not is_upgradable(dep.package):
continue
else:
if dependency.installed_satisfies_dep(dep):
continue
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
else:
raise Error(_('Dependency %s of %s cannot be satisfied') % (dep, x))
B = Bp
# now, search reverse dependencies to see if anything
# should be upgraded
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x)
rev_deps = packagedb.get_rev_deps(x)
for (rev_dep, depinfo) in rev_deps:
if ctx.get_option('eager'):
# add all upgradable reverse deps
if is_upgradable(rev_dep):
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
else:
# add only installed but unsatisfied reverse dependencies
if ctx.installdb.is_installed(rev_dep) and \
(not dependency.installed_satisfies_dep(depinfo)):
if not dependency.repo_satisfies_dep(depinfo):
raise Error(_('Reverse dependency %s of %s cannot be satisfied') % (rev_dep, x))
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
order.reverse()
check_conflicts(order, ctx.packagedb)
return G_f, order
def remove(A):
"""remove set A of packages from system (A is a list of package names)"""
# filter packages that are not installed
A_0 = A = expand_components(set(A))
if not ctx.get_option('bypass_safety'):
if ctx.componentdb.has_component('system.base'):
refused = A.intersection(set(ctx.componentdb.get_component('system.base').packages))
if refused:
ctx.ui.warning(_('Safety switch: cannot remove the following packages in system.base: ') +
util.strlist(refused))
A = A - set(ctx.componentdb.get_component('system.base').packages)
else:
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
Ap = []
for x in A:
if ctx.installdb.is_installed(x):
Ap.append(x)
else:
ctx.ui.info(_('Package %s does not exist. Cannot remove.') % x)
A = set(Ap)
if len(A)==0:
ctx.ui.info(_('No packages to remove.'))
return False
if not ctx.config.get_option('ignore_dependency'):
G_f, order = plan_remove(A)
else:
G_f = None
order = A
ctx.ui.info(_("""The following minimal list of packages will be removed
in the respective order to satisfy dependencies:
""") + util.strlist(order))
if len(order) > len(A_0):
if not ctx.ui.confirm(_('Do you want to continue?')):
ctx.ui.warning(_('Package removal declined'))
return False
if ctx.get_option('dry_run'):
return
ctx.ui.notify(ui.packagestogo, order = order)
for x in order:
if ctx.installdb.is_installed(x):
atomicoperations.remove_single(x)
else:
ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x)
def plan_remove(A):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
# find the (install closure) graph of G_f by package
# set A using packagedb
for x in A:
G_f.add_package(x)
B = A
while len(B) > 0:
Bp = set()
for x in B:
pkg = ctx.packagedb.get_package(x, pisi.itembyrepodb.installed)
rev_deps = ctx.packagedb.get_rev_deps(x, pisi.itembyrepodb.installed)
for (rev_dep, depinfo) in rev_deps:
# we don't deal with uninstalled rev deps
# and unsatisfied dependencies (this is important, too)
if ctx.packagedb.has_package(rev_dep, pisi.itembyrepodb.installed) and \
dependency.installed_satisfies_dep(depinfo):
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order = G_f.topological_sort()
return G_f, order
def expand_src_components(A):
Ap = set()
for x in A:
if ctx.componentdb.has_component(x):
Ap = Ap.union(ctx.componentdb.get_component(x).sources)
else:
Ap.add(x)
return Ap
##def build_names(A, rebuild = true):
##
## # A was a list, remove duplicates and expand components
## A_0 = A = expand_src_components(set(A))
## ctx.ui.debug('A = %s' % str(A))
##
## # filter packages that are already installed
## if not rebuild:
## Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A))
## d = A - Ap
## if len(d) > 0:
## ctx.ui.warning(_('Not re-building the following packages: ') +
## util.strlist(d))
## A = Ap
##
## if len(A)==0:
## ctx.ui.info(_('No packages to build.'))
## return
##
## if not ctx.config.get_option('ignore_dependency'):
## G_f, order_inst = plan_build_pkg_names(A)
## else:
## G_f = None
## order_inst = []
##
## ctx.ui.info(_("""The following minimal list of packages will be installed
##in the respective order to satisfy dependencies:
##""") + util.strlist(order))
##
## if ctx.get_option('dry_run'):
## return
##
## if len(order) > len(A_0):
## if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
## return False
##
## ctx.ui.notify(ui.packagestogo, order = order)
##
## for x in order:
## atomicoperations.install_single_name(x)
##
##def plan_build_names(A):
## # try to construct a pisi graph of packages to
## # install / reinstall
##
## G_f = pgraph.PGraph(packagedb) # construct G_f
##
## # find the "install closure" graph of G_f by package
## # set A using packagedb
## for x in A:
## G_f.add_package(x)
## B = A
##
## while len(B) > 0:
## Bp = set()
## for x in B:
## pkg = packagedb.get_package(x)
## for dep in pkg.runtimeDependencies():
## ctx.ui.debug('checking %s' % str(dep))
## # we don't deal with already *satisfied* dependencies
## if not dependency.installed_satisfies_dep(dep):
## if not dep.package in G_f.vertices():
## Bp.add(str(dep.package))
## G_f.add_dep(x, dep)
## B = Bp
## if ctx.config.get_option('debug'):
## G_f.write_graphviz(sys.stdout)
## order = G_f.topological_sort()
## order.reverse()
## check_conflicts(order)
## return G_f, order
def emerge(A, rebuild_all = False):
# A was a list, remove duplicates and expand components
A_0 = A = expand_src_components(set(A))
ctx.ui.debug('A = %s' % str(A))
if len(A)==0:
ctx.ui.info(_('No packages to emerge.'))
return
if not ctx.config.get_option('ignore_dependency'):
G_f, order_inst, order_build = plan_emerge(A, rebuild_all)
else:
G_f = None
order_inst = []
order_build = A
if order_inst:
ctx.ui.info(_("""The following minimal list of packages will be installed
from repository in the respective order to satisfy dependencies:
""") + util.strlist(order_inst))
ctx.ui.info(_("""The following minimal list of packages will be built and
installed in the respective order to satisfy dependencies:
""") + util.strlist(order_build))
if ctx.get_option('dry_run'):
return
if len(order_inst) + len(order_build) > len(A_0):
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
return False
ctx.ui.notify(ui.packagestogo, order = order_inst)
for x in order_inst:
atomicoperations.install_single_name(x)
#ctx.ui.notify(ui.packagestogo, order = order_build)
for x in order_build:
package_names, blah = atomicoperations.build(x)
install_pkg_files(package_names) # handle inter-package deps here
def plan_emerge(A, rebuild_all):
# try to construct a pisi graph of packages to
# install / reinstall
G_f = pisi.graph.Digraph()
def get_spec(name):
if ctx.sourcedb.has_spec(name):
return ctx.sourcedb.get_spec(name)
else:
raise Error(_('Cannot find source package: %s') % name)
def get_src(name):
return get_spec(name).source
def add_src(src):
if not str(src.name) in G_f.vertices():
G_f.add_vertex(str(src.name), (src.version, src.release))
def pkgtosrc(pkg):
return ctx.sourcedb.pkgtosrc(pkg)
# setup first
#specfiles = [ ctx.sourcedb.get_source(x)[1] for x in A ]
#pkgtosrc = {}
B = A
install_list = set()
while len(B) > 0:
Bp = set()
for x in B:
sf = get_spec(x)
src = sf.source
add_src(src)
# add dependencies
def process_dep(dep):
if not dependency.installed_satisfies_dep(dep):
if dependency.repo_satisfies_dep(dep):
install_list.add(dep.package)
return
srcdep = pkgtosrc(dep.package)
if not srcdep in G_f.vertices():
Bp.add(srcdep)
add_src(get_src(srcdep))
if not src.name == srcdep: # firefox - firefox-devel thing
G_f.add_edge(src.name, srcdep)
for builddep in src.buildDependencies:
process_dep(builddep)
for pkg in sf.packages:
for rtdep in pkg.packageDependencies:
process_dep(rtdep)
B = Bp
if ctx.config.get_option('debug'):
G_f.write_graphviz(sys.stdout)
order_build = G_f.topological_sort()
order_build.reverse()
G_f2, order_inst = plan_install_pkg_names(install_list)
return G_f, order_inst, order_build
-159
View File
@@ -1,159 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# package abstraction
# provides methods to add/remove files, extract control files
# maintainer: baris and meren
from os.path import join, exists, basename, dirname
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.context as ctx
import pisi.archive as archive
from pisi.uri import URI
from pisi.metadata import MetaData
from pisi.files import Files
import pisi.util as util
class Error(pisi.Error):
pass
class Package:
"""PISI Package Class provides access to a pisi package (.pisi
file)."""
def __init__(self, packagefn, mode='r'):
self.filepath = packagefn
url = URI(packagefn)
if url.is_remote_file():
from fetcher import fetch_url, FetchError
dest = ctx.config.packages_dir()
self.filepath = join(dest, url.filename())
# get current package name without version info (like tasma)
current_pkg = util.pure_package_name(url.filename())
# if package is installed (which means we are upgrading it),
# calculate needed info
if ctx.installdb.is_installed(current_pkg):
installed = True
# calculate currently installed package's name and pisi file's location
(version, release, build) = ctx.installdb.get_version(current_pkg)
current = util.package_name(current_pkg, version, release, build, prependSuffix=False)
current_fn = join(dest, current) + ctx.const.package_suffix
# calculate xdelta file location (format: oldpackage_newpackage.xdelta)
# ex: tasma-1.0.2-4-1_tasma-1.0.3-5-2.xdelta
delta = basename(url.filename()).rstrip(ctx.const.package_suffix)
delta = "%s_%s" % (current, delta) + ctx.const.xdelta_suffix
# create xdelta_url for fetching
xdelta_url = URI(join(dirname(str(url)), delta))
xdelta_fn = join(dest, xdelta_url.filename())
else:
installed = False
if installed and exists(current_fn):
# if package is installed and old pisi file exists, fetch xdelta
if not exists(xdelta_fn):
ctx.ui.info(_("Trying to fetch xdelta: %s") % xdelta_url)
try:
fetch_url(xdelta_url, dest, ctx.ui.Progress)
except FetchError,e:
ctx.ui.error(_('XDelta %s: not exists') % xdelta_url)
else:
# generate new one using old pisi file and xdelta
util.generate_pisi_file(xdelta_fn, current_fn, self.filepath)
else:
util.generate_pisi_file(xdelta_fn, current_fn, self.filepath)
# FIXME: exists is not enough, also sha1sum check needed
# when implemented in pisi-index.xml
if not exists(self.filepath):
fetch_url(url, dest, ctx.ui.Progress)
else:
ctx.ui.info(_('%s [cached]') % url.filename())
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
def add_to_package(self, fn, an=None):
"""Add a file or directory to package"""
self.impl.add_to_archive(fn, an)
def close(self):
"""Close the package archive"""
self.impl.close()
def extract(self, outdir):
"""Extract entire package contents to directory"""
self.extract_dir('', outdir) # means package root
def extract_files(self, paths, outdir):
"""Extract paths to outdir"""
self.impl.unpack_files(paths, outdir)
def extract_file(self, path, outdir):
"""Extract file with path to outdir"""
self.extract_files([path], outdir)
def extract_dir(self, dir, outdir):
"""Extract directory recursively, this function
copies the directory archiveroot/dir to outdir"""
self.impl.unpack_dir(dir, outdir)
def extract_dir_flat(self, dir, outdir):
"""Extract directory recursively, this function
unpacks the *contents* of directory archiveroot/dir inside outdir
this is the function used by the installer"""
self.impl.unpack_dir_flat(dir, outdir)
def extract_PISI_files(self, outdir):
"""Extract PISI control files: metadata.xml, files.xml,
action scripts, etc."""
self.extract_files([ctx.const.metadata_xml, ctx.const.files_xml], outdir)
self.extract_dir('config', outdir)
def read(self, outdir = None):
if not outdir:
outdir = ctx.config.tmp_dir()
# extract control files
self.extract_PISI_files(outdir)
self.metadata = MetaData()
self.metadata.read( join(outdir, ctx.const.metadata_xml) )
errs = self.metadata.errors()
if errs:
util.Checks.print_errors(errs)
raise Error, _("MetaData format wrong")
self.files = Files()
self.files.read( join(outdir, ctx.const.files_xml) )
if self.files.errors():
raise Error, _("Invalid %s") % ctx.const.files_xml
def pkg_dir(self):
packageDir = self.metadata.package.name + '-' \
+ self.metadata.package.version + '-' \
+ self.metadata.package.release
return join( ctx.config.lib_dir(), 'package', packageDir)
def comar_dir(self):
return join(self.pkg_dir(), ctx.const.comar_dir)
-148
View File
@@ -1,148 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# package database
# interface for update/query to local package repository
# Authors: Eray Ozkural <eray at pardus.org.tr>
# Baris Metin <baris at pardus.org.tr>
# we basically store everything in PackageInfo class
# yes, we are cheap
import os
import fcntl
import types
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.util as util
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
from pisi.itembyrepodb import ItemByRepoDB
import pisi.itembyrepodb as itembyrepodb
class Error(pisi.Error):
pass
class NotfoundError(pisi.Error):
def __init__(self, pkg):
pisi.Error.__init__("Package %s not found" % pkg)
self.pkg = pkg
class PackageDB(object):
"""PackageDB class provides an interface to the package database
using shelf objects"""
def __init__(self):
self.d = ItemByRepoDB('package')
self.dr = ItemByRepoDB('revdep')
def close(self):
self.d.close()
self.dr.close()
def destroy(self):
self.d.destroy()
self.dr.destroy()
def has_package(self, name, repo=None, txn = None):
return self.d.has_key(name, repo, txn=txn)
def get_package(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn=txn)
except pisi.itembyrepodb.NotfoundError, e:
raise Error(_('Package %s not found') % name)
def get_package_repo(self, name, repo=None, txn = None):
return self.d.get_item_repo(name, repo, txn=txn)
def which_repo(self, name, txn = None):
return self.d.which_repo(name, txn=txn)
def get_rev_deps(self, name, repo = None, txn = None):
if self.dr.has_key(name, repo, txn=txn):
return self.dr.get_item(name, repo, txn=txn)
else:
return []
def list_packages(self, repo=None):
return self.d.list(repo)
def add_package(self, package_info, repo, txn = None):
name = str(package_info.name)
def proc(txn):
self.d.add_item(name, package_info, repo, txn)
for dep in package_info.runtimeDependencies():
dep_name = str(dep.package)
if self.dr.has_key(dep_name, None, txn):
revdep = self.dr.get_item(dep_name, None, txn)
revdep = filter(lambda (n,d):n!=name, revdep)
revdep.append( (name, dep) )
self.dr.add_item(dep_name, revdep, repo, txn)
else:
self.dr.add_item(dep_name, [ (name, dep) ], repo, txn)
# add component
ctx.componentdb.add_package(package_info.partOf, package_info.name, repo, txn)
# index summary and description
for (lang, doc) in package_info.summary.iteritems():
if lang in ['en', 'tr']:
pisi.search.add_doc('summary', lang, package_info.name, doc, txn)
for (lang, doc) in package_info.description.iteritems():
if lang in ['en', 'tr']:
pisi.search.add_doc('description', lang, package_info.name, doc, txn)
ctx.txn_proc(proc, txn)
def clear(self, txn = None):
self.d.clear()
self.dr.clear()
def remove_package(self, name, repo = None, txn = None):
name = str(name)
def proc(txn):
package_info = self.d.get_item(name, repo, txn=txn)
self.d.remove_item(name, repo, txn=txn)
if self.dr.has_key(name, repo, txn=txn):
self.dr.remove_item(name, repo, txn=txn)
ctx.componentdb.remove_package(package_info.partOf, package_info.name, repo, txn)
self.d.txn_proc(proc, txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
self.d.remove_repo(repo, txn=txn)
self.dr.remove_repo(repo, txn=txn)
self.d.txn_proc(proc, txn)
pkgdb = None
def remove_tracking_package(name, txn = None):
# remove the guy from the tracking databases
if pkgdb.has_package(name, itembyrepodb.installed, txn=txn):
pkgdb.remove_package(name, itembyrepodb.installed, txn=txn)
if pkgdb.has_package(name, itembyrepodb.thirdparty, txn=txn):
pkgdb.remove_package(name, itembyrepodb.thirdparty, txn=txn)
def init_db():
global pkgdb
pkgdb = PackageDB()
return pkgdb
def finalize_db():
global pkgdb
if pkgdb:
pkgdb.close()
-81
View File
@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# PISI package relation graph that represents the state of packagedb
from graph import *
# Cache the results from packagedb queries in a graph
class PGraph(Digraph):
def __init__(self, packagedb):
super(PGraph, self).__init__()
self.packagedb = packagedb
def add_package(self, pkg):
pkg1 = self.packagedb.get_package(pkg)
self.add_vertex(str(pkg), (pkg1.version, pkg1.release))
def add_plain_dep(self, pkg1name, pkg2name):
pkg1data = None
if not pkg1name in self.vertices():
pkg1 = self.packagedb.get_package(pkg1name)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not pkg2name in self.vertices():
pkg2 = self.packagedb.get_package(pkg2name)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(pkg1name), str(pkg2name), ('d', None),
pkg1data, pkg2data )
def add_dep(self, pkg, depinfo):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(pkg)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not depinfo.package in self.vertices():
pkg2 = self.packagedb.get_package(depinfo.package)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo),
pkg1data, pkg2data )
def add_rev_dep(self, depinfo, pkg):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(depinfo.package)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not depinfo.package in self.vertices():
pkg2 = self.packagedb.get_package(pkg)
pkg2data = (pkg2.version, pkg2.release)
self.add_edge(str(depinfo.package), str(pkg), ('d', depinfo),
pkg1data, pkg2data )
def add_conflict(self, pkg, conflinfo):
pkg1data = None
if not pkg in self.vertices():
pkg1 = self.packagedb.get_package(pkg)
pkg1data = (pkg1.version, pkg1.release)
pkg2data = None
if not pkg in self.vertices():
pkg2 = self.packagedb.get_package(conflinfo.package)
pkg2data = (pkg2.version, pkg2.release)
self.add_biedge(str(pkg), str(conflinfo.package), ('c', conflinfo)
, pkg1data, pkg2data )
def write_graphviz_vlabel(self, f, u):
(v, r) = self.vertex_data(u)
f.write('[ label = \"' + str(u) + '(' + str(v) + ',' + str(r) + ')\" ]')
-23
View File
@@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import sys
import locale
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class Error(pisi.Error):
pass
-841
View File
@@ -1,841 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Gurer Ozen <gurer@pardus.org.tr>
# Bahadir Kandemir <bahadir@haftalik.net>
# Baris Metin <baris@pardus.org.tr>
"""
autoxml is a metaclass for automatic XML translation, using
a miniature type system. (w00t!) This is based on an excellent
high-level XML processing prototype that Gurer prepared.
Method names are mixedCase for compatibility with minidom,
an old library.
"""
# System
import locale
import codecs
import types
import formatter
import sys
from StringIO import StringIO
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi
import pisi
from pisi.pxml.xmlext import *
from pisi.pxml.xmlfile import XmlFile
import pisi.context as ctx
import pisi.util as util
import pisi.oo as oo
class Error(pisi.Error):
pass
# requirement specs
mandatory, optional = range(2) # poor man's enum
# basic types
String = types.StringType
Text = types.UnicodeType
Integer = types.IntType
Long = types.LongType
Float = types.FloatType
#class datatype(type):
# def __init__(cls, name, bases, dict):
# """entry point for metaclass code"""
# # standard initialization
# super(autoxml, cls).__init__(name, bases, dict)
class LocalText(dict):
"""Handles XML tags with localized text"""
def __init__(self, tag = "", req = optional):
self.tag = tag
self.req = req
dict.__init__(self)
def decode(self, node, errs, where = ""):
# flags, tag name, instance attribute
assert self.tag != ''
nodes = getAllNodes(node, self.tag)
if not nodes:
if self.req == mandatory:
errs.append(where + ': ' + _("At least one '%s' tag should have local text") %
self.tag )
else:
for node in nodes:
lang = getNodeAttribute(node, 'xml:lang')
c = getNodeText(node)
if not c:
errs.append(where + ': ' + _("'%s' language of tag '%s' is empty") %
(lang, self.tag))
# FIXME: check for dups and 'en'
if not lang:
lang = 'en'
self[lang] = c
def encode(self, node, errs):
assert self.tag != ''
for key in self.iterkeys():
newnode = addNode(node, self.tag)
setNodeAttribute(newnode, 'xml:lang', key)
addText(newnode, '', self[key])
#FIXME: maybe more appropriate for pisi.util
@staticmethod
def get_lang():
try:
(lang, encoding) = locale.getlocale()
if not lang:
(lang, encoding) = locale.getdefaultlocale()
if lang==None: # stupid python means it is C locale
return 'en'
else:
return lang[0:2]
except:
raise Error(_('LocalText: unable to get either current or default locale'))
def errors(self, where = unicode()):
errs = []
langs = [ LocalText.get_lang(), 'en', 'tr', ]
if not util.any(lambda x : self.has_key(x), langs):
errs.append( where + ': ' + _("Tag should have at least the current locale, or failing that an English or Turkish version"))
#FIXME: check if all entries are unicode
return errs
def format(self, f, errs):
L = LocalText.get_lang()
if self.has_key(L):
f.add_flowing_data(self[L])
elif self.has_key('en'):
# fallback to English, blah
f.add_flowing_data(self['en'])
elif self.has_key('tr'):
# fallback to Turkish
f.add_flowing_data(self['tr'])
else:
errs.append(_("Tag should have at least the current locale, or failing that an English or Turkish version"))
#FIXME: factor out these common routines
def print_text(self, file = sys.stdout):
w = Writer(file) # plain text
f = formatter.AbstractFormatter(w)
errs = []
self.format(f, errs)
if errs:
for x in errs:
ctx.ui.warning(x)
def __str__(self):
L = LocalText.get_lang()
if self.has_key(L):
return unicode(self[L])
elif self.has_key('en'):
# fallback to English, blah
return unicode(self['en'])
elif self.has_key('tr'):
# fallback to Turkish
return unicode(self['tr'])
else:
return unicode()
class Writer(formatter.DumbWriter):
"""adds unicode support"""
def __init__(self, file=None, maxcol=78):
formatter.DumbWriter.__init__(self, file, maxcol)
def send_literal_data(self, data):
self.file.write(data.encode("utf-8"))
i = data.rfind('\n')
if i >= 0:
self.col = 0
data = data[i+1:]
data = data.expandtabs()
self.col = self.col + len(data)
self.atbreak = 0
class autoxml(oo.autosuper, oo.autoprop):
"""High-level automatic XML transformation interface for xmlfile.
The idea is to declare a class for each XML tag. Inside the
class the tags and attributes nested in the tag are further
elaborated. A simple example follows:
class Employee:
__metaclass__ = autoxml
t_Name = [xmlfile.Text, xmlfile.mandatory]
a_Type = [xmlfile.Integer, xmlfile.optional]
This class defines a tag and an attribute nested in Employee
class. Name is a string and type is an integer, called basic
types.
While the tag is mandatory, the attribute may be left out.
Other basic types supported are: xmlfile.Float, xmlfile.Double
and (not implemented yet): xmlfile.Binary
By default, the class name is taken as the corresponding tag,
which may be overridden by defining a tag attribute. Thus,
the same tag may also be written as:
class EmployeeXML:
...
tag = 'Employee'
...
In addition to basic types, we allow for two kinds of complex
types: class types and list types.
A declared class can be nested in another class as follows
class Position:
__metaclass__ = autoxml
t_Name = [xmlfile.Text, xmlfile.mandatory]
t_Description = [xmlfile.Text, xmlfile.optional]
which we can add to our Employee class.
class Employee:
__metaclass__ = autoxml
t_Name = [xmlfile.Text, xmlfile.mandatory]
a_Type = [xmlfile.Integer, xmlfile.optional]
t_Position = [Position, xmlfile.mandatory]
Note some unfortunate redundancy here with Position; this is
justified by the implementation (kidding). Still, you might
want to assign a different name than the class name that
goes in there, which may be fully qualified.
There is more! Suppose we want to define a company, with
of course many employees.
class Company:
__metaclass__ = autoxml
t_Employees = [ [Employee], xmlfile.mandatory, 'Employees/Employee']
Logically, inside the Company/Employees tag, we will have several
Employee tags, which are inserted to the Employees instance variable of
Company in order of appearance. We can define lists of any other valid
type. Here we used a list of an autoxml class defined above.
The mandatory flag here asserts that at least one such record
is to be found.
You see, it works like magic, when it works of course. All of it
done without a single brain exploding.
"""
def __init__(cls, name, bases, dict):
"""entry point for metaclass code"""
#print 'generating class', name
# standard initialization
super(autoxml, cls).__init__(name, bases, dict)
xmlfile_support = XmlFile in bases
cls.autoxml_bases = filter(lambda base: isinstance(base, autoxml), bases)
#TODO: initialize class attribute __xml_tags
#setattr(cls, 'xml_variables', [])
# default class tag is class name
if not dict.has_key('tag'):
cls.tag = name
# generate helper routines, for each XML component
names = []
inits = []
decoders = []
encoders = []
errorss = []
formatters = []
#order = dict.keys()
#order.sort()
# read declaration order from source
# code contributed by bahadir kandemir
from inspect import getsourcelines
from itertools import ifilter
import re
fn = re.compile('\s*([tas]_[a-zA-Z]+).*').findall
lines = filter(fn, getsourcelines(cls)[0])
decl_order = map(lambda x:x.split()[0], lines)
# there should be at most one str member, and it should be
# the first to process
order = filter(lambda x: not x.startswith('s_'), decl_order)
# find string member
str_members = filter(lambda x:x.startswith('s_'), decl_order)
if len(str_members)>1:
raise Error('Only one str member can be defined')
elif len(str_members)==1:
order.insert(0, str_members[0])
for var in order:
if var.startswith('t_') or var.startswith('a_') or var.startswith('s_'):
name = var[2:]
if var.startswith('a_'):
x = autoxml.gen_attr_member(cls, name)
elif var.startswith('t_'):
x = autoxml.gen_tag_member(cls, name)
elif var.startswith('s_'):
x = autoxml.gen_str_member(cls, name)
(name, init, decoder, encoder, errors, format_x) = x
names.append(name)
inits.append(init)
decoders.append(decoder)
encoders.append(encoder)
errorss.append(errors)
formatters.append(format_x)
# generate top-level helper functions
cls.initializers = inits
def initialize(self, uri = None, keepDoc = False, tmpDir = '/tmp',
**args):
if xmlfile_support:
# totally dailywtf
#if args.has_key('tag'):
# tag = args['tag']
#else:
# tag = self.__class__.tag # ought to be autoxml
#XmlFile.__init__(self, tag = tag)
if not args.has_key('tag'):
XmlFile.__init__(self, tag = cls.tag)
for base in cls.autoxml_bases:
base.__init__(self)
#super(cls, self).__init__(tag = tag) cooperative shit disabled for now
for init in inits:#self.__class__.initializers:
init(self)
for x in args.iterkeys():
setattr(self, x, args[x])
# init hook
if hasattr(self, 'init'):
self.init(tag)
if xmlfile_support and uri:
self.read(uri, keepDoc, tmpDir)
cls.__init__ = initialize
cls.decoders = decoders
def decode(self, node, errs, where = unicode(cls.tag)):
for base in cls.autoxml_bases:
base.decode(self, node, errs, where)
for decode_member in decoders:#self.__class__.decoders:
decode_member(self, node, errs, where)
if hasattr(self, 'decode_hook'):
self.decode_hook(node, errs, where)
cls.decode = decode
cls.encoders = encoders
def encode(self, node, errs):
for base in cls.autoxml_bases:
base.encode(self, node, errs)
for encode_member in encoders:#self.__class__.encoders:
encode_member(self, node, errs)
if hasattr(self, 'encode_hook'):
self.encode_hook(node, errs)
cls.encode = encode
cls.errorss = errorss
def errors(self, where = unicode(name)):
errs = []
for base in cls.autoxml_bases:
errs.extend(base.errors(self, where))
for errors in errorss:#self.__class__.errorss:
errs.extend(errors(self, where))
if hasattr(self, 'errors_hook'):
errs.extend(self.errors_hook(where))
return errs
cls.errors = errors
def check(self):
errs = self.errors()
if errs:
errs.append(_("autoxml.check: '%s' errors") % len(errs))
raise Error(*errs)
cls.check = check
cls.formatters = formatters
def format(self, f, errs):
for base in cls.autoxml_bases:
base.format(self, f, errs)
for formatter in formatters:#self.__class__.formatters:
formatter(self, f, errs)
cls.format = format
def print_text(self, file = sys.stdout):
w = Writer(file) # plain text
f = formatter.AbstractFormatter(w)
errs = []
self.format(f, errs)
if errs:
for x in errs:
ctx.ui.warning(x)
cls.print_text = print_text
if not dict.has_key('__str__'):
def str(self):
strfile = StringIO()
self.print_text(strfile)
str = strfile.getvalue()
strfile.close()
return str
cls.__str__ = str
if not dict.has_key('__eq__'):
def equal(self, other):
for name in names:
try:
if getattr(self, name) != getattr(other, name):
return False
except:
return False
return True
def notequal(self, other):
return not self.__eq__(other)
cls.__eq__ = equal
cls.__ne__ = notequal
if xmlfile_support:
def read(self, uri, keepDoc = False, tmpDir = '/tmp',
sha1sum = False, compress = None, sign = None):
"read XML file and decode it into a python object"
self.readxml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign)
errs = []
self.decode(self.rootNode(), errs)
if errs:
errs.append(_("autoxml.read: File '%s' has errors") % uri)
raise Error(*errs)
if hasattr(self, 'read_hook'):
self.read_hook(errs)
if not keepDoc:
self.unlink() # get rid of the tree
errs = self.errors()
if errs:
errs.append(_("autoxml.read: File '%s' has errors") % uri)
raise Error(*errs)
def write(self, uri, keepDoc = False, tmpDir = '/tmp',
sha1sum = False, compress = None, sign = None):
"encode the contents of the python object into an XML file"
errs = self.errors()
if errs:
errs.append(_("autoxml.write: object validation has failed"))
raise Error(*errs)
errs = []
self.newDocument()
self.encode(self.rootNode(), errs)
if hasattr(self, 'write_hook'):
self.write_hook(errs)
if errs:
errs.append(_("autoxml.write: File encoding '%s' has errors") % uri)
raise Error(*errs)
self.writexml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign)
if not keepDoc:
self.unlink() # get rid of the tree
cls.read = read
cls.write = write
def gen_attr_member(cls, attr):
"""generate readers and writers for an attribute member"""
#print 'attr:', attr
spec = getattr(cls, 'a_' + attr)
tag_type = spec[0]
assert type(tag_type) == type(type)
def readtext(node, attr):
return getNodeAttribute(node, attr)
def writetext(node, attr, text):
#print 'write attr', attr, text
setNodeAttribute(node, attr, text)
anonfuns = cls.gen_anon_basic(attr, spec, readtext, writetext)
return cls.gen_named_comp(attr, spec, anonfuns)
def gen_tag_member(cls, tag):
"""generate helper funs for tag member of class"""
#print 'tag:', tag
spec = getattr(cls, 't_' + tag)
anonfuns = cls.gen_tag(tag, spec)
return cls.gen_named_comp(tag, spec, anonfuns)
def gen_tag(cls, tag, spec):
"""generate readers and writers for the tag"""
tag_type = spec[0]
if type(tag_type) is types.TypeType and \
autoxml.basic_cons_map.has_key(tag_type):
def readtext(node, tagpath):
#print 'read tag', node, tagpath
return getNodeText(node, tagpath)
def writetext(node, tagpath, text):
#print 'write tag', node, tagpath, text
addText(node, tagpath, text)
return cls.gen_anon_basic(tag, spec, readtext, writetext)
elif type(tag_type) is types.ListType:
return cls.gen_list_tag(tag, spec)
elif tag_type is LocalText:
return cls.gen_insetclass_tag(tag, spec)
elif type(tag_type) is autoxml or type(tag_type) is types.TypeType:
return cls.gen_class_tag(tag, spec)
else:
raise Error(_('gen_tag: unrecognized tag type %s in spec') %
str(tag_type))
def gen_str_member(cls, token):
"""generate readers and writers for a string member"""
spec = getattr(cls, 's_' + token)
tag_type = spec[0]
assert type(tag_type) == type(type)
def readtext(node, blah):
#node.normalize() # piksemel doesn't have this
return getNodeText(node)
def writetext(node, blah, text):
addText(node, "", text)
anonfuns = cls.gen_anon_basic(token, spec, readtext, writetext)
return cls.gen_named_comp(token, spec, anonfuns)
def gen_named_comp(cls, token, spec, anonfuns):
"""generate a named component tag/attr. a decoration of
anonymous functions that do not bind to variable names"""
name = cls.mixed_case(token)
token_type = spec[0]
req = spec[1]
(init_a, decode_a, encode_a, errors_a, format_a) = anonfuns
def init(self):
"""initialize component"""
setattr(self, name, init_a())
def decode(self, node, errs, where):
"""decode component from DOM node"""
setattr(self, name, decode_a(node, errs, where + '.' + unicode(name)))
def encode(self, node, errs):
"""encode self inside, possibly new, DOM node using xml"""
if hasattr(self, name):
value = getattr(self, name)
else:
value = None
encode_a(node, value, errs)
def errors(self, where):
"""return errors in the object"""
errs = []
if hasattr(self, name) and getattr(self, name) != None:
value = getattr(self,name)
errs.extend(errors_a(value, where + '.' + name))
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory variable %s not available') % name)
return errs
def format(self, f, errs):
if hasattr(self, name):
value = getattr(self,name)
f.add_literal_data(token + ': ')
format_a(value, f, errs)
f.add_line_break()
else:
if req == mandatory:
errs.append(_('Mandatory variable %s not available') % name)
return (name, init, decode, encode, errors, format)
def mixed_case(cls, identifier):
"""helper function to turn token name into mixed case"""
if identifier is "":
return ""
else:
if identifier[0]=='I':
lowly = 'i' # because of pythonic idiots we can't choose locale in lower
else:
lowly = identifier[0].lower()
return lowly + identifier[1:]
def tagpath_head_last(cls, tagpath):
"returns split of the tag path into last tag and the rest"
try:
lastsep = tagpath.rindex('/')
except ValueError, e:
return ('', tagpath)
return (tagpath[:lastsep], tagpath[lastsep+1:])
def parse_spec(cls, token, spec):
"""decompose member specification"""
name = cls.mixed_case(token)
token_type = spec[0]
req = spec[1]
if len(spec)>=3:
path = spec[2] # an alternative path specified
elif type(token_type) is type([]):
if type(token_type[0]) is autoxml:
# if list of class, by default nested like in most PSPEC
path = token + '/' + token_type[0].tag
else:
# if list of ordinary type, just take the name for
path = token
elif type(token_type) is autoxml:
# if a class, by default its tag
path = token_type.tag
else:
path = token # otherwise it's the same name as
# the token
return name, token_type, req, path
def gen_anon_basic(cls, token, spec, readtext, writetext):
"""Generate a tag or attribute with one of the basic
types like integer. This has got to be pretty generic
so that we can invoke it from the complex types such as Class
and List. The readtext and writetext arguments achieve
the DOM text access for this datatype."""
name, token_type, req, tagpath = cls.parse_spec(token, spec)
def initialize():
"""default value for all basic types is None"""
return None
def decode(node, errs, where):
"""decode from DOM node, the value, watching the spec"""
text = readtext(node, token)
#print 'read text ', text
if text:
try:
value = autoxml.basic_cons_map[token_type](text)
except:
value = None
errs.append(where + ': ' + _('Type mismatch: read text cannot be decoded'))
return value
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory token %s not available') % token)
return None
def encode(node, value, errs):
"""encode given value inside DOM node"""
if value:
writetext(node, token, unicode(value))
else:
if req == mandatory:
errs.append(_('Mandatory token %s not available') % token)
def errors(value, where):
errs = []
if value and not isinstance(value, token_type):
errs.append(where + ': ' + _('Type mismatch. Expected %s, got %s') %
(token_type, type(value)) )
return errs
def format(value, f, errs):
"""format value for pretty printing"""
f.add_literal_data(unicode(value))
return initialize, decode, encode, errors, format
def gen_class_tag(cls, tag, spec):
"""generate a class datatype"""
name, tag_type, req, path = cls.parse_spec(tag, spec)
def make_object():
obj = tag_type.__new__(tag_type)
obj.__init__(tag=tag, req=req)
return obj
def init():
return make_object()
def decode(node, errs, where):
node = getNode(node, tag)
if node:
try:
obj = make_object()
obj.decode(node, errs, where)
return obj
except Error:
errs.append(where + ': '+ _('Type mismatch: DOM cannot be decoded'))
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory argument not available'))
return None
def encode(node, obj, errs):
if node and obj:
try:
#FIXME: this doesn't look pretty
classnode = newNode(node, tag)
obj.encode(classnode, errs)
addNode(node, '', classnode)
except Error:
if req == mandatory:
# note: we can receive an error if obj has no content
errs.append(_('Object cannot be encoded'))
else:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
def errors(obj, where):
return obj.errors(where)
def format(obj, f, errs):
try:
obj.format(f, errs)
except Error:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
return (init, decode, encode, errors, format)
def gen_list_tag(cls, tag, spec):
"""generate a list datatype. stores comps in tag/comp_tag"""
name, tag_type, req, path = cls.parse_spec(tag, spec)
pathcomps = path.split('/')
comp_tag = pathcomps.pop()
list_tagpath = util.makepath(pathcomps, sep='/', relative=True)
if len(tag_type) != 1:
raise Error(_('List type must contain only one element'))
x = cls.gen_tag(comp_tag, [tag_type[0], mandatory])
(init_item, decode_item, encode_item, errors_item, format_item) = x
def init():
return []
def decode(node, errs, where):
l = []
nodes = getAllNodes(node, path)
#print node, tag + '/' + comp_tag, nodes
if len(nodes)==0 and req==mandatory:
errs.append(where + ': ' + _('Mandatory list empty'))
ix = 1
for node in nodes:
dummy = newNode(node, "Dummy")
addNode(dummy, '', node)
l.append(decode_item(dummy, errs, where + unicode("[%s]" % ix)))
#l.append(decode_item(node, errs, where + unicode("[%s]" % ix)))
ix += 1
return l
def encode(node, l, errs):
if l and len(l) > 0:
for item in l:
if list_tagpath:
listnode = addNode(node, list_tagpath, branch = False)
else:
listnode = node
encode_item(listnode, item, errs)
#encode_item(node, item, errs)
else:
if req is mandatory:
errs.append(_('Mandatory list empty'))
def errors(l, where):
errs = []
ix = 1
for node in l:
errs.extend(errors_item(node, where + '[%s]' % ix))
ix += 1
return errs
def format(l, f, errs):
# TODO: indent here
ix = 1
length = len(l)
for node in l:
f.add_flowing_data(str(ix) + ': ')
format_item(node, f, errs)
if ix != length:
f.add_flowing_data(', ')
ix += 1
return (init, decode, encode, errors, format)
def gen_insetclass_tag(cls, tag, spec):
"""generate a class datatype that is highly integrated
don't worry if that means nothing to you. this is a silly
hack to implement local text quickly. it's not the most
elegant thing in the world. it's basically a copy of
class tag"""
name, tag_type, req, path = cls.parse_spec(tag, spec)
def make_object():
obj = tag_type.__new__(tag_type)
obj.__init__(tag=tag, req=req)
return obj
def init():
return make_object()
def decode(node, errs, where):
if node:
try:
obj = make_object()
obj.decode(node, errs, where)
return obj
except Error:
errs.append(where + ': ' + _('Type mismatch: DOM cannot be decoded'))
else:
if req == mandatory:
errs.append(where + ': ' + _('Mandatory argument not available'))
return None
def encode(node, obj, errs):
if node and obj:
try:
#FIXME: this doesn't look pretty
obj.encode(node, errs)
except Error:
if req == mandatory:
# note: we can receive an error if obj has no content
errs.append(_('Object cannot be encoded'))
else:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
def errors(obj, where):
return obj.errors(where)
def format(obj, f, errs):
try:
obj.format(f, errs)
except Error:
if req == mandatory:
errs.append(_('Mandatory argument not available'))
return (init, decode, encode, errors, format)
basic_cons_map = {
types.StringType : str,
types.UnicodeType : unicode,
types.IntType : int,
types.FloatType : float,
types.LongType : long
}
-24
View File
@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
try:
from xmlextpiks import *
except:
try:
#if pisi.context.use_mdom:
# gibidi
from xmlextcdom import *
except:
#raise Error('cannot find 4suite implementation')
print 'xmlext: piksemel or cDomlette implementation cannot be loaded, falling back to minidom'
from xmlextmdom import *
-218
View File
@@ -1,218 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr
# Gurer Ozen <gurer@pardus.org.tr>
# Faik Uygur <faik@pardus.org.tr>
"""
xmlext is a helper module for accessing XML files using
xml.dom.minidom . It is a convenient wrapper for some
DOM functions, and provides path based get/add functions
as in KDE API.
function names are mixedCase for compatibility with minidom,
an 'old library'
this implementation uses cDomlette from 4suite.org
adapted from the minidom implementation by Faik Uygur and fixed by Eray
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import Ft
from Ft.Xml.Domlette import implementation
from Ft.Xml.Domlette import NoExtDtdReader
import Ft.Lib
import pisi
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
XHTML_NS = unicode(XHTML_NAMESPACE)
XML_NS = unicode(XML_NAMESPACE)
class XmlError(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
# Document wrappers
def newDocument(tag):
# create a document and return document root
doc = implementation.createDocument(None, tag, None)
return doc.documentElement
def parse(filename):
# parse file and return document root
try:
doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(filename))
return doc.documentElement
except Ft.FtException, e:
raise Error(_("File '%s' has invalid XML: %s") % (filename, str(e)))
def newNode(node, tag):
return node.ownerDocument.createElementNS(None, tag)
def newTextNode(node, text):
return node.ownerDocument.createTextNode(text)
# Node related wrappers
def getNodeAttribute(node, attrname):
"""get named attribute from DOM node"""
if attrname.startswith('xml:'):
if node.hasAttributeNS(XML_NS, attrname[4:]):
return node.getAttributeNS(XML_NS, attrname[4:])
else:
return None
else:
if not node.hasAttributeNS(None, attrname):
return None
else:
return node.getAttributeNS(None, attrname)
def setNodeAttribute(node, attrname, value):
"""get named attribute from DOM node"""
if attrname.startswith('xml:'):
node.setAttributeNS(XML_NS, attrname, value)
else:
node.setAttributeNS(None, attrname, value)
def getChildElts(node):
"""get only child elements"""
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
def getTagByName(parent, childName):
return [x for x in parent.childNodes
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
def getNodeText(node, tagpath = ""):
"""get the first child and expect it to be text!"""
if tagpath!="":
node = getNode(node, tagpath)
try:
child = node.childNodes[0]
except IndexError:
return None
except AttributeError: # no node by that name
return None
if child.nodeType == child.TEXT_NODE:
# in any case, strip whitespaces...
return child.data.strip()
else:
raise XmlError(_("getNodeText: Expected text node, got something else!"))
def getChildText(node_s, tagpath):
"""get the text of a child at the end of a tag path"""
node = getNode(node_s, tagpath)
if not node:
return None
return getNodeText(node)
def getNode(node, tagpath):
"""returns the *first* matching node for given tag path."""
assert type(tagpath)==str
tags = tagpath.split('/')
assert len(tags)>0
# iterative code to search for the path
for tag in tags:
currentNode = None
for child in node.childNodes:
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
currentNode = child
break
if not currentNode:
return None
else:
node = currentNode
return currentNode
def getAllNodes(node, tagPath):
"""retrieve all nodes that match a given tag path."""
tags = tagPath.split('/')
if len(tags) == 0:
return []
nodeList = [node] # basis case
for tag in tags:
results = map(lambda x: getTagByName(x, tag), nodeList)
nodeList = []
for x in results:
nodeList.extend(x)
pass # emacs indentation error, keep it here
if len(nodeList) == 0:
return []
return nodeList
def createTagPath(node, tags):
"""create new child at the end of a tag chain starting from node
no matter what"""
if len(tags)==0:
return node
dom = node.ownerDocument
for tag in tags:
node = node.appendChild(dom.createElementNS(None, tag))
return node
def addTagPath(node, tags, newnode=None):
"""add newnode at the end of a tag chain, smart one"""
node = createTagPath(node, tags)
if newnode: # node to add specified
node.appendChild(newnode)
return node
def addNode(node, tagpath, newnode = None, branch=True):
"""add a new node at the end of the tree and returns it
if newnode is given adds that node, too."""
assert type(tagpath)==str
tags = []
if tagpath != "":
tags = tagpath.split('/') # tag chain
else:
addTagPath(node, [], newnode)
return node #FIXME: is this correct!?!?
assert len(tags)>0 # we want a chain
# iterative code to search for the path
if branch:
rem = 1
else:
rem = 0
while len(tags) > rem:
tag = tags.pop(0)
nodeList = getTagByName(node, tag)
if len(nodeList) == 0: # couldn't find
tags.insert(0, tag) # put it back in
return addTagPath(node, tags, newnode)
else:
node = nodeList[len(nodeList)-1] # discard other matches
else:
# had only one tag..
return addTagPath(node, tags, newnode)
return node
def addText(node, tagPath, text, branch = True):
newnode = newTextNode(node, text)
return addNode(node, tagPath, newnode, branch = branch)
-197
View File
@@ -1,197 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr
# Gurer Ozen <gurer@pardus.org.tr>
"""
xmlext is a helper module for accessing XML files using
xml.dom.minidom . It is a convenient wrapper for some
DOM functions, and provides path based get/add functions
as in KDE API.
function names are mixedCase for compatibility with minidom,
an 'old library'
note: this is a particularly inefficient implementation of xmlext.
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import xml.dom.minidom as mdom
from xml.parsers.expat import ExpatError
import pisi
class XmlError(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
# Document wrappers
def newDocument(tag):
impl = mdom.getDOMImplementation()
dom = impl.createDocument(None, tag, None)
return dom.documentElement
def parse(filename):
try:
dom = mdom.parse(filename)
return dom.documentElement
except ExpatError, inst:
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
str(inst)))
def newNode(node, tag):
return node.ownerDocument.createElement(tag)
def newTextNode(node, text):
return node.ownerDocument.createTextNode(text)
# Node related wrappers
def getNodeAttribute(node, attrname):
"""get named attribute from DOM node"""
if not node.hasAttribute(attrname):
return None
return node.getAttribute(attrname)
def getChildElts(node):
"""get only child elements"""
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
def getTagByName(parent, childName):
return [x for x in parent.childNodes
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
def getNodeText(node, tagpath = ""):
"""get the first child and expect it to be text!"""
if tagpath!="":
node = getNode(node, tagpath)
try:
child = node.childNodes[0]
except IndexError:
return None
except AttributeError: # no node by that name
return None
if child.nodeType == child.TEXT_NODE:
# in any case, strip whitespaces...
return child.data.strip()
else:
raise XmlError(_("getNodeText: Expected text node, got something else!"))
def getChildText(node_s, tagpath):
"""get the text of a child at the end of a tag path"""
node = getNode(node_s, tagpath)
if not node:
return None
return getNodeText(node)
def getNode(node, tagpath):
"""returns the *first* matching node for given tag path."""
assert type(tagpath)==str
tags = tagpath.split('/')
assert len(tags)>0
# iterative code to search for the path
for tag in tags:
currentNode = None
for child in node.childNodes:
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
currentNode = child
break
if not currentNode:
return None
else:
node = currentNode
return currentNode
def getAllNodes(node, tagPath):
"""retrieve all nodes that match a given tag path."""
tags = tagPath.split('/')
if len(tags) == 0:
return []
nodeList = [node] # basis case
for tag in tags:
results = map(lambda x: getTagByName(x, tag), nodeList)
nodeList = []
for x in results:
nodeList.extend(x)
pass # emacs indentation error, keep it here
if len(nodeList) == 0:
return []
return nodeList
def createTagPath(node, tags):
"""create new child at the end of a tag chain starting from node
no matter what"""
if len(tags)==0:
return node
dom = node.ownerDocument
for tag in tags:
node = node.appendChild(dom.createElement(tag))
return node
def addTagPath(node, tags, newnode=None):
"""add newnode at the end of a tag chain, smart one"""
node = createTagPath(node, tags)
if newnode: # node to add specified
node.appendChild(newnode)
return node
def addNode(node, tagpath, newnode = None, branch=True):
"""add a new node at the end of the tree and returns it
if newnode is given adds that node, too."""
assert type(tagpath)==str
tags = []
if tagpath != "":
tags = tagpath.split('/') # tag chain
else:
addTagPath(node, [], newnode)
return node #FIXME: is this correct!?!?
assert len(tags)>0 # we want a chain
# iterative code to search for the path
if branch:
rem = 1
else:
rem = 0
while len(tags) > rem:
tag = tags.pop(0)
nodeList = getTagByName(node, tag)
if len(nodeList) == 0: # couldn't find
tags.insert(0, tag) # put it back in
return addTagPath(node, tags, newnode)
else:
node = nodeList[len(nodeList)-1] # discard other matches
else:
# had only one tag..
return addTagPath(node, tags, newnode)
return node
def addText(node, tagPath, text, branch = True):
newnode = newTextNode(node, text)
return addNode(node, tagPath, newnode, branch = branch)
-171
View File
@@ -1,171 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray at pardus.org.tr>
# Gurer Ozen <gurer at pardus.org.tr>
#
# piksemel written by Gurer Ozen <gurer at pardus.org.tr>
"""
xmlext is a helper module for accessing XML files using
xml.dom.minidom . It is a convenient wrapper for some
DOM functions, and provides path based get/add functions
as in KDE API.
function names are mixedCase for compatibility with minidom,
an 'old library'
this implementation uses piksemel
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import piksemel as iks
parse = iks.parse
newDocument = iks.newDocument
def getAllNodes(node, tagPath):
"""retrieve all nodes that match a given tag path."""
tags = tagPath.split('/')
if len(tags) == 0:
return []
nodeList = [node] # basis case
for tag in tags:
results = map(lambda x: getTagByName(x, tag), nodeList)
nodeList = []
for x in results:
nodeList.extend(x)
pass # emacs indentation error, keep it here
if len(nodeList) == 0:
return []
return nodeList
def getNodeAttribute(node, attrname):
"""get named attribute from DOM node"""
return node.getAttribute(attrname)
def setNodeAttribute(node, attrname, value):
"""get named attribute from DOM node"""
return node.setAttribute(attrname, value)
def getChildElts(parent):
"""get only child elements"""
return [x for x in parent.tags()]
def getTagByName(parent, childName):
return [x for x in parent.tags(childName)]
def getNodeText(node, tagpath = ""):
"""get the first child and expect it to be text!"""
if tagpath!="":
node = getNode(node, tagpath)
if not node:
return None
child = node.firstChild()
if not child:
return None
if child.type() == iks.DATA:
# in any case, strip whitespaces...
return child.data().strip()
else:
raise XmlError(_("getNodeText: Expected text node, got something else!"))
def getChildText(node_s, tagpath):
"""get the text of a child at the end of a tag path"""
node = getNode(node_s, tagpath)
if not node:
return None
return getNodeText(node)
def getNode(node, tagpath):
"""returns the *first* matching node for given tag path."""
if tagpath == "":
return node
assert type(tagpath)==str
tags = tagpath.split('/')
assert len(tags)>0
# iterative code to search for the path
for tag in tags:
currentNode = None
for child in node.tags():
if child.name() == tag:
currentNode = child
break
if not currentNode:
return None
else:
node = currentNode
return currentNode
def createTagPath(node, tags):
"""create new child at the end of a tag chain starting from node
no matter what"""
if len(tags)==0:
return node
for tag in tags:
node = node.insertTag(tag)
return node
def addTagPath(node, tags, newnode=None):
"""add newnode at the end of a tag chain, smart one"""
node = createTagPath(node, tags)
if newnode: # node to add specified
node.insertNode(newnode)
return node
def addNode(node, tagpath, newnode = None, branch=True):
"""add a new node at the end of the tree and returns it
if newnode is given adds that node, too."""
assert type(tagpath)==str
tags = []
if tagpath != "":
tags = tagpath.split('/') # tag chain
else:
addTagPath(node, [], newnode)
return node #FIXME: is this correct!?!?
assert len(tags)>0 # we want a chain
# iterative code to search for the path
if branch:
rem = 1
else:
rem = 0
while len(tags) > rem:
tag = tags.pop(0)
nodeList = getTagByName(node, tag)
if len(nodeList) == 0: # couldn't find
tags.insert(0, tag) # put it back in
return addTagPath(node, tags, newnode)
else:
node = nodeList[len(nodeList)-1] # discard other matches
else:
# had only one tag..
return addTagPath(node, tags, newnode)
return node
def addText(node, tagpath, text):
node = addNode(node, tagpath)
node.insertData(text)
def newNode(node, tag):
return iks.newDocument(tag)
-27
View File
@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
import pisi.context
try:
from xmlfilepiks import *
except:
try:
#if pisi.context.use_mdom:
# zibidi
#else:
from xmlfilecdom import *
except:
#raise Error('cannot find 4suite implementation')
print 'xmlext: cDomlette/piksemel implementation cannot be loaded, falling back to minidom'
from xmlfilemdom import *
@@ -1,171 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr
# Faik Uygur <faik@pardus.org.tr>
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
function names are mixedCase for compatibility with minidom,
an 'old library'
this implementation uses cDomlette from 4suite.org
adapted from the minidom implementation by Faik Uygur and fixed by Eray
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import codecs
import exceptions
import Ft
from Ft.Xml.Domlette import implementation
from Ft.Xml.Domlette import NoExtDtdReader
from Ft.Xml.Domlette import Print, PrettyPrint
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
XHTML_NS = unicode(XHTML_NAMESPACE)
XML_NS = unicode(XML_NAMESPACE)
import pisi
from pisi.pxml.xmlextcdom import *
from pisi.file import File
from pisi.util import join_path as join
class Error(pisi.Error):
pass
class XmlFile(object):
"""A class to help reading and writing an XML file"""
def __init__(self, tag):
self.rootTag = tag
def newDocument(self):
"""clear DOM"""
self.doc = implementation.createDocument(None, self.rootTag, None)
def unlink(self):
"""deallocate DOM structure"""
del self.doc
def rootNode(self):
"""returns root document element"""
return self.doc.documentElement
def readxml(self, uri, tmpDir='/tmp', sha1sum=False, compress=None, sign=None):
uri = File.make_uri(uri)
localpath = File.download(uri, tmpDir,sha1sum=sha1sum,compress=compress,sign=sign)
try:
self.doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(localpath))
return self.doc.documentElement
except Ft.FtException, e:
raise Error(_("File '%s' has invalid XML: %s") % (localpath, str(e)) )
except exceptions.ValueError, e:
raise Error(_("File '%s' not found") % localpath )
def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None):
f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign)
PrettyPrint(self.rootNode(), stream = f)
f.close()
def verifyRootTag(self):
actual_roottag = self.rootNode().tagName
if actual_roottag != self.rootTag:
raise Error(_("Root tagname %s not identical to %s as expected") %
(actual_roottag, self.rootTag) )
# construction helpers
def newNode(self, tag):
return self.doc.createElementNS(None, tag)
def newTextNode(self, text):
return self.doc.createTextNode(text)
def newAttribute(self, attr):
return self.doc.createAttribute(attr)
# read helpers
def getNode(self, tagPath = ""):
"""returns the *first* matching node for given tag path."""
self.verifyRootTag()
return getNode(self.doc.documentElement, tagPath)
def getNodeText(self, tagPath):
"""returns the text of *first* matching node for given tag path."""
node = self.getNode(tagPath)
if not node:
return None
return getNodeText(node)
def getAllNodes(self, tagPath):
"""returns all nodes matching a given tag path."""
self.verifyRootTag()
return getAllNodes(self.doc.documentElement, tagPath)
def getChildren(self, tagpath):
""" returns the children of the given path"""
node = self.getNode(tagpath)
return node.childNodes
# get only elements of a given type
#FIXME: this doesn't work
def getChildrenWithType(self, tagpath, type):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
return filter(lambda x:x.nodeType == type, node.childNodes)
# get only child elements
def getChildElts(self, tagpath):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
try:
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
node.childNodes)
except AttributeError:
return None
# write helpers
def addNode(self, tagPath, newnode = None):
"this adds the newnode under given tag path"
self.verifyRootTag()
return addNode(self.doc.documentElement, tagPath, newnode)
def addNodeUnder(self, node, tagPath, newnode = None):
"this adds the new stuff under node and then following tag path"
self.verifyRootTag()
return addNode(node, tagPath, newnode)
def addChild(self, newnode):
"add a new child node right under root element document"
self.doc.documentElement.appendChild(newnode)
def addText(self, node, text):
"add text to node"
node.appendChild(self.newTextNode(text))
def addTextNode(self, tagPath, text):
"add a text node with given tag path"
node = self.addNode(tagPath, self.newTextNode(text))
return node
def addTextNodeUnder(self, node, tagPath, text):
"add a text node under given node with tag path (phew)"
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
@@ -1,162 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr
# Gurer Ozen <gurer@pardus.org.tr>
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
function names are mixedCase for compatibility with minidom,
an 'old library'
note: this is a particularly inefficient implementation of xmlfile
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import xml.dom.minidom as mdom
from xml.parsers.expat import ExpatError
import codecs
import pisi
from pisi.file import File
class Error(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
class XmlFile(object):
"""A class to help reading and writing an XML file"""
def __init__(self, tag):
self.rootTag = tag
def newDocument(self):
"""clear DOM"""
impl = mdom.getDOMImplementation()
self.doc = impl.createDocument(None, self.rootTag, None)
def unlink(self):
"""deallocate DOM structure"""
self.doc.unlink()
del self.doc
def rootNode(self):
"""returns root document element"""
return self.doc.documentElement
def readxml(self, uri, tmpDir = '/tmp'):
uri = File.make_uri(uri)
localpath = File.download(uri, tmpDir)
try:
self.doc = mdom.parse(localpath)
except ExpatError, inst:
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
str(inst)))
def writexml(self, fileName):
f = codecs.open(fileName,'w', "utf-8")
f.write(self.doc.toprettyxml())
f.close()
def verifyRootTag(self):
actual_roottag = self.rootNode().tagName
if actual_roottag != self.rootTag:
raise Error(_("Root tagname %s not identical to %s as expected") %
(actual_roottag, self.rootTag) )
# construction helpers
def newNode(self, tag):
return self.doc.createElement(tag)
def newTextNode(self, text):
return self.doc.createTextNode(text)
def newAttribute(self, attr):
return self.doc.createAttribute(attr)
# read helpers
def getNode(self, tagPath = ""):
"""returns the *first* matching node for given tag path."""
self.verifyRootTag()
return getNode(self.doc.documentElement, tagPath)
def getNodeText(self, tagPath):
"""returns the text of *first* matching node for given tag path."""
node = self.getNode(tagPath)
if not node:
return None
return getNodeText(node)
def getAllNodes(self, tagPath):
"""returns all nodes matching a given tag path."""
self.verifyRootTag()
return getAllNodes(self.doc.documentElement, tagPath)
def getChildren(self, tagpath):
""" returns the children of the given path"""
node = self.getNode(tagpath)
return node.childNodes
# get only elements of a given type
#FIXME: this doesn't work
def getChildrenWithType(self, tagpath, type):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
return filter(lambda x:x.nodeType == type, node.childNodes)
# get only child elements
def getChildElts(self, tagpath):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
try:
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
node.childNodes)
except AttributeError:
return None
# write helpers
def addNode(self, tagPath, newnode = None):
"this adds the newnode under given tag path"
self.verifyRootTag()
return addNode(self.doc.documentElement, tagPath, newnode)
def addNodeUnder(self, node, tagPath, newnode = None):
"this adds the new stuff under node and then following tag path"
self.verifyRootTag()
return addNode(node, tagPath, newnode)
def addChild(self, newnode):
"add a new child node right under root element document"
self.doc.documentElement.appendChild(newnode)
def addText(self, node, text):
"add text to node"
node.appendChild(self.newTextNode(text))
def addTextNode(self, tagPath, text):
"add a text node with given tag path"
node = self.addNode(tagPath, self.newTextNode(text))
return node
def addTextNodeUnder(self, node, tagPath, text):
"add a text node under given node with tag path (phew)"
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
@@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
function names are mixedCase for compatibility with minidom,
an 'old library'
this implementation uses piksemel
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import codecs
import exceptions
import piksemel as iks
import pisi
from pisi.file import File
from pisi.util import join_path as join
class Error(pisi.Error):
pass
class XmlFile(object):
"""A class to help reading and writing an XML file"""
def __init__(self, tag):
self.rootTag = tag
def newDocument(self):
"""clear DOM"""
self.doc = iks.newDocument(self.rootTag)
def unlink(self):
"""deallocate DOM structure"""
del self.doc
def rootNode(self):
"""returns root document element"""
return self.doc
def readxml(self, uri, tmpDir='/tmp', sha1sum=False, compress=None, sign=None):
uri = File.make_uri(uri)
localpath = File.download(uri, tmpDir,sha1sum=sha1sum,compress=compress,sign=sign)
try:
self.doc = iks.parse(localpath)
return self.doc
except Exception, e:
raise Error(_("File '%s' has invalid XML") % (localpath) )
def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None):
f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign)
f.write(self.doc.toPrettyString())
f.close()
-121
View File
@@ -1,121 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural
import os, fcntl
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.lockeddbshelve as shelve
import pisi.context as ctx
import pisi.packagedb as packagedb
import pisi.util as util
from pisi.uri import URI
class Error(pisi.Error):
pass
class Repo:
def __init__(self, indexuri):
self.indexuri = indexuri
#class HttpRepo
#class FtpRepo
#class RemovableRepo
class RepoDB(object):
"""RepoDB maps repo ids to repository information"""
def __init__(self, txn = None):
self.d = shelve.LockedDBShelf("repo")
def proc(txn):
if not self.d.has_key("order", txn):
self.d.put("order", [], txn)
self.d.txn_proc(proc, txn)
def close(self):
self.d.close()
def repo_name(self, ix):
l = self.list()
return l[ix]
def swap(self, x, y):
def proc(txn):
l = self.d.get("order", txn)
t = l[x]
l[x] = l[y]
l[y] = t
self.d.put("order", l, txn)
self.d.txn_proc(proc, txn)
def has_repo(self, name):
name = str(name)
return self.d.has_key("repo-" + name)
def get_repo(self, name):
name = str(name)
return self.d["repo-" + name]
def add_repo(self, name, repo_info, txn = None):
name = str(name)
assert (isinstance(repo_info,Repo))
def proc(txn):
if self.d.has_key("repo-" + name, txn):
raise Error(_('Repository %s already exists') % name)
self.d.put("repo-" + name, repo_info, txn)
order = self.d.get("order", txn)
order.append(name)
self.d.put("order", order, txn)
self.d.txn_proc(proc, txn)
def list(self):
return self.d["order"]
def clear(self):
self.d.clear()
def remove_repo(self, name, txn = None):
name = str(name)
def proc(txn):
self.d.delete("repo-" + name, txn)
list = self.d.get("order", txn)
list.remove(name)
self.d.put("order", list, txn)
ctx.packagedb.remove_repo(name, txn=txn)
ctx.sourcedb.remove_repo(name, txn=txn)
self.d.txn_proc(proc, txn)
db = None
def init():
global db
if db:
return db
db = RepoDB()
return db
def finalize():
global db
if db:
db.close()
db = None
@@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
#
# Author: Eray Ozkural <eray@pardus.org.tr>
import pisi
import pisi.context as ctx
class Error(pisi.Error):
pass
class Exception(pisi.Exception):
pass
# API
from invertedindex import InvertedIndex
import preprocess as p
def init(ids, langs):
"initialize databases"
assert type(ids)==type([])
assert type(langs)==type([])
ctx.invidx = {}
for id in ids:
ctx.invidx[id] = {}
for lang in langs:
ctx.invidx[id][lang] = InvertedIndex(id, lang)
def finalize():
import pisi.context as ctx
if ctx.invidx:
for id in ctx.invidx.iterkeys():
for lang in ctx.invidx[id].iterkeys():
ctx.invidx[id][lang].close()
ctx.invidx = {}
def add_doc(id, lang, docid, str, txn = None):
terms = p.preprocess(lang, str)
ctx.invidx[id][lang].add_doc(docid, terms, txn)
def remove_doc(id, lang, docid, str, txn = None):
ctx.invidx[id][lang].remove_doc(docid, txn)
def query_terms(id, lang, terms, txn = None):
terms = map(lambda x: p.lower(lang, x), terms)
return ctx.invidx[id][lang].query(terms, txn)
def query(id, lang, str, txn = None):
terms = p.preprocess(lang, str)
return query_terms(id, lang, terms, txn)
@@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
import types
import pisi.lockeddbshelve as shelve
class InvertedIndex(object):
"""a database of term -> set of documents"""
def __init__(self, id, lang):
self.d = shelve.LockedDBShelf('ii-%s-%s' % (id, lang))
def close(self):
self.d.close()
def has_term(self, term, txn = None):
return self.d.has_key(shelve.LockedDBShelf.encodekey(term), txn)
def get_term(self, term, txn = None):
"""get set of doc ids given term"""
term = shelve.LockedDBShelf.encodekey(term)
def proc(txn):
if not self.has_term(term, txn):
return set()
return self.d.get(term, txn)
return self.d.txn_proc(proc, txn)
def query(self, terms, txn = None):
def proc(txn):
docs = [ self.get_term(x, txn) for x in terms ]
if docs:
return reduce(lambda x,y: x.intersection(y), docs)
else:
return set()
return self.d.txn_proc(proc, txn)
def list_terms(self, txn= None):
list = []
def f(txn):
for term in self.d.iterkeys(txn):
list.append(term)
return list
return self.d.txn_proc(f, txn)
def add_doc(self, doc, terms, txn = None):
def f(txn):
for term_i in terms:
term_i = shelve.LockedDBShelf.encodekey(term_i)
term_i_docs = self.get_term(term_i, txn)
term_i_docs.add(doc)
self.d.put(term_i, term_i_docs, txn) # update
return self.d.txn_proc(f, txn)
def remove_doc(self, doc, terms):
def f(txn):
for term_i in terms:
term_i = shelve.LockedDBShelf.encodekey(term_i)
term_i_docs = self.get_term(term_i)
term_i_docs.remove(doc)
self.d.put(term_i, term_i_docs, txn) # update
return self.d.txn_proc(f, txn)
@@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
import tokenize
def lowly_python(str):
def lowly_char(c):
if c=='I':
lowly = 'i' # because of some fools we can't choose locale in lower
else:
lowly = c.lower()
return c
r = ""
for c in str:
r += lowly_char(c)
return r
def lower(lang, str):
if lang=='tr':
return lowly_python(str)
else:
return str.lower()
def preprocess(lang, str):
terms = tokenize.tokenize(lang, str)
# normalize
terms = map(lambda x: lower(lang, x), terms)
return terms
@@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
#
# rev 1: very simple tokenization, for testing
import string
def tokenize(lang, str):
if type(str) != type(unicode()):
str = unicode(str)
tokens = []
token = unicode()
for x in str:
if x in string.whitespace or x in string.punctuation:
if len(token) > 0:
tokens.append(token)
token = unicode()
else:
token += x
if token:
tokens.append(token)
return tokens
-71
View File
@@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Baris Metin <baris@pardus.org.tr>
# Eray Ozkural <eray@pardus.org.tr>
# python standard library
from os.path import join
from os import access, R_OK
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# pisi modules
import pisi
import pisi.util as util
import pisi.context as ctx
from pisi.archive import Archive
from pisi.uri import URI
from pisi.fetcher import fetch_url
class Error(pisi.Error):
pass
class SourceArchive:
"""source archive. this is a class responsible for fetching
and unpacking a source archive"""
def __init__(self, spec, pkg_work_dir):
self.url = URI(spec.source.archive.uri)
self.pkg_work_dir = pkg_work_dir
self.archiveFile = join(ctx.config.archives_dir(), self.url.filename())
self.archive = spec.source.archive
def fetch(self, interactive=True):
if not self.is_cached(interactive):
if interactive:
progress = ctx.ui.Progress
else:
progress = None
fetch_url(self.url, ctx.config.archives_dir(), progress)
def is_cached(self, interactive=True):
if not access(self.archiveFile, R_OK):
return False
# check hash
if util.check_file_hash(self.archiveFile, self.archive.sha1sum):
if interactive:
ctx.ui.info(_('%s [cached]') % self.archive.name)
return True
return False
def unpack(self, clean_dir=True):
# check archive file's integrity
if not util.check_file_hash(self.archiveFile, self.archive.sha1sum):
raise Error, _("unpack: check_file_hash failed")
archive = Archive(self.archiveFile, self.archive.type)
archive.unpack(self.pkg_work_dir, clean_dir)
-111
View File
@@ -1,111 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Author: Eray Ozkural <eray@pardus.org.tr>
"""
package source database
interface for update/query to local package repository
we basically store everything in sourceinfo class
yes, we are cheap
to handle multiple repositories, for sources, we
store a set of repositories in which the source appears.
the actual guy to take is determined from the repo order.
"""
import os
import fcntl
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.util as util
import pisi.context as ctx
import pisi.lockeddbshelve as shelve
import pisi.repodb
from pisi.itembyrepodb import ItemByRepoDB
class NotfoundError(pisi.Error):
pass
class SourceDB(object):
def __init__(self):
self.d = ItemByRepoDB('source')
self.dpkgtosrc = ItemByRepoDB('pkgtosrc')
def close(self):
self.d.close()
self.dpkgtosrc.close()
def list(self):
return self.d.list()
def has_spec(self, name, repo=None, txn=None):
return self.d.has_key(name, repo, txn)
def get_spec(self, name, repo=None, txn = None):
try:
return self.d.get_item(name, repo, txn)
except pisi.itembyrepodb.NotfoundError, e:
raise NotfoundError(_("Source package %s not found") % name)
def get_spec_repo(self, name, repo=None, txn = None):
try:
return self.d.get_item_repo(name, repo, txn)
except pisi.itembyrepodb.NotfoundError, e:
raise NotfoundError(_("Source package %s not found") % name)
def pkgtosrc(self, name, txn = None):
return self.dpkgtosrc.get_item(name, txn=txn)
def add_spec(self, spec, repo, txn = None):
assert not spec.errors()
name = str(spec.source.name)
def proc(txn):
self.d.add_item(name, spec, repo, txn)
for pkg in spec.packages:
self.dpkgtosrc.add_item(pkg.name, name, repo, txn)
self.d.txn_proc(proc, txn)
def remove_spec(self, name, repo, txn = None):
name = str(name)
def proc(txn):
assert self.has_spec(name, txn=txn)
spec = self.d.get_item(name, repo, txn)
self.d.remove_item(name, txn=txn)
for pkg in spec.packages:
self.dpkgtosrc.remove_item_repo(pkg.name, repo, txn)
self.d.txn_proc(proc, txn)
def remove_repo(self, repo, txn = None):
def proc(txn):
self.d.remove_repo(repo, txn=txn)
self.dpkgtosrc.remove_repo(repo, txn=txn)
self.d.txn_proc(proc, txn)
sourcedb = None
def init():
global sourcedb
if sourcedb:
return sourcedb
sourcedb = SourceDB()
return sourcedb
def finalize():
global sourcedb
if sourcedb:
sourcedb.close()
sourcedb = None
-274
View File
@@ -1,274 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr>
"""
Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
files are specification files for PISI source packages. This module
provides read and write routines for PSPEC files.
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# standard python modules
from os.path import basename
# pisi modules
from pisi.pxml.xmlfile import XmlFile
import pisi.pxml.autoxml as autoxml
import pisi.context as ctx
from pisi.dependency import Dependency
import pisi.dependency
import pisi.component as component
from pisi.util import Checks
import pisi.util as util
class Error(pisi.Error):
pass
__metaclass__ = autoxml.autoxml
class Packager:
t_Name = [autoxml.Text, autoxml.mandatory]
t_Email = [autoxml.String, autoxml.mandatory]
def __str__(self):
s = "%s <%s>" % (self.name, self.email)
return s
class AdditionalFile:
s_Filename = [autoxml.String, autoxml.mandatory]
a_target = [autoxml.String, autoxml.mandatory]
a_permission = [autoxml.String, autoxml.optional]
def __str__(self):
s = "%s -> %s " % (self.filename, self.target)
if self.permission:
s += '(%s)' % self.permission
return s
class Patch:
s_Filename = [autoxml.String, autoxml.mandatory]
a_compressionType = [autoxml.String, autoxml.optional]
a_level = [autoxml.Integer, autoxml.optional]
a_target = [autoxml.String, autoxml.optional]
#FIXME: what's the cleanest way to give a default value for reading level?
#def decode_hook(self, node, errs, where):
# if self.level == None:
# self.level = 0
def __str__(self):
s = self.filename
if self.compressionType:
s += ' (' + self.compressionType + ')'
if self.level:
s += ' level:' + self.level
if self.target:
s += ' target:' + self.target
return s
class Update:
a_release = [autoxml.String, autoxml.mandatory]
t_Date = [autoxml.String, autoxml.mandatory]
t_Version = [autoxml.String, autoxml.mandatory]
t_Type = [autoxml.String, autoxml.optional]
t_Name = [autoxml.Text, autoxml.optional]
t_Email = [autoxml.String, autoxml.optional]
t_Comment = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.date
s += ", ver=" + self.version
s += ", rel=" + self.release
if self.type:
s += ", type=" + self.type
return s
class Path:
s_Path = [autoxml.String, autoxml.mandatory]
a_fileType = [autoxml.String, autoxml.optional]
a_permanent = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.path
s += ", type=" + self.fileType
return s
class ComarProvide:
s_om = [autoxml.String, autoxml.mandatory]
a_script = [autoxml.String, autoxml.mandatory]
def __str__(self):
# FIXME: descriptive enough?
s = self.script
s += ' (' + self.om + ')'
return s
class Archive:
s_uri = [ autoxml.String, autoxml.mandatory ]
a_type =[ autoxml.String, autoxml.mandatory ]
a_sha1sum =[ autoxml.String, autoxml.mandatory ]
def decode_hook(self, node, errs, where):
self.name = basename(self.uri)
def __str__(self):
s = _('URI: %s, type: %s, sha1sum: %s') % (self.uri, self.type, self.sha1sum)
return s
class Source:
t_Name = [autoxml.String, autoxml.mandatory]
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [Packager, autoxml.mandatory]
t_Summary = [autoxml.LocalText, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_IsA = [ [autoxml.String], autoxml.optional]
t_PartOf = [autoxml.String, autoxml.optional]
t_Icon = [ autoxml.String, autoxml.optional]
t_License = [ [autoxml.String], autoxml.mandatory]
t_Archive = [Archive, autoxml.mandatory ]
t_Patches = [ [Patch], autoxml.optional]
t_BuildDependencies = [ [Dependency], autoxml.optional]
t_Version = [ autoxml.String, autoxml.optional]
t_Release = [ autoxml.String, autoxml.optional]
t_SourceURI = [ autoxml.String, autoxml.optional ] # used in index
class Package:
t_Name = [ autoxml.String, autoxml.mandatory ]
t_Summary = [ autoxml.LocalText, autoxml.optional ]
t_Description = [ autoxml.LocalText, autoxml.optional ]
t_IsA = [ [autoxml.String], autoxml.optional]
t_PartOf = [autoxml.String, autoxml.optional]
t_License = [ [autoxml.String], autoxml.optional]
t_Icon = [ autoxml.String, autoxml.optional]
t_PackageDependencies = [ [Dependency], autoxml.optional, "RuntimeDependencies/Dependency"]
t_ComponentDependencies = [ [autoxml.String], autoxml.optional, "RuntimeDependencies/Component"]
t_Files = [ [Path], autoxml.optional]
t_Conflicts = [ [autoxml.String], autoxml.optional, "Conflicts/Package"]
t_ProvidesComar = [ [ComarProvide], autoxml.optional, "Provides/COMAR"]
#t_RequiresComar = [ [autoxml.String], autoxml.mandatory, "Requires/COMAR"]
t_AdditionalFiles = [ [AdditionalFile], autoxml.optional]
t_History = [ [Update], autoxml.optional]
# FIXME: needed in build process, to distinguish dynamically generated debug packages.
# find a better way to do this.
debug_package = False
def runtimeDependencies(self):
deps = self.packageDependencies
deps += [ ctx.componentdb.get_component[x].packages for x in self.componentDependencies ]
return deps
def pkg_dir(self):
packageDir = self.name + '-' \
+ self.version + '-' \
+ self.release
return util.join_path( ctx.config.lib_dir(), 'package', packageDir)
def installable(self):
"""calculate if pkg is installable currently"""
deps = self.runtimeDependencies()
return pisi.dependency.satisfies_dependencies(self.name, deps)
def __str__(self):
s = _('Name: %s, version: %s, release: %s, build %s\n') % (
self.name, self.version, self.release, self.build)
s += _('Summary: %s\n') % unicode(self.summary)
s += _('Description: %s\n') % unicode(self.description)
s += _('Component: %s\n') % unicode(self.partOf)
s += _('Provides: ')
for x in self.providesComar:
s += x.om
return s + '\n'
class SpecFile(XmlFile):
__metaclass__ = autoxml.autoxml #needed when we specify a superclass
tag = "PISI"
t_Source = [ Source, autoxml.mandatory]
t_Packages = [ [Package], autoxml.mandatory, "Package"]
t_History = [ [Update], autoxml.mandatory]
t_Components = [ [component.Component], autoxml.optional, "Component"]
def read_hook(self, errs):
"""Read PSPEC file"""
self.merge_tags()
self.override_tags()
def merge_tags(self):
"""Merge tags from Source in Packages. Some tags in Packages merged
with the tags from Source. There is a more detailed
description in documents."""
# FIXME: copy only needed information
# no need to keep full history with comments in metadata.xml
self.source.history = self.history
# To avoid tag duplication in PSPEC we need to get
# the last version and release information
# from the most recent History/Update.
if not self.source.version:
self.source.version = self.history[0].version
if not self.source.release:
self.source.release = self.history[0].release
tmp = []
for pkg in self.packages:
pkg.isA.extend(self.source.isA)
pkg.history = self.history
tmp.append(pkg)
self.packages = tmp
def override_tags(self):
"""Override tags from Source in Packages. Some tags in Packages
overrides the tags from Source. There is a more detailed
description in documents."""
tmp = []
for pkg in self.packages:
if not pkg.summary:
pkg.summary = self.source.summary
if not pkg.description:
pkg.description = self.source.description
if not pkg.partOf:
pkg.partOf = self.source.partOf
if not pkg.license:
pkg.license = self.source.license
if not pkg.icon:
pkg.icon = self.source.icon
tmp.append(pkg)
self.packages = tmp
-103
View File
@@ -1,103 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# generic user interface
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# A. Murat Eren <meren@pardus.org.tr>
import sys
import pisi
import pisi.context as ctx
(installed, upgraded, removed, installing, removing,
configuring, configured, extracting, downloading, packagestogo) = range(10)
class UI(object):
"Abstract class for UI operations, derive from this."
class Progress:
def __init__(self, totalsize, existsize = 0):
self.totalsize = totalsize
try:
self.percent = (existsize * 100) / totalsize
except:
self.percent = 0
def update(self, size):
if not self.totalsize:
return 100
try:
self.percent = (size * 100) / self.totalsize
except:
self.percent = 0
return self.percent
def __init__(self, debuggy = False, verbose = False):
self.show_debug = debuggy
self.show_verbose = verbose
def close(self):
"cleanup stuff here"
pass
def set_verbose(self, flag):
self.show_verbose = flag
def set_debug(self, flag):
self.show_debug = flag
def info(self, msg, verbose = False, noln = False):
"give an informative message"
pass
def ack(self, msg):
"inform the user of an important event and wait for acknowledgement"
pass
def debug(self, msg):
"show debugging info"
if self.show_debug:
self.info('DEBUG: ' + msg)
def warning(self,msg):
"warn the user"
pass
def error(self,msg):
"inform a (possibly fatal) error"
pass
#FIXME: merge this with info, this just means "important message"
def action(self,msg):
"uh?"
pass
def choose(self, msg, list):
"ask the user to choose from a list of alternatives"
pass
def confirm(self, msg):
"ask a yes/no question"
pass
def display_progress(self, pd):
"display progress"
pass
def status(self, msg = None):
"set status, if not given clear it"
pass
def notify(self, event, **keywords):
"notify UI of a significant event"
pass
-109
View File
@@ -1,109 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Simplifies working with URLs, purl module provides common URL
# parsing and processing
from urlparse import urlparse
from os.path import basename
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi.util as util
class URI(object):
"""URI class provides a URL parser and simplifies working with
URLs."""
def __init__(self, uri=None):
if uri:
self.set_uri(str(uri))
else:
self.__scheme = None
self.__location = None
self.__path = None
self.__filename = None
self.__params = None
self.__query = None
self.__fragment = None
self.__uri = None
self.__authinfo = None
def get_uri(self):
if self.__uri:
return self.__uri
return None
def set_uri(self, uri):
# (scheme, location, path, params, query, fragment)
uri = str(uri)
u = urlparse(uri, "file")
self.__scheme = u[0]
self.__location = u[1]
self.__path = u[2]
self.__filename = basename(self.__path)
self.__params = u[3]
self.__query = u[4]
self.__fragment = u[5]
self.__uri = uri
def is_local_file(self):
if self.scheme() == "file":
return True
else:
return False
def is_remote_file(self):
return not self.is_local_file()
def is_absolute_path(self):
return util.absolute_path(self.__path)
def is_relative_path(self):
return not self.is_absolute_path()
def set_auth_info(self, authTuple):
if not isinstance(authTuple, tuple):
raise Exception, _("setAuthInfo needs a tuple (user, pass)")
self.__authinfo = authTuple
def auth_info(self):
return self.__authinfo
def scheme(self):
return self.__scheme
def location(self):
return self.__location
def path(self):
return self.__path
def filename(self):
return self.__filename
def params(self):
return self.__params
def query(self):
return self.__query
def fragment(self):
return self.__fragment
def __str__(self):
return self.get_uri()
uri = property(get_uri, set_uri)
-638
View File
@@ -1,638 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# misc. utility functions, including process and file utils
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr>
# S. Caglar Onur <caglar@pardus.org.tr>
# A. Murat Eren <meren@pardus.org.tr>
# standard python modules
import os
import re
import sys
import sha
import shutil
import string
import statvfs
import operator
import subprocess
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# pisi modules
import pisi
import pisi.context as ctx
class Error(pisi.Error):
pass
class FileError(Error):
pass
#########################
# spec validation utility #
#########################
class Checks:
def __init__(self):
self.list = []
def add(self, err):
self.list.append(err)
def join(self, list):
self.list.extend(list)
def has_tag(self, var, section, name):
if not var:
self.list.append(_("%s section should have a '%s' tag") % (section, name))
def has_error():
return len(self.list)>0
def print_errors(list):
for x in list:
ctx.ui.error(x)
print_errors = staticmethod(print_errors)
#########################
# string/list/functional#
#########################
def every(pred, seq):
return reduce(operator.and_, map(pred, seq))
def any(pred, seq):
return reduce(operator.or_, map(pred, seq))
def unzip(seq):
return zip(*seq)
def concat(l):
'''concatenate a list of lists'''
return reduce( operator.concat, l )
def strlist(l):
"""concatenate string reps of l's elements"""
return "".join(map(lambda x: str(x) + ' ', l))
def multisplit(str, chars):
""" split str with any of chars"""
l = [str]
for c in chars:
l = concat(map(lambda x:x.split(c), l))
return l
def same(l):
'''check if all elements of a sequence are equal'''
if len(l)==0:
return True
else:
last = l.pop()
for x in l:
if x!=last:
return False
return True
def prefix(a, b):
'''check if sequence a is a prefix of sequence b'''
if len(a)>len(b):
return False
for i in range(0,len(a)):
if a[i]!=b[i]:
return False
return True
def remove_prefix(a,b):
"remove prefix a from sequence b"
assert prefix(a,b)
return b[len(a):]
def human_readable_size(size = 0):
symbols, depth = [' B', 'KB', 'MB', 'GB'], 0
while size > 1000 and depth < 3:
size = float(size / 1024)
depth += 1
return size, symbols[depth]
def human_readable_rate(size = 0):
x = human_readable_size(size)
return x[0], x[1] + '/s'
##############################
# Process Releated Functions #
##############################
def run_batch(cmd, realtime = False):
"""run command non-interactively/realtime and report return value and output"""
ctx.ui.info(_('Running ') + cmd, verbose=True)
if ctx.stdout:
stdout = ctx.stdout
else:
stdout = subprocess.PIPE
if ctx.stderr:
stderr = ctx.stderr
else:
stderr = subprocess.STDOUT
out = err = ""
p = subprocess.Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
if realtime and not ctx.stdout:
while p.poll() == None:
line = p.stdout.readline()
if line:
ctx.ui.debug(line[:-1])
else:
out, err = p.communicate()
ctx.ui.debug(_('return value for "%s" is %s') % (cmd, p.returncode))
return (p.returncode, out, err)
######################
# Terminal functions #
######################
def has_xterm():
return os.environ.has_key("TERM") and sys.stderr.isatty()
def xterm_title(message):
"""sets message as a console window's title"""
if os.environ.has_key("TERM") and sys.stderr.isatty():
terminalType = os.environ["TERM"]
for term in ["xterm", "Eterm", "aterm", "rxvt", "screen", "kterm", "rxvt-unicode"]:
if terminalType.startswith(term):
sys.stderr.write("\x1b]2;"+str(message)+"\x07")
sys.stderr.flush()
break
def xterm_title_reset():
"""resets console window's title"""
if os.environ.has_key("TERM"):
terminalType = os.environ["TERM"]
xterm_title(os.environ["TERM"])
#############################
# Path Processing Functions #
#############################
def splitpath(a):
"""split path into components and return as a list
os.path.split doesn't do what I want like removing trailing /"""
comps = a.split(os.path.sep)
if comps[len(comps)-1]=='':
comps.pop()
return comps
def makepath(comps, relative = False, sep = os.path.sep):
"""reconstruct a path from components"""
path = reduce(lambda x,y: x + sep + y, comps, '')
if relative:
return path[len(sep):]
else:
return path
def parentpath(a, sep = os.path.sep):
# remove trailing '/'
a = a.rstrip(sep)
return a[:a.rfind(sep)]
def parenturi(a):
return parentpath(a, '/')
# I'm not sure how necessary this is. Ahem.
def commonprefix(l):
"""an improved version of os.path.commonprefix,
returns a list of path components"""
common = []
comps = map(splitpath, l)
for i in range(0, min(len,l)):
compi = map(lambda x: x[i], comps) # get ith slice
if same(compi):
common.append(compi[0])
return common
# but this one is necessary
def subpath(a, b):
"find if path a is before b in the directory tree"
return prefix(splitpath(a), splitpath(b))
def removepathprefix(prefix, path):
"remove path prefix a from b, finding the pathname rooted at a"
comps = remove_prefix(splitpath(prefix), splitpath(path))
if len(comps) > 0:
return join_path(*tuple(comps))
else:
return ""
def absolute_path(path):
"determine if given @path is absolute"
comps = splitpath(path)
return comps[0] == ''
def join_path(a, *p):
"""Join two or more pathname components, inserting '/' as needed"""
"""The python original version has a silly logic"""
path = a
for b in p:
b = b.lstrip('/')
if path == '' or path.endswith('/'):
path += b
else:
path += '/' + b
return path
####################################
# File/Directory Related Functions #
####################################
def check_file(file, mode = os.F_OK):
"shorthand to check if a file exists"
if not os.access(file, mode):
raise FileError("File " + file + " not found")
return True
def check_dir(dir):
"""check if directory exists, and create if it doesn't.
works recursively"""
dir = dir.strip().rstrip("/")
if not os.access(dir, os.F_OK):
os.makedirs(dir)
def clean_dir(path):
"Remove all content of a directory (top)"
# don't reimplement the wheel
if os.path.exists(path):
shutil.rmtree(path)
def creation_time(file):
"""returns the creation time of the given file"""
if check_file(file):
import time
st = os.stat(file)
return time.localtime(st.st_ctime)
def dir_size(dir):
""" calculate the size of files under a dir
based on the os module example"""
# It's really hard to give an approximate value for package's
# installed size. Gettin a sum of all files' sizes if far from
# being true. Using 'du' command (like Debian does) can be a
# better solution :(.
# Not really, du calculates size on disk, this is much better -- exa
from os.path import getsize, islink, isdir, exists
join = join_path
if exists(dir) and (not isdir(dir) and not islink(dir)):
#so, this is not a directory but file..
return getsize(dir)
if islink(dir):
return long(len(os.readlink(dir)))
def sizes():
for root, dirs, files in os.walk(dir):
yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))])
yield sum([long(len(os.readlink((join(root, name))))) for name in files if islink(join(root,name))])
return sum( sizes() )
def copy_file(src,dest):
"""copy source file to destination file"""
check_file(src)
check_dir(os.path.dirname(dest))
shutil.copyfile(src, dest)
def is_ar_file(file_path):
return open(file_path).readline().strip() == '!<arch>'
def clean_ar_timestamps(ar_file):
"""clean the timestamps of the ar files"""
if not is_ar_file(ar_file):
return
content = open(ar_file).readlines()
fp = open(ar_file, 'w')
for line in content:
pos = line.rfind(chr(32) + chr(96))
if pos > -1 and line[pos - 57:pos + 2].find(chr(47)) > -1:
line = line[:pos - 41] + '0000000000' + line[pos - 31:]
fp.write(line)
fp.close()
# FIXME: this should be done in a much much simpler way
# as it stands, it seems to be a kludge to solve
# an unrelated problem
def get_file_hashes(top, excludePrefix=None, removePrefix=None):
"""Generator function iterates over a toplevel path and returns the
(filePath, sha1Hash) tuples for all files. If excludePrefixes list
is given as a parameter, function will exclude the filePaths
matching those prefixes. The removePrefix string parameter will be
used to remove prefix from filePath while matching excludes, if
given."""
def sha1_sum(f, data=False):
if not data and f.endswith('.a'):
#workaround for .a issue..
#don't skip .a files,
#but pad their timestamps with '0'..
clean_ar_timestamps(f)
func = None
if data:
func = sha1_data
else:
func = sha1_file
try:
return func(f)
except FileError, e:
if os.path.islink(f):
ctx.ui.info(_("Including external link '%s'") % f)
elif os.path.isdir(f):
ctx.ui.info(_("Including directory '%s'") % f)
else:
raise e
return None
def has_excluded_prefix(filename):
if excludePrefix and removePrefix:
tempfnam = remove_prefix(removePrefix, filename)
for p in excludePrefix:
if tempfnam.startswith(p):
return 1
return 0
# handle single file
if os.path.isfile(top):
yield (top, sha1_sum(top))
return
# handle single symlink declaration here.
if os.path.islink(top):
yield (top, sha1_sum(os.readlink(top), True))
return
for root, dirs, files in os.walk(top, topdown=False):
#bug 339
if os.path.islink(root) and not has_excluded_prefix(root):
#yield the symlink..
#bug 373
yield (root, sha1_sum(os.readlink(root), True))
excludePrefix.append(remove_prefix(removePrefix, root) + "/")
continue
#bug 397
for dir in dirs:
d = join_path(root, dir)
if os.path.islink(d) and not has_excluded_prefix(d):
yield (d, sha1_sum(os.readlink(d), True))
excludePrefix.append(remove_prefix(removePrefix, d) + "/")
#bug 340
if os.path.isdir(root) and not has_excluded_prefix(root):
parent, r, d, f = root, '', '', ''
for r, d, f in os.walk(parent, topdown=False): pass
if not f and not d:
yield (parent, sha1_sum(parent))
for fname in files:
f = join_path(root, fname)
if has_excluded_prefix(f):
continue
#bug 373
elif os.path.islink(f):
yield (f, sha1_sum(os.readlink(f), True))
else:
yield (f, sha1_sum(f))
def copy_dir(src, dest):
"""copy source dir to destination dir recursively"""
shutil.copytree(src, dest)
def check_file_hash(filename, hash):
"""Check the files integrity with a given hash"""
return sha1_file(filename) == hash
def sha1_file(filename):
"""calculate sha1 hash of filename"""
# Broken links can cause problem!
try:
m = sha.new()
f = file(filename, 'rb')
for line in f:
m.update(line)
return m.hexdigest()
except IOError:
raise FileError(_("I/O Error: Cannot calculate SHA1 hash of %s") % filename)
def sha1_data(data):
"""calculate sha1 hash of given data"""
try:
m = sha.new()
m.update(data)
return m.hexdigest()
except:
raise Error(_("Cannot calculate SHA1 hash of given data"))
def uncompress(patchFile, compressType="gz", targetDir=None):
"""uncompresses a file and returns the path of the uncompressed
file"""
if targetDir:
filePath = join_path(targetDir,
os.path.basename(patchFile))
else:
filePath = os.path.basename(patchFile)
if compressType == "gz":
from gzip import GzipFile
obj = GzipFile(patchFile)
elif compressType == "bz2":
from bz2 import BZ2File
obj = BZ2File(patchFile)
open(filePath, "w").write(obj.read())
return filePath
def do_patch(sourceDir, patchFile, level = 0, target = ''):
"""simple function to apply patches.."""
cwd = os.getcwd()
os.chdir(sourceDir)
if level == None:
level = 0
if target == None:
target = ''
check_file(patchFile)
(ret, out, err) = run_batch("patch -p%d %s < %s" %
(level, target, patchFile))
if ret:
if out is None and err is None:
# Which means stderr and stdout directed so they are None
raise Error(_("ERROR: patch (%s) failed") % (patchFile))
else:
raise Error(_("ERROR: patch (%s) failed: %s") % (patchFile, out))
os.chdir(cwd)
def strip_directory(top, excludelist=[]):
for root, dirs, files in os.walk(top):
for fn in files:
frpath = join_path(root, fn)
drpath = join_path(os.path.dirname(top),
ctx.const.debug_dir_suffix,
remove_prefix(top, frpath))
# Some upstream sources have buggy libtool and ltmain.sh with them,
# which causes wrong path entries in *.la files. And these wrong path
# entries sometimes triggers compile-time errors or linkage problems.
# Instead of patching all these buggy sources and maintain these patches,
# PISI removes wrong paths...
extension = os.path.splitext(frpath)[1]
if extension == ".la":
# FIXME: I'm regular expr. idiot, so one can convert this to python...
os.system("sed -i -e 's~-L/var/tmp/pisi/[[:graph:]]* ~~g' %s" % frpath)
# real path in .pisi package
p = '/' + removepathprefix(top, frpath)
strip = True
for exclude in excludelist:
if p.startswith(exclude):
strip = False
ctx.ui.debug("%s [%s]" %(p, "NoStrip"))
if strip:
if strip_file(frpath, drpath):
ctx.ui.debug("%s [%s]" %(p, "stripped"))
def strip_file(filepath, outpath):
"""strip a file"""
p = os.popen("file \"%s\"" % filepath)
o = p.read()
def run_strip(f, flags=""):
p = os.popen("strip %s %s" %(flags, f))
ret = p.close()
if ret:
ctx.ui.warning(_("strip command failed for file '%s'!") % f)
def save_elf_debug(f, o):
"""copy debug info into file.debug file"""
p = os.popen("objcopy --only-keep-debug %s %s%s" % (f, o, ctx.const.debug_file_suffix))
ret = p.close()
if ret:
ctx.ui.warning(_("objcopy (keep-debug) command failed for file '%s'!") % f)
"""mark binary/shared objects to use file.debug"""
p = os.popen("objcopy --add-gnu-debuglink=%s%s %s" % (o, ctx.const.debug_file_suffix, f))
ret = p.close()
if ret:
ctx.ui.warning(_("objcopy (add-debuglink) command failed for file '%s'!") % f)
if "current ar archive" in o:
run_strip(filepath, "-g")
return True
elif "SB executable" in o:
if not ctx.get_option('no_debug'):
check_dir(os.path.dirname(outpath))
save_elf_debug(filepath, outpath)
run_strip(filepath)
return True
elif "SB shared object" in o:
if not ctx.get_option('no_debug'):
check_dir(os.path.dirname(outpath))
save_elf_debug(filepath, outpath)
run_strip(filepath, "--strip-unneeded")
# FIXME: warn for TEXTREL
return True
return False
def partition_freespace(directory):
""" returns free space of given directory's partition """
st = os.statvfs(directory)
return st[statvfs.F_BSIZE] * st[statvfs.F_BFREE]
def clean_locks(top = '.'):
for root, dirs, files in os.walk(top):
for fn in files:
if fn.endswith('.lock'):
path = join_path(root, fn)
ctx.ui.info(_('Removing lock %s'), path)
os.unlink(path)
########################################
# Package/Repository Related Functions #
########################################
def package_name(name, version, release, build, prependSuffix=True):
fn = name + '-' + version + '-' + release
if build:
fn += '-' + str(build)
if prependSuffix:
fn += ctx.const.package_suffix
return fn
def is_package_name(fn, package_name = None):
"check if fn is a valid filename for given package_name"
"if not given a package name, see if fn fits the package name rules"
if (package_name==None) or fn.startswith(package_name + '-'):
if fn.endswith(ctx.const.package_suffix):
# get version string, skip separator '-'
verstr = fn[len(package_name) + 1:
len(fn)-len(ctx.const.package_suffix)]
import string
for x in verstr.split('-'):
# weak rule: version components after '-' start with a digit
if x is '' or (not x[0] in string.digits):
return False
return True
return False
def env_update():
import pisi.environment
ctx.ui.info(_('Updating environment...'))
env_dir = join_path(ctx.config.dest_dir(), "/etc/env.d")
if not os.path.exists(env_dir):
os.makedirs(env_dir, 0755)
pisi.environment.update_environment(ctx.config.dest_dir())
def pure_package_name(package_name):
"return pure package name from given string"
"ex: package_name=tasma-1.0.3-5-2.pisi, returns tasma"
if package_name.endswith(ctx.const.package_suffix):
package_name = package_name.rstrip(ctx.const.package_suffix)
return '-'.join([part for part in package_name.split('-') if part[0] not in string.digits])
def generate_pisi_file(patchFile, fromFile, toFile):
if run_batch("xdelta patch %s %s %s" % (patchFile, fromFile, toFile))[0]:
raise Error(_("ERROR: xdelta patch %s %s %s failed") % (patchFile, fromFile, toFile))
-199
View File
@@ -1,199 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Baris Metin <baris@pardus.org.tr>
#
# History:
# Eray wrote the first version
# Baris added support for the fancy keywords and
# alphanumeric version string components
"""version structure"""
import re
import string
import pisi.util as util
# Basic rule is:
# p > (no suffix) > m > rc > pre > beta > alpha
# m: milestone. this was added for OO.o
# p: patch-level
keywords = {"alpha": 0,
"beta" : 1,
"pre" : 2,
"rc" : 3,
"m" : 4,
"NOKEY" : 5,
"p" : 6}
# helper functions
def has_keyword(versionitem):
if versionitem._keyword != "NOKEY":
return True
return False
class VersionItem:
_keyword = "NOKEY"
_value = 0
def __init__(self, itemstring):
# special-case: 1.5p
# here "p" comes as a separate itemstring and conflicts with
# keyword "p". But keyword should allways contain an integer prefix.
# So, skipping looking for keywords if the length() is 1 makes the
# trick.
if len(itemstring) > 1:
for keyword in keywords.keys():
if itemstring.startswith(keyword):
if self._keyword == "NOKEY":
self._keyword = keyword
else:
# longer match is correct
if len(keyword) > len(self._keyword):
self._keyword = keyword
if self._keyword == "NOKEY":
if len(itemstring) == 1 and itemstring in string.ascii_letters:
# single letter version item ('a' to 'Z')
self._value = itemstring
else:
self._value = int(itemstring)
else:
# rest is the version item's value. And each must have
# one!
self._value = int(itemstring[len(self._keyword):])
def __str__(self):
return str(self._value)
def __lt__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l < r:
return True
elif l == r:
return self._value < rhs._value
else: # l > r
return False
def __le__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l < r:
return True
elif l == r:
return self._value <= rhs._value
else: # l > r
return False
def __gt__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l > r:
return True
elif l == r:
return self._value > rhs._value
else: # l < r
return False
def __ge__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l > r:
return True
elif l == r:
return self._value >= rhs._value
else: # l < r
return False
def __eq__(self,rhs):
l = keywords[self._keyword]
r = keywords[rhs._keyword]
if l == r and self._value == rhs._value:
return True
return False
class Version:
def __init__(self, verstring):
self.comps = []
for i in util.multisplit(verstring,'.-_'):
# some version strings can contain ascii chars at the
# back. As an example: 2.11a
# We split '11a' as two items like '11' and 'a'
s = re.compile("[a-z-A-Z]$").search(i)
if s:
head = i[:s.start()]
tail = s.group()
self.comps.append(VersionItem(head))
self.comps.append(VersionItem(tail))
else:
self.comps.append(VersionItem(i))
self.verstring = verstring
def string(self):
return self.verstring
def compare(self, rhs):
"""this comparison routine is essentially a comparison routine
for two rationals in (0,1) interval. we compare two sequences
of digits one by one. We start with the leftmost digit
in the expansion. If they are equal, we proceed to the next. If
not we use the comparison operator. And we iterate to the left.
The result is, 0 if two are equal, -1 if self < rhs, and +1
if self>rhs"""
lhs = self.comps
rhs = rhs.comps
# pad the short version string with zeros
if len(lhs) < len(rhs):
lhs.extend( [VersionItem('0')] * (len(rhs) - len(lhs)) )
elif len(lhs) > len(rhs):
rhs.extend( [VersionItem('0')] * (len(lhs) - len(rhs)) )
# now let's iterate from left to right
for (litem, ritem) in zip(lhs, rhs):
if litem < ritem:
return -1
elif litem > ritem:
return +1
return 0
# premature optimization is the root of all evil
def __lt__(self,rhs):
return self.compare(rhs) < 0
def __le__(self,rhs):
return self.compare(rhs) <= 0
def __gt__(self,rhs):
return self.compare(rhs) > 0
def __ge__(self,rhs):
return self.compare(rhs) >= 0
def __eq__(self,rhs):
return self.compare(rhs) == 0
def __str__(self):
return self.verstring
-310
View File
@@ -1,310 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2006, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@pardus.org.tr>
# Faik Uygur <faik@pardus.org.tr>
#
# Extends zipfile module with lzma and bzip2 support
# python standard library modules
import os
import struct
import time
import binascii
from zipfile import *
try:
import zlib
except ImportError:
zlib = None
try:
import bzip2
except ImportError:
bzip2 = None
try:
import pylzma
except ImportError:
pylzma = None
ZIP_BZIP2 = 12
ZIP_LZMA = 255
compression_methods = [ZIP_STORED, ZIP_DEFLATED, ZIP_BZIP2, ZIP_LZMA]
class FileEntry:
"""File-like object used to access entries in a ZipFile"""
def __init__(self, fp, length):
self.fp = fp
self.readBytes = 0
self.returnedBytes = 0
self.length = length
self.finished = 0
class LzmaFileEntry(FileEntry):
"""File-like object used to read a LZMA entry in a ZipFile"""
def __init__(self, fp, length):
FileEntry.__init__(self, fp, length)
self.decomp = pylzma.decompressobj()
self.buffer = ""
def tell(self):
return self.returnedBytes
def read(self, n=None):
if self.finished:
return ""
if n is None:
result = [self.buffer,]
result.append(self.decomp.decompress(self.fp.read(self.length - self.readBytes)))
result.append(self.decomp.flush())
self.buffer = ""
self.finished = 1
result = "".join(result)
self.returnedBytes += len(result)
return result
else:
while len(self.buffer) < n:
data = self.fp.read(min(n, 1024, self.length - self.readBytes))
self.readBytes += len(data)
if not data:
result = self.buffer + self.decomp.decompress() + self.decomp.flush()
self.finished = 1
self.buffer = ""
self.returnedBytes += len(result)
return result
else:
self.buffer += self.decomp.decompress(data)
result = self.buffer[:n]
self.buffer = self.buffer[n:]
self.returnedBytes += len(result)
return result
def close(self):
self.finished = 1
del self.fp
class ZipFileEntry(FileEntry):
"""File-like object used to read an uncompressed entry in a ZipFile"""
def __init__(self, fp, length):
FileEntry.__init__(self, fp, length)
def tell(self):
return self.readBytes
def read(self, n=None):
if n is None:
n = self.length - self.readBytes
if n == 0 or self.finished:
return ''
data = self.fp.read(n)
self.readBytes += len(data)
if self.readBytes == self.length or len(data) < n:
self.finished = 1
return data
def close(self):
self.finished = 1
del self.fp
class DeflatedZipFileEntry(FileEntry):
"""File-like object used to read a deflated entry in a ZipFile"""
def __init__(self, fp, length):
FileEntry.__init__(self, fp, length)
self.decomp = zlib.decompressobj(-15)
self.buffer = ""
def tell(self):
return self.returnedBytes
def read(self, n=None):
if self.finished:
return ""
if n is None:
result = [self.buffer,]
result.append(self.decomp.decompress(self.fp.read(self.length - self.readBytes)))
result.append(self.decomp.decompress("Z"))
result.append(self.decomp.flush())
self.buffer = ""
self.finished = 1
result = "".join(result)
self.returnedBytes += len(result)
return result
else:
while len(self.buffer) < n:
data = self.fp.read(min(n, 1024, self.length - self.readBytes))
self.readBytes += len(data)
if not data:
result = self.buffer + self.decomp.decompress("Z") + self.decomp.flush()
self.finished = 1
self.buffer = ""
self.returnedBytes += len(result)
return result
else:
self.buffer += self.decomp.decompress(data)
result = self.buffer[:n]
self.buffer = self.buffer[n:]
self.returnedBytes += len(result)
return result
def close(self):
self.finished = 1
del self.fp
class ZipFileExt(ZipFile):
def _writecheck(self, zinfo):
"""Check for errors before writing a file to the archive."""
if zinfo.filename in self.NameToInfo:
if self.debug: # Warning for duplicate names
print "Duplicate name:", zinfo.filename
if self.mode not in ("w", "a"):
raise RuntimeError, 'write() requires mode "w" or "a"'
if not self.fp:
raise RuntimeError, \
"Attempt to write ZIP archive that was already closed"
if zinfo.compress_type not in compression_methods:
raise RuntimeError, \
"Compression method %s is not supported" % zinfo.compress_type
if zinfo.compress_type == ZIP_DEFLATED and not zlib:
raise RuntimeError, \
"Compression requires the (missing) zlib module"
if zinfo.compress_type == ZIP_BZIP2 and not bzip2:
raise RuntimeError, \
"Compression requires the (missing) bzip2 module"
if zinfo.compress_type == ZIP_LZMA and not pylzma:
raise RuntimeError, \
"Compression requires the (missing) pylzma module"
def write(self, filename, arcname=None, compress_type=None):
"""Put the bytes from filename into the archive under the name
arcname."""
st = os.stat(filename)
mtime = time.localtime(st.st_mtime)
date_time = mtime[0:6]
# Create ZipInfo instance to store file information
if arcname is None:
zinfo = ZipInfo(filename, date_time)
else:
zinfo = ZipInfo(arcname, date_time)
zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes
if compress_type is None:
zinfo.compress_type = self.compression
else:
zinfo.compress_type = compress_type
self._writecheck(zinfo)
fp = open(filename, "rb")
zinfo.flag_bits = 0x00
zinfo.header_offset = self.fp.tell() # Start of header bytes
# Must overwrite CRC and sizes with correct data later
zinfo.CRC = CRC = 0
zinfo.compress_size = compress_size = 0
zinfo.file_size = file_size = 0
self.fp.write(zinfo.FileHeader())
zinfo.file_offset = self.fp.tell() # Start of file bytes
# really compress
if zinfo.compress_type == ZIP_DEFLATED or zinfo.compress_type == ZIP_STORED:
if zinfo.compress_type == ZIP_DEFLATED:
cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
zlib.DEFLATED, -15)
else:
cmpr = None
while 1:
buf = fp.read(1024 * 8)
if not buf:
break
file_size = file_size + len(buf)
CRC = binascii.crc32(buf, CRC)
if cmpr:
buf = cmpr.compress(buf)
compress_size = compress_size + len(buf)
self.fp.write(buf)
fp.close()
if cmpr:
buf = cmpr.flush()
compress_size = compress_size + len(buf)
self.fp.write(buf)
zinfo.compress_size = compress_size
else:
zinfo.compress_size = file_size
elif zinfo.compress_type == ZIP_LZMA:
# unfortunately pylzma.compressobj is not implemented yet.
# So in order to calculate the CRC, we are going to read
# all the file at once until it is implemented.
buf = fp.read()
file_size = len(buf)
CRC = binascii.crc32(buf, CRC)
compressed = pylzma.compress(buf, eos=1)
self.fp.write(compressed)
zinfo.compress_size = len(compressed)
zinfo.CRC = CRC
zinfo.file_size = file_size
# Seek backwards and write CRC and file sizes
position = self.fp.tell() # Preserve current position in file
self.fp.seek(zinfo.header_offset + 14, 0)
self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size,
zinfo.file_size))
self.fp.seek(position, 0)
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
def readfile(self, name):
"""Return file-like object for name."""
if self.mode not in ("r", "a"):
raise RuntimeError, 'read() requires mode "r" or "a"'
if not self.fp:
raise RuntimeError, \
"Attempt to read ZIP archive that was already closed"
zinfo = self.getinfo(name)
self.fp.seek(zinfo.file_offset, 0)
if zinfo.compress_type == ZIP_STORED:
return ZipFileEntry(self.fp, zinfo.compress_size)
elif zinfo.compress_type == ZIP_DEFLATED:
if not zlib:
raise RuntimeError, \
"De-compression requires the (missing) zlib module"
return DeflatedZipFileEntry(self.fp, zinfo.compress_size)
## elif zinfo.compress_type == ZIP_BZIP2:
## if not bzip2:
## raise RuntimeError, \
## "De-compression requires the (missing) bzip2 module"
## return LZMAFileEntry(self.fp, zinfo.compress_size)
elif zinfo.compress_type == ZIP_LZMA:
if not pylzma:
raise RuntimeError, \
"De-compression requires the (missing) pylzma module"
return LzmaFileEntry(self.fp, zinfo.compress_size)
else:
raise BadZipfile, \
"Unsupported compression method %d for file %s" % \
(zinfo.compress_type, name)
def read(self, name):
"""Return file bytes (as a string) for name."""
f = self.readfile(name)
zinfo = self.getinfo(name)
bytes = f.read()
crc = binascii.crc32(bytes)
if crc != zinfo.CRC:
raise BadZipfile, "Bad CRC-32 for file %s" % name
return bytes
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-100
View File
@@ -1,100 +0,0 @@
#!/usr/bin/env python
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: {eray,gurer}@pardus.org.tr
import os
import shutil
import glob
import sys
from distutils.core import setup
from distutils.command.install import install
sys.path.insert(0, '.')
import pisi
i18n_domain = "pisi"
i18n_languages = "tr"
class Install(install):
def run(self):
install.run(self)
self.installi18n()
self.installdoc()
def installi18n(self):
for lang in i18n_languages.split(' '):
print "Installing '%s' translations..." % lang
os.popen("msgfmt po/%s.po -o po/%s.mo" % (lang, lang))
if not self.root:
self.root = "/"
destpath = os.path.join(self.root, "usr/share/locale/%s/LC_MESSAGES" % lang)
try:
os.makedirs(destpath)
except:
pass
shutil.copy("po/%s.mo" % lang, os.path.join(destpath, "%s.mo" % i18n_domain))
def installdoc(self):
destpath = os.path.join(self.root, "usr/share/doc/pisi")
try:
os.makedirs(destpath)
except:
pass
os.chdir('doc')
for pdf in glob.glob('*.pdf'):
print 'Installing', pdf
shutil.copy(pdf, os.path.join(destpath, pdf))
os.chdir('..')
setup(name="pisi",
version= pisi.__version__,
description="PISI (Packages Installed Successfully as Intended)",
long_description="PISI is the package management system of Pardus Linux.",
license="GNU GPL2",
author="Pardus Developers",
author_email="pisi@pardus.org.tr",
url="http://www.pardus.org.tr/eng/pisi/",
package_dir = {'': ''},
packages = ['pisi', 'pisi.cli', 'pisi.actionsapi', 'pisi.pxml', 'pisi.search'],
scripts = ['pisi-cli', 'tools/repostats.py', 'tools/find-lib-deps.py',
'tools/update-environ.py', 'tools/lspisi', 'tools/calc-build-order.py'],
cmdclass = {
'install' : Install
}
)
# the below stuff is really nice but we already have a version
# we can use this stuff for svn snapshots in a separate
# script, or with a parameter I don't know -- exa
PISI_VERSION = pisi.__version__
def getRevision():
import os
try:
p = os.popen("svn info 2> /dev/null")
for line in p.readlines():
line = line.strip()
if line.startswith("Revision:"):
return line.split(":")[1].strip()
except:
pass
# doesn't working in a Subversion directory
return None
def getVersion():
rev = getRevision()
if rev:
return "-r".join([PISI_VERSION, rev])
else:
return PISI_VERSION
-11
View File
@@ -1,11 +0,0 @@
PISI test suite
---------------
There are python unit tests and shell scripts in this directory.
Run the python tests with
$ tests/run.py
Shell scripts use the CLIs to perform package operations.
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<A href="http://www.cs.bilkent.edu.tr/~erayo">
cıvık mantardır bu
<Name>Eray Ozkural</Name>
<Number>868</Number>
<Description xml:lang="en">Lazy Tin</Description>
<Description xml:lang="tr">Tembel Teneke</Description>
<Project>pisi</Project>
<Project>noatun</Project>
<Project>kdevelop</Project>
<OtherInfo>
<BirthDate>18071976</BirthDate>
<Interest>AI</Interest>
<CodesWith>
<Person>Baris</Person>
<Person>Gurer</Person>
<Person>Caglar</Person>
<Person>Meren</Person>
</CodesWith>
</OtherInfo>
</A>
@@ -1,155 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
import unittest
import zipfile
import shutil
import os
import testcase
class ActionsAPITestCase(testcase.TestCase):
def setUp(self):
from pisi.actionsapi.variables import initVariables
testcase.TestCase.setUp(self)
initVariables()
#FIXME: test incomplete
return
self.f = zipfile.ZipFile("helloworld-0.1-1.pisi", "r")
self.filelist = []
for file in self.f.namelist():
self.filelist.append(file)
def testFileList(self):
#FIXME: test incomplete
return
fileContent = ["files.xml", \
"install/bin/helloworld", \
"install/opt/PARDUS", \
"install/opt/helloworld/helloworld", \
"install/opt/uludag", \
"install/sbin/helloworld", \
"install/sys/PARDUS", \
"install/sys/uludag", \
"install/usr/bin/goodbye", \
"install/usr/bin/helloworld", \
"install/usr/lib/helloworld.o", \
"install/usr/sbin/goodbye", \
"install/usr/sbin/helloworld", \
"install/usr/share/doc/helloworld-0.1-1/Makefile.am", \
"install/usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp", \
"install/usr/share/info/Makefile.am", \
"install/usr/share/info/Makefile.cvs", \
"install/usr/share/info/Makefile.in", \
"install/var/goodbye", \
"install/var/hello", \
"metadata.xml"]
'''check number of files in package'''
self.assertEqual(fileContent.__len__(), self.filelist.__len__())
'''check file content'''
for file in self.filelist:
self.assert_(fileContent.__contains__(file))
def testShelltoolsCopy(self):
from pisi.actionsapi.shelltools import copy
copy('tests/actionsapitests/brokenlink', 'tests/actionsapitests/brokenlink-copy')
self.assertEqual(os.path.islink('tests/actionsapitests/brokenlink-copy'), True)
os.remove('tests/actionsapitests/brokenlink-copy')
copy('tests/actionsapitests/brokenlink', 'tests/actionsapitests/adirectory')
self.assertEqual(os.path.islink('tests/actionsapitests/adirectory/brokenlink'), True)
copy('tests/actionsapitests/brokenlink', 'tests/actionsapitests/adirectory/brknlnk')
self.assertEqual(os.path.islink('tests/actionsapitests/adirectory/brknlnk'), True)
os.remove('tests/actionsapitests/adirectory/brknlnk')
self.assertEqual(os.readlink('tests/actionsapitests/adirectory/brokenlink'), '/no/such/place')
os.remove('tests/actionsapitests/adirectory/brokenlink')
copy('tests/actionsapitests/linktoadirectory', 'tests/actionsapitests/adirectory/', False)
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/linktoadirectory/file'), True)
self.assertEqual(os.path.getsize('tests/actionsapitests/adirectory/linktoadirectory/file'), 321)
shutil.rmtree('tests/actionsapitests/adirectory/linktoadirectory')
copy('tests/actionsapitests/file', 'tests/actionsapitests/adirectory')
self.assertEqual(os.path.isfile('tests/actionsapitests/adirectory/file'), True)
#overwrite..
copy('tests/actionsapitests/file', 'tests/actionsapitests/adirectory')
os.remove('tests/actionsapitests/adirectory/file')
copy('tests/actionsapitests/linktoafile', 'tests/actionsapitests/adirectory', False)
ourguy = 'tests/actionsapitests/%s' % os.readlink('tests/actionsapitests/linktoafile')
self.assert_(os.path.exists(ourguy))
copy('tests/actionsapitests/file', 'tests/actionsapitests/file-copy')
self.assertEqual(os.path.exists('tests/actionsapitests/file-copy'), True)
os.remove('tests/actionsapitests/file-copy')
copy('tests/actionsapitests/file', 'tests/actionsapitests/adirectory/filewithanothername')
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/filewithanothername'), True)
os.remove('tests/actionsapitests/adirectory/filewithanothername')
copy('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory')
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/linkeddir/file'), True)
shutil.rmtree('tests/actionsapitests/adirectory/linkeddir')
copy('tests/actionsapitests/linkeddir', 'tests/actionsapitests/adirectory/withanothername')
self.assertEqual(os.path.exists('tests/actionsapitests/adirectory/withanothername/file'), True)
shutil.rmtree('tests/actionsapitests/adirectory/withanothername')
def testShelltoolsCanAccessFile(self):
from pisi.actionsapi.shelltools import can_access_file
self.assert_(can_access_file('tests/actionsapitests/file'))
self.assert_(not can_access_file('tests/actionsapitests/fileX'))
self.assert_(can_access_file('tests/actionsapitests/linktoafile'))
def testShelltoolsCanAccessDir(self):
from pisi.actionsapi.shelltools import can_access_directory
self.assert_(can_access_directory('tests/actionsapitests/adirectory'))
self.assert_(not can_access_directory('tests/actionsapitests/adirectoryX'))
self.assert_(can_access_directory('tests/actionsapitests/linktoadirectory'))
def testShelltoolsMakedirs(self):
from pisi.actionsapi.shelltools import makedirs
makedirs('tests/actionsapitests/testdirectory/into/a/directory')
self.assertEqual(os.path.exists('tests/actionsapitests/testdirectory/into/a/directory'), True)
shutil.rmtree('tests/actionsapitests/testdirectory')
def testShelltoolsEcho(self):
from pisi.actionsapi.shelltools import echo
echo('tests/actionsapitests/echo-file', 'hububat fiyatları')
self.assertEqual(os.path.exists('tests/actionsapitests/echo-file'), True)
self.assertEqual(open('tests/actionsapitests/echo-file').readlines()[0].strip(), "hububat fiyatları")
echo('tests/actionsapitests/echo-file', 'fiyat hububatları')
self.assertEqual(open('tests/actionsapitests/echo-file').readlines()[1].strip(), "fiyat hububatları")
os.remove('tests/actionsapitests/echo-file')
def testShelltoolsSystem(self):
from pisi.actionsapi.shelltools import system as s
self.assertEqual(os.path.exists('tests/actionsapitests/systest'), False)
s('touch tests/actionsapitests/systest')
self.assertEqual(os.path.exists('tests/actionsapitests/systest'), True)
os.remove('tests/actionsapitests/systest')
suite = unittest.makeSuite(ActionsAPITestCase)
@@ -1 +0,0 @@
/no/such/place

Some files were not shown because too many files have changed in this diff Show More