Move pisi-1.0.1 tag

This commit is contained in:
Fatih Aşıcı
2009-10-27 10:12:42 +00:00
parent 5417ebdf41
commit e1f7e99ac5
165 changed files with 0 additions and 25591 deletions
-10
View File
@@ -1,10 +0,0 @@
Authors:
Eray Ozkural <eray@uludag.org.tr>
Baris Metin <baris@uludag.org.tr>
Murat Eren <meren@uludag.org.tr>
S. Caglar Onur <caglar@uludag.org.tr>
Gurer Ozen <gurer@uludag.org.tr>
Ismail Donmez <ismail@uludag.org.tr>
Gorkem Cetin <gorkem@uludag.org.tr>
Faik Uygur <faikuygur@gmail.com>
-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.
-13
View File
@@ -1,13 +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 rebuild-db command
-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
-41
View File
@@ -1,41 +0,0 @@
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 implemented in python for the PARDUS
distribution.
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 both low-level and high-level package operations.
- Framework approach to build applications and tools upon
- Comprehensive CLI and a user-friendly qt-based GUI (GUI
distributed separately)
- Extremely simple package construction
-6
View File
@@ -1,6 +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.
db version has changed, so a rebuild-db will be unfortunately
necessary.
-245
View File
@@ -1,245 +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)
+ implement a simple (dandik) version for better reliability
+ database version file
* medium priority
- component improvements (optional)
- maintain a list of components each component has
- maintain a list of packages
* low priority
+ search command: (eray)
+ implement an inverted index
+ search in summary and description
/ 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
5. Post Release
* high priority
- high-level build commands
- 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
- an emerge command, just for fun
- more support for categories:
- put categories into a database
- fast search for categories
/ make a package and sourcedb for each repo
+ packagedb
- sourceb
- improve url support: consider adding "other" protocols
- removable media: media://
- examples:
- PSPEC i18n example for our great project with Bahadir :)
* medium priority
- non-interactive use
- multi-architecture support (baris, caglar)
/ design decisions
/ extend XML specs to support that
- cross-platform building support
- incremental build/fetch for repository index (pisi-index.xml)
- diffsets (caglar)
- a command to check if repos are consistent wrt dep relations
* low priority
- 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.
-187
View File
@@ -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'.
-80
View File
@@ -1,80 +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 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):
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 only have to run rebuild-db if there is file corruption."""))
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."))
v = ''
for e in value.args:
v += str(e) + '\n'
if ctx.get_option('debug'):
ui.error("%s: %s" % (exception, v))
else:
ui.error(v)
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__":
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)>
-520
View File
@@ -1,520 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
<!-- Project file for project pisi -->
<!-- Saved: 2005-12-15, 04:01:47 -->
<!-- Copyright (C) 2005 PiSi Development Team, -->
<Project version="3.8">
<ProgLanguage mixed="0">Python</ProgLanguage>
<UIType>Qt</UIType>
<Description>The package management software of Pardus distribution.
</Description>
<Author>PiSi Development Team</Author>
<EmaIl></EmaIl>
<Sources>
<Source>
<Dir>tools</Dir>
<Name>ebuild2pisi.py</Name>
</Source>
<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>
</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="*.ui.h" type="FORMS" />
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
<FiletypeAssociation pattern="*.ui" type="FORMS" />
<FiletypeAssociation pattern="*.py" type="SOURCES" />
</FiletypeAssociations>
</Project>
-36
View File
@@ -1,36 +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.0.1"
__dbversion__ = "1.0.2"
__all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ]
class Exception(Exception):
"""Class of exceptions that must be caught and handled within PISI"""
def __str__(self):
s = ''
for x in self.args:
if s != '':
s += '\n'
s += x.encode('utf-8')
return s
class Error(Exception):
"""Class of exceptions that lead to program termination"""
pass
# FIXME: can't do this due to name clashes in config and other singletons booo
#from 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()
-197
View File
@@ -1,197 +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
from variables import glb
class BinutilsError(pisi.actionsapi.Error):
def __init__(self, value=''):
pisi.actionsapi.Error.__init__(self, value)
self.value = value
ctx.ui.error(value)
# Globals
env = glb.env
dirs = glb.dirs
def curDIR():
'''returns current work directory's path'''
return os.getcwd()
def curKERNEL():
'''returns currently running kernel's version'''
versionString = file("/proc/version").readline()
return versionString.split()[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():
# FIXME: Get followings from env.d or somewhere else
return dirs.kde
def qtDIR():
# FIXME: Get followings from env.d or somewhere else
return dirs.qt
def qtLIBDIR():
# FIXME: Get followings from env.d or somewhere else
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')
-82
View File
@@ -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 file in files:
if file in ['config.sub', 'config.guess']:
targetFile = os.path.join(root, file)
if os.path.islink(targetFile):
unlink(targetFile)
copy('/usr/share/gnuconfig/%s' % file, join_path(root, file))
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,240 +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 dohard(sourceFile, destinationFile):
'''creates hard link between sourceFile and destinationFile'''
#FIXME: How can i use hard-links in Python?
ctx.ui.warning('*** Hard links not implemented yet')
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 file in glob.glob(join_path(get.installDIR(), sourceFile)):
if not destinationFile:
move(file, join_path(get.installDIR(), join_path(destination, os.path.basename(file))))
else:
move(file, 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 = ''):
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
makedirs(join_path(get.installDIR(), destinationDirectory))
if not destinationFile:
for file in glob.glob(sourceFile):
if can_access_file(file):
copy(file, join_path(get.installDIR(), join_path(destinationDirectory, os.path.basename(file))))
else:
copy(sourceFile, join_path(get.installDIR(), join_path(destinationDirectory, destinationFile)))
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 file in glob.glob(join_path(get.installDIR(), sourceFile)):
unlink(file)
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
-44
View File
@@ -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'):
if system("scons prefix=%s %s" % (get.installDIR(), parameters)):
raise InstallError(_('Install failed.'))
@@ -1,212 +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 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
def can_access_file(sourceFile):
'''test the existence of file'''
return os.access(sourceFile, 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(sourceFile, mode = 0755):
'''change the mode of sourceFile to the mode'''
for file in glob.glob(sourceFile):
if can_access_file(file):
try:
os.chmod(file, mode)
except OSError:
ctx.ui.error(_('ActionsAPI [chmod]: Operation not permitted: %s (mode: %s)') \
% (file, mode))
else:
ctx.ui.error(_('ActionsAPI [chmod]: File %s doesn\'t exists.') % (file))
def chown(sourceFile, uid = "root", gid = "root"):
'''change the owner and group id of sourceFile to uid and gid'''
if can_access_file(sourceFile):
try:
os.chown(sourceFile, pwd.getpwnam(uid)[2], grp.getgrnam(gid)[2])
except OSError:
ctx.ui.error(_('ActionsAPI [chown]: Operation not permitted: %s (uid: %s, gid: %s)') \
% (sourceFile, uid, gid))
else:
ctx.ui.error(_('ActionsAPI [chown]: File %s doesn\'t exists.') % sourceFile)
def sym(sourceFile, destinationFile):
'''creates symbolic link'''
try:
os.symlink(sourceFile, destinationFile)
except OSError:
ctx.ui.error(_('ActionsAPI [sym]: Permission denied: %s to %s') % (file, destinationFile))
def unlink(sourceFile):
'''remove the file path'''
if isFile(sourceFile) or isLink(sourceFile):
try:
os.unlink(sourceFile)
except OSError:
ctx.ui.error(_('ActionsAPI [unlink]: Permission denied: %s.') % (sourceFile))
elif isDirectory(sourceFile):
pass
else:
ctx.ui.error(_('ActionsAPI [unlink]: File %s doesn\'t exists.') % (sourceFile))
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(sourceFile, destinationFile):
'''recursively move a sourceFile or directory to destinationFile'''
for file in glob.glob(sourceFile):
if isFile(file) or isLink(file) or isDirectory(file):
try:
shutil.move(file, destinationFile)
except OSError:
error(_('ActionsAPI [move]: Permission denied: %s to %s') % (file, destinationFile))
else:
error(_('ActionsAPI [move]: File %s doesn\'t exists.') % (file))
def copy(sourceFile, destinationFile):
'''recursively copy a sourceFile or directory to destinationFile'''
for file in glob.glob(sourceFile):
if isFile(file) or isLink(file):
try:
shutil.copy(file, destinationFile)
except IOError:
error(_('ActionsAPI [copy]: Permission denied: %s to %s') % (file, destinationFile))
elif isDirectory(file):
copytree(file, destinationFile)
else:
error(_('ActionsAPI [copy]: File %s does not exist.') % file)
def copytree(source, destination, sym = False):
'''recursively copy an entire directory tree rooted at source'''
if isDirectory(source) or isLink(source):
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(sourceFile):
'''changes the access time of the 'sourceFile', or creates it if it is not exist'''
if glob.glob(sourceFile):
for file in glob.glob(sourceFile):
os.utime(file, None)
else:
try:
f = open(sourceFile, 'w')
f.close()
except IOError:
error(_('ActionsAPI [touch]: Permission denied: %s') % (sourceFile))
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(sourceFile):
'''return True if sourceFile refers to a symbolic link'''
return os.path.islink(sourceFile)
def isFile(sourceFile):
'''return True if sourceFile is an existing regular file'''
return os.path.isfile(sourceFile)
def isDirectory(sourceDirectory):
'''Return True if sourceFile is an existing directory'''
return os.path.isdir(sourceDirectory)
def realPath(sourceFile):
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
return os.path.realpath(sourceFile)
def baseName(sourceFile):
'''return the base name of pathname sourceFile'''
return os.path.basename(sourceFile)
def dirName(sourceFile):
'''return the directory name of pathname path'''
return os.path.dirname(sourceFile)
def system(command):
#FIXME: might be better to merge this with util.run_batch
#FIXME: String formatting
command = command.replace(" ", " ")
ctx.ui.debug(_('executing %s') % command)
p = os.popen(command)
while 1:
line = p.readline()
if not line:
break
ctx.ui.debug(line[:-1])
return p.close()
@@ -1,85 +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['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'
values = ctx.config.values
kde = values.dirs.kde_dir
qt = values.dirs.qt_dir
def initVariables():
ctx.env = Env()
ctx.dirs = Dirs()
return ctx
glb = initVariables()
-323
View File
@@ -1,323 +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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr>
"""Top level PISI interfaces. a facade to the entire PISI system"""
import os
import sys
from os.path import exists
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.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
from pisi.build import build, build_until
from pisi.atomicoperations import resurrect_package
from pisi.metadata import MetaData
from pisi.files import Files
import pisi.search
import pisi.lockeddbshelve as shelve
class Error(pisi.Error):
pass
def init(database = True, options = None, ui = None, comar = True):
"""Initialize PiSi subsystem"""
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')
if ui is None:
if options:
pisi.context.ui = pisi.cli.CLI(options.debug)
else:
pisi.context.ui = pisi.cli.CLI()
else:
pisi.context.ui = ui
# initialize repository databases
if database:
shelve.init_dbenv()
ctx.repodb = pisi.repodb.init()
ctx.installdb = pisi.installdb.init()
ctx.filesdb = pisi.files.FilesDB()
ctx.componentdb = pisi.component.ComponentDB()
packagedb.init_db()
pisi.sourcedb.init()
pisi.search.init(['summary', 'description'], ['en', 'tr'])
else:
ctx.repodb = None
ctx.installdb = None
ctx.filesdb = None
ctx.componentdb = None
ctx.ui.debug('PISI API initialized')
ctx.initialized = True
def finalize():
if ctx.initialized:
pisi.repodb.finalize()
pisi.installdb.finalize()
if ctx.filesdb != None:
ctx.filesdb.close()
if ctx.componentdb != None:
ctx.componentdb.close()
packagedb.finalize_db()
pisi.sourcedb.finalize()
pisi.search.finalize()
if ctx.dbenv:
ctx.dbenv.close()
ctx.ui.debug('PISI API finalized')
ctx.ui.close()
ctx.initialized = False
def list_available():
'''returns a set of available package names'''
available = set()
for repo in pisi.context.repodb.list():
pkg_db = pisi.packagedb.get_db(repo)
available.update(pkg_db.list_packages())
return available
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 = packagedb.get_package(x)
if ignore_build or (not build):
if release < pkg.release:
Ap.append(x)
elif build < pkg.build:
Ap.append(x)
else:
pass
#ctx.ui.info('Package %s cannot be upgraded. ' % x)
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(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 = 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(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 = 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
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):
if package.endswith(ctx.const.package_suffix):
return info_file(package)
else:
return info_name(package)
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 installed:
pkgdb = packagedb.inst_packagedb
else:
pkgdb = packagedb
if pkgdb.has_package(package_name):
package = pkgdb.get_package(package_name)
from pisi.metadata import MetaData
metadata = MetaData()
metadata.package = package
#FIXME: get it from sourcedb
metadata.source = None
#TODO: fetch the files from server if possible
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_terms(terms, lang = None):
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)
return r
def search_package(query, lang = None):
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)
return r
def check(package):
md, files = info(package)
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, output = 'pisi-index.xml'):
index = Index()
if not dirs:
dirs = ['.']
for repo_dir in dirs:
ctx.ui.info(_('* Building index of PISI files under %s') % repo_dir)
index.index(repo_dir)
index.write(output)
ctx.ui.info(_('* Index file written'))
def add_repo(name, indexuri):
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)
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):
ctx.ui.info(_('* Updating repository: %s') % repo)
index = Index()
if ctx.repodb.has_repo(repo):
index.read_uri(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo)
else:
raise Error(_('No repository named %s found.') % repo)
index.update_db(repo)
ctx.ui.info(_('\n* Package database updated.'))
def delete_cache():
util.clean_dir(ctx.config.packages_dir())
util.clean_dir(ctx.config.archives_dir())
util.clean_dir(ctx.config.tmp_dir())
-247
View File
@@ -1,247 +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 zipfile
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi modules
import pisi
import pisi.util as util
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'
def __init__(self, file_path, arch_type = "zip", mode = 'r'):
super(ArchiveZip, self).__init__(file_path, arch_type)
self.zip_obj = zipfile.ZipFile(self.file_path, mode)
def close(self):
"""Close the zip archive."""
self.zip_obj.close()
def add_to_archive(self, file_name):
"""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 = zipfile.ZipInfo()
attr.filename = file_name
attr.create_system = 3
attr.external_attr = self.symmagic
self.zip_obj.writestr(attr, dest)
else:
self.zip_obj.write(file_name, file_name, zipfile.ZIP_DEFLATED)
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)
-515
View File
@@ -1,515 +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@uludag.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
#import conflicts
class Error(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):
# download package and return an installer object
# find package in repository
repo = packagedb.which_repo(name)
if repo:
repo = ctx.repodb.get_repo(repo)
pkg = 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)
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 = 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()
if self.metadata.package.providesComar and ctx.comar:
import pisi.comariface as comariface
self.register_comar_scripts()
self.config_later = False
if 'System.Package' in [x.om for x in self.metadata.package.providesComar]:
if ctx.comar:
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
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 packagedb.has_package(self.pkginfo.name):
db = packagedb.thirdparty_packagedb
db.add_package(self.pkginfo)
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?
(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 (not ibuild) or (not pkg.build) or ignore_build:
# we don't look at builds to compare two package versions
if pkg.release == 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 pkg.version > iversion:
ctx.ui.info(_('Upgrading to new upstream version'))
upgrade = True
elif pkg.release > 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 pkg.version < iversion:
x = _('Downgrade to old upstream version?')
elif pkg.release < 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
Remove(pkg.name).run_preremove()
def extract_install(self):
"unzip package in place"
ctx.ui.notify(pisi.ui.extracting, package = self.pkginfo, files = self.files)
self.package.extract_dir_flat('install', ctx.config.dest_dir())
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 leftover:
Remove.remove_file( old_fileinfo[path] )
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..."""
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:
Remove(self.metadata.package.name).remove_db(txn)
if not self.same_ver:
Remove.remove_pisi_files(self.old_path)
# installdb
ctx.installdb.install(self.metadata.package.name,
self.metadata.package.version,
self.metadata.package.release,
self.metadata.package.build,
self.metadata.package.distribution,
self.config_later,
False,
txn)
# filesdb
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn)
# installed packages
packagedb.inst_packagedb.add_package(self.pkginfo, 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 = packagedb.get_package(self.package_name)
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"""
inst_packagedb = packagedb.inst_packagedb
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(self.package.pkg_dir())
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 == "config":
if os.path.isfile(fpath):
os.rename(fpath, fpath + ".oldconfig")
else:
# check if file is removed manually.
# And we don't remove directories!
# TODO: remove directory if there is nothing under it?
if os.path.isfile(fpath) or os.path.islink(fpath):
os.unlink(fpath)
else:
ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath)
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
@staticmethod
def remove_pisi_files(path):
#TODO: what does this have to do with pisi files??
util.clean_dir(path)
def remove_db(self, txn):
ctx.installdb.remove(self.package_name, txn)
ctx.filesdb.remove_files(self.files, txn)
if packagedb.thirdparty_packagedb.has_package(self.package_name, txn):
packagedb.thirdparty_packagedb.remove_package(self.package_name, txn)
if packagedb.inst_packagedb.has_package(self.package_name, txn):
packagedb.inst_packagedb.remove_package(self.package_name, txn)
def remove_single(package_name):
Remove(package_name).run()
def __is_virtual_upgrade(metadata):
pkg = metadata.package
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
upgrade = False
if pkg.version > iversion:
upgrade = True
elif pkg.release > irelease:
upgrade = True
elif (ibuild and pkg.build and pkg.build > ibuild):
upgrade = True
return upgrade
def virtual_install(metadata, files, txn):
"""Recreate the package info for rebuilddb command"""
pkg = metadata.package
# normally this can't be true. Just for backward compatibility
# TODO: for speed only ctx.installdb.install exception can be
# handled but this is much cleaner
if ctx.installdb.is_installed(pkg.name, txn):
if __is_virtual_upgrade(metadata):
ctx.installdb.remove(pkg.name, txn)
packagedb.remove_package(pkg.name, txn)
ctx.filesdb.remove_files(ctx.installdb.files(pkg.name), txn)
else:
return
pkginfo = metadata.package
# installdb
ctx.installdb.install(metadata.package.name,
metadata.package.version,
metadata.package.release,
metadata.package.build,
metadata.package.distribution,
False,
True,
txn)
# filesdb
ctx.filesdb.add_files(metadata.package.name, files, txn)
# installed packages
packagedb.inst_packagedb.add_package(pkginfo, txn)
def resurrect_package(package_fn):
"""Resurrect the package in the PiSi databases"""
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
# FIXME: there won't be any previous versions of the same package under /var/lib/pisi
# therefore there won't be any need for this check and __is_virtual_upgrade function
# this is just for backward compatibility for sometime
# yes, this is an ugly hack
passed = False
if ctx.installdb.is_installed(metadata.package.name):
passed = True
if not passed:
ctx.ui.info(_('* Adding \'%s\' to db... ') % (metadata.package.name), noln=True)
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
import pisi.atomicoperations
def f(txn):
pisi.atomicoperations.virtual_install(metadata, files, txn)
ctx.txn_proc(f)
if not passed:
ctx.ui.info(_('OK.'))
-740
View File
@@ -1,740 +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 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
class Error(pisi.Error):
pass
# this DAILYWTF approach will be removed in the next release.
# Helper Functions
def get_file_type(path, pinfoList):
"""Return the file type of a path according to the given PathInfo
list"""
# not only confusing but totally non-deterministic behavior -- exa
# The usage of depth is somewhat confusing. It is used for finding
# the best match to paths(in pinfolist). For an example, if paths
# contain ['/usr/share','/usr/share/doc'] and path is
# /usr/share/doc/filename our iteration over paths should match
# the second item.
depth = 0
ftype = ""
permanent = False
path = "/"+path # we need a real path.
for pinfo in pinfoList:
if util.subpath(pinfo.path, path):
length = len(pinfo.path)
if depth < length:
depth = length
ftype = pinfo.fileType
permanent = pinfo.permanent
return ftype, 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 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!
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_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"""
evn = {
"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(evn)
# First check icecream, if not found use ccache, no need to use both
# together (according to kde-wiki it cause performance loss)
if os.path.exists("/opt/icecream/bin/gcc"):
# Add icecream directory for support distributed compiling :)
os.environ["PATH"] = "/opt/icecream/bin/:" + os.environ["PATH"]
ctx.ui.info(_("IceCream detected. Make sure your daemon is up and running..."))
elif os.path.exists("/usr/lib/ccache/bin/gcc"):
# Add ccache directory for support Compiler Cache :)
os.environ["PATH"] = "/usr/lib/ccache/bin/:" + 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 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):
"""fail if dependencies not satisfied"""
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]) )
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:
raise Error(_('Cannot build package due to unsatisfied build dependencies'))
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 strip_install_dir(self):
"""strip install directory"""
ctx.ui.action(_("Stripping files.."))
install_dir = self.pkg_install_dir()
pkg_name = self.spec.source.name + '-' + self.spec.source.version + '-' + self.spec.source.release
try:
nostrip = self.actionGlobals['NoStrip']
util.strip_directory(install_dir, pkg_name, nostrip)
except KeyError:
util.strip_directory(install_dir, pkg_name)
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, d = 0, self.pkg_install_dir()
for path in package.files:
size += util.dir_size(util.join_path(d, path.path))
metadata.package.installedSize = size
# build no
if ctx.config.options.ignore_build_no:
metadata.package.build = None # means, build no information n/a
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 = 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
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()
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 :).
# sure -- exa
collisions = check_path_collision(package, self.spec.packages)
# FIXME: material collisions after expanding globs must be
# reported as errors, and an exception must be raised
d = {}
def add_path(path):
# add the files under material path
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
frpath = util.removepathprefix(install_dir, fpath) # relative path
ftype, permanent = get_file_type(frpath, package.files)
try: # broken links and empty dirs can cause problem
fsize = os.path.getsize(fpath)
except OSError:
fsize = None
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 output dir and packages dir
found = []
def locate_package_names(files):
for fn in files:
fn = fn.decode('utf-8')
if util.is_package_name(fn, package_name):
old_package_fn = util.join_path(root, fn)
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)
continue
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)
continue
for root, dirs, files in os.walk(ctx.config.options.output_dir):
dirs = [] # don't recurse
locate_package_names(files)
for root, dirs, files in os.walk(ctx.config.packages_dir()):
locate_package_names(files)
if not found:
return 1
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:
#FIXME: workaround for .a issue, skip .a files
if fn.path.endswith('.a') and fn.type=='library':
continue
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
elif changed:
ctx.ui.info(_('There are changes, incrementing build no to %d') % (old_build + 1))
return old_build + 1
else:
ctx.ui.info(_('There is no change from previous build %d') % old_build)
return 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()
package_names = []
for package in self.spec.packages:
# 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)
self.gen_metadata_xml(package)
ctx.ui.info(_("Creating PISI package %s.") % package.name)
name = util.package_name(package.name,
self.spec.source.version,
self.spec.source.release,
self.metadata.package.build)
pkg = Package(name, 'w')
package_names.append(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:
pkg.add_to_package(join("install", finfo.path))
pkg.close()
os.chdir(c)
self.set_state("buildpackages")
ctx.ui.info(_("Done."))
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
# build functions...
def build(pspecfile, authinfo=None):
pb = pisi.build.Builder(pspecfile, 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(pspecfile, state, authinfo=None):
pb = pisi.build.Builder(pspecfile, 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 == "setupaction":
__buildState_setupaction(pb, last)
return
if state == "buildaction":
__buildState_buildaction(pb, last)
return
if state == "installaction":
__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.
-146
View File
@@ -1,146 +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
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):
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'
if verbose and self.show_verbose:
self.output(msg + msgend)
elif not verbose:
self.output(msg + msgend)
def warning(self,msg):
if ctx.get_option('no_color'):
self.output(_('Warning: ') + msg + '\n', err=True)
else:
self.output(colorize(msg + '\n', 'purple'), err=True)
def error(self,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):
#TODO: this seems quite redundant?
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):
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):
out = '\r%-30.30s %3d%% %12.2f %s [%s]' % \
(ka['filename'], 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:
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'))
-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
-107
View File
@@ -1,107 +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'))
self.opts.append(rargs[0][2:])
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()
elif 'help' 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()
-99
View File
@@ -1,99 +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@uludag.org.tr>
# Gurer Ozen <gurer@uludag.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
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 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:
reply = com.read_cmd()
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!"
-139
View File
@@ -1,139 +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@uludag.org.tr>
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import pisi.pxml.xmlfile as xmlfile
import pisi.pxml.autoxml as autoxml
import pisi.lockeddbshelve as shelve
class Error(pisi.Error):
pass
__metaclass__ = autoxml.autoxml
class Distribution:
t_Name = [autoxml.Text, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_Version = [autoxml.Text, autoxml.mandatory]
t_Type = [autoxml.Text, autoxml.mandatory]
t_Dependencies = [ [autoxml.Text], autoxml.optional, "Dependencies/Distribution"]
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 = shelve.LockedDBShelf('components')
def close(self):
self.d.close()
def has_component(self, name, txn = None):
name = shelve.LockedDBShelf.encodekey(name)
return self.d.has_key(str(name), txn)
def get_component(self, name, txn = None):
name = shelve.LockedDBShelf.encodekey(name)
def proc(txn):
if not self.has_component(name, txn):
self.d.put(name, Component(name = name), txn)
return self.d.get(name, txn)
return self.d.txn_proc(proc, txn)
def list_components(self):
list = []
for (pkg, x) in self.d.items():
list.append(pkg)
return list
def update_component(self, component, txn = None):
def proc(txn):
if self.d.has_key(component.name):
# preserve the list of packages
component.packages = self.d[component.name].packages
self.d[component.name] = component
self.d.txn_proc(proc, txn)
def add_package(self, component_name, package, txn = None):
def proc(txn):
component = self.get_component(component_name, txn)
if not package in component.packages:
component.packages.append(package)
self.d.put(component_name, component, txn) # update
self.d.txn_proc(proc, txn)
def remove_package(self, component_name, package, txn = None):
def proc(txn):
if not self.has_component(component_name, txn):
raise Error(_('Information for component %s not available') % component_name)
component = self.get_component(component_name, txn)
if package in component.packages:
component.packages.remove(package)
self.d.put(component_name, component, txn) # update
self.d.txn_proc(proc, txn)
def clear(self, txn = None):
self.d.clear(txn)
def remove_component(self, name, txn = None):
name = str(name)
self.d.delete(name, txn)
-100
View File
@@ -1,100 +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@uludag.org.tr>
# Eray Ozkural <eray@uludag.org.tr>
"""
PISI Configuration module is used for gathering and providing
regular PISI configurations.
"""
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")
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):
dir = self.get_option('destdir')
if dir:
dir = str(dir)
else:
dir = self.values.general.destinationdirectory
import os.path
if not os.path.exists(dir):
ctx.ui.warning( _('Destination directory %s does not exist. Creating it.') % dir)
os.makedirs(dir)
return dir
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):
return self.subdir(self.values.dirs.tmp_dir)
# 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()
-148
View File
@@ -1,148 +0,0 @@
# -*- conding: 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 = /tmp
#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=
#build=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 = "/"
autoclean = False
distribution = "Pardus"
distribution_release = "0.1"
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/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"
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)
-88
View File
@@ -1,88 +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@uludag.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):
# prefix for package names
self.__c.package_suffix = ".pisi"
# directory suffixes for build
self.__c.work_dir_suffix = "/work"
self.__c.install_dir_suffix = "/install"
# file/directory names
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"
self.__c.pisi_index = "pisi-index.xml"
# functions in actions_file
self.__c.setup_func = "setup"
self.__c.build_func = "build"
self.__c.install_func = "install"
# file types
# FIXME: these are 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)
-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.
#
# Context module.
# Authors: Baris Metin <baris@uludag.org.tr
# Eray Ozkural <eray@uludag.org.tr>
# global variables here
import pisi.constants
const = pisi.constants.Constants()
config = None
use_mdom = False
def get_option(opt):
return config and config.get_option(opt)
# default UI is CLI
ui = None # not now
dbenv = None
installdb = None
repodb = None
invidx = None
comar = None
initialized = False
#def register(_impl):
# """ Register a UI implementation"""
# ui = _impl
import bsddb3.db as db
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
autotxn.commit()
else:
retval = proc(None)
return retval
else:
return proc(txn)
-297
View File
@@ -1,297 +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.
#
#------------------------------------------------------------------------
"""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
"""
#------------------------------------------------------------------------
import cPickle
import bsddb3.db as db
import bsddb3.dbobj as dbobj
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)
self.binary = 1
# it is better to explicitly close a shelf
#def __del__(self):
# self.close()
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
autotxn.commit()
else: # execute without transactions
retval = proc(None)
return retval
else:
return proc(txn)
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 cPickle.loads(data)
return self.txn_proc(proc, None)
def __setitem__(self, key, value):
# hyperdandik transactions
def proc(txn):
data = cPickle.dumps(value, self.binary)
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, cPickle.loads(v)) )
return newitems
def values(self, txn=None):
if txn != None:
values = self.db.values(txn)
else:
values = self.db.values()
return map(cPickle.loads, values)
#-----------------------------------
# Other methods
def __append(self, value, txn=None):
data = cPickle.dumps(value, self.binary)
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 = cPickle.loads(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 cPickle.loads(data)
except (TypeError, cPickle.UnpicklingError):
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 = cPickle.dumps(value, self.binary)
data = self.db.get(key, data, txn, flags)
return cPickle.loads(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 = cPickle.dumps(value, self.binary)
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 = cPickle.dumps(value, self.binary)
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 = cPickle.dumps(value, self.binary)
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 = cPickle.dumps(value, self.binary)
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, cPickle.loads(data)
#----------------------------------------------
# Methods allowed to pass-through to self.dbc
#
# close, count, delete, get_recno, join_item
#---------------------------------------------------------------------------
-116
View File
@@ -1,116 +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@uludag.org.tr>
"""dependency analyzer"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
#import pisi.db as db
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
class Dependency:
__metaclass__ = autoxml.autoxml
s_Package = [autoxml.String, autoxml.mandatory]
a_versionFrom = [autoxml.String, autoxml.optional]
a_versionTo = [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 += 'ver >= ' + self.versionFrom
if self.versionTo:
s += 'ver <= ' + self.versionTo
if self.releaseFrom:
s += 'rel >= ' + self.releaseFrom
if self.releaseTo:
s += 'rel <= ' + 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.versionFrom:
ret &= v >= Version(self.versionFrom)
if self.versionTo:
ret &= v <= Version(self.versionTo)
if self.releaseFrom:
ret &= v <= Version(self.releaseFrom)
if self.releaseTo:
ret &= v <= 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 = packagedb.inst_packagedb.get_package(pkg_name)
(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 packagedb.has_package(pkg_name):
return False
else:
pkg = 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 = 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 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
-331
View File
@@ -1,331 +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
# helper functions
def fetch_url(url, dest, progress=None):
fetch = Fetcher(url, dest)
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, dest):
if not isinstance(url, URI):
url = URI(url)
self.scheme = url.scheme()
self.url = url
self.filedest = dest
util.check_dir(self.filedest)
self.eta = '??:??:??'
self.percent = 0
self.rate = 0.0
self.progress = None
self.existsize = 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.filedest, os.W_OK):
self.err(_('Access denied to write to destination directory'))
archivefile = os.path.join(self.filedest, self.url.filename())
if self.url.is_local_file():
self.fetchLocalFile(archivefile + '.part')
else:
self.fetchRemoteFile(archivefile + '.part')
if os.stat(archivefile + '.part').st_size == 0:
os.remove(archivefile + '.part')
self.err(_('A problem occured. Please check the archive address and/or permissions again.'))
else:
move(archivefile + '.part', archivefile)
return archivefile
def _do_grab(self, fileURI, dest, totalsize):
symbols = [' B/s', 'KB/s', 'MB/s', 'GB/s']
bs, tt, = 1024, int(time())
s_time = time()
Tdiff = lambda: time() - s_time
size = existsize = self.existsize
symbol, depth = 'B/s', 0
st = time()
chunk = fileURI.read(bs)
size += len(chunk)
if self.progress:
p = self.progress(totalsize, existsize)
self.percent = p.update(size)
self.complete = False
while chunk:
dest.write(chunk)
chunk = fileURI.read(bs)
size += len(chunk)
ct = time()
if int(tt) != int(ct):
self.rate = (size - existsize) / (ct - st)
if self.percent:
self.eta = '%02d:%02d:%02d' %\
tuple([i for i in gmtime((Tdiff() * (100 - self.percent)) / self.percent)[3:6]])
while self.rate > 1000 and depth < 3:
self.rate /= 1024
depth += 1
symbol, depth = symbols[depth], 0
tt = time()
if self.progress:
if p.update(size):
self.percent = p.percent
if not self.complete:
ctx.ui.display_progress(filename = self.url.filename(),
percent = self.percent,
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, archivefile):
url = self.url
if not os.access(url.path(), os.F_OK):
self.err(_('No such file or no permission to read'))
dest = open(archivefile, 'w')
totalsize = os.path.getsize(url.path())
fileObj = open(url.path())
self._do_grab(fileObj, dest, totalsize)
def fetchRemoteFile (self, archivefile):
from httplib import HTTPException
if os.path.exists(archivefile):
if self.scheme == 'http' or self.scheme == 'https' or self.scheme == 'ftp':
self.existsize = os.path.getsize(archivefile)
dest = open(archivefile, 'ab')
else:
dest = open(archivefile, 'wb')
uri = self.url.uri
flag = 1
try:
try:
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(archivefile).st_size == 0:
os.remove(archivefile)
try:
totalsize = int(headers['Content-Length']) + self.existsize
except:
totalsize = 0
self._do_grab(fileObj, dest, totalsize)
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.existsize 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.existsize)
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 FetchError(_('Requested range not satisfiable'))
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)
rest = int(rawr.split("=")[1].rstrip("-"))
fp, retrlen = fw.retrfile(file, type, rest)
fb, lb = rest, retrlen
if retrlen is None or retrlen == 0:
raise FetchError(_('Requested Range Not Satisfiable'))
retrlen = lb - fb
if retrlen < 0:
# beginning of range is larger than file
raise FetchError(_('Requested Range Not Satisfiable'))
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])
-137
View File
@@ -1,137 +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@uludag.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.path
import types
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 Error(pisi.Error):
pass
class File:
(read, write) = range(2) # modes
@staticmethod
def make_uri(uri):
"handle URI arg"
if type(uri) == types.StringType or type(uri) == types.UnicodeType:
uri = URI(uri)
elif type(uri) != URI:
raise Error(_("uri must have type either URI or string"))
return uri
@staticmethod
def download(uri, transfer_dir = "/tmp"):
assert type(uri == URI)
if uri.is_remote_file():
ctx.ui.info(_("Fetching %s") % uri.get_uri())
localfile = join(transfer_dir, uri.filename())
fetch_url(uri, transfer_dir) # FIXME: localfile would look better for fetch iface?
else:
localfile = uri.get_uri() #TODO: use a special function here?
return localfile
def __init__(self, uri, mode, transfer_dir = "/tmp"):
"it is pointless to open a file without a URI and a mode"
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:
ctx.ui.info(_("Fetching %s") % uri.get_uri())
localfile = join(transfer_dir, uri.filename())
fetch_url(uri, transfer_dir) # FIXME: localfile would look better for fetch iface?
else:
raise Error(_("Remote write not implemented"))
else:
localfile = uri.get_uri() #TODO: use a special function here?
if self.mode == File.read:
access = 'r'
else:
access = 'w'
self.__file__ = file(localfile, access)
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()
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)
-93
View File
@@ -1,93 +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@uludag.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:
self.put(str(x.path), (pkg_name, x), txn)
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:
return self.get(path, txn)
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
-134
View File
@@ -1,134 +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@uludag.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.util as util
from pisi.package import Package
from pisi.pxml.xmlfile import XmlFile
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_Sources = [ [specfile.Source], autoxml.optional, "Source"]
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
t_Components = [ [component.Component], autoxml.optional, "Component"]
def read_uri(self, filename, repo = None):
"""Read PSPEC file"""
self.filepath = filename
url = URI(filename)
if url.is_remote_file():
from fetcher import fetch_url
assert repo
dest = os.path.join(ctx.config.index_dir(), repo)
if not os.path.exists(dest):
os.makedirs(dest)
fetch_url(url, dest, ctx.ui.Progress)
self.filepath = os.path.join(dest, url.filename())
self.read(self.filepath)
def index(self, repo_uri):
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':
self.add_source(os.path.join(root, fn), repo_uri)
def update_db(self, repo):
pkgdb = packagedb.get_db(repo)
pkgdb.clear()
for comp in self.components:
ctx.componentdb.update_component(comp)
for pkg in self.packages:
pkgdb.add_package(pkg)
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))
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') % md.package.name)
ctx.ui.error(str(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:
ctx.ui.error(_('Component in %s is corrupt') % path)
#ctx.ui.error(str(Error(*errs)))
def add_source(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') % 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.sources.append(sf.source)
-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@uludag.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.inst_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
-152
View File
@@ -1,152 +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@uludag.org.tr>
import bsddb3.db as db
import bsddb3.dbobj as dbobj
#import bsddb3.dbshelve as shelve
import pisi.dbshelve as shelve
import os
import fcntl
import types
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
def init_dbenv():
if os.access(pisi.context.config.db_dir(), os.R_OK):
# try to read version
verfn = join_path(pisi.context.config.db_dir(), 'dbversion')
if os.path.exists(verfn):
verfile = file(verfn, 'r')
ls = verfile.readlines()
currver = Version(ls[0])
dbver = Version(pisi.__dbversion__)
if currver < dbver:
raise Error(_('Database version insufficient. Please run rebuild-db command.'))
elif currver > dbver:
raise Error(_('Database version greater than PiSi version. You need a newer PiSi.'))
else:
if os.access(pisi.context.config.db_dir(), os.W_OK):
ctx.ui.warning(_('Writing current database version'))
verfile = file(verfn, 'w')
verfile.write(pisi.__dbversion__)
verfile.close()
else:
raise Error(_('Cannot attain write access to database environment'))
else:
raise Error(_('Cannot attain read access to database environment'))
if os.access(pisi.context.config.db_dir(), os.W_OK):
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.open(pisi.context.config.db_dir(), flags)
ctx.dbenv.set_flags(db.DB_LOG_AUTOREMOVE, 1) # clear inactive logs automatically
else:
ctx.ui.warning(_('Opening PİSİ 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 __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 os.access(os.path.dirname(filename), os.W_OK):
flags = 'w'
if not self.dbenv:
raise Error(_('Database writes not allowed without transactions'))
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 __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')
-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.
#
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.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_PackageURI = [ autoxml.String, 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.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)
-488
View File
@@ -1,488 +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@uludag.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
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()
order.reverse()
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
if ctx.get_option('dry_run'):
return
for x in order:
atomicoperations.install_single_file(dfn[x])
def check_conflicts(order):
"""check if upgrading to the latest versions will cause havoc
done in a simple minded way without regard for dependencies of
conflicts, etc."""
B_0 = B = set(order)
C = set()
# calculate conflict closure
while len(B) > 0:
Bp = set()
for x in B:
pkg = packagedb.get_package(x) # get latest version!
#TODO: read conflicts from a conflicts db...
for conflict in pkg.conflicts:
if ctx.installdb.is_installed(self.pkginfo):
Bp.add(conflict)
C.add(conflict)
B = Bp
if B_0.intersection(C):
raise Error(_("Selected packages %s and %s are in conflict.") % (x, pkg))
if C:
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
#raise Error(_("Package %s conflicts installed package %s") % (x, pkg))
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 install_pkg_names(A, reinstall = False):
"""This is the real thing. It installs packages from
the repository, trying to perform a minimum number of
installs"""
# A was a list, remove duplicates and expand components
A_0 = A = expand_components(set(A))
ctx.ui.debug('A = %s' % str(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(_("""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
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(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 upgrade(A):
upgrade_pkg_names(A)
def upgrade_pkg_names(A = []):
"""Re-installs packages from the repository, trying to perform
a maximum number of upgrades."""
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))
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)
continue
(version, release, build) = ctx.installdb.get_version(x)
pkg = packagedb.get_package(x)
if ignore_build or (not build) or (not pkg.build):
if release < pkg.release:
Ap.append(x)
else:
ctx.ui.info(_('Package %s is already at the latest release %s.')
% (pkg.name, pkg.release))
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))
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)
else:
G_f = None
order = A
ctx.ui.info(_("""The following packages will be upgraded:\n""") +
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
install_ops = [atomicoperations.Install.from_name(x) for x in order]
for install in install_ops:
install.install(True)
def plan_upgrade(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
def upgradable(dep):
#pre dep.package is installed
(v,r,b) = ctx.installdb.get_version(dep.package)
rep_pkg = packagedb.get_package(dep.package)
(vp,rp,bp) = (rep_pkg.version, rep_pkg.release,
rep_pkg.build)
if ignore_build or (not b) or (not bp):
# if we can't look at build
if r >= rp: # installed already new
return False
elif b and bp and b >= bp:
return False
return True
# 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):
#TODO: distinguish must upgrade and upgradable
if ctx.installdb.is_installed(dep.package):
if not ctx.get_option('eager'):
if dependency.installed_satisfies_dep(dep):
continue
else:
if not upgradable(dep):
continue
if not dep.package in G_f.vertices():
Bp.add(str(dep.package))
G_f.add_dep(x, dep)
else:
raise Error(_("Reverse dependency %s cannot be satisfied") % rev_dep)
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 not ctx.get_option('eager'):
# add unsatisfied reverse dependencies
if packagedb.has_package(rev_dep) and \
(not dependency.installed_satisfies_dep(depinfo)):
if not dependency.repo_satisfies_dep(dep):
raise Error(_("Reverse dependency %s cannot be satisfied") % rev_dep)
if not rev_dep in G_f.vertices():
Bp.add(rev_dep)
G_f.add_plain_dep(rev_dep, x)
else:
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)
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
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
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(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)
rev_deps = packagedb.get_rev_deps(x)
for (rev_dep, depinfo) in rev_deps:
# we don't deal with uninstalled rev deps
# and unsatisfied dependencies (this is important, too)
if packagedb.inst_packagedb.has_package(rev_dep) 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
-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.
#
# package abstraction
# provides methods to add/remove files, extract control files
# maintainer: baris and meren
from os.path import join, exists
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
dest = ctx.config.packages_dir()
self.filepath = join(dest, url.filename())
# 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):
"""Add a file or directory to package"""
self.impl.add_to_archive(fn)
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)
-193
View File
@@ -1,193 +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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr>
# we basically store everything in PackageInfo class
# yes, we are cheap
import os, fcntl
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
class Error(pisi.Error):
pass
class PackageDB(object):
"""PackageDB class provides an interface to the package database
using shelf objects"""
def __init__(self, id):
self.id = id
self.d = shelve.LockedDBShelf('package-%s' % id )
self.dr = shelve.LockedDBShelf('revdep-%s' % id )
def close(self):
self.d.close()
self.dr.close()
def has_package(self, name, txn = None):
name = str(name)
return self.d.has_key(name, txn)
def get_package(self, name):
name = str(name)
return self.d[name]
def get_rev_deps(self, name):
name = str(name)
if self.dr.has_key(name):
return self.dr[name]
else:
return []
def list_packages(self):
list = []
for (pkg, x) in self.d.items():
list.append(pkg)
return list
#TODO: list_upgrades?
def add_package(self, package_info, txn = None):
name = str(package_info.name)
def proc(txn):
self.d.put(name, package_info, txn)
for dep in package_info.runtimeDependencies():
dep_name = str(dep.package)
if self.dr.has_key(dep_name, txn):
revdep = self.dr.get(dep_name, txn)
revdep = filter(lambda (n,d):n!=name, revdep)
revdep.append( (name, dep) )
self.dr.put(dep_name, revdep, txn)
else:
self.dr.put(dep_name, [ (name, dep) ], txn)
# add component
ctx.componentdb.add_package(package_info.partOf, package_info.name, 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)
self.d.txn_proc(proc, txn)
def clear(self):
self.d.clear()
def remove_package(self, name, txn = None):
name = str(name)
def proc(txn):
package_info = self.d.get(name, txn)
self.d.delete(name, txn)
#FIXME: what's happening to dr?
#WORKAROUND: do not remove component if it is not in repo
if self.id.startswith('repo'):
ctx.componentdb.remove_package(package_info.partOf, package_info.name, txn)
self.d.txn_proc(proc, txn)
packagedbs = {}
def add_db(name):
pisi.packagedb.packagedbs[name] = PackageDB('repo-' + name)
def get_db(name):
return pisi.packagedb.packagedbs[name]
def remove_db(name):
del pisi.packagedb.packagedbs[name]
#FIXME: erase database file?
def has_package(name, txn = None):
def proc(txn):
repo = which_repo(name)
if repo or thirdparty_packagedb.has_package(name, txn) or inst_packagedb.has_package(name, txn):
return True
return False
return ctx.txn_proc(proc, txn)
def which_repo(name, txn = None):
import pisi.repodb
def proc(txn):
for repo in pisi.repodb.db.list():
if get_db(repo).has_package(name, txn):
return repo
return None
return ctx.txn_proc(proc, txn)
def get_package(name):
repo = which_repo(name)
if repo:
return get_db(repo).get_package(name)
if thirdparty_packagedb.has_package(name):
return thirdparty_packagedb.get_package(name)
if inst_packagedb.has_package(name):
return inst_packagedb.get_package(name)
raise Error(_('get_package: package %s not found') % name)
def get_rev_deps(name):
repo = which_repo(name)
if repo:
return get_db(repo).get_rev_deps(name)
if thirdparty_packagedb.has_package(name):
return thirdparty_packagedb.get_rev_deps(name)
if inst_packagedb.has_package(name):
return inst_packagedb.get_rev_deps(name)
return []
def remove_package(name, txn = None):
# remove the guy from the tracking databases
inst_packagedb.remove_package(name, txn)
if thirdparty_packagedb.has_package(name, txn):
thirdparty_packagedb.remove_package(name, txn)
# tracking databases for non-repository information
thirdparty_packagedb = inst_packagedb = None
def init_db():
global thirdparty_packagedb
global inst_packagedb
if not thirdparty_packagedb:
thirdparty_packagedb = PackageDB('thirdparty')
if not pisi.packagedb.inst_packagedb:
inst_packagedb = PackageDB('installed')
def finalize_db():
global thirdparty_packagedb
global inst_packagedb
global packagedbs
if thirdparty_packagedb:
thirdparty_packagedb.close()
thirdparty_packagedb = None
if inst_packagedb:
inst_packagedb.close()
inst_packagedb = None
packagedbs.clear()
-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
-821
View File
@@ -1,821 +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@uludag.org.tr>
# Gurer Ozen <gurer@uludag.org.tr>
# Baris Metin <baris@uludag.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 xml.dom.minidom as mdom
#from xml.parsers.expat import ExpatError
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
# PiSi
import pisi
# TODO: find the appropriate xmlext implementation and import from there
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")
lang = node.getAttributeNS(XML_NS, '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 = newNode(node, self.tag)
newnode.setAttributeNS(XML_NS, 'xml:lang', key)
newtext = newTextNode(node, self[key])
newnode.appendChild(newtext)
node.appendChild(newnode)
#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(), 'tr', 'en' ]
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 ''
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()
#TODO: there should be at most one str member, and it should be the first
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 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'):
"read XML file and decode it into a python object"
self.readxml(uri, tmpDir)
errs = []
self.decode(self.rootNode(), errs)
if hasattr(self, 'read_hook'):
self.read_hook(errs)
if errs:
errs.append(_("autoxml.read: File '%s' has errors") % uri)
raise Error(*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'):
"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)
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
node.setAttributeNS(XML_NS, 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):
errs = []
if hasattr(self, name):
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)
node.appendChild(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")
dummy.appendChild(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):
dom = node.ownerDocument
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
}
-31
View File
@@ -1,31 +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@uludag.org.tr>
try:
zimbabwe # comment out to disable piks
from xmlextpiks import *
except:
try:
#if pisi.context.use_mdom:
# gibidi
#else:
from xmlextcdom import *
except:
#raise Error('cannot find 4suite implementation')
print 'xmlext: cDomlette/piksemel implementation cannot be loaded, falling back to minidom'
from xmlextmdom import *
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
XHTML_NS = unicode(XHTML_NAMESPACE)
XML_NS = unicode(XML_NAMESPACE)
-202
View File
@@ -1,202 +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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Gurer Ozen <gurer@uludag.org.tr>
# Faik Uygur <faikuygur@gmail.com>
"""
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
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 not node.hasAttributeNS(None, attrname):
return None
else:
return node.getAttributeNS(None, 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."""
#FIXME: better tag split
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)
-198
View File
@@ -1,198 +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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Gurer Ozen <gurer@uludag.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."""
#FIXME: better tag split
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)
-175
View File
@@ -1,175 +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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Gurer Ozen <gurer@uludag.org.tr>
"""
some helper functions for high-level access to XML structures
this implementation uses piksemel, a fast C-based XML library
function names are mixedCase for compatibility with minidom,
an old library
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
from piksemel import *
class XmlError(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
def getNodeAttribute(node, attrname):
"""get named attribute from DOM node"""
return node.getAttribute(attrname)
def getChildElts(node):
"""get only child elements"""
return [x for x in parent.childNodes if x.type() == TAG ]
def getTagByName(parent, childName):
return [x for x in parent if x.type() == TAG and x.name() == childName]
def getNodeText(node, tagpath = ""):
"""get the first child and expect it to be text!"""
if tagpath!="":
node = getNode(node, tagpath)
try:
child = node.firstChild()
except IndexError:
return None
except AttributeError: # no node by that name
return None
if child.type() == CDATA: #FIXME: ??? TEXT type?
# in any case, strip whitespaces... :/ you don't like it?
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.tags():
if child.name() == 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."""
#FIXME: better tag split
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
for tag in tags:
node = node.appendTag(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.appendTag(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):
node = addNode(node, tagPath, branch = branch)
return node.appendData(text)
# cannot be implemented with piksemel
#def newNode(node, tag):
# return Node(tag)
# cannot be implemented with piksemel :(
#def newTextNode(node, text):
# return Node(text)
-30
View File
@@ -1,30 +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@uludag.org.tr>
import pisi.context
try:
horagata # comment out to disable piks
#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 *
#print 'gumburdop'
-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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Faik Uygur <faikuygur@gmail.com>
"""
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'):
uri = File.make_uri(uri)
localpath = File.download(uri, tmpDir)
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'):
f = file(uri, 'w')
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))
-162
View File
@@ -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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Gurer Ozen <gurer@uludag.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))
-140
View File
@@ -1,140 +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@uludag.org.tr>
# Gurer Ozen <gurer@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module
this implementation uses piksemel, a fast C-based XML library
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
from piksemel import *
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"""
self.doc = newDocument(self.rootTag)
def unlink(self):
"""deallocate DOM structure"""
pass # piksemel is garbage collected!
def rootNode(self):
"""returns root document element"""
return self.doc
def readxml(self, fileName):
try:
self.doc = parse(fileName)
except ParseError, inst:
raise XmlError(_("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.toPrettyString())
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.dom.createElement(tag)
#def newTextNode(self, text):
# return self.dom.createTextNode(text)
#def newAttribute(self, attr):
# return self.dom.createAttribute(attr)
# read helpers
def getNode(self, tagPath = ""):
"""returns the *first* matching node for given tag path."""
self.verifyRootTag()
return getNode(self.doc, 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, tagPath)
def getChildren(self, tagpath):
""" returns the children of the given path"""
node = self.getNode(tagpath)
return [x for x in parent.childNodes]
# get only child elements
def getChildElts(self, tagpath):
""" returns the children of the given path, only with given type """
return getChildElts(self.getNode(tagpath))
# write helpers
def addNode(self, tagPath, newnode = None):
"this adds the newnode under given tag path"
self.verifyRootTag()
return addNode(self.doc, 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.dom.documentElement.appendChild(newnode)
def addText(self, node, text):
"add text to node"
raise Error('NOT IMPLEMENTED')
#node.appendChild(self.newTextNode(text))
def addTextNode(self, tagPath, text):
"add a text node with given tag path"
raise Error('NOT IMPLEMENTED')
#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)"
raise Error('NOT IMPLEMENTED')
#return self.addNodeUnder(node, tagPath, self.newTextNode(text))
-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):
self.d = shelve.LockedDBShelf("repo")
if not self.d.has_key("order"):
self.d["order"] = []
def init_dbs(self):
# initialize package/source dbs
for x in self.list():
packagedb.add_db(x)
def close(self):
self.d.close()
def repo_name(self, ix):
l = self.list()
return l[ix]
def swap(self, x, y):
l = self.d["order"]
t = l[x]
l[x] = l[y]
l[y] = t
self.d["order"] = l
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):
name = str(name)
assert (isinstance(repo_info,Repo))
if self.d.has_key("repo-" + name):
raise Error(_('Repository %s already exists') % name)
self.d["repo-" + name] = repo_info
order = self.d["order"]
order.append(name)
self.d["order"] = order
packagedb.add_db(name)
def list(self):
return self.d["order"]
def clear(self):
self.d.clear()
def remove_repo(self, name):
name = str(name)
del self.d["repo-" + name]
l = self.d["order"]
l.remove(name)
self.d["order"] = l
db = None
def init():
global db
if db:
return db
db = RepoDB()
db.init_dbs()
return db
def finalize():
global db
if db:
db.close()
db = None
-63
View File
@@ -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@uludag.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@uludag.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)
-40
View File
@@ -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
-34
View File
@@ -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@uludag.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@uludag.org.tr>
# Eray Ozkural <eray@uludag.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)
-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.
#
# Author: Eray Ozkural <eray@uludag.org.tr>
"""
package source database
interface for update/query to local package repository
we basically store everything in sourceinfo class
yes, we are cheap
"""
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
class SourceDB(object):
def __init__(self, id = 'repo'):
self.d = shelve.LockedDBShelf('source-%s' % id)
def close(self):
self.d.close()
def has_source(self, name):
name = str(name)
return self.d.has_key(name)
def get_source(self, name):
name = str(name)
return self.d[name]
def add_source(self, source_info):
assert not source_info.errors()
name = str(source_info.name)
self.d[name] = source_info
def remove_source(self, name):
name = str(name)
del self.d[name]
sourcedb = None
def init():
global sourcedb
if sourcedb:
return sourcedb
sourcedb = SourceDB()
return sourcedb
def finalize():
global sourcedb
if sourcedb:
sourcedb.close()
sourcedb = None
-267
View File
@@ -1,267 +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@uludag.org.tr>
# Baris Metin <baris@uludag.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]
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.mandatory]
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]
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@uludag.org.tr>
# A. Murat Eren <meren@uludag.org.tr>
import sys
import pisi
import pisi.context as ctx
(installed, upgraded, removed, installing, removing,
configuring, configured, extracting) = range(8)
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
-108
View File
@@ -1,108 +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(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)
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)
-599
View File
@@ -1,599 +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@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr>
# S. Caglar Onur <caglar@uludag.org.tr>
# A. Murat Eren <meren@uludag.org.tr>
# standard python modules
import os
import re
import sys
import sha
import shutil
import statvfs
import operator
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):]
##############################
# Process Releated Functions #
##############################
def run_batch(cmd):
"""run command non-interactively and report return value and output"""
ctx.ui.info(_('running ') + cmd)
a = os.popen(cmd)
lines = a.readlines()
ret = a.close()
ctx.ui.debug(_('return value %s') % ret)
successful = ret == None
if not successful:
ctx.ui.error(_('Failed command: %s') % cmd + strlist(lines))
return (successful,lines)
######################
# 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)
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))])
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)
# 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):
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
# also handle single files
if os.path.isfile(top):
yield (top, sha1_sum(top))
return
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
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"""
if sha1_file(filename) == hash:
return True
return False
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)
(successful, lines) = run_batch("patch -p%d %s< %s" %
(level, target, patchFile))
if not successful:
raise Error(_("ERROR: patch (%s) failed: %s") % (patchFile,
strlist (lines)))
os.chdir(cwd)
def strip_directory(top, package_name, excludelist=[]):
for root, dirs, files in os.walk(top):
for fn in files:
frpath = join_path(root, fn)
# 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, package_name):
ctx.ui.debug("%s [%s]" %(p, "stripped"))
def strip_file(filepath, package_name):
"""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, package_name):
"""copy debug info into /debug/PACKAGE_NAME/file.debug file"""
try:
os.makedirs("/debug/%s" % package_name)
except OSError:
pass
p = os.popen("objcopy --only-keep-debug %s /debug/%s/%s.debug" % (f, package_name, os.path.basename(f)))
ret = p.close()
if ret:
ctx.ui.warning(_("objcopy (keep-debug) command failed for file '%s'!") % f)
"""mark binary/shared objects to use /debug/PACKAGE_NAME/file.debug"""
p = os.popen("objcopy --add-gnu-debuglink=/debug/%s/%s.debug %s" % (package_name, os.path.basename(f), 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 ctx.config.values.build.debug == "True":
save_elf_debug(filepath, package_name)
run_strip(filepath)
return True
elif "SB shared object" in o:
if ctx.config.values.build.debug == "True":
save_elf_debug(filepath, package_name)
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():
ctx.ui.info(_('Updating environment...'))
env_dir = join_path(ctx.config.dest_dir(), "/etc/env.d")
profile_file = join_path(ctx.config.dest_dir(), "/etc/profile.env")
ldconf_file = join_path(ctx.config.dest_dir(), "/etc/ld.so.conf")
if not os.path.exists(env_dir):
os.makedirs(env_dir, 0755)
list = []
for file in os.listdir(env_dir):
if not os.path.isdir(join_path(env_dir, file)):
list.append(file)
list.sort()
keys = {}
for file in list:
f = open(join_path(env_dir, file), "r")
for line in f:
if not re.search("^#", line.strip()):
currentLine = line.strip().split("=")
try:
if keys.has_key(currentLine[0]):
keys[currentLine[0]] += ":" + currentLine[1].replace("\"", "")
else:
keys[currentLine[0]] = currentLine[1].replace("\"", "")
except IndexError:
pass
# generate profile.env
f = open(profile_file, "w")
for key in keys:
f.write("export %s=\"%s\"\n" % (key, keys[key]))
f.close()
# generate ld.so.conf
f = open(ldconf_file, "w")
if keys.has_key("LDPATH"):
for path in keys["LDPATH"].split(":"):
f.write("%s\n" % path)
f.close()
# run ldconfig
run_batch("/sbin/ldconfig -X -r %s" % ctx.config.dest_dir())
-194
View File
@@ -1,194 +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@uludag.org.tr>
# Baris Metin <baris@uludag.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) > rc > pre > beta > alpha
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-101
View File
@@ -1,101 +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}@uludag.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@uludag.org.tr",
url="http://www.uludag.org.tr/eng/pisi/",
package_dir = {'': ''},
packages = ['pisi', 'pisi.cli', 'pisi.actionsapi', 'pisi.pxml', 'pisi.search'],
scripts = ['pisi-cli', 'tools/ebuild2pisi.py', '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
-10
View File
@@ -1,10 +0,0 @@
PISI test suite
---------------
There are python unit tests and shell scripts in this directory.
Shell scripts use the CLIs to perform package operations.
For this there must be a symlink "packages" to the PISI package repository
until we can find a few sample packages to put here.
-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>
-58
View File
@@ -1,58 +0,0 @@
# 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 testcase
class ActionsAPITestCase(testcase.TestCase):
def setUp(self):
testcase.TestCase.setUp(self)
#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))
suite = unittest.makeSuite(ActionsAPITestCase)
-114
View File
@@ -1,114 +0,0 @@
# 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 os
from os.path import exists as pathexists
from os.path import basename, islink, join
import pisi.context as ctx
import pisi.api
from pisi import archive
from pisi import sourcearchive
from pisi import fetcher
from pisi import util
from pisi.specfile import SpecFile
from pisi import uri
import testcase
class ArchiveFileTestCase(testcase.TestCase):
def testUnpackTar(self):
spec = SpecFile("tests/popt/pspec.xml")
targetDir = '/tmp/pisitest'
achv = sourcearchive.SourceArchive(spec, targetDir)
assert spec.source.archive.type == "targz"
# skip fetching and directly unpack the previously fetched (by
# fetchertests) archive
if not achv.is_cached(interactive=False):
achv.fetch(interactive=False)
achv.unpack()
# but testing is hard
# "var/tmp/pisi/popt-1.7-3/work" (targetDir)
assert pathexists(targetDir + "/popt-1.7")
testfile = targetDir + "/popt-1.7/Makefile.am"
assert pathexists(testfile)
# check file integrity
self.assertEqual(util.sha1_file(testfile),
"5af9dd7d754f788cf511c57ce0af3d555fed009d")
def testUnpackZip(self):
spec = SpecFile("tests/pccts/pspec.xml")
targetDir = '/tmp/pisitest'
assert spec.source.archive.type == "zip"
achv = sourcearchive.SourceArchive(spec, targetDir)
achv.fetch(interactive=False)
achv.unpack(clean_dir=True)
assert pathexists(targetDir + "/pccts")
testfile = targetDir + "/pccts/history.txt"
assert pathexists(testfile)
# check file integrity
self.assertEqual(util.sha1_file(testfile),
"f2be0f9783e84e98fe4e2b8201a8f506fcc07a4d")
# TODO: no link file in pccts package. Need to find a ZIP file
# containing a symlink
# check for symbolic links
# testfile = targetDir + "/sandbox/testdir/link1"
# assert islink(testfile)
def testMakeZip(self):
# first unpack our dear sandbox.zip
spec = SpecFile("tests/pccts/pspec.xml")
targetDir = '/tmp/pisitest'
achv = sourcearchive.SourceArchive(spec, targetDir)
achv.fetch(interactive=False)
achv.unpack(clean_dir=True)
del achv
newZip = targetDir + "/new.zip"
zip = archive.ArchiveZip(newZip, 'zip', 'w')
sourceDir = targetDir + "/pccts"
zip.add_to_archive(sourceDir)
zip.close()
#TODO: do some more work to test the integrity of new zip file
def testUnpackZipCond(self):
spec = SpecFile("tests/pccts/pspec.xml")
targetDir = '/tmp'
achv = sourcearchive.SourceArchive(spec, targetDir)
url = uri.URI(spec.source.archive.uri)
filePath = join(ctx.config.archives_dir(), url.filename())
# check cached
if util.sha1_file(filePath) != spec.source.archive.sha1sum:
fetch = fetcher.Fetcher(spec.source.archive.uri, targetDir)
fetch.fetch()
assert spec.source.archive.type == "zip"
achv = archive.Archive(filePath, spec.source.archive.type)
achv.unpack_files(["pccts/history.txt"], targetDir)
assert pathexists(targetDir + "/pccts")
testfile = targetDir + "/pccts/history.txt"
assert pathexists(testfile)
suite = unittest.makeSuite(ArchiveFileTestCase)
-102
View File
@@ -1,102 +0,0 @@
# 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 os
import xml.dom.minidom as mdom
from xml.parsers.expat import ExpatError
import types
import pisi
import pisi.api
from pisi.pxml import xmlfile
from pisi.pxml import autoxml
import pisi.util as util
class AutoXmlTestCase(unittest.TestCase):
def setUp(self):
class OtherInfo:
__metaclass__ = autoxml.autoxml
t_BirthDate = [types.StringType, autoxml.mandatory]
t_Interest = [types.StringType, autoxml.optional]
t_CodesWith = [ [types.StringType], autoxml.optional, 'CodesWith/Person']
class A(xmlfile.XmlFile):
__metaclass__ = autoxml.autoxml
t_Name = [types.StringType, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_Number = [types.IntType, autoxml.optional]
t_Email = [types.StringType, autoxml.optional]
a_href = [types.StringType, autoxml.mandatory]
t_Projects = [ [types.StringType], autoxml.mandatory, 'Project']
t_OtherInfo = [ OtherInfo, autoxml.optional ]
s_Comment = [ autoxml.Text, autoxml.mandatory]
self.A = A
def testDeclaration(self):
self.assertEqual(len(self.A.decoders), 8) # how many fields in A?
self.assert_(hasattr(self.A, 'encode'))
def testReadWrite(self):
a = self.A()
# test initializer
self.assertEqual(a.href, None)
# test read
a.read('tests/a.xml')
self.assert_(a.href.startswith('http'))
self.assertEqual(a.number, 868)
self.assertEqual(a.name, 'Eray Ozkural')
self.assertEqual(len(a.projects), 3)
self.assertEqual(len(a.otherInfo.codesWith), 4)
self.assert_(not a.errors())
a.print_text(file('/tmp/a', 'w'))
la = file('/tmp/a').readlines()
self.assert_( util.any(lambda x:x.find('18071976')!=-1, la) )
a.write('/tmp/a.xml')
def testWriteRead(self):
a = self.A()
a.name = "Baris Metin"
a.email = "baris@uludag.org.tr"
a.description['tr'] = u'Melek, melek'
a.comment = u'Bu da zibidi aslinda ama caktirmiyor'
a.href = 'http://cekirdek.uludag.org.tr/~baris'
a.otherInfo.birthDate = '30101979'
a.projects = [ 'pisi', 'tasma', 'plasma' ]
errs = a.errors()
if errs:
self.fail( 'We got a bunch of errors: ' + str(errs))
a.write('/tmp/a2.xml')
a2 = self.A()
a2.read('/tmp/a2.xml')
self.assertEqual(a, a2)
class LocalTextTestCase(unittest.TestCase):
def setUp(self):
a = autoxml.LocalText()
a['tr'] = u'Zibidi'
a['en'] = u'ingiliz hiyarlari ne anlar zibididen'
self.a = a
def testStr(self):
s = str(self.a)
self.assert_(s!= None and len(s)>=6)
suite1 = unittest.makeSuite(AutoXmlTestCase)
suite2 = unittest.makeSuite(LocalTextTestCase)
suite = unittest.TestSuite((suite1, suite2))
-41
View File
@@ -1,41 +0,0 @@
#!/bin/sh
echo "beta functionality test script"
echo "working directory:" `pwd`
echo "cleaning destination dir: tmp"
PATH=$PATH:.
set -x # xtrace
set -e # errexit
rm -rf tmp
#echo "*** build tests"
pisi-cli -Dtmp build http://svn.uludag.org.tr/pardus/pardus-devel/system/base/zip/pspec.xml http://svn.uludag.org.tr/pardus/pardus-devel/system/base/unzip/pspec.xml
#partial-builds
pisi-cli -Dtmp build-setup http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
pisi-cli -Dtmp build-build http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
pisi-cli -Dtmp build-install http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
pisi-cli -Dtmp build-package http://svn.uludag.org.tr/pardus/pardus-devel/system/base/hdparm/pspec.xml
#echo "*** repository tests"
pisi-cli -Dtmp index .
pisi-cli -Dtmp add-repo repo1 pisi-index.xml
pisi-cli -Dtmp update-repo repo1
pisi-cli -Dtmp list-repo
#echo "*** package ops"
pisi-cli -Dtmp info *.pisi
# pisi-cli list-available
pisi-cli -Dtmp install --ignore-comar zip
pisi-cli -Dtmp list-installed
pisi-cli -Dtmp remove --ignore-comar unzip
pisi-cli -Dtmp install --ignore-comar zip*.pisi
pisi-cli -Dtmp install --ignore-comar hdparm*.pisi flex*.pisi grep*.pisi
pisi-cli -Dtmp remove-repo repo1
# pisi-cli list-available
echo "*** database contents"
for x in `find tmp -iname '*.bdb'`; do
echo "contents of database " $x;
tools/cat-db.py $x;
done
-61
View File
@@ -1,61 +0,0 @@
# 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
from pisi.configfile import ConfigurationFile
# NB: no need for pisi testcase in these things that do well without pisi init
class ConfigFileTestCase(unittest.TestCase):
def setUp(self):
self.cf = ConfigurationFile("tests/pisi.conf")
def testSections(self):
cf = self.cf
if not cf.general:
self.fail("No 'general' section found in ConfigurationFile")
if not cf.build:
self.fail("No 'build' section found in ConfigurationFile")
if not cf.dirs:
self.fail("No 'dirs' section found in ConfigurationFile")
def testValues(self):
cf = self.cf
# test values from pisi.conf file
self.assertEqual(cf.general.destinationdirectory, "/testing")
self.assertEqual(cf.dirs.archives_dir, "/disk2/pisi/archives")
# test default values
self.assertEqual(cf.dirs.tmp_dir, "/var/tmp/pisi")
def testAccessMethods(self):
cf = self.cf
self.assertEqual(cf.build.host, cf.build["host"])
self.assertEqual(cf.dirs.index_dir, cf.dirs["index_dir"])
def testFlagsExists(self):
cf = self.cf
#build
self.assert_(cf.build.cflags)
self.assert_(cf.build.cxxflags)
#general
self.assert_(cf.general.destinationdirectory)
#dirs
self.assert_(cf.dirs.index_dir)
self.assert_(cf.dirs.tmp_dir)
self.assert_(cf.dirs.packages_dir)
suite = unittest.makeSuite(ConfigFileTestCase)

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