Move pisi-1.1_rc3 tag
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
Authors and contributors
|
||||
------------------------
|
||||
|
||||
Eray Özkural <eray [at] pardus.org.tr>
|
||||
Barış Metin <baris [at] pardus.org.tr>
|
||||
S. Çağlar Onur <caglar [at] pardus.org.tr>
|
||||
A. Murat Eren <meren [at] pardus.org.tr>
|
||||
Faik Uygur <faik [at] pardus.org.tr>
|
||||
Gürer Özen <gurer [at] pardus.org.tr>
|
||||
İsmail Dönmez <ismail [at] pardus.org.tr>
|
||||
Görkem Çetin <gorkem [at] pardus.org.tr>
|
||||
Bahadır Kandemir <bahadir [at] pardus.org.tr>
|
||||
Ahmet Aygun <ahmet.aygun [at] pardus.org.tr>
|
||||
Onur Küçük <onur [at] pardus.org.tr>
|
||||
Furkan Duman <fduman [at] pardus.org.tr>
|
||||
@@ -1,150 +0,0 @@
|
||||
Like every serious project, there are guidelines.
|
||||
"Coding Standards" for serious.
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
0. Before reading any further please observe
|
||||
PEP 8: Style Guide for Python Code
|
||||
http://www.python.org/peps/pep-0008.html
|
||||
|
||||
In particular this means no lameCaps
|
||||
|
||||
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. :)
|
||||
|
||||
Branches and SVN
|
||||
----------------
|
||||
|
||||
There are two branches of pisi, one is called pisi-devel and
|
||||
new features that are large enough to cause instability go
|
||||
into that branch. The trunk version is supposed to be stable at
|
||||
all times. This means that you *must* run unit tests and other
|
||||
test scripts after committing any change that cannot be tested
|
||||
in isolation. Run the unit tests periodically to catch unseen
|
||||
bugs. A release from the stable branch *must not* break any tests
|
||||
whatsoever, so extensive use of the test suite must precede any
|
||||
release.
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
Other tests
|
||||
-----------
|
||||
|
||||
There are a couple of nice test scripts for testing the basic
|
||||
capabilities of the command line interface such as building and
|
||||
upgrading. Unlike unit tests, you have to take a look at the output
|
||||
to understand that the scripts are doing well :)
|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
@@ -1,16 +0,0 @@
|
||||
You can install PiSi on your system with a single command.
|
||||
|
||||
# ./setup.py install
|
||||
|
||||
If you are on a Pardus system, you should delete /usr/lib/pardus/pisi
|
||||
when you are installing the SVN version
|
||||
|
||||
PiSi requires piksemel XML processing library, the newest version
|
||||
bsd bindings called "bsddb3" and Pardus configuration manager COMAR
|
||||
in order to run.
|
||||
|
||||
If you are upgrading from an old PiSi release you may have to
|
||||
run the following command as root:
|
||||
|
||||
# pisi rebuild-db
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
recursive-include po *
|
||||
recursive-include tests *.py
|
||||
recursive-include tools *
|
||||
recursive-include doc *
|
||||
include . *.dtd
|
||||
include . README NEWS CODING COPYING
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
PISI 1.0.9 25 April 2006:
|
||||
|
||||
* source index/database and remote builds by name
|
||||
* "emerge" command, now we are just like gentoo :)
|
||||
* performance and usability improvements. faster database access and
|
||||
faster updates. index integrity, authenticity and compression.
|
||||
smaller db files and simplification in db schema / less seeks.
|
||||
correct use of berkeley DB cache.
|
||||
* several other improvements and bugfixes
|
||||
|
||||
PISI 1.0.2 25 December 2005:
|
||||
|
||||
* some minor bugs were corrected
|
||||
|
||||
PISI 1.0.1 24 December 2005:
|
||||
|
||||
* bugfix release. one important improvement is preventing the accumulation
|
||||
of inactive database logs (we handle catastrophic failure in
|
||||
another way, by making all critical data available as xml)
|
||||
|
||||
PISI 1.0 21 December 2005:
|
||||
|
||||
* new command "check"
|
||||
* a new tool "calc-build-order.py" to calculate build order
|
||||
for the entire distro
|
||||
* various bugfixes and improvements, no significant issues remain
|
||||
|
||||
PISI release candidate 2 12 December 2005:
|
||||
|
||||
* maintenance release for Pardus 1.0 RC1, various bugfixes
|
||||
|
||||
PISI release candidate 1 9 December 2005:
|
||||
|
||||
* a command to search package text
|
||||
* better reliability through transaction support for berkeley db
|
||||
* db version file
|
||||
* loads of bugfixes
|
||||
|
||||
PISI Beta 1/2 release 17/18 November 2005:
|
||||
|
||||
* support for components, see the completed items in TODO
|
||||
* a tool to find library dependencies
|
||||
* various improvements and bugfixes
|
||||
|
||||
PISI Alpha 9 release 13 November 2005:
|
||||
|
||||
* new package: pisi.pxml
|
||||
* first release of autoxml, a metaclass for high-level xml processing
|
||||
* uses 4suite.org for fast xml processing
|
||||
* supports multiple parsers: minidom, 4suite and in the future: piksemel
|
||||
* all xml structures in PISI have been updated to use autoxml
|
||||
* improvements in the ui interface
|
||||
* significant fixes for Turkish translations
|
||||
|
||||
previous PISI releases had too many things to mention :)
|
||||
@@ -1,18 +0,0 @@
|
||||
PISI - Packages Installed Succesfully as Intended
|
||||
|
||||
PISI is a new package manager for the PARDUS
|
||||
distribution. In Turkish PISI means "kitty", and
|
||||
like a kitty, it is featureful and small.
|
||||
|
||||
Some of its distinctive features:
|
||||
|
||||
- Implemented in python
|
||||
- Efficient and small
|
||||
- Package sources are written in XML and python
|
||||
- Uses LZMA for a better compression ratio
|
||||
- Fast database access implemented with berkeley DB
|
||||
- Integrates low-level and high-level package operations (dependency resolution)
|
||||
- Framework approach to build applications and tools upon
|
||||
- Comprehensive CLI and a user-friendly qt GUI (distributed separately)
|
||||
- Extremely simple package construction
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
For upgrading from pisi 1.0 and before
|
||||
|
||||
You have to move the packages in /var/lib/pisi under /var/lib/pisi/package and remove /var/db/pisi/ directory.
|
||||
|
||||
db version has changed, so a rebuild-db will be unfortunately necessary.
|
||||
@@ -1,247 +0,0 @@
|
||||
|
||||
|
||||
PiSi TODO List
|
||||
==============
|
||||
|
||||
A list of tasks to accomplish, organized into priority sections
|
||||
|
||||
Legend:
|
||||
|
||||
- Todo
|
||||
? Not determined if/how we have to do
|
||||
/ In progress
|
||||
+ Accomplished
|
||||
|
||||
1. Pre-Alpha
|
||||
|
||||
+ reading spec file
|
||||
+ writing
|
||||
+ files
|
||||
+ metadata
|
||||
+ Actions API framework
|
||||
+ sample api modules (autotools)
|
||||
+ unit test
|
||||
+ source building
|
||||
+ package module
|
||||
+ extraction
|
||||
+ add file / compression
|
||||
+ package creation
|
||||
+ install database
|
||||
+ package database
|
||||
+ command line interface
|
||||
+ pisi-build
|
||||
+ pisi-install
|
||||
+ pisi-index
|
||||
+ pisi-updatedb
|
||||
+ single repository index
|
||||
+ simple dependency checking
|
||||
+ Package/Files
|
||||
|
||||
2. Alpha
|
||||
|
||||
+ multi-package dependency analysis (eray)
|
||||
+ design a package operation planner
|
||||
+ install/remove operations (eray)
|
||||
+ command line interface:
|
||||
+ pisi remove
|
||||
+ svn-like CLI
|
||||
+ configuration file
|
||||
+ define the format of the configuration file
|
||||
+ extend Config module (config.py) accordingly
|
||||
+ define configuration keys (baris - meren)
|
||||
+ query
|
||||
+ list of installed packages (eray)
|
||||
+ refactor actionsAPI (caglar)
|
||||
+ fix xml indentation (meren)
|
||||
+ define file types (doc, executable, conf, etc...) (baris - meren)
|
||||
+ write specfile (eray)
|
||||
+ file locking for database access [not tested!] (eray)
|
||||
+ implement file uri
|
||||
+ extend the modules dealing with files accordingly
|
||||
+ ui module improvements
|
||||
+ methods for interaction (yes, no questions, etc.)
|
||||
+ implement source database (eray)
|
||||
+ easy package preparation
|
||||
+ convert ebuild to pisi
|
||||
+ COMAR interface
|
||||
+ what do we need to specify in a package.
|
||||
+ package install: register config script
|
||||
+ package remove: unregister config script
|
||||
+ internet installation
|
||||
+ support URI's whereever a filename is supported
|
||||
+ http server
|
||||
+ pisi updatedb over internet
|
||||
|
||||
3. Beta
|
||||
|
||||
+ build cmd: installl build deps
|
||||
+ improve url support, it's a little dumb at the moment
|
||||
+ devise a File class that works with URLs
|
||||
+ add URL support to autoxml
|
||||
+ info cmd: reverse dependencies of a package
|
||||
+ a helper script for finding dependencies (eray)
|
||||
+ autoxml: automated xml processing (eray)
|
||||
+ design
|
||||
+ basic types
|
||||
+ list type
|
||||
+ class type
|
||||
+ localtext/text types
|
||||
+ revised interface
|
||||
+ improved error handling, check routines, hooks, format/print routines
|
||||
+ xmlfile support (read/write)
|
||||
+ string member, in addition to tag/attribute members
|
||||
+ replace specfile with new specfile using autoxml
|
||||
+ autoxmlize: metadata, index, dependency and related code paths
|
||||
+ inheritance support
|
||||
+ revise writing, error reporting, tests
|
||||
+ components (eray)
|
||||
+ requirements
|
||||
+ xml format, add to PSPEC
|
||||
+ index component.xml's and make use of them in build
|
||||
+ a database of components:
|
||||
+ query components by name
|
||||
+ query list of packages in a component
|
||||
+ index: update component database(s)
|
||||
+ bug fixing
|
||||
+ install/remove/upgrade components
|
||||
+ safety switch: preserve system.base, install system.devel for build, etc.
|
||||
+ info <component>
|
||||
+ started documentation
|
||||
+ component dependencies
|
||||
+ multiple package repository (eray)
|
||||
+ decide how to implement
|
||||
+ support repo order
|
||||
+ refactor xml support and implement multiple backends
|
||||
+ 4suite support (initial patch by faik, completed by eray)
|
||||
+ piksemel support (eray)
|
||||
+ fix autoxml implementation to use 4suite by default
|
||||
+ partial caching and automatic resume for file download (meren)
|
||||
+ use a '.part' extension
|
||||
+ search-file command (eray)
|
||||
+ exception handling (eray)
|
||||
+ uniform exception hierarchy
|
||||
+ proper handling
|
||||
+ API: we even have application users (eray)
|
||||
+ a facade module pisi.api (eray)
|
||||
+ a library init command (eray)
|
||||
+ high-level package ops, index (eray)
|
||||
+ info/util commands (eray)
|
||||
+ build commands (baris)
|
||||
+ documentation
|
||||
+ actionsAPI documentation, unittests (caglar - meren)
|
||||
+ versioning information document (eray - baris)
|
||||
+ UI
|
||||
+ PISIMAT
|
||||
+ pykde GUI (cartman)
|
||||
+ improve interface (eray)
|
||||
+ refactor UI, reintroduce base class
|
||||
+ add an ack interface, start cleaning up a bit
|
||||
+ cli (eray)
|
||||
+ ask alternatives to choose from
|
||||
+ eliminate \n's from infos, what's the point?
|
||||
+ metaclass coolness for CLI command framework (eray)
|
||||
+ i18n support
|
||||
+ translations and correct language for summary, description (packagers)
|
||||
+ make pisi messages translatable (eray)
|
||||
+ .pot support for dist script (gurer)
|
||||
+ translate messages mesajların çevirisinin yapılması (pisi tr.po) (eray)
|
||||
+ proper unicode handling in code - tons of fixes (eray - baris)
|
||||
+ configure-pending (eray)
|
||||
+ proper commar connection (baris - gurer)
|
||||
+ ignore-deps (eray)
|
||||
+ more robust upgrade (eray)
|
||||
+ query (eray)
|
||||
+ comar OM information (Provides)
|
||||
+ find package by name, display summary/description
|
||||
+ info --files option (like rpm -ql, but better of course)
|
||||
+ implement missing unit tests
|
||||
+ sourcedb
|
||||
+ package (baris)
|
||||
+ configuration file (baris)
|
||||
+ upgrade (eray)
|
||||
+ upgrade operation
|
||||
+ test upgrade op
|
||||
+ system-wide upgrade (upgrade-all) komutu
|
||||
+ incremental build (eray)
|
||||
+ generate binary release number by comparing MD5s (eray)
|
||||
+ keep track of successfully completed configure, make, install
|
||||
steps (necessary for large-scale builds?) (meren)
|
||||
+ database locking bugs (eray)
|
||||
|
||||
4. Release
|
||||
|
||||
* high priority
|
||||
|
||||
+ transaction stuff for database (eray)
|
||||
+ database version file
|
||||
|
||||
* low priority
|
||||
|
||||
+ search command: (eray)
|
||||
+ implement an inverted index
|
||||
+ search in summary and description
|
||||
|
||||
5. Post Release
|
||||
|
||||
* high priority
|
||||
|
||||
- package signing
|
||||
- a flag to sign package after building
|
||||
- code to check if the package has a valid signature
|
||||
- cli improvements
|
||||
- non interactive mode (similar to --auto in urpmi)
|
||||
/ high-level build commands
|
||||
+ an emerge command, just for fun
|
||||
+ make use of binary repo to satisfy dependencies in emerge
|
||||
/ build command enhancements:
|
||||
+ aware of repo sources
|
||||
? full dependency analysis like install/upgrade etc.
|
||||
? build-all (or build-distro) command, or as a separate tool
|
||||
+ make a package and sourcedb for each repo
|
||||
+ packagedb
|
||||
+ sourceb
|
||||
? improve url support: consider adding "other" protocols
|
||||
- removable media: media://
|
||||
|
||||
* medium priority
|
||||
|
||||
- UI impl: non-interactive use that selects defaults and logs output
|
||||
- UI impl: a general purpose Qt UI
|
||||
/ multi-architecture support
|
||||
+ design decisions
|
||||
+ extend XML specs to support that
|
||||
- cross-platform building support
|
||||
- incremental build/fetch for repository index (pisi-index.xml)
|
||||
/ xdelta support (caglar)
|
||||
- diffsets
|
||||
- a command (check-repo) to check if repos are consistent wrt dep relations
|
||||
- component improvements (optional)
|
||||
- maintain a list of components each component has
|
||||
- maintain a list of packages
|
||||
- more support for categories:
|
||||
- put categories into a database
|
||||
- fast search for categories
|
||||
|
||||
* low priority
|
||||
|
||||
/ overhaul installdb (try to merge its use with packagedb) (eray)
|
||||
/ verify metodlari
|
||||
+ SpecFile
|
||||
+ MetaData
|
||||
+ Files
|
||||
- Index (somebody should write one :} )
|
||||
- autoxml:
|
||||
- revise error checking, printing and tests
|
||||
- uri type
|
||||
- date type
|
||||
- enum type
|
||||
- make autoxml dependent on only xmlext and xmlfile interfaces
|
||||
- search command: (eray)
|
||||
- search components
|
||||
- english stemming
|
||||
- turkish stemming
|
||||
- phonetic/syntactic fault tolerance (whichever works!)
|
||||
- in particular evaluate soundex and metaphone
|
||||
- research if there are language independent phonetic encodings
|
||||
- implement as an option (or make it possible to turn it off if it works well :))
|
||||
- fault tolerant search in package names (somehow)
|
||||
@@ -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}}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
Binary file not shown.
@@ -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}
|
||||
|
||||
Binary file not shown.
@@ -1,187 +0,0 @@
|
||||
\documentclass[a4paper,11pt]{article}
|
||||
|
||||
\title{P\.IS\.I Packages: Version Policy v0.2}
|
||||
\date{\today}
|
||||
\author{Eray \"Ozkural and T. Bar\i{}\c s Metin}
|
||||
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
|
||||
\section*{Revision History}
|
||||
\begin{itemize}
|
||||
\item v0.1: Bar\i\c s Metin wrote the first version preparing the outline,
|
||||
detailed Source Version Section, and started the Section on Release Number.
|
||||
\item v0.2: Eray \"Ozkural wrote a detailed introduction, added
|
||||
explanations of release and build numbers, reorganized a bit.
|
||||
\end{itemize}
|
||||
|
||||
\section{Introduction}
|
||||
|
||||
This document explains the \emph{version policy} that applies to
|
||||
P\.IS\.I packages. Classically, the issue of distinguishing source and
|
||||
binary distributions unambiguously has not received a rigorous
|
||||
treatment in the context of LINUX distributions. We have identified
|
||||
several shortcomings of the usual practices of extending the original
|
||||
version with suffixes and prefixes, colorfully illustrated in the
|
||||
following common problems.
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[The problem of future downgrades]
|
||||
The distribution chooses to use a previous version of the package in
|
||||
the next release. There is no way to indicate this, so ad-hoc
|
||||
solutions such as version prefixes are used. It is
|
||||
impossible to denote a future dependency that requires at least this
|
||||
distribution source release in this case, either.
|
||||
|
||||
\item[The problem of redundant distributions]
|
||||
A trivial patch has been applied to the source. While few binary
|
||||
packages have been affected by this change, all binary packages
|
||||
built from the source are redistributed.
|
||||
|
||||
\item[The problem of underdetermined rebuilds]
|
||||
There have been rapid changes in the system, and although no
|
||||
changes have been made to the package source, a new binary
|
||||
distribution must be prepared.
|
||||
|
||||
\end{description}
|
||||
|
||||
We have devised a slightly new approach in order to alleviate these
|
||||
problems. Our solution consists of encoding the history of source and
|
||||
binary package developments in separate version strings we call release and
|
||||
build numbers.
|
||||
|
||||
Since the source version is usually used by the users and developers
|
||||
to identify software, we retain the notion of a source version in
|
||||
P\.IS\.I as a convenience.
|
||||
|
||||
In the following sections, we explain the components of our
|
||||
versioning scheme.
|
||||
|
||||
\subsection{Source Version}
|
||||
|
||||
Source version is the version number provided by the
|
||||
upstream maintainer of the source archive used in package. It must
|
||||
always be the same as the upstream version used.
|
||||
|
||||
\textbf{Example}: If the upstream archive name is
|
||||
\emph{bash-3.0.tar.gz} the version number of the package is \emph{3.0}
|
||||
|
||||
\subsubsection{Version Suffixes}
|
||||
|
||||
There is a pre-defined list of suffixes a package version can
|
||||
take.
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{alpha} Source/Package is in alpha state
|
||||
\item \textbf{beta} Source/Package is in beta state
|
||||
\item \textbf{pre} Source/Pacgage passed the beta state but stable
|
||||
version is not relased yet.
|
||||
\item \textbf{rc} Source/Package is a release-candidate.
|
||||
\item \textbf{m} Source/Package is a milestone before stable version.
|
||||
\item \textbf{p} Source/Package is released and some patches are
|
||||
applied after the release. This is the patch level.
|
||||
\end{itemize}
|
||||
|
||||
The suffix should be written after the special separator
|
||||
character \textbf{\_}. And there must allways be a number after a
|
||||
suffix. \textbf{Example}: packagename-1.0\_beta1
|
||||
|
||||
The basic order of the priorities for suffixes is:\newline
|
||||
\emph{p $>$ (no suffix) $>$ m $>$ rc $>$ pre $>$ beta $>$ alpha}.
|
||||
|
||||
The scope of a source version string is global in the literal
|
||||
sense. It shall not vary from repository to repository.
|
||||
|
||||
The support for these special suffixes as well as usual alphanumeric
|
||||
version string ordering has been implemented in P\.IS\.I.
|
||||
|
||||
\section{Identifying Package Sources}
|
||||
|
||||
A P\.IS\.I source has three identity elements written under
|
||||
\texttt{SOURCE} tag: name, source version, and source release number.
|
||||
We usually say just version and release number/release instead of
|
||||
source version and source release number, respectively. Name is available in
|
||||
the \texttt{<Name>} tag. Version and release are available in the last
|
||||
\texttt{<Update>} element of \texttt{<History>} tag of a \texttt{PSPEC}.
|
||||
|
||||
The name of a source package is constant throughout its revision
|
||||
history. The version is the original version, given by its
|
||||
programmers. Release is a positive integer. Name and release
|
||||
is sufficient to uniquely identify a particular PISI source revision.
|
||||
That is, version and release are independent.
|
||||
|
||||
\subsection{Release Number}
|
||||
|
||||
Release number is the number of the changes that are made to the
|
||||
package source since the initial version in the distribution source. A
|
||||
change can be a patch applied to the source archive, modification in
|
||||
the actions.py, pspec.xml or any file in the source package
|
||||
directory. This change is indicated in \texttt{<Update>} tags manually
|
||||
by the package maintainer.
|
||||
|
||||
The initial release of a package is by default \texttt{1}. The release
|
||||
number always increments by $1$ in each revision in the
|
||||
\texttt{History}, even the slightest ones, but it never decrements.
|
||||
|
||||
The scope of the release number is a given distribution, regardless of
|
||||
its version, e.g. Pardus.
|
||||
|
||||
In the future, PISI will have strict checks for release numbers.
|
||||
|
||||
\subsection{Dependency Specifications}
|
||||
|
||||
We allow a package to use both source version and release to identify
|
||||
a particular version or a range of package versions.
|
||||
|
||||
\section{Identifying Binary packages}
|
||||
|
||||
A PISI binary package is produced from a PISI source package. It has a
|
||||
name that is constant throughout the history of the source package,
|
||||
and it inherits the source version and release number from the source
|
||||
package. However, a binary package has in addition a binary build
|
||||
number. Shortly, build number or just build. For each of the
|
||||
architecture targets, e.g. particular binaries, it also has an
|
||||
architecture tag.
|
||||
|
||||
A binary package is uniquely identified by its name, build number, and
|
||||
architecture regardless of the source version.
|
||||
|
||||
\section{Build Number}
|
||||
|
||||
Similarly to source release number, binary build number is the number
|
||||
of changes that are made to a binary package. By change, we mean any
|
||||
bit change. The existence of a change is tested by comparing the
|
||||
cryptographic checksums in files.xml with those of the previous build, and the
|
||||
build number is automatically determined by the P\.IS\.I build system.
|
||||
The build number starts from $1$ as in release number, and increments
|
||||
by one with each binary change.
|
||||
|
||||
The user never interferes with the build number himself. However, if
|
||||
the user fails to provide the previous build, then a package without
|
||||
a build number is built. A package without a build number is evaluated
|
||||
on the basis of release number, which is guaranteed to exist.
|
||||
|
||||
The scope of a build number is a given distribution build environment
|
||||
for a particular architecture, which may vary from repository to
|
||||
repository. Therefore, it is not used in dependency
|
||||
specifications. However, the system does assume that a build of a
|
||||
given package and architecture is unique in a given repository.
|
||||
|
||||
\section{Package File Names}
|
||||
|
||||
A P\.IS\.I binary package file name contains all the components relevant
|
||||
to its identification, separated by dashes:
|
||||
\begin{verbatim}
|
||||
<binary name>-<source version>-<source release>-<binary build>.pisi
|
||||
\end{verbatim}
|
||||
|
||||
\section{Future Work}
|
||||
|
||||
In the future, it may be necessary to extend the notion of release
|
||||
number and build number to support branches and forks of a
|
||||
distribution. A proposal was to have CVS-like branching, but it
|
||||
was dismissed as unnecessary.
|
||||
|
||||
\end{document}
|
||||
@@ -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'.
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import locale
|
||||
import traceback
|
||||
import exceptions
|
||||
import signal
|
||||
import bsddb3.db as db
|
||||
|
||||
import pisi.ui
|
||||
import pisi.context as ctx
|
||||
import pisi.cli.pisicli as pisicli
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
def exit():
|
||||
pisi.api.finalize()
|
||||
sys.exit(1)
|
||||
|
||||
def handle_exception(exception, value, tb):
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN) # disable further interrupts
|
||||
ui = pisi.cli.CLI() # make a temporary UI
|
||||
show_traceback = False
|
||||
if exception == exceptions.KeyboardInterrupt:
|
||||
ui.error(_("Keyboard Interrupt: Exiting..."))
|
||||
exit()
|
||||
elif isinstance(value, pisi.Error):
|
||||
ui.error(_("Program Terminated."))
|
||||
show_traceback = ctx.get_option('debug')
|
||||
elif isinstance(value, db.DBRunRecoveryError):
|
||||
ui.error(_("""A database operation has been aborted. You should run pisi
|
||||
again for normal DB recovery procedure. Make sure you have free disk space.
|
||||
You have to run rebuild-db only when there is file corruption and database upgrades."""))
|
||||
elif isinstance(value, pisi.Exception):
|
||||
show_traceback = True
|
||||
ui.error(_("""Unhandled internal exception.
|
||||
Please file a bug report. (http://bugs.uludag.org.tr)"""))
|
||||
else:
|
||||
# For any other exception (possibly Python exceptions) show
|
||||
# the traceback!
|
||||
show_traceback = ctx.get_option('debug')
|
||||
ui.error(_("System Error. Program Terminated."))
|
||||
|
||||
if ctx.get_option('debug'):
|
||||
ui.error(u"%s: %s" % (exception, value))
|
||||
else:
|
||||
ui.error(unicode(value))
|
||||
|
||||
ui.info(_("Please use 'pisi help' for general help."))
|
||||
|
||||
if show_traceback:
|
||||
ui.info(_("Traceback:"))
|
||||
traceback.print_tb(tb)
|
||||
else:
|
||||
if not exception is pisicli.Error:
|
||||
ui.info(_("Use --debug to see a traceback."))
|
||||
|
||||
exit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
cli = pisicli.PisiCLI()
|
||||
cli.run_command()
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/python2.5
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import locale
|
||||
import traceback
|
||||
import exceptions
|
||||
import signal
|
||||
import bsddb3.db as db
|
||||
|
||||
import pisi.ui
|
||||
import pisi.context as ctx
|
||||
import pisi.cli.pisicli as pisicli
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
def exit():
|
||||
pisi.api.finalize()
|
||||
sys.exit(1)
|
||||
|
||||
def handle_exception(exception, value, tb):
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN) # disable further interrupts
|
||||
ui = pisi.cli.CLI() # make a temporary UI
|
||||
show_traceback = False
|
||||
if exception == exceptions.KeyboardInterrupt:
|
||||
ui.error(_("Keyboard Interrupt: Exiting..."))
|
||||
exit()
|
||||
elif isinstance(value, pisi.Error):
|
||||
ui.error(_("Program Terminated."))
|
||||
show_traceback = ctx.get_option('debug')
|
||||
elif isinstance(value, db.DBRunRecoveryError):
|
||||
ui.error(_("""A database operation has been aborted. You should run pisi
|
||||
again for normal DB recovery procedure. Make sure you have free disk space.
|
||||
You have to run rebuild-db only when there is file corruption and database upgrades."""))
|
||||
elif isinstance(value, pisi.Exception):
|
||||
show_traceback = True
|
||||
ui.error(_("""Unhandled internal exception.
|
||||
Please file a bug report. (http://bugs.uludag.org.tr)"""))
|
||||
else:
|
||||
# For any other exception (possibly Python exceptions) show
|
||||
# the traceback!
|
||||
show_traceback = ctx.get_option('debug')
|
||||
ui.error(_("System Error. Program Terminated."))
|
||||
|
||||
if ctx.get_option('debug'):
|
||||
ui.error(u"%s: %s" % (exception, value))
|
||||
else:
|
||||
ui.error(unicode(value))
|
||||
|
||||
ui.info(_("Please use 'pisi help' for general help."))
|
||||
|
||||
if show_traceback:
|
||||
ui.info(_("Traceback:"))
|
||||
traceback.print_tb(tb)
|
||||
else:
|
||||
if not exception is pisicli.Error:
|
||||
ui.info(_("Use --debug to see a traceback."))
|
||||
|
||||
exit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
cli = pisicli.PisiCLI()
|
||||
cli.run_command()
|
||||
@@ -1,123 +0,0 @@
|
||||
<!-- Document Type Declaration file for PISI pspec.xml files -->
|
||||
<!-- http://www.pardus.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>
|
||||
<!ATTLIST Dependency releaseFrom CDATA #IMPLIED>
|
||||
<!ATTLIST Dependency releaseTo CDATA #IMPLIED>
|
||||
<!ATTLIST Dependency release CDATA #IMPLIED>
|
||||
|
||||
<!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 (Package)+>
|
||||
<!-- we have a problem here, this Package tag only contains a package name
|
||||
while dtd treats this as another Package section, who said dtd doesnt
|
||||
suck anyway -->
|
||||
|
||||
<!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)
|
||||
#REQUIRED>
|
||||
<!ATTLIST Path permanent (false|true) #IMPLIED>
|
||||
|
||||
|
||||
<!-- 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)>
|
||||
@@ -1,623 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
|
||||
<!-- Project file for project pisi -->
|
||||
<!-- Saved: 2006-06-12, 19:40:22 -->
|
||||
<!-- Copyright (C) 2006 PiSi Development Team, -->
|
||||
<Project version="3.8">
|
||||
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||
<UIType>Console</UIType>
|
||||
<Description>The package management software of Pardus distribution.
|
||||
</Description>
|
||||
<Author>PiSi Development Team</Author>
|
||||
<Email></Email>
|
||||
<Sources>
|
||||
<Source>
|
||||
<Dir>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>pisi</Dir>
|
||||
<Name>uri.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>api.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>TODO</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>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>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>svndist.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>tests</Dir>
|
||||
<Name>repodbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>itembyrepodb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>buildtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>pygettext.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>conflicttests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>environment.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>cat-db.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>rmcomp-source-repo.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>missing-binary.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>update-environ.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>convert_history.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>calc-build-order.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>fix_old_metadata_files.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>repo-diff.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>find-lib-deps.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>check-source-repo.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>fetchAll.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>repostats.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>zip2</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>unzip2</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>lang</Dir>
|
||||
<Dir>de</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>lang</Dir>
|
||||
<Dir>ef</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>ab</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>ab</Dir>
|
||||
<Dir>comar</Dir>
|
||||
<Name>package.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>bc</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>cd</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>helloworld</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>pccts</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>lspisi</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>pisish</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>pisimedia</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>
|
||||
@@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PiSi version
|
||||
|
||||
__version__ = "1.1_rc3"
|
||||
|
||||
__dbversion__ = "1.1_beta8"
|
||||
__filesdbversion__ = "1.0.5" # yes, this is the real bottleneck
|
||||
|
||||
__all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ]
|
||||
|
||||
class Exception(Exception):
|
||||
"""Class of exceptions that must be caught and handled within PiSi"""
|
||||
def __str__(self):
|
||||
s = u''
|
||||
for x in self.args:
|
||||
if s != '':
|
||||
s += '\n'
|
||||
s += unicode(x)
|
||||
return s
|
||||
|
||||
class Error(Exception):
|
||||
"""Class of exceptions that lead to program termination"""
|
||||
pass
|
||||
|
||||
import pisi.api
|
||||
|
||||
# FIXME: can't do this due to name clashes in config and other singletons booo
|
||||
#pisi.api import *
|
||||
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import pisi
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
import pisi.context as ctx
|
||||
|
||||
def error(msg):
|
||||
if ctx.config.get_option('ignore_action_errors'):
|
||||
ctx.ui.error(msg)
|
||||
else:
|
||||
raise Error(msg)
|
||||
@@ -1,163 +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.
|
||||
|
||||
# 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 unlink
|
||||
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 fixInfoDir():
|
||||
infoDir = "%s/usr/share/info/dir" % get.installDIR()
|
||||
if can_access_file(infoDir):
|
||||
unlink(infoDir)
|
||||
|
||||
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.'))
|
||||
|
||||
fixInfoDir()
|
||||
|
||||
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.'))
|
||||
|
||||
fixInfoDir()
|
||||
|
||||
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,80 +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.
|
||||
|
||||
# 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()
|
||||
@@ -1,196 +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.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi.variables
|
||||
|
||||
class BinutilsError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
# Globals
|
||||
env = pisi.actionsapi.variables.glb.env
|
||||
dirs = pisi.actionsapi.variables.glb.dirs
|
||||
|
||||
def curDIR():
|
||||
'''returns current work directory's path'''
|
||||
return os.getcwd()
|
||||
|
||||
def curKERNEL():
|
||||
'''returns currently running kernel's version'''
|
||||
return os.uname()[2]
|
||||
|
||||
def curPYTHON():
|
||||
''' returns currently used python's version'''
|
||||
(a, b, c, x, y) = sys.version_info
|
||||
return 'python%s.%s' % (a, b)
|
||||
|
||||
def curPERL():
|
||||
''' returns currently used perl's version'''
|
||||
return os.path.realpath("/usr/bin/perl").split("perl")[1]
|
||||
|
||||
def ENV(environ):
|
||||
'''returns any given environ variable'''
|
||||
try:
|
||||
return os.environ[environ];
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
# PİSİ Related Functions
|
||||
|
||||
def pkgDIR():
|
||||
'''returns the path of binary packages'''
|
||||
'''Default: /var/cache/pisi/packages'''
|
||||
return env.pkg_dir
|
||||
|
||||
def workDIR():
|
||||
return env.work_dir
|
||||
|
||||
def installDIR():
|
||||
'''returns the path of binary packages'''
|
||||
return env.install_dir
|
||||
|
||||
# PSPEC Related Functions
|
||||
|
||||
def srcNAME():
|
||||
return env.src_name
|
||||
|
||||
def srcVERSION():
|
||||
return env.src_version
|
||||
|
||||
def srcRELEASE():
|
||||
return env.src_release
|
||||
|
||||
def srcTAG():
|
||||
return '%s-%s-%s' % (env.src_name, env.src_version, env.src_release)
|
||||
|
||||
def srcDIR():
|
||||
return '%s-%s' % (env.src_name, env.src_version)
|
||||
|
||||
# Build Related Functions
|
||||
|
||||
def HOST():
|
||||
return env.host
|
||||
|
||||
def CHOST():
|
||||
# FIXME: Currently it behave same as HOST,
|
||||
# but will be used for cross-compiling when PİSİ ready...
|
||||
return env.host
|
||||
|
||||
def CFLAGS():
|
||||
return env.cflags
|
||||
|
||||
def CXXFLAGS():
|
||||
return env.cxxflags
|
||||
|
||||
def LDFLAGS():
|
||||
return env.ldflags
|
||||
|
||||
def makeJOBS():
|
||||
return env.jobs
|
||||
|
||||
# Directory Related Functions
|
||||
|
||||
def docDIR():
|
||||
return dirs.doc
|
||||
|
||||
def sbinDIR():
|
||||
return dirs.sbin
|
||||
|
||||
def infoDIR():
|
||||
return dirs.info
|
||||
|
||||
def manDIR():
|
||||
return dirs.man
|
||||
|
||||
def dataDIR():
|
||||
return dirs.data
|
||||
|
||||
def confDIR():
|
||||
return dirs.conf
|
||||
|
||||
def localstateDIR():
|
||||
return dirs.localstate
|
||||
|
||||
def defaultprefixDIR():
|
||||
return dirs.defaultprefix
|
||||
|
||||
def kdeDIR():
|
||||
return dirs.kde
|
||||
|
||||
def qtDIR():
|
||||
return dirs.qt
|
||||
|
||||
def qtLIBDIR():
|
||||
return '%s/lib/' % qtDIR()
|
||||
|
||||
# Binutils Variables
|
||||
|
||||
def existBinary(bin):
|
||||
# determine if path has binary
|
||||
path = os.environ['PATH'].split(':')
|
||||
for directory in path:
|
||||
if os.path.exists(os.path.join(directory, bin)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def getBinutilsInfo(util):
|
||||
cross_build_name = '%s-%s' % (HOST(), util)
|
||||
if not existBinary(cross_build_name):
|
||||
if not existBinary(util):
|
||||
raise BinutilsError(_('Util %s cannot be found') % util)
|
||||
else:
|
||||
ctx.ui.debug(_('Warning: %s does not exist, using plain name %s') \
|
||||
% (cross_build_name, util))
|
||||
return util
|
||||
else:
|
||||
return cross_build_name
|
||||
|
||||
def AR():
|
||||
return getBinutilsInfo('ar')
|
||||
|
||||
def AS():
|
||||
return getBinutilsInfo('as')
|
||||
|
||||
def CC():
|
||||
return getBinutilsInfo('gcc')
|
||||
|
||||
def CXX():
|
||||
return getBinutilsInfo('g++')
|
||||
|
||||
def LD():
|
||||
return getBinutilsInfo('ld')
|
||||
|
||||
def NM():
|
||||
return getBinutilsInfo('nm')
|
||||
|
||||
def RANLIB():
|
||||
return getBinutilsInfo('ranlib')
|
||||
|
||||
def F77():
|
||||
return getBinutilsInfo('f77')
|
||||
|
||||
def GCJ():
|
||||
return getBinutilsInfo('gcj')
|
||||
@@ -1,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.
|
||||
|
||||
# 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,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.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi-Core Modules
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
from pisi.actionsapi.shelltools import *
|
||||
import pisi.actionsapi.get as get
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def preplib(sourceDirectory = '/usr/lib'):
|
||||
sourceDirectory = join_path(get.installDIR(), sourceDirectory)
|
||||
if can_access_directory(sourceDirectory):
|
||||
if system('/sbin/ldconfig -n -N %s' % sourceDirectory):
|
||||
raise RunTimeError(_('Running ldconfig failed.'))
|
||||
|
||||
def gnuconfig_update():
|
||||
''' copy newest config.* onto source\'s '''
|
||||
for root, dirs, files in os.walk(os.getcwd()):
|
||||
for fileName in files:
|
||||
if fileName in ['config.sub', 'config.guess']:
|
||||
targetFile = os.path.join(root, fileName)
|
||||
if os.path.islink(targetFile):
|
||||
unlink(targetFile)
|
||||
copy('/usr/share/gnuconfig/%s' % fileName, join_path(root, fileName))
|
||||
ctx.ui.info(_('GNU Config Update Finished.'))
|
||||
|
||||
def libtoolize(parameters = ''):
|
||||
if system('/usr/bin/libtoolize %s' % parameters):
|
||||
raise RunTimeError(_('Running libtoolize failed.'))
|
||||
|
||||
def gen_usr_ldscript(dynamicLib):
|
||||
|
||||
makedirs('%s/usr/lib' % get.installDIR())
|
||||
|
||||
destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w')
|
||||
content = '''
|
||||
/* GNU ld script
|
||||
Since Pardus has critical dynamic libraries
|
||||
in /lib, and the static versions in /usr/lib,
|
||||
we need to have a "fake" dynamic lib in /usr/lib,
|
||||
otherwise we run into linking problems.
|
||||
*/
|
||||
GROUP ( /lib/%s )
|
||||
''' % dynamicLib
|
||||
|
||||
destinationFile.write(content)
|
||||
destinationFile.close()
|
||||
chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib))
|
||||
@@ -1,85 +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.
|
||||
|
||||
# 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
|
||||
from pisi.actionsapi.shelltools import unlink
|
||||
|
||||
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.')
|
||||
|
||||
fixLocalPod()
|
||||
|
||||
def fixLocalPod():
|
||||
podFiles = [ "%s/usr/lib/perl5/vendor_perl/%s/i686-linux/perllocal.pod" % (get.installDIR(), get.curPERL()),
|
||||
"%s/usr/lib/perl5/%s/i686-linux/perllocal.pod" % (get.installDIR(), get.curPERL()),
|
||||
"%s/usr/lib/perl5/site_perl/%s/perllocal.pod" % (get.installDIR(), get.curPERL())]
|
||||
|
||||
for podFile in podFiles:
|
||||
if can_access_file(podFile):
|
||||
unlink(podFile)
|
||||
@@ -1,234 +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.
|
||||
|
||||
'''supports globs in sourceFile arguments'''
|
||||
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import sys
|
||||
import fileinput
|
||||
import re
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.pisitoolsfunctions import *
|
||||
from pisi.actionsapi.shelltools import *
|
||||
|
||||
from pisi.actionsapi import error
|
||||
|
||||
def dobin(sourceFile, destinationDirectory = '/usr/bin'):
|
||||
'''insert a executable file into /bin or /usr/bin'''
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/bin", "xload") '''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dodir(destinationDirectory):
|
||||
'''creates a directory tree'''
|
||||
makedirs(join_path(get.installDIR(), destinationDirectory))
|
||||
|
||||
def dodoc(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE'''
|
||||
readable_insinto(join_path(get.installDIR(), join_path('/usr/share/doc', get.srcTAG())), *sourceFiles)
|
||||
|
||||
def doexe(sourceFile, destinationDirectory):
|
||||
'''insert a executable file into destination directory'''
|
||||
|
||||
''' example call: pisitools.doexe("kde-3.4.sh", "/etc/X11/Sessions")'''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dohtml(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE/html'''
|
||||
|
||||
''' example call: pisitools.dohtml("doc/doxygen/html/*")'''
|
||||
destionationDirectory = join_path(get.installDIR(), 'usr/share/doc' ,get.srcTAG(), 'html')
|
||||
|
||||
if not can_access_directory(destionationDirectory):
|
||||
makedirs(destionationDirectory)
|
||||
|
||||
allowed_extensions = ['.png', '.gif', '.html', '.htm', '.jpg', '.css', '.js']
|
||||
disallowed_directories = ['CVS']
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
if os.path.isfile(source) and os.path.splitext(source)[1] in allowed_extensions:
|
||||
system('install -m0644 "%s" %s' % (source, destionationDirectory))
|
||||
if os.path.isdir(source) and os.path.basename(source) not in disallowed_directories:
|
||||
for root, dirs, files in os.walk(source):
|
||||
for source in files:
|
||||
if os.path.splitext(source)[1] in allowed_extensions:
|
||||
makedirs(destionationDirectory)
|
||||
system('install -m0644 %s %s' % (join_path(root, source), destionationDirectory))
|
||||
|
||||
def doinfo(*sourceFiles):
|
||||
'''inserts the into files in the list of files into /usr/share/info'''
|
||||
readable_insinto(join_path(get.installDIR(), get.infoDIR()), *sourceFiles)
|
||||
|
||||
def dolib(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the library into /usr/lib'''
|
||||
|
||||
'''example call: pisitools.dolib_a("libz.a")'''
|
||||
'''example call: pisitools.dolib_a("libz.so")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 755)
|
||||
|
||||
def dolib_a(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the static library into /usr/lib with permission 0644'''
|
||||
|
||||
'''example call: pisitools.dolib_a("staticlib/libvga.a")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 644)
|
||||
|
||||
def dolib_so(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the static library into /usr/lib with permission 0755'''
|
||||
|
||||
'''example call: pisitools.dolib_so("pppd/plugins/minconn.so")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 755)
|
||||
|
||||
def doman(*sourceFiles):
|
||||
'''inserts the man pages in the list of files into /usr/share/man/'''
|
||||
|
||||
'''example call: pisitools.doman("man.1", "pardus.*")'''
|
||||
manDIR = join_path(get.installDIR(), get.manDIR())
|
||||
if not can_access_directory(manDIR):
|
||||
makedirs(manDIR)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
try:
|
||||
pageName, pageDirectory = source[:source.rindex('.')], \
|
||||
source[source.rindex('.')+1:]
|
||||
except ValueError:
|
||||
error(_('ActionsAPI [doman]: Wrong man page file: %s') % (source))
|
||||
|
||||
makedirs(join_path(manDIR, '/man%s' % pageDirectory))
|
||||
system('install -m0644 %s %s' % (source, join_path(manDIR, '/man%s' % pageDirectory)))
|
||||
|
||||
def domo(sourceFile, locale, destinationFile ):
|
||||
'''inserts the mo files in the list of files into /usr/share/locale/LOCALE/LC_MESSAGES'''
|
||||
|
||||
'''example call: pisitools.domo("po/tr.po", "tr", "pam_login.mo")'''
|
||||
|
||||
system('msgfmt %s' % sourceFile)
|
||||
makedirs('%s/usr/share/locale/%s/LC_MESSAGES/' % (get.installDIR(), locale))
|
||||
move('messages.mo', '%s/usr/share/locale/%s/LC_MESSAGES/%s' % (get.installDIR(), locale, destinationFile))
|
||||
|
||||
def domove(sourceFile, destination, destinationFile = ''):
|
||||
'''moves sourceFile/Directory into destinationFile/Directory'''
|
||||
|
||||
''' example call: pisitools.domove("/usr/bin/bash", "/bin/bash")'''
|
||||
''' example call: pisitools.domove("/usr/bin/", "/usr/sbin")'''
|
||||
makedirs(join_path(get.installDIR(), destination))
|
||||
|
||||
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
|
||||
if not destinationFile:
|
||||
move(filePath, join_path(get.installDIR(), join_path(destination, os.path.basename(filePath))))
|
||||
else:
|
||||
move(filePath, join_path(get.installDIR(), join_path(destination, destinationFile)))
|
||||
|
||||
def rename(sourceFile, destinationFile):
|
||||
''' renames sourceFile as destinationFile'''
|
||||
|
||||
''' example call: pisitools.rename("/usr/bin/bash", "bash.old") '''
|
||||
''' the result of the previous example would be "/usr/bin/bash.old" '''
|
||||
|
||||
baseDir = os.path.dirname(sourceFile)
|
||||
|
||||
try:
|
||||
os.rename(join_path(get.installDIR(), sourceFile), join_path(get.installDIR(), baseDir, destinationFile))
|
||||
except OSError:
|
||||
error(_('ActionsAPI [rename]: No such file or directory: %s') % (sourceFile))
|
||||
|
||||
def dosed(sourceFiles, findPattern, replacePattern = ''):
|
||||
'''replaces patterns in sourceFiles'''
|
||||
|
||||
''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")'''
|
||||
''' example call: pisitools.dosed("/etc/passwd", "caglar")'''
|
||||
''' example call: pisitools.dosed("/etc/pass*", "caglar")'''
|
||||
''' example call: pisitools.dosed("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")'''
|
||||
|
||||
for sourceFile in glob.glob(sourceFiles):
|
||||
if can_access_file(sourceFile):
|
||||
for line in fileinput.input(sourceFile, inplace = 1):
|
||||
#FIXME: In-place filtering is disabled when standard input is read
|
||||
line = re.sub(findPattern, replacePattern, line)
|
||||
sys.stdout.write(line)
|
||||
else:
|
||||
raise FileError(_('File does not exist or permission denied: %s') % sourceFile)
|
||||
|
||||
def dosbin(sourceFile, destinationDirectory = '/usr/sbin'):
|
||||
'''insert a executable file into /sbin or /usr/sbin'''
|
||||
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/sbin") '''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dosym(sourceFile, destinationFile):
|
||||
'''creates soft link between sourceFile and destinationFile'''
|
||||
|
||||
''' example call: pisitools.dosym("/usr/bin/bash", "/bin/bash")'''
|
||||
makedirs(join_path(get.installDIR(), os.path.dirname(destinationFile)))
|
||||
|
||||
try:
|
||||
os.symlink(sourceFile, join_path(get.installDIR() ,destinationFile))
|
||||
except OSError:
|
||||
error(_('ActionsAPI [dosym]: File exists: %s') % (sourceFile))
|
||||
|
||||
def insinto(destinationDirectory, sourceFile, destinationFile = '', sym = True):
|
||||
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
|
||||
makedirs(join_path(get.installDIR(), destinationDirectory))
|
||||
|
||||
if not destinationFile:
|
||||
for filePath in glob.glob(sourceFile):
|
||||
if can_access_file(filePath):
|
||||
copy(filePath, join_path(get.installDIR(), join_path(destinationDirectory, os.path.basename(filePath))), sym)
|
||||
else:
|
||||
copy(sourceFile, join_path(get.installDIR(), join_path(destinationDirectory, destinationFile)), sym)
|
||||
|
||||
def newdoc(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile'''
|
||||
destinationDirectory = '' #490
|
||||
destinationDirectory = os.path.dirname(destinationFile)
|
||||
destinationFile = os.path.basename(destinationFile)
|
||||
# Use copy instead of move or let build-install scream like file not found!
|
||||
copy(sourceFile, destinationFile)
|
||||
readable_insinto(join_path(get.installDIR(), 'usr/share/doc', get.srcTAG(), destinationDirectory), destinationFile)
|
||||
|
||||
def newman(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile'''
|
||||
# Use copy instead of move or let build-install scream like file not found!
|
||||
copy(sourceFile, destinationFile)
|
||||
doman(destinationFile)
|
||||
|
||||
def remove(sourceFile):
|
||||
'''removes sourceFile'''
|
||||
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
|
||||
unlink(filePath)
|
||||
|
||||
def removeDir(destinationDirectory):
|
||||
'''removes destinationDirectory and its subtrees'''
|
||||
for directory in glob.glob(join_path(get.installDIR(), destinationDirectory)):
|
||||
unlinkDir(directory)
|
||||
@@ -1,80 +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 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,66 +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.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system, can_access_file
|
||||
from pisi.actionsapi.pisitools import dodoc
|
||||
|
||||
class CompileError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def compile(parameters = ''):
|
||||
'''compile source with given parameters.'''
|
||||
if system('python setup.py build %s' % (parameters)):
|
||||
raise CompileError, _('Make failed.')
|
||||
|
||||
def install(parameters = ''):
|
||||
'''does python setup.py install'''
|
||||
if system('python setup.py install --root=%s --no-compile %s' % (get.installDIR(), parameters)):
|
||||
raise InstallError, _('Install failed.')
|
||||
|
||||
DDOCS = 'CHANGELOG COPYRIGHT KNOWN_BUGS MAINTAINERS PKG-INFO \
|
||||
CONTRIBUTORS LICENSE COPYING* Change* MANIFEST* README*'
|
||||
|
||||
for doc in DDOCS:
|
||||
if can_access_file(doc):
|
||||
pisitools.dodoc(doc)
|
||||
|
||||
def run(parameters = ''):
|
||||
'''executes parameters with python'''
|
||||
if system('python %s' % (parameters)):
|
||||
raise RunTimeError, _('Running %s failed.') % parameters
|
||||
@@ -1,43 +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 Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def make(parameters = ''):
|
||||
if system('scons %s' % parameters):
|
||||
raise MakeError(_('Make failed.'))
|
||||
|
||||
def install(parameters = 'install', prefix = get.installDIR(), argument='prefix'):
|
||||
if system('scons %s=%s %s' % (argument, prefix, parameters)):
|
||||
raise InstallError(_('Install failed.'))
|
||||
@@ -1,224 +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.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
import string
|
||||
import pwd
|
||||
import grp
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get
|
||||
|
||||
from pisi.actionsapi import error
|
||||
from pisi.util import run_logged
|
||||
from pisi.util import join_path
|
||||
|
||||
def can_access_file(filePath):
|
||||
'''test the existence of file'''
|
||||
return os.access(filePath, os.F_OK)
|
||||
|
||||
def can_access_directory(destinationDirectory):
|
||||
'''test readability, writability and executablility of directory'''
|
||||
return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK)
|
||||
|
||||
def makedirs(destinationDirectory):
|
||||
'''recursive directory creation function'''
|
||||
try:
|
||||
if not os.access(destinationDirectory, os.F_OK):
|
||||
os.makedirs(destinationDirectory)
|
||||
except OSError:
|
||||
error(_('Cannot create directory %s') % destinationDirectory)
|
||||
|
||||
def echo(destionationFile, content):
|
||||
try:
|
||||
f = open(destionationFile, 'a')
|
||||
f.write('%s\n' % content)
|
||||
f.close()
|
||||
except IOError:
|
||||
error(_('ActionsAPI [echo]: Can\'t append to file %s.') % (destionationFile))
|
||||
|
||||
def chmod(filePath, mode = 0755):
|
||||
'''change the mode of filePath to the mode'''
|
||||
for fileName in glob.glob(filePath):
|
||||
if can_access_file(fileName):
|
||||
try:
|
||||
os.chmod(fileName, mode)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [chmod]: Operation not permitted: %s (mode: %s)') \
|
||||
% (fileName, mode))
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [chmod]: File %s doesn\'t exists.') % (fileName))
|
||||
|
||||
def chown(filePath, uid = "root", gid = "root"):
|
||||
'''change the owner and group id of filePath to uid and gid'''
|
||||
if can_access_file(filePath):
|
||||
try:
|
||||
os.chown(filePath, pwd.getpwnam(uid)[2], grp.getgrnam(gid)[2])
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [chown]: Operation not permitted: %s (uid: %s, gid: %s)') \
|
||||
% (filePath, uid, gid))
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [chown]: File %s doesn\'t exists.') % filePath)
|
||||
|
||||
def sym(source, destination):
|
||||
'''creates symbolic link'''
|
||||
try:
|
||||
os.symlink(source, destination)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [sym]: Permission denied: %s to %s') % (source, destination))
|
||||
|
||||
def unlink(filePath):
|
||||
'''remove the file path'''
|
||||
if isFile(filePath) or isLink(filePath):
|
||||
try:
|
||||
os.unlink(filePath)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [unlink]: Permission denied: %s.') % (filePath))
|
||||
elif isDirectory(filePath):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [unlink]: File %s doesn\'t exists.') % (filePath))
|
||||
|
||||
def unlinkDir(sourceDirectory):
|
||||
'''delete an entire directory tree'''
|
||||
if isDirectory(sourceDirectory) or isLink(sourceDirectory):
|
||||
try:
|
||||
shutil.rmtree(sourceDirectory)
|
||||
except OSError:
|
||||
error(_('ActionsAPI [unlinkDir]: Operation not permitted: %s') % (sourceDirectory))
|
||||
elif isFile(sourceDirectory):
|
||||
pass
|
||||
else:
|
||||
error(_('ActionsAPI [unlinkDir]: Directory %s doesn\'t exists.') % (sourceDirectory))
|
||||
|
||||
def move(source, destination):
|
||||
'''recursively move a "source" file or directory to "destination"'''
|
||||
for filePath in glob.glob(source):
|
||||
if isFile(filePath) or isLink(filePath) or isDirectory(filePath):
|
||||
try:
|
||||
shutil.move(filePath, destination)
|
||||
except OSError:
|
||||
error(_('ActionsAPI [move]: Permission denied: %s to %s') % (filePath, destination))
|
||||
else:
|
||||
error(_('ActionsAPI [move]: File %s doesn\'t exists.') % (filePath))
|
||||
|
||||
# FIXME: instead of passing a sym parameter, split copy and copytree into 4 different function
|
||||
def copy(source, destination, sym = True):
|
||||
'''recursively copy a "source" file or directory to "destination"'''
|
||||
for filePath in glob.glob(source):
|
||||
if isFile(filePath) and not isLink(filePath):
|
||||
try:
|
||||
shutil.copy(filePath, destination)
|
||||
except IOError:
|
||||
error(_('ActionsAPI [copy]: Permission denied: %s to %s') % (filePath, destination))
|
||||
elif isLink(filePath) and sym:
|
||||
if isDirectory(destination):
|
||||
os.symlink(os.readlink(filePath), join_path(destination, os.path.basename(filePath)))
|
||||
else:
|
||||
if isFile(destination):
|
||||
os.remove(destination)
|
||||
os.symlink(os.readlink(filePath), destination)
|
||||
elif isLink(filePath) and not sym:
|
||||
if isDirectory(filePath):
|
||||
copytree(filePath, destination)
|
||||
else:
|
||||
shutil.copy(filePath, destination)
|
||||
elif isDirectory(filePath):
|
||||
copytree(filePath, destination, sym)
|
||||
else:
|
||||
error(_('ActionsAPI [copy]: File %s does not exist.') % filePath)
|
||||
|
||||
def copytree(source, destination, sym = True):
|
||||
'''recursively copy an entire directory tree rooted at source'''
|
||||
if isDirectory(source):
|
||||
if os.path.exists(destination):
|
||||
if isDirectory(destination):
|
||||
copytree(source, join_path(destination, os.path.basename(source.strip('/'))))
|
||||
return
|
||||
else:
|
||||
copytree(source, join_path(destination, os.path.basename(source)))
|
||||
return
|
||||
try:
|
||||
shutil.copytree(source, destination, sym)
|
||||
except OSError, e:
|
||||
error(_('ActionsAPI [copytree] %s to %s: %s') % (source, destination, e))
|
||||
else:
|
||||
error(_('ActionsAPI [copytree]: Directory %s doesn\'t exists.') % (source))
|
||||
|
||||
def touch(filePath):
|
||||
'''changes the access time of the 'filePath', or creates it if it is not exist'''
|
||||
if glob.glob(filePath):
|
||||
for f in glob.glob(filePath):
|
||||
os.utime(f, None)
|
||||
else:
|
||||
try:
|
||||
f = open(filePath, 'w')
|
||||
f.close()
|
||||
except IOError:
|
||||
error(_('ActionsAPI [touch]: Permission denied: %s') % (filePath))
|
||||
|
||||
def cd(directoryName = ''):
|
||||
'''change directory'''
|
||||
current = os.getcwd()
|
||||
if directoryName:
|
||||
os.chdir(directoryName)
|
||||
else:
|
||||
os.chdir(os.path.dirname(current))
|
||||
|
||||
def ls(source):
|
||||
'''listdir'''
|
||||
if os.path.isdir(source):
|
||||
return os.listdir(source)
|
||||
else:
|
||||
return glob.glob(source)
|
||||
|
||||
def export(key, value):
|
||||
'''export environ variable'''
|
||||
os.environ[key] = value
|
||||
|
||||
def isLink(filePath):
|
||||
'''return True if filePath refers to a symbolic link'''
|
||||
return os.path.islink(filePath)
|
||||
|
||||
def isFile(filePath):
|
||||
'''return True if filePath is an existing regular file'''
|
||||
return os.path.isfile(filePath)
|
||||
|
||||
def isDirectory(filePath):
|
||||
'''Return True if filePath is an existing directory'''
|
||||
return os.path.isdir(filePath)
|
||||
|
||||
def realPath(filePath):
|
||||
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
|
||||
return os.path.realpath(filePath)
|
||||
|
||||
def baseName(filePath):
|
||||
'''return the base name of pathname filePath'''
|
||||
return os.path.basename(filePath)
|
||||
|
||||
def dirName(filePath):
|
||||
'''return the directory name of pathname path'''
|
||||
return os.path.dirname(filePath)
|
||||
|
||||
def system(command):
|
||||
command = string.join(string.split(command))
|
||||
return run_logged(command)
|
||||
@@ -1,101 +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.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
from copy import deepcopy
|
||||
|
||||
# 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.'''
|
||||
|
||||
# first reset environ
|
||||
os.environ = {}
|
||||
os.environ = deepcopy(ctx.config.environ)
|
||||
|
||||
# Build systems depend on these environment variables. That is why
|
||||
# we export them instead of using as (instance) variables.
|
||||
values = ctx.config.values
|
||||
os.environ['HOST'] = values.build.host
|
||||
os.environ['CFLAGS'] = values.build.cflags
|
||||
os.environ['CXXFLAGS'] = values.build.cxxflags
|
||||
os.environ['LDFLAGS'] = values.build.ldflags
|
||||
os.environ['USER_LDFLAGS'] = values.build.ldflags
|
||||
os.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 os.getenv(self.__vars[attr])
|
||||
else:
|
||||
return None
|
||||
|
||||
class Dirs:
|
||||
'''General directories used in actions API.'''
|
||||
# TODO: Eventually we should consider getting these from a/the
|
||||
# configuration file
|
||||
doc = 'usr/share/doc'
|
||||
sbin = 'usr/sbin'
|
||||
man = 'usr/share/man'
|
||||
info = 'usr/share/info'
|
||||
data = 'usr/share'
|
||||
conf = 'etc'
|
||||
localstate = 'var/lib'
|
||||
defaultprefix = 'usr'
|
||||
|
||||
# These should be owned by object not the class. Or else Python
|
||||
# will bug us with NoneType errors because of uninitialized
|
||||
# context (ctx) because of the import in build.py.
|
||||
def __init__(self):
|
||||
self.values = ctx.config.values
|
||||
self.kde = self.values.dirs.kde_dir
|
||||
self.qt = self.values.dirs.qt_dir
|
||||
|
||||
|
||||
# As we import this module from build.py, we can't init glb as a
|
||||
# singleton here. Or else Python will bug us with NoneType errors
|
||||
# because of uninitialized context (ctx) because of exportFlags().
|
||||
#
|
||||
# We import this modue from build.py becase we need to reset/init glb
|
||||
# for each build. # See bug #2575
|
||||
glb = None
|
||||
|
||||
def initVariables():
|
||||
global glb
|
||||
ctx.env = Env()
|
||||
ctx.dirs = Dirs()
|
||||
glb = ctx
|
||||
@@ -1,490 +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.
|
||||
|
||||
"""Top level PiSi interfaces. a facade to the entire PiSi system"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import logging.handlers
|
||||
from os.path import exists
|
||||
import bsddb3.db as db
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
import pisi.util as util
|
||||
import pisi.dependency as dependency
|
||||
import pisi.pgraph as pgraph
|
||||
import pisi.operations as operations
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
import pisi.sourcedb
|
||||
import pisi.component as component
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
from pisi.operations import install, remove, upgrade, emerge
|
||||
from pisi.build import build_until
|
||||
from pisi.atomicoperations import resurrect_package, build
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
from pisi.file import File
|
||||
import pisi.search
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
def init(database = True, write = True,
|
||||
options = None, ui = None, comar = True,
|
||||
stdout = None, stderr = None,
|
||||
comar_sockname = None):
|
||||
"""Initialize PiSi subsystem"""
|
||||
|
||||
# UI comes first
|
||||
|
||||
if ui is None:
|
||||
from pisi.cli import CLI
|
||||
if options:
|
||||
ctx.ui = CLI(options.debug, options.verbose)
|
||||
else:
|
||||
ctx.ui = CLI()
|
||||
else:
|
||||
ctx.ui = ui
|
||||
|
||||
if os.access('/var/log', os.W_OK):
|
||||
handler = logging.handlers.RotatingFileHandler('/var/log/pisi.log')
|
||||
#handler.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)-12s: %(levelname)-8s %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
ctx.log = logging.getLogger('pisi')
|
||||
ctx.log.addHandler(handler)
|
||||
ctx.loghandler = handler
|
||||
ctx.log.setLevel(logging.DEBUG)
|
||||
else:
|
||||
ctx.log = None
|
||||
|
||||
# If given define stdout and stderr. Needed by buildfarm currently
|
||||
# but others can benefit from this too.
|
||||
if stdout:
|
||||
ctx.stdout = stdout
|
||||
if stderr:
|
||||
ctx.stderr = stderr
|
||||
|
||||
import pisi.config
|
||||
ctx.config = pisi.config.Config(options)
|
||||
|
||||
# TODO: this is definitely not dynamic beyond this point!
|
||||
ctx.comar = comar and not ctx.config.get_option('ignore_comar')
|
||||
# This is for YALI, used in comariface.py:make_com()
|
||||
ctx.comar_sockname = comar_sockname
|
||||
|
||||
# initialize repository databases
|
||||
ctx.database = database
|
||||
if database:
|
||||
shelve.init_dbenv(write=write)
|
||||
ctx.repodb = pisi.repodb.init()
|
||||
ctx.installdb = pisi.installdb.init()
|
||||
ctx.filesdb = pisi.files.FilesDB()
|
||||
ctx.componentdb = pisi.component.ComponentDB()
|
||||
ctx.packagedb = packagedb.init_db()
|
||||
ctx.sourcedb = pisi.sourcedb.init()
|
||||
pisi.search.init(['summary', 'description'], ['en', 'tr'])
|
||||
else:
|
||||
ctx.repodb = None
|
||||
ctx.installdb = None
|
||||
ctx.filesdb = None
|
||||
ctx.componentdb = None
|
||||
ctx.packagedb = None
|
||||
ctx.sourcedb = None
|
||||
ctx.ui.debug('PiSi API initialized')
|
||||
ctx.initialized = True
|
||||
|
||||
def finalize():
|
||||
if ctx.initialized:
|
||||
|
||||
if ctx.log:
|
||||
ctx.loghandler.flush()
|
||||
ctx.log.removeHandler(ctx.loghandler)
|
||||
|
||||
pisi.repodb.finalize()
|
||||
pisi.installdb.finalize()
|
||||
if ctx.filesdb != None:
|
||||
ctx.filesdb.close()
|
||||
ctx.filesdb = None
|
||||
if ctx.componentdb != None:
|
||||
ctx.componentdb.close()
|
||||
ctx.componentdb = None
|
||||
if ctx.packagedb:
|
||||
packagedb.finalize_db()
|
||||
ctx.packagedb = None
|
||||
if ctx.sourcedb:
|
||||
pisi.sourcedb.finalize()
|
||||
ctx.sourcedb = None
|
||||
pisi.search.finalize()
|
||||
if ctx.dbenv:
|
||||
ctx.dbenv.close()
|
||||
ctx.dbenv_lock.close()
|
||||
if ctx.build_leftover and os.path.exists(ctx.build_leftover):
|
||||
os.unlink(ctx.build_leftover)
|
||||
|
||||
ctx.ui.debug('PiSi API finalized')
|
||||
ctx.ui.close()
|
||||
ctx.initialized = False
|
||||
|
||||
def list_installed():
|
||||
'''returns a set of installed package names'''
|
||||
return set(ctx.installdb.list_installed())
|
||||
|
||||
def list_available(repo = None):
|
||||
'''returns a set of available package names'''
|
||||
return set(ctx.packagedb.list_packages(repo = repo))
|
||||
|
||||
def list_upgradable():
|
||||
ignore_build = ctx.get_option('ignore_build_no')
|
||||
|
||||
return filter(pisi.operations.is_upgradable, ctx.installdb.list_installed())
|
||||
|
||||
def package_graph(A, repo = pisi.itembyrepodb.installed, ignore_installed = False):
|
||||
"""Construct a package relations graph, containing
|
||||
all dependencies of packages A, if ignore_installed
|
||||
option is True, then only uninstalled deps will
|
||||
be added."""
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(ctx.packagedb, repo) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = ctx.packagedb.get_package(x, repo)
|
||||
#print pkg
|
||||
for dep in pkg.runtimeDependencies():
|
||||
if ignore_installed:
|
||||
if dependency.installed_satisfies_dep(dep):
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
return G_f
|
||||
|
||||
def configure_pending():
|
||||
# start with pending packages
|
||||
# configure them in reverse topological order of dependency
|
||||
A = ctx.installdb.list_pending()
|
||||
G_f = pgraph.PGraph(ctx.packagedb, pisi.itembyrepodb.installed) # construct G_f
|
||||
for x in A.keys():
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B.keys():
|
||||
pkg = ctx.packagedb.get_package(x, pisi.itembyrepodb.installed)
|
||||
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)
|
||||
# FIXME: we need a full package info here!
|
||||
pkginfo.name = x
|
||||
ctx.ui.notify(pisi.ui.configuring, package = pkginfo, files = None)
|
||||
pisi.comariface.post_install(
|
||||
pkginfo.name,
|
||||
m.package.providesComar,
|
||||
util.join_path(pkg_path, ctx.const.comar_dir),
|
||||
util.join_path(pkg_path, ctx.const.metadata_xml),
|
||||
util.join_path(pkg_path, ctx.const.files_xml),
|
||||
)
|
||||
ctx.ui.notify(pisi.ui.configured, package = pkginfo, files = None)
|
||||
ctx.installdb.clear_pending(x)
|
||||
except ImportError:
|
||||
raise Error(_("comar package is not fully installed"))
|
||||
|
||||
def info(package, installed = False):
|
||||
if package.endswith(ctx.const.package_suffix):
|
||||
return info_file(package)
|
||||
else:
|
||||
return info_name(package, installed)
|
||||
|
||||
def info_file(package_fn):
|
||||
from package import Package
|
||||
|
||||
if not os.path.exists(package_fn):
|
||||
raise Error (_('File %s not found') % package_fn)
|
||||
|
||||
package = Package(package_fn)
|
||||
package.read()
|
||||
return package.metadata, package.files
|
||||
|
||||
def info_name(package_name, installed=False):
|
||||
"""fetch package information for a package"""
|
||||
if installed:
|
||||
package = ctx.packagedb.get_package(package_name, pisi.itembyrepodb.installed)
|
||||
else:
|
||||
package, repo = ctx.packagedb.get_package_repo(package_name, pisi.itembyrepodb.repos)
|
||||
repostr = repo
|
||||
|
||||
from pisi.metadata import MetaData
|
||||
metadata = MetaData()
|
||||
metadata.package = package
|
||||
#FIXME: get it from sourcedb if available
|
||||
metadata.source = None
|
||||
#TODO: fetch the files from server if possible (wow, you maniac -- future exa)
|
||||
if installed and ctx.installdb.is_installed(package.name):
|
||||
try:
|
||||
files = ctx.installdb.files(package.name)
|
||||
except pisi.Error, e:
|
||||
ctx.ui.warning(e)
|
||||
files = None
|
||||
else:
|
||||
files = None
|
||||
return metadata, files
|
||||
|
||||
def search_package_names(query):
|
||||
r = set()
|
||||
packages = ctx.packagedb.list_packages()
|
||||
for pkgname in packages:
|
||||
if query in pkgname:
|
||||
r.add(pkgname)
|
||||
return r
|
||||
|
||||
def search_package_terms(terms, lang = None, search_names = True, repo = pisi.itembyrepodb.all):
|
||||
if not lang:
|
||||
lang = pisi.pxml.autoxml.LocalText.get_lang()
|
||||
r1 = pisi.search.query_terms('summary', lang, terms, repo = repo)
|
||||
r2 = pisi.search.query_terms('description', lang, terms, repo = repo)
|
||||
r = r1.union(r2)
|
||||
if search_names:
|
||||
for term in terms:
|
||||
r |= search_package_names(term)
|
||||
return r
|
||||
|
||||
def search_package(query, lang = None, search_names = True, repo = pisi.itembyrepodb.all):
|
||||
if not lang:
|
||||
lang = pisi.pxml.autoxml.LocalText.get_lang()
|
||||
r1 = pisi.search.query('summary', lang, query, repo = repo)
|
||||
r2 = pisi.search.query('description', lang, query, repo = repo)
|
||||
r = r1.union(r2)
|
||||
if search_names:
|
||||
r |= search_package_names(query)
|
||||
return r
|
||||
|
||||
def check(package):
|
||||
md, files = info(package, True)
|
||||
corrupt = []
|
||||
for file in files.list:
|
||||
if file.hash and file.type != "config" \
|
||||
and not os.path.islink('/' + file.path):
|
||||
ctx.ui.info(_("Checking /%s ") % file.path, noln=True, verbose=True)
|
||||
if file.hash != util.sha1_file('/' + file.path):
|
||||
corrupt.append(file)
|
||||
ctx.ui.info(_("\nCorrupt file: %s") % file, noln=True)
|
||||
else:
|
||||
ctx.ui.info(_("OK"), verbose=True)
|
||||
return corrupt
|
||||
|
||||
def index(dirs=None, output='pisi-index.xml', skip_sources=False, skip_signing=False):
|
||||
"""accumulate PiSi XML files in a directory"""
|
||||
index = Index()
|
||||
index.distribution = None
|
||||
if not dirs:
|
||||
dirs = ['.']
|
||||
for repo_dir in dirs:
|
||||
repo_dir = str(repo_dir)
|
||||
ctx.ui.info(_('* Building index of PiSi files under %s') % repo_dir)
|
||||
index.index(repo_dir, skip_sources)
|
||||
|
||||
if skip_signing:
|
||||
index.write(output, sha1sum=True, compress=File.bz2, sign=None)
|
||||
else:
|
||||
index.write(output, sha1sum=True, compress=File.bz2, sign=File.detached)
|
||||
ctx.ui.info(_('* Index file written'))
|
||||
|
||||
def add_repo(name, indexuri, at = None):
|
||||
if ctx.repodb.has_repo(name):
|
||||
raise Error(_('Repo %s already present.') % name)
|
||||
else:
|
||||
repo = pisi.repodb.Repo(URI(indexuri))
|
||||
ctx.repodb.add_repo(name, repo, at = at)
|
||||
ctx.ui.info(_('Repo %s added to system.') % name)
|
||||
|
||||
def remove_repo(name):
|
||||
if ctx.repodb.has_repo(name):
|
||||
ctx.repodb.remove_repo(name)
|
||||
pisi.util.clean_dir(os.path.join(ctx.config.index_dir(), name))
|
||||
ctx.ui.info(_('Repo %s removed from system.') % name)
|
||||
else:
|
||||
ctx.ui.error(_('Repository %s does not exist. Cannot remove.')
|
||||
% name)
|
||||
|
||||
def list_repos():
|
||||
return ctx.repodb.list()
|
||||
|
||||
def update_repo(repo, force=False):
|
||||
ctx.ui.info(_('* Updating repository: %s') % repo)
|
||||
index = Index()
|
||||
if ctx.repodb.has_repo(repo):
|
||||
repouri = ctx.repodb.get_repo(repo).indexuri.get_uri()
|
||||
try:
|
||||
index.read_uri_of_repo(repouri, repo)
|
||||
except pisi.file.AlreadyHaveException, e:
|
||||
ctx.ui.info(_('No updates available for repository %s.') % repo)
|
||||
if force:
|
||||
ctx.ui.info(_('Updating database at any rate as requested'))
|
||||
index.read_uri_of_repo(repouri, repo, force = force)
|
||||
else:
|
||||
return
|
||||
|
||||
try:
|
||||
index.check_signature(repouri, repo)
|
||||
except pisi.file.NoSignatureFound, e:
|
||||
ctx.ui.warning(e)
|
||||
|
||||
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
|
||||
ctx.ui.info(_('* Package database updated.'))
|
||||
else:
|
||||
raise Error(_('No repository named %s found.') % repo)
|
||||
|
||||
def delete_cache():
|
||||
util.clean_dir(ctx.config.packages_dir())
|
||||
util.clean_dir(ctx.config.archives_dir())
|
||||
util.clean_dir(ctx.config.tmp_dir())
|
||||
|
||||
def rebuild_repo(repo):
|
||||
ctx.ui.info(_('* Rebuilding \'%s\' named repo... ') % repo, noln=True)
|
||||
|
||||
if ctx.repodb.has_repo(repo):
|
||||
repouri = URI(ctx.repodb.get_repo(repo).indexuri.get_uri())
|
||||
indexname = repouri.filename()
|
||||
index = Index()
|
||||
indexpath = pisi.util.join_path(ctx.config.index_dir(), repo, indexname)
|
||||
tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
|
||||
pisi.util.clean_dir(tmpdir)
|
||||
pisi.util.check_dir(tmpdir)
|
||||
try:
|
||||
index.read_uri(indexpath, tmpdir, force=True) # don't look for sha1sum there
|
||||
except IOError, e:
|
||||
ctx.ui.warning(_("Input/Output error while reading %s: %s") % (indexpath, unicode(e)))
|
||||
return
|
||||
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
|
||||
ctx.ui.info(_('OK.'))
|
||||
else:
|
||||
raise Error(_('No repository named %s found.') % repo)
|
||||
|
||||
def rebuild_db(files=False):
|
||||
|
||||
assert ctx.database == False
|
||||
|
||||
# Bug 2596
|
||||
# finds and cleans duplicate package directories under '/var/lib/pisi/package'
|
||||
# deletes the _older_ versioned package directories.
|
||||
def clean_duplicates():
|
||||
i_version = {} # installed versions
|
||||
replica = []
|
||||
for pkg in os.listdir(pisi.util.join_path(pisi.api.ctx.config.lib_dir(), 'package')):
|
||||
(name, ver) = util.parse_package_name(pkg)
|
||||
if i_version.has_key(name):
|
||||
if Version(ver) > Version(i_version[name]):
|
||||
# found a greater version, older one is a replica
|
||||
replica.append(name + '-' + i_version[name])
|
||||
i_version[name] = ver
|
||||
else:
|
||||
# found an older version which is a replica
|
||||
replica.append(name + '-' + ver)
|
||||
else:
|
||||
i_version[name] = ver
|
||||
|
||||
for pkg in replica:
|
||||
pisi.util.clean_dir(pisi.util.join_path(pisi.api.ctx.config.lib_dir(), 'package', pkg))
|
||||
|
||||
def destroy(files):
|
||||
#TODO: either don't delete version files here, or remove force flag...
|
||||
import bsddb3.db
|
||||
for db in os.listdir(ctx.config.db_dir()):
|
||||
if db.endswith('.bdb'):# or db.startswith('log'): # delete only db files
|
||||
if db.startswith('files') or db.startswith('filesdbversion'):
|
||||
clean = files
|
||||
else:
|
||||
clean = True
|
||||
if clean:
|
||||
fn = pisi.util.join_path(ctx.config.db_dir(), db)
|
||||
#NB: there is a parameter bug with python-bsddb3, fixed in pardus
|
||||
ctx.dbenv.dbremove(file=fn, flags=bsddb3.db.DB_AUTO_COMMIT)
|
||||
|
||||
def reload_packages(files, txn):
|
||||
for package_fn in os.listdir( pisi.util.join_path( ctx.config.lib_dir(),
|
||||
'package' ) ):
|
||||
if not package_fn == "scripts":
|
||||
ctx.ui.debug('Resurrecting %s' % package_fn)
|
||||
pisi.api.resurrect_package(package_fn, files, txn)
|
||||
|
||||
def reload_indices(txn):
|
||||
index_dir = ctx.config.index_dir()
|
||||
if os.path.exists(index_dir): # it may have been erased, or we may be upgrading from a previous version -- exa
|
||||
for repo in os.listdir(index_dir):
|
||||
indexuri = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, 'uri')
|
||||
indexuri = open(indexuri, 'r').readline()
|
||||
pisi.api.add_repo(repo, indexuri)
|
||||
pisi.api.rebuild_repo(repo)
|
||||
|
||||
# check db schema versions
|
||||
try:
|
||||
pisi.lockeddbshelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=False)
|
||||
except:
|
||||
files = True # exception means the files db version was wrong
|
||||
pisi.lockeddbshelve.init_dbenv(write=True, writeversion=True)
|
||||
destroy(files) # bye bye
|
||||
|
||||
# save parameters and shutdown pisi
|
||||
options = ctx.config.options
|
||||
ui = ctx.ui
|
||||
comar = ctx.comar
|
||||
finalize()
|
||||
|
||||
# construct new database
|
||||
init(database=True, options=options, ui=ui, comar=comar)
|
||||
clean_duplicates()
|
||||
reload_packages(files, None)
|
||||
reload_indices(None)
|
||||
@@ -1,408 +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."""
|
||||
|
||||
# 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
|
||||
import pisi.context as ctx
|
||||
|
||||
class ArchiveError(pisi.Error):
|
||||
pass
|
||||
|
||||
class LZMAError(pisi.Error):
|
||||
def __init__(self, err):
|
||||
pisi.Error.__init__(self, _("An error has occured while running LZMA:\n%s") % err)
|
||||
|
||||
|
||||
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", no_same_permissions = True):
|
||||
super(ArchiveTar, self).__init__(file_path, arch_type)
|
||||
self.tar = None
|
||||
self.no_same_permissions = no_same_permissions
|
||||
|
||||
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)
|
||||
self.unpack_dir(target_dir)
|
||||
|
||||
def unpack_dir(self, target_dir):
|
||||
rmode = ""
|
||||
if self.type == 'tar':
|
||||
rmode = 'r:'
|
||||
elif self.type == 'targz':
|
||||
rmode = 'r:gz'
|
||||
elif self.type == 'tarbz2':
|
||||
rmode = 'r:bz2'
|
||||
elif self.type == 'tarlzma':
|
||||
rmode = 'r:'
|
||||
self.file_path = self.file_path.rstrip(ctx.const.lzma_suffix)
|
||||
ret, out, err = util.run_batch("lzma d %s %s" % (self.file_path + ctx.const.lzma_suffix,
|
||||
self.file_path))
|
||||
if ret != 0:
|
||||
raise LZMAError(err)
|
||||
else:
|
||||
raise ArchiveError(_("Archive type not recognized"))
|
||||
|
||||
self.tar = tarfile.open(self.file_path, rmode)
|
||||
oldwd = os.getcwd()
|
||||
os.chdir(target_dir)
|
||||
|
||||
install_tar_path = util.join_path(ctx.config.tmp_dir(), ctx.const.install_tar)
|
||||
for tarinfo in self.tar:
|
||||
# Installing packages (especially shared libraries) is a
|
||||
# bit tricky. You should also change the inode if you
|
||||
# change the file, cause the file is opened allready and
|
||||
# accessed. Removing and creating the file will also
|
||||
# change the inode and will do the trick (in fact, old
|
||||
# file will be deleted only when its closed).
|
||||
#
|
||||
# Also, tar.extract() doesn't write on symlinks... Not any
|
||||
# more :).
|
||||
if self.file_path == install_tar_path:
|
||||
if os.path.isfile(tarinfo.name) or os.path.islink(tarinfo.name):
|
||||
try:
|
||||
os.unlink(tarinfo.name)
|
||||
except OSError, e:
|
||||
ctx.ui.warning(e)
|
||||
|
||||
self.tar.extract(tarinfo)
|
||||
|
||||
# tarfile.extract does not honor umask. It must be honored explicitly.
|
||||
# see --no-same-permissions option of tar(1), which is the deafult
|
||||
# behaviour.
|
||||
#
|
||||
# Note: This is no good while installing a pisi package. Thats why
|
||||
# this is optional.
|
||||
if self.no_same_permissions and not os.path.islink(tarinfo.name):
|
||||
os.chmod(tarinfo.name, tarinfo.mode & ~ctx.const.umask)
|
||||
|
||||
os.chdir(oldwd)
|
||||
self.close()
|
||||
|
||||
def add_to_archive(self, file_name, arc_name=None):
|
||||
"""Add file or directory path to the tar archive"""
|
||||
if not self.tar:
|
||||
if self.type == 'tar':
|
||||
wmode = 'w:'
|
||||
elif self.type == 'targz':
|
||||
wmode = 'w:gz'
|
||||
elif self.type == 'tarbz2':
|
||||
wmode = 'w:bz2'
|
||||
elif self.type == 'tarlzma':
|
||||
wmode = 'w:'
|
||||
self.file_path = self.file_path.rstrip(ctx.const.lzma_suffix)
|
||||
else:
|
||||
raise ArchiveError(_("Archive type not recognized"))
|
||||
self.tar = tarfile.open(self.file_path, wmode)
|
||||
|
||||
self.tar.add(file_name, arc_name)
|
||||
|
||||
def close(self):
|
||||
self.tar.close()
|
||||
|
||||
if self.tar.mode == 'wb' and self.type == 'tarlzma':
|
||||
batch = None
|
||||
if ctx.config.values.build.compressionlevel:
|
||||
batch = "lzmash -%s %s" % (ctx.config.values.build.compressionlevel, self.file_path)
|
||||
else:
|
||||
batch = "lzmash %s" % self.file_path
|
||||
|
||||
ret, out, err = util.run_batch(batch)
|
||||
if ret != 0:
|
||||
raise LZMAError(err)
|
||||
|
||||
|
||||
class MyZipFile(zipfile.ZipFile):
|
||||
def decompressToFile(self, name, outname):
|
||||
import zlib
|
||||
import binascii
|
||||
|
||||
block_size = 1024 * 1024 * 2
|
||||
|
||||
if self.mode not in ("r", "a"):
|
||||
raise RuntimeError, 'read() requires mode "r" or "a"'
|
||||
if not self.fp:
|
||||
raise RuntimeError, \
|
||||
"Attempt to read ZIP archive that was already closed"
|
||||
zinfo = self.getinfo(name)
|
||||
filepos = self.fp.tell()
|
||||
self.fp.seek(zinfo.file_offset, 0)
|
||||
|
||||
destfile = file(outname, 'wb')
|
||||
|
||||
if zinfo.compress_type == zipfile.ZIP_STORED:
|
||||
total_read = 0
|
||||
crc = None
|
||||
while total_read < zinfo.compress_size:
|
||||
if zinfo.compress_size - total_read < block_size:
|
||||
block_size = zinfo.compress_size - total_read
|
||||
buff = self.fp.read(block_size)
|
||||
destfile.write(buff)
|
||||
total_read += (block_size)
|
||||
if crc:
|
||||
crc = binascii.crc32(buff, crc)
|
||||
else:
|
||||
crc = binascii.crc32(buff)
|
||||
destfile.close()
|
||||
self.fp.seek(filepos, 0)
|
||||
if crc and crc != zinfo.CRC:
|
||||
raise zipfile.BadZipfile, "Bad CRC-32 for file %s" % name
|
||||
return
|
||||
elif zinfo.compress_type != zipfile.ZIP_DEFLATED:
|
||||
raise BadZipfile, \
|
||||
"Unsupported compression method %d for file %s" % \
|
||||
(zinfo.compress_type, name)
|
||||
|
||||
if not zlib:
|
||||
raise RuntimeError, \
|
||||
"De-compression requires the (missing) zlib module"
|
||||
# zlib compress/decompress code by Jeremy Hylton of CNRI
|
||||
dc = zlib.decompressobj(-15)
|
||||
|
||||
total_read = 0
|
||||
crc = None
|
||||
while total_read < zinfo.compress_size:
|
||||
if zinfo.compress_size - total_read < block_size:
|
||||
block_size = zinfo.compress_size - total_read
|
||||
buff = self.fp.read(block_size)
|
||||
dcbuff = dc.decompress(dc.unconsumed_tail + buff)
|
||||
destfile.write(dcbuff)
|
||||
total_read += block_size
|
||||
if crc:
|
||||
crc = binascii.crc32(dcbuff, crc)
|
||||
else:
|
||||
crc = binascii.crc32(dcbuff)
|
||||
|
||||
# need to feed in unused pad byte so that zlib won't choke
|
||||
ex = dc.decompress(dc.unconsumed_tail + 'Z') + dc.flush()
|
||||
if ex:
|
||||
if crc:
|
||||
crc = binascii.crc32(ex, crc)
|
||||
else:
|
||||
crc = binascii.crc32(ex)
|
||||
destfile.write(ex)
|
||||
|
||||
if crc and crc != zinfo.CRC:
|
||||
raise zipfile.BadZipfile, "Bad CRC-32 for file %s" % name
|
||||
|
||||
destfile.close()
|
||||
self.fp.seek(filepos, 0)
|
||||
|
||||
|
||||
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 = MyZipFile(self.file_path, mode)
|
||||
|
||||
def close(self):
|
||||
"""Close the zip archive."""
|
||||
self.zip_obj.close()
|
||||
|
||||
def add_to_archive(self, file_name, arc_name=None):
|
||||
"""Add file or directory path to the zip file"""
|
||||
# It's a pity that zipfile can't handle unicode strings. Grrr!
|
||||
file_name = str(file_name)
|
||||
if os.path.isdir(file_name) and not os.path.islink(file_name):
|
||||
self.zip_obj.writestr(file_name + '/', '')
|
||||
attr_obj = self.zip_obj.getinfo(file_name + '/')
|
||||
attr_obj.external_attr = stat.S_IMODE(os.stat(file_name)[0]) << 16L
|
||||
for f in os.listdir(file_name):
|
||||
self.add_to_archive(os.path.join(file_name, f))
|
||||
else:
|
||||
if os.path.islink(file_name):
|
||||
dest = os.readlink(file_name)
|
||||
attr = 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, arc_name, zipfile.ZIP_DEFLATED)
|
||||
|
||||
if not arc_name:
|
||||
zinfo = self.zip_obj.getinfo(file_name)
|
||||
else:
|
||||
zinfo = self.zip_obj.getinfo(arc_name)
|
||||
zinfo.create_system = 3
|
||||
|
||||
def add_basename_to_archive(self, file_name):
|
||||
"""Add only the basepath to the zip file. For example; if the given
|
||||
file_name parameter is /usr/local/bin/somedir, this function
|
||||
will create only the base directory/file somedir in the
|
||||
archive."""
|
||||
cwd = os.getcwd()
|
||||
path_name = os.path.dirname(file_name)
|
||||
file_name = os.path.basename(file_name)
|
||||
if path_name:
|
||||
os.chdir(path_name)
|
||||
self.add_to_archive(file_name)
|
||||
os.chdir(cwd)
|
||||
|
||||
def has_file(self, file_path):
|
||||
""" Returns true if file_path is member of the zip archive"""
|
||||
return file_path in self.zip_obj.namelist()
|
||||
|
||||
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
|
||||
zip_obj.decompressToFile(info.filename, ofile)
|
||||
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,
|
||||
'tarlzma': ArchiveTar,
|
||||
'tar': ArchiveTar,
|
||||
'zip': ArchiveZip,
|
||||
'binary': ArchiveBinary
|
||||
}
|
||||
|
||||
self.archive = handlers.get(arch_type)(file_path, arch_type)
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
self.archive.unpack(target_dir, clean_dir)
|
||||
|
||||
def unpack_files(self, files, target_dir):
|
||||
self.archive.unpack_files(files, target_dir)
|
||||
@@ -1,561 +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.
|
||||
|
||||
"""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.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
from pisi.uri import URI
|
||||
import pisi.ui
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
pass
|
||||
|
||||
# single package operations
|
||||
|
||||
class AtomicOperation(object):
|
||||
|
||||
def __init__(self, ignore_dep = None):
|
||||
#self.package = package
|
||||
if ignore_dep==None:
|
||||
self.ignore_dep = ctx.config.get_option('ignore_dependency')
|
||||
else:
|
||||
self.ignore_dep = ignore_dep
|
||||
|
||||
def run(self, package):
|
||||
"perform an atomic package operation"
|
||||
pass
|
||||
|
||||
|
||||
class Install(AtomicOperation):
|
||||
"Install class, provides install routines for pisi packages"
|
||||
|
||||
@staticmethod
|
||||
def from_name(name, ignore_dep = None):
|
||||
# download package and return an installer object
|
||||
# find package in repository
|
||||
repo = ctx.packagedb.which_repo(name)
|
||||
if repo:
|
||||
ctx.ui.info(_("Package %s found in repository %s") % (name, repo))
|
||||
repo = ctx.repodb.get_repo(repo)
|
||||
pkg = ctx.packagedb.get_package(name)
|
||||
|
||||
# FIXME: let pkg.packageURI be stored as URI type rather than string
|
||||
pkg_uri = URI(pkg.packageURI)
|
||||
if pkg_uri.is_absolute_path():
|
||||
pkg_path = str(pkg.packageURI)
|
||||
else:
|
||||
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(pkg_uri.path()))
|
||||
|
||||
ctx.ui.info(_("Package URI: %s") % pkg_path, verbose=True)
|
||||
|
||||
return Install(pkg_path, ignore_dep)
|
||||
else:
|
||||
raise Error(_("Package %s not found in any active repository.") % name)
|
||||
|
||||
def __init__(self, package_fname, ignore_dep = None, ignore_file_conflicts = None):
|
||||
"initialize from a file name"
|
||||
super(Install, self).__init__(ignore_dep)
|
||||
if not ignore_file_conflicts:
|
||||
ignore_file_conflicts = ctx.get_option('ignore_file_conflicts')
|
||||
self.ignore_file_conflicts = ignore_file_conflicts
|
||||
self.package_fname = package_fname
|
||||
self.package = pisi.package.Package(package_fname)
|
||||
self.package.read()
|
||||
self.metadata = self.package.metadata
|
||||
self.files = self.package.files
|
||||
self.pkginfo = self.metadata.package
|
||||
|
||||
def install(self, ask_reinstall = True):
|
||||
"entry point"
|
||||
ctx.ui.status(_('Installing %s, version %s, release %s, build %s') %
|
||||
(self.pkginfo.name, self.pkginfo.version,
|
||||
self.pkginfo.release, self.pkginfo.build))
|
||||
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
|
||||
|
||||
self.ask_reinstall = ask_reinstall
|
||||
self.check_requirements()
|
||||
self.check_relations()
|
||||
self.check_reinstall()
|
||||
self.extract_install()
|
||||
self.store_pisi_files()
|
||||
|
||||
self.postinstall()
|
||||
|
||||
txn = ctx.dbenv.txn_begin()
|
||||
try:
|
||||
self.update_databases(txn)
|
||||
txn.commit()
|
||||
except db.DBError, e:
|
||||
txn.abort()
|
||||
raise e
|
||||
|
||||
ctx.ui.status()
|
||||
if self.upgrade:
|
||||
event = pisi.ui.upgraded
|
||||
else:
|
||||
event = pisi.ui.installed
|
||||
ctx.ui.notify(event, package = self.pkginfo, files = self.files)
|
||||
|
||||
def check_requirements(self):
|
||||
"""check system requirements"""
|
||||
#TODO: IS THERE ENOUGH SPACE?
|
||||
# what to do if / is split into /usr, /var, etc.
|
||||
# check comar
|
||||
if self.metadata.package.providesComar and ctx.comar:
|
||||
import pisi.comariface as comariface
|
||||
com = comariface.make_com()
|
||||
|
||||
def check_relations(self):
|
||||
# check conflicts
|
||||
for pkg in self.metadata.package.conflicts:
|
||||
if ctx.installdb.is_installed(self.pkginfo):
|
||||
raise Error(_("Package conflicts %s") % pkg)
|
||||
|
||||
# check dependencies
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
if not self.pkginfo.installable():
|
||||
ctx.ui.error(_('Dependencies for %s not satisfied') %
|
||||
self.pkginfo.name)
|
||||
raise Error(_("Package not installable"))
|
||||
|
||||
# check if package is in database
|
||||
# If it is not, put it into 3rd party packagedb
|
||||
if not ctx.packagedb.has_package(self.pkginfo.name):
|
||||
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.thirdparty)
|
||||
|
||||
# check file conflicts
|
||||
file_conflicts = []
|
||||
for file in self.files.list:
|
||||
if ctx.filesdb.has_file(file.path):
|
||||
pkg, existing_file = ctx.filesdb.get_file(file.path)
|
||||
dst = pisi.util.join_path(ctx.config.dest_dir(), file.path)
|
||||
if pkg != self.pkginfo.name and not os.path.isdir(dst):
|
||||
file_conflicts.append( (pkg, existing_file) )
|
||||
if file_conflicts:
|
||||
file_conflicts_str = ""
|
||||
for (pkg, existing_file) in file_conflicts:
|
||||
file_conflicts_str += _("/%s from %s package\n") % (existing_file.path, pkg)
|
||||
msg = _('File conflicts:\n%s') % file_conflicts_str
|
||||
if self.ignore_file_conflicts:
|
||||
ctx.ui.warning(msg)
|
||||
else:
|
||||
raise Error(msg)
|
||||
|
||||
def check_reinstall(self):
|
||||
"check reinstall, confirm action, and schedule reinstall"
|
||||
|
||||
pkg = self.pkginfo
|
||||
|
||||
self.reinstall = False
|
||||
self.upgrade = False
|
||||
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
|
||||
|
||||
#FIXME: consider REPOSITORY instead of DISTRIBUTION -- exa
|
||||
#ipackage = ctx.packagedb.get_package(pkg.name, pisi.itembyrepodb.installed)
|
||||
ipkg = ctx.installdb.get_info(pkg.name)
|
||||
repomismatch = ipkg.distribution != pkg.distribution
|
||||
|
||||
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
|
||||
|
||||
# determine if same version
|
||||
self.same_ver = False
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
if repomismatch or (not ibuild) or (not pkg.build) or ignore_build:
|
||||
# we don't look at builds to compare two package versions
|
||||
if Version(pkg.release) == Version(irelease):
|
||||
self.same_ver = True
|
||||
else:
|
||||
if pkg.build == ibuild:
|
||||
self.same_ver = True
|
||||
|
||||
if self.same_ver:
|
||||
if self.ask_reinstall:
|
||||
if not ctx.ui.confirm(_('Re-install same version package?')):
|
||||
raise Error(_('Package re-install declined'))
|
||||
else:
|
||||
upgrade = False
|
||||
# is this an upgrade?
|
||||
# determine and report the kind of upgrade: version, release, build
|
||||
if Version(pkg.version) > Version(iversion):
|
||||
ctx.ui.info(_('Upgrading to new upstream version'))
|
||||
upgrade = True
|
||||
elif Version(pkg.release) > Version(irelease):
|
||||
ctx.ui.info(_('Upgrading to new distribution release'))
|
||||
upgrade = True
|
||||
elif ((not ignore_build) and ibuild and pkg.build
|
||||
and pkg.build > ibuild):
|
||||
ctx.ui.info(_('Upgrading to new distribution build'))
|
||||
upgrade = True
|
||||
self.upgrade = upgrade
|
||||
|
||||
# is this a downgrade? confirm this action.
|
||||
if self.ask_reinstall and (not upgrade):
|
||||
if Version(pkg.version) < Version(iversion):
|
||||
#x = _('Downgrade to old upstream version?')
|
||||
x = None
|
||||
elif Version(pkg.release) < Version(irelease):
|
||||
x = _('Downgrade to old distribution release?')
|
||||
else:
|
||||
x = _('Downgrade to old distribution build?')
|
||||
if x and not ctx.ui.confirm(x):
|
||||
raise Error(_('Package downgrade declined'))
|
||||
|
||||
# schedule for reinstall
|
||||
self.old_files = ctx.installdb.files(pkg.name)
|
||||
self.old_path = ctx.installdb.pkg_dir(pkg.name, iversion, irelease)
|
||||
self.reinstall = True
|
||||
self.remove_old = Remove(pkg.name)
|
||||
self.remove_old.run_preremove()
|
||||
|
||||
def postinstall(self):
|
||||
self.config_later = False
|
||||
if ctx.comar:
|
||||
import pisi.comariface
|
||||
ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files)
|
||||
pisi.comariface.post_install(
|
||||
self.pkginfo.name,
|
||||
self.metadata.package.providesComar,
|
||||
self.package.comar_dir(),
|
||||
os.path.join(self.package.pkg_dir(), ctx.const.metadata_xml),
|
||||
os.path.join(self.package.pkg_dir(), ctx.const.files_xml),
|
||||
)
|
||||
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files)
|
||||
else:
|
||||
self.config_later = True
|
||||
|
||||
def extract_install(self):
|
||||
"unzip package in place"
|
||||
|
||||
ctx.ui.notify(pisi.ui.extracting, package = self.pkginfo, files = self.files)
|
||||
|
||||
config_changed = []
|
||||
def check_config_changed(config):
|
||||
changed = False
|
||||
fpath = pisi.util.join_path(ctx.config.dest_dir(), config.path)
|
||||
if os.path.exists(fpath) and not os.path.isdir(fpath):
|
||||
if os.path.islink(fpath):
|
||||
file = os.readlink(fpath)
|
||||
if os.path.exists(file) and pisi.util.sha1_data(file) != config.hash:
|
||||
changed = True
|
||||
else:
|
||||
if pisi.util.sha1_file(fpath) != config.hash:
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
config_changed.append(fpath)
|
||||
if os.path.exists(fpath + '.old'):
|
||||
os.unlink(fpath + '.old')
|
||||
os.rename(fpath, fpath + '.old')
|
||||
|
||||
# old config files are kept as they are. New config files from the installed
|
||||
# packages are saved with ".newconfig" string appended to their names.
|
||||
def rename_configs():
|
||||
for path in config_changed:
|
||||
newconfig = path + '.newconfig'
|
||||
oldconfig = path + '.old'
|
||||
if os.path.exists(newconfig):
|
||||
os.unlink(newconfig)
|
||||
os.rename(path, newconfig)
|
||||
os.rename(oldconfig, path)
|
||||
|
||||
# remove left over files from the old package.
|
||||
def clean_leftovers():
|
||||
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] )
|
||||
|
||||
if self.reinstall:
|
||||
# get 'config' typed file objects
|
||||
new = filter(lambda x: x.type == 'config', self.files.list)
|
||||
old = filter(lambda x: x.type == 'config', self.old_files.list)
|
||||
|
||||
# get config path lists
|
||||
newconfig = set(map(lambda x: str(x.path), new))
|
||||
oldconfig = set(map(lambda x: str(x.path), old))
|
||||
|
||||
config_overlaps = newconfig & oldconfig
|
||||
if config_overlaps:
|
||||
files = filter(lambda x: x.path in config_overlaps, old)
|
||||
for file in files:
|
||||
check_config_changed(file)
|
||||
else:
|
||||
for file in self.files.list:
|
||||
if file.type == 'config':
|
||||
# there may be left over config files
|
||||
check_config_changed(file)
|
||||
|
||||
self.package.extract_install(ctx.config.dest_dir())
|
||||
|
||||
if config_changed:
|
||||
rename_configs()
|
||||
|
||||
if self.reinstall:
|
||||
clean_leftovers()
|
||||
|
||||
|
||||
def store_pisi_files(self):
|
||||
"""put files.xml, metadata.xml, actions.py and COMAR scripts
|
||||
somewhere in the file system. We'll need these in future..."""
|
||||
|
||||
if self.reinstall:
|
||||
util.clean_dir(self.old_path)
|
||||
|
||||
ctx.ui.info(_('Storing %s, ') % ctx.const.files_xml, verbose=True)
|
||||
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
|
||||
|
||||
ctx.ui.info(_('Storing %s.') % ctx.const.metadata_xml, verbose=True)
|
||||
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, verbose=True)
|
||||
self.package.extract_file(fpath, self.package.pkg_dir())
|
||||
|
||||
def update_databases(self, txn):
|
||||
"update databases"
|
||||
if self.reinstall:
|
||||
self.remove_old.remove_db(txn)
|
||||
|
||||
# installdb
|
||||
ctx.installdb.install(self.metadata.package.name,
|
||||
self.metadata.package.version,
|
||||
self.metadata.package.release,
|
||||
self.metadata.package.build,
|
||||
self.metadata.package.distribution,
|
||||
config_later = self.config_later,
|
||||
txn = txn)
|
||||
|
||||
# filesdb
|
||||
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn=txn)
|
||||
|
||||
# installed packages
|
||||
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.installed, txn=txn)
|
||||
|
||||
|
||||
def install_single(pkg, upgrade = False):
|
||||
"""install a single package from URI or ID"""
|
||||
url = URI(pkg)
|
||||
# Check if we are dealing with a remote file or a real path of
|
||||
# package filename. Otherwise we'll try installing a package from
|
||||
# the package repository.
|
||||
if url.is_remote_file() or os.path.exists(url.uri):
|
||||
install_single_file(pkg, upgrade)
|
||||
else:
|
||||
install_single_name(pkg, upgrade)
|
||||
|
||||
# FIXME: Here and elsewhere pkg_location must be a URI
|
||||
def install_single_file(pkg_location, upgrade = False):
|
||||
"""install a package file"""
|
||||
Install(pkg_location).install(not upgrade)
|
||||
|
||||
def install_single_name(name, upgrade = False):
|
||||
"""install a single package from ID"""
|
||||
install = Install.from_name(name)
|
||||
install.install(not upgrade)
|
||||
|
||||
class Remove(AtomicOperation):
|
||||
|
||||
def __init__(self, package_name, ignore_dep = None):
|
||||
super(Remove, self).__init__(ignore_dep)
|
||||
self.package_name = package_name
|
||||
self.package = ctx.packagedb.get_package(self.package_name, pisi.itembyrepodb.installed)
|
||||
try:
|
||||
self.files = ctx.installdb.files(self.package_name)
|
||||
except pisi.Error, e:
|
||||
# for some reason file was deleted, we still allow removes!
|
||||
ctx.ui.error(unicode(e))
|
||||
ctx.ui.warning(_('File list could not be read for package %s, continuing removal.') % package_name)
|
||||
self.files = Files()
|
||||
|
||||
def run(self):
|
||||
"""Remove a single package"""
|
||||
|
||||
ctx.ui.status(_('Removing package %s') % self.package_name)
|
||||
ctx.ui.notify(pisi.ui.removing, package = self.package, files = self.files)
|
||||
if not ctx.installdb.is_installed(self.package_name):
|
||||
raise Exception(_('Trying to remove nonexistent package ')
|
||||
+ self.package_name)
|
||||
|
||||
self.check_dependencies()
|
||||
|
||||
self.run_preremove()
|
||||
for fileinfo in self.files.list:
|
||||
self.remove_file(fileinfo)
|
||||
|
||||
txn = ctx.dbenv.txn_begin()
|
||||
try:
|
||||
self.remove_db(txn)
|
||||
txn.commit()
|
||||
except db.DBError, e:
|
||||
txn.abort()
|
||||
raise e
|
||||
|
||||
self.remove_pisi_files()
|
||||
ctx.ui.status()
|
||||
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files)
|
||||
|
||||
def check_dependencies(self):
|
||||
#FIXME: why is this not implemented? -- exa
|
||||
#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?
|
||||
|
||||
@staticmethod
|
||||
def remove_file(fileinfo):
|
||||
fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path)
|
||||
|
||||
# we should check if the file belongs to another
|
||||
# package (this can legitimately occur while upgrading
|
||||
# two packages such that a file has moved from one package to
|
||||
# another as in #2911)
|
||||
if ctx.filesdb.has_file(fpath):
|
||||
pkg, existing_file = ctx.filesdb.get_file(fpath)
|
||||
if pkg != self.package_name:
|
||||
ctx.ui.warning(_('Not removing conflicted file : %s') % fpath)
|
||||
return
|
||||
|
||||
if fileinfo.permanent:
|
||||
# do not remove precious files :)
|
||||
pass
|
||||
elif fileinfo.type == ctx.const.conf:
|
||||
# config files are precious, leave them as they are
|
||||
# unless they are the same as provided by package.
|
||||
try:
|
||||
if pisi.util.sha1_file(fpath) == fileinfo.hash:
|
||||
os.unlink(fpath)
|
||||
except pisi.util.FileError, e:
|
||||
pass
|
||||
else:
|
||||
if os.path.isfile(fpath) or os.path.islink(fpath):
|
||||
os.unlink(fpath)
|
||||
elif os.path.isdir(fpath) and not os.listdir(fpath):
|
||||
os.rmdir(fpath)
|
||||
else:
|
||||
ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath)
|
||||
return
|
||||
|
||||
# remove emptied directories
|
||||
dpath = os.path.dirname(fpath)
|
||||
while dpath != '/' and not os.listdir(dpath):
|
||||
os.rmdir(dpath)
|
||||
dpath = os.path.dirname(dpath)
|
||||
|
||||
def run_preremove(self):
|
||||
if ctx.comar:
|
||||
import pisi.comariface
|
||||
pisi.comariface.pre_remove(
|
||||
self.package_name,
|
||||
os.path.join(self.package.pkg_dir(), ctx.const.metadata_xml),
|
||||
os.path.join(self.package.pkg_dir(), ctx.const.files_xml),
|
||||
)
|
||||
|
||||
def remove_pisi_files(self):
|
||||
util.clean_dir(self.package.pkg_dir())
|
||||
|
||||
def remove_db(self, txn):
|
||||
ctx.installdb.remove(self.package_name, txn)
|
||||
ctx.filesdb.remove_files(self.files, txn)
|
||||
pisi.packagedb.remove_tracking_package(self.package_name, txn)
|
||||
|
||||
|
||||
def remove_single(package_name):
|
||||
Remove(package_name).run()
|
||||
|
||||
def build(package):
|
||||
# wrapper for build op
|
||||
import pisi.build
|
||||
return pisi.build.build(package)
|
||||
|
||||
def virtual_install(metadata, files, txn):
|
||||
"""Recreate the package info for rebuilddb command"""
|
||||
# installdb
|
||||
ctx.installdb.install(metadata.package.name,
|
||||
metadata.package.version,
|
||||
metadata.package.release,
|
||||
metadata.package.build,
|
||||
metadata.package.distribution,
|
||||
rebuild=True,
|
||||
txn=txn)
|
||||
|
||||
# filesdb
|
||||
if files:
|
||||
ctx.filesdb.add_files(metadata.package.name, files, txn=txn)
|
||||
|
||||
# installed packages
|
||||
ctx.packagedb.add_package(metadata.package, pisi.itembyrepodb.installed, txn=txn)
|
||||
|
||||
def resurrect_package(package_fn, write_files, txn = None):
|
||||
"""Resurrect the package from xml files"""
|
||||
|
||||
from os.path import exists
|
||||
|
||||
metadata_xml = util.join_path(ctx.config.lib_dir(), 'package',
|
||||
package_fn, ctx.const.metadata_xml)
|
||||
if not exists(metadata_xml):
|
||||
raise Error, _("Metadata XML '%s' cannot be found") % metadata_xml
|
||||
|
||||
metadata = MetaData()
|
||||
metadata.read(metadata_xml)
|
||||
|
||||
errs = metadata.errors()
|
||||
if errs:
|
||||
util.Checks.print_errors(errs)
|
||||
raise Error, _("MetaData format wrong (%s)") % package_fn
|
||||
|
||||
ctx.ui.info(_('* Adding \'%s\' to db... ') % (metadata.package.name), noln=True)
|
||||
|
||||
if write_files:
|
||||
files_xml = util.join_path(ctx.config.lib_dir(), 'package',
|
||||
package_fn, ctx.const.files_xml)
|
||||
if not exists(files_xml):
|
||||
raise Error, _("Files XML '%s' cannot be found") % files_xml
|
||||
|
||||
files = Files()
|
||||
files.read(files_xml)
|
||||
if files.errors():
|
||||
raise Error, _("Invalid %s") % ctx.const.files_xml
|
||||
else:
|
||||
files = None
|
||||
|
||||
#import pisi.atomicoperations
|
||||
def f(t):
|
||||
pisi.atomicoperations.virtual_install(metadata, files, t)
|
||||
ctx.txn_proc(f, txn)
|
||||
|
||||
ctx.ui.info(_('OK.'))
|
||||
@@ -1,937 +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 building code"""
|
||||
|
||||
# python standard library
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
from copy import deepcopy
|
||||
from os.path import basename, dirname
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
from pisi.specfile import SpecFile
|
||||
import pisi.util as util
|
||||
from pisi.util import join_path as join, parenturi
|
||||
from pisi.file import File
|
||||
import pisi.context as ctx
|
||||
import pisi.dependency as dependency
|
||||
import pisi.operations as operations
|
||||
from pisi.sourcearchive import SourceArchive
|
||||
from pisi.files import Files, FileInfo
|
||||
from pisi.fetcher import fetch_url
|
||||
from pisi.uri import URI
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.package import Package
|
||||
import pisi.component as component
|
||||
import pisi.archive as archive
|
||||
import pisi.actionsapi.variables
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
# Helper Functions
|
||||
def get_file_type(path, pinfo_list, install_dir):
|
||||
"""Return the file type of a path according to the given PathInfo
|
||||
list"""
|
||||
|
||||
Match = lambda x: [match for match in glob.glob(install_dir + x) if join(install_dir, path).find(match) > -1]
|
||||
|
||||
def Sort(x):
|
||||
x.sort(reverse=True)
|
||||
return x
|
||||
|
||||
best_matched_path = Sort([pinfo.path for pinfo in pinfo_list if Match(pinfo.path)])[0]
|
||||
info = [pinfo for pinfo in pinfo_list if best_matched_path == pinfo.path][0]
|
||||
return info.fileType, info.permanent
|
||||
|
||||
def check_path_collision(package, pkgList):
|
||||
"""This function will check for collision of paths in a package with
|
||||
the paths of packages in pkgList. The return value will be the
|
||||
list containing the paths that collide."""
|
||||
collisions = []
|
||||
for pinfo in package.files:
|
||||
for pkg in pkgList:
|
||||
if pkg is package:
|
||||
continue
|
||||
for path in pkg.files:
|
||||
# if pinfo.path is a subpath of path.path like
|
||||
# the example below. path.path is marked as a
|
||||
# collide. Exp:
|
||||
# pinfo.path: /usr/share
|
||||
# path.path: /usr/share/doc
|
||||
if (path.path.endswith(ctx.const.ar_file_suffix) and ctx.get_option('create_static')) or \
|
||||
(path.path.endswith(ctx.const.debug_file_suffix) and ctx.config.values.build.generatedebug):
|
||||
# don't throw collision error for these files.
|
||||
# we'll handle this in gen_files_xml..
|
||||
continue
|
||||
if util.subpath(pinfo.path, path.path):
|
||||
collisions.append(path.path)
|
||||
ctx.ui.debug(_('Path %s belongs in multiple packages') %
|
||||
path.path)
|
||||
return collisions
|
||||
|
||||
|
||||
class Builder:
|
||||
"""Provides the package build and creation routines"""
|
||||
#FIXME: this class and every other class must use URLs as paths!
|
||||
|
||||
@staticmethod
|
||||
def from_name(name):
|
||||
# download package and return an installer object
|
||||
# find package in repository
|
||||
sf, reponame = ctx.sourcedb.get_spec_repo(name)
|
||||
src = sf.source
|
||||
if src:
|
||||
|
||||
src_uri = URI(src.sourceURI)
|
||||
if src_uri.is_absolute_path():
|
||||
src_path = str(src_uri)
|
||||
else:
|
||||
repo = ctx.repodb.get_repo(reponame)
|
||||
#FIXME: don't use dirname to work on URLs
|
||||
src_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(src_uri.path()))
|
||||
|
||||
ctx.ui.debug(_("Source URI: %s") % src_path)
|
||||
|
||||
return Builder(src_path)
|
||||
else:
|
||||
raise Error(_("Source %s not found in any active repository.") % name)
|
||||
|
||||
def __init__(self, specuri):
|
||||
|
||||
# process args
|
||||
if not isinstance(specuri, URI):
|
||||
specuri = URI(specuri)
|
||||
|
||||
# read spec file, we'll need it :)
|
||||
self.set_spec_file(specuri)
|
||||
|
||||
if specuri.is_remote_file():
|
||||
self.specdir = self.fetch_files()
|
||||
else:
|
||||
self.specdir = dirname(self.specuri.get_uri())
|
||||
|
||||
self.sourceArchive = SourceArchive(self.spec, self.pkg_work_dir())
|
||||
|
||||
self.set_environment_vars()
|
||||
|
||||
self.actionLocals = None
|
||||
self.actionGlobals = None
|
||||
self.srcDir = None
|
||||
|
||||
def set_spec_file(self, specuri):
|
||||
if not specuri.is_remote_file():
|
||||
specuri = URI(os.path.realpath(specuri.get_uri())) # FIXME: doesn't work for file://
|
||||
self.specuri = specuri
|
||||
spec = SpecFile()
|
||||
spec.read(specuri, ctx.config.tmp_dir())
|
||||
self.spec = spec
|
||||
|
||||
# directory accessor functions
|
||||
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def pkg_dir(self):
|
||||
"package build directory"
|
||||
packageDir = self.spec.source.name + '-' + \
|
||||
self.spec.source.version + '-' + self.spec.source.release
|
||||
return util.join_path(ctx.config.dest_dir(), ctx.config.values.dirs.tmp_dir,
|
||||
packageDir)
|
||||
|
||||
def pkg_work_dir(self):
|
||||
return self.pkg_dir() + ctx.const.work_dir_suffix
|
||||
|
||||
def pkg_debug_dir(self):
|
||||
return self.pkg_dir() + ctx.const.debug_dir_suffix
|
||||
|
||||
def pkg_install_dir(self):
|
||||
return self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
def set_state(self, state):
|
||||
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
|
||||
open(stateFile, "w").write(state)
|
||||
|
||||
def get_state(self):
|
||||
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
|
||||
if not os.path.exists(stateFile): # no state
|
||||
return None
|
||||
return open(stateFile, "r").read()
|
||||
|
||||
def build(self):
|
||||
"""Build the package in one shot."""
|
||||
|
||||
ctx.ui.status(_("Building PiSi source package: %s") % self.spec.source.name)
|
||||
|
||||
self.compile_action_script()
|
||||
|
||||
# check if all patch files exists, if there are missing no need to unpack!
|
||||
self.patch_exists()
|
||||
|
||||
self.check_build_dependencies()
|
||||
self.fetch_component()
|
||||
self.fetch_source_archive()
|
||||
self.unpack_source_archive()
|
||||
|
||||
# apply the patches and prepare a source directory for build.
|
||||
self.apply_patches()
|
||||
|
||||
self.run_setup_action()
|
||||
self.run_build_action()
|
||||
self.run_install_action()
|
||||
|
||||
# after all, we are ready to build/prepare the packages
|
||||
return self.build_packages()
|
||||
|
||||
def set_environment_vars(self):
|
||||
"""Sets the environment variables for actions API to use"""
|
||||
|
||||
# Each time a builder is created we must reset
|
||||
# environment. See bug #2575
|
||||
pisi.actionsapi.variables.initVariables()
|
||||
|
||||
env = {
|
||||
"PKG_DIR": self.pkg_dir(),
|
||||
"WORK_DIR": self.pkg_work_dir(),
|
||||
"INSTALL_DIR": self.pkg_install_dir(),
|
||||
"SRC_NAME": self.spec.source.name,
|
||||
"SRC_VERSION": self.spec.source.version,
|
||||
"SRC_RELEASE": self.spec.source.release
|
||||
}
|
||||
os.environ.update(env)
|
||||
|
||||
# First check icecream, if not found use ccache, no need to use both
|
||||
# together (according to kde-wiki it cause performance loss)
|
||||
if ctx.config.values.build.buildhelper == "icecream":
|
||||
if os.path.exists("/opt/icecream/bin/gcc"):
|
||||
# Add icecream directory for support distributed compiling :)
|
||||
os.environ["PATH"] = "/opt/icecream/bin/:%s" % os.environ["PATH"]
|
||||
ctx.ui.info(_("IceCream detected. Make sure your daemon is up and running..."))
|
||||
elif ctx.config.values.build.buildhelper == "ccache":
|
||||
if os.path.exists("/usr/lib/ccache/bin/gcc"):
|
||||
# Add ccache directory for support Compiler Cache :)
|
||||
os.environ["PATH"] = "/usr/lib/ccache/bin/:%s" % os.environ["PATH"]
|
||||
ctx.ui.info(_("CCache detected..."))
|
||||
|
||||
def fetch_files(self):
|
||||
self.specdiruri = dirname(self.specuri.get_uri())
|
||||
pkgname = basename(self.specdiruri)
|
||||
self.destdir = join(ctx.config.tmp_dir(), pkgname)
|
||||
#self.location = dirname(self.url.uri)
|
||||
|
||||
self.fetch_actionsfile()
|
||||
self.fetch_patches()
|
||||
self.fetch_comarfiles()
|
||||
self.fetch_additionalFiles()
|
||||
|
||||
return self.destdir
|
||||
|
||||
def fetch_actionsfile(self):
|
||||
actionsuri = join(self.specdiruri, ctx.const.actions_file)
|
||||
self.download(actionsuri, self.destdir)
|
||||
|
||||
def fetch_patches(self):
|
||||
spec = self.spec
|
||||
for patch in spec.source.patches:
|
||||
file_name = basename(patch.filename)
|
||||
dir_name = dirname(patch.filename)
|
||||
patchuri = join(self.specdiruri,
|
||||
ctx.const.files_dir, dir_name, file_name)
|
||||
self.download(patchuri, join(self.destdir, ctx.const.files_dir, dir_name))
|
||||
|
||||
def fetch_comarfiles(self):
|
||||
spec = self.spec
|
||||
for package in spec.packages:
|
||||
for pcomar in package.providesComar:
|
||||
comaruri = join(self.specdiruri,
|
||||
ctx.const.comar_dir, pcomar.script)
|
||||
self.download(comaruri, join(self.destdir, ctx.const.comar_dir))
|
||||
|
||||
def fetch_additionalFiles(self):
|
||||
spec = self.spec
|
||||
for pkg in spec.packages:
|
||||
for afile in pkg.additionalFiles:
|
||||
file_name = basename(afile.filename)
|
||||
dir_name = dirname(afile.filename)
|
||||
afileuri = join(self.specdiruri,
|
||||
ctx.const.files_dir, dir_name, file_name)
|
||||
self.download(afileuri, join(self.destdir, ctx.const.files_dir, dir_name))
|
||||
|
||||
def download(self, uri, transferdir):
|
||||
# fix auth info and download
|
||||
uri = File.make_uri(uri)
|
||||
File.download(uri, transferdir)
|
||||
|
||||
def fetch_component(self):
|
||||
if not self.spec.source.partOf:
|
||||
ctx.ui.warning(_('PartOf tag not defined, looking for component'))
|
||||
diruri = parenturi(self.specuri.get_uri())
|
||||
parentdir = parenturi(diruri)
|
||||
url = util.join_path(parentdir, 'component.xml')
|
||||
progress = ctx.ui.Progress
|
||||
if URI(url).is_remote_file():
|
||||
fetch_url(url, self.pkg_work_dir(), progress)
|
||||
path = util.join_path(self.pkg_work_dir(), 'component.xml')
|
||||
else:
|
||||
if not os.path.exists(url):
|
||||
raise Exception(_('Cannot find component.xml in upper directory'))
|
||||
path = url
|
||||
comp = component.Component()
|
||||
comp.read(path)
|
||||
ctx.ui.info(_('Source is part of %s component') % comp.name)
|
||||
self.spec.source.partOf = comp.name
|
||||
self.spec.override_tags()
|
||||
|
||||
def fetch_source_archive(self):
|
||||
ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archive.uri)
|
||||
self.sourceArchive.fetch()
|
||||
ctx.ui.info(_("Source archive is stored: %s/%s")
|
||||
%(ctx.config.archives_dir(), self.spec.source.archive.name))
|
||||
|
||||
def unpack_source_archive(self):
|
||||
ctx.ui.info(_("Unpacking archive..."), noln = True)
|
||||
self.sourceArchive.unpack()
|
||||
ctx.ui.info(_(" unpacked (%s)") % self.pkg_work_dir())
|
||||
self.set_state("unpack")
|
||||
|
||||
def run_setup_action(self):
|
||||
# Run configure, build and install phase
|
||||
ctx.ui.action(_("Setting up source..."))
|
||||
self.run_action_function(ctx.const.setup_func)
|
||||
self.set_state("setupaction")
|
||||
|
||||
def run_build_action(self):
|
||||
ctx.ui.action(_("Building source..."))
|
||||
self.run_action_function(ctx.const.build_func)
|
||||
self.set_state("buildaction")
|
||||
|
||||
def run_install_action(self):
|
||||
ctx.ui.action(_("Installing..."))
|
||||
|
||||
# Before install make sure install_dir is clean
|
||||
if os.path.exists(self.pkg_install_dir()):
|
||||
util.clean_dir(self.pkg_install_dir())
|
||||
|
||||
# install function is mandatory!
|
||||
self.run_action_function(ctx.const.install_func, True)
|
||||
self.set_state("installaction")
|
||||
|
||||
def get_abandoned_files(self):
|
||||
# return the files those are not collected from the install dir
|
||||
|
||||
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
abandoned_files = []
|
||||
all_paths_in_packages = []
|
||||
|
||||
|
||||
for package in self.spec.packages:
|
||||
for path in package.files:
|
||||
map(lambda p: all_paths_in_packages.append(p), [p for p in glob.glob(install_dir + path.path)])
|
||||
|
||||
for root, dirs, files in os.walk(install_dir):
|
||||
for file_ in files:
|
||||
already_in_package = False
|
||||
fpath = util.join_path(root, file_)
|
||||
for path in all_paths_in_packages:
|
||||
if not fpath.find(path):
|
||||
already_in_package = True
|
||||
if not already_in_package:
|
||||
abandoned_files.append(fpath)
|
||||
|
||||
return abandoned_files
|
||||
|
||||
|
||||
def compile_action_script(self):
|
||||
"""Compiles actions.py and sets the actionLocals and actionGlobals"""
|
||||
scriptfile = util.join_path(self.specdir, ctx.const.actions_file)
|
||||
try:
|
||||
localSymbols = globalSymbols = {}
|
||||
buf = open(scriptfile).read()
|
||||
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
|
||||
except IOError, e:
|
||||
raise Error(_("Unable to read Action Script (%s): %s") %(scriptfile,e))
|
||||
except SyntaxError, e:
|
||||
raise Error(_("SyntaxError in Action Script (%s): %s") %(scriptfile,e))
|
||||
|
||||
self.actionLocals = localSymbols
|
||||
self.actionGlobals = globalSymbols
|
||||
self.srcDir = self.pkg_src_dir()
|
||||
|
||||
def pkg_src_dir(self):
|
||||
"""Returns the real path of WorkDir for an unpacked archive."""
|
||||
try:
|
||||
workdir = self.actionGlobals['WorkDir']
|
||||
except KeyError:
|
||||
workdir = self.spec.source.name + "-" + self.spec.source.version
|
||||
|
||||
return util.join_path(self.pkg_work_dir(), workdir)
|
||||
|
||||
def run_action_function(self, func, mandatory=False):
|
||||
"""Calls the corresponding function in actions.py.
|
||||
|
||||
If mandatory parameter is True, and function is not present in
|
||||
actionLocals pisi.build.Error will be raised."""
|
||||
# we'll need our working directory after actionscript
|
||||
# finished its work in the archive source directory.
|
||||
curDir = os.getcwd()
|
||||
os.chdir(self.srcDir)
|
||||
|
||||
|
||||
if func in self.actionLocals:
|
||||
self.actionLocals[func]()
|
||||
else:
|
||||
if mandatory:
|
||||
Error, _("unable to call function from actions: %s") %func
|
||||
|
||||
os.chdir(curDir)
|
||||
|
||||
def check_build_dependencies(self):
|
||||
"""check and try to install build dependencies, otherwise fail."""
|
||||
|
||||
build_deps = self.spec.source.buildDependencies
|
||||
|
||||
if not ctx.get_option('bypass_safety'):
|
||||
if ctx.componentdb.has_component('system.devel'):
|
||||
build_deps_names = set([x.package for x in build_deps])
|
||||
devel_deps_names = set(ctx.componentdb.get_component('system.devel').packages)
|
||||
extra_names = devel_deps_names - build_deps_names
|
||||
extra_names = filter(lambda x: not ctx.installdb.is_installed(x), extra_names)
|
||||
if extra_names:
|
||||
ctx.ui.warning(_('Safety switch: following extra packages in system.devel will be installed: ') +
|
||||
util.strlist(extra_names))
|
||||
extra_deps = [dependency.Dependency(package = x) for x in extra_names]
|
||||
build_deps.extend(extra_deps)
|
||||
else:
|
||||
ctx.ui.warning(_('Safety switch: system.devel is already installed'))
|
||||
else:
|
||||
ctx.ui.warning(_('Safety switch: the component system.devel cannot be found'))
|
||||
|
||||
# find out the build dependencies that are not satisfied...
|
||||
dep_unsatis = []
|
||||
for dep in build_deps:
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
dep_unsatis.append(dep)
|
||||
|
||||
if dep_unsatis:
|
||||
ctx.ui.info(_("Unsatisfied Build Dependencies:") + ' '
|
||||
+ util.strlist([str(x) for x in dep_unsatis]) )
|
||||
|
||||
def fail():
|
||||
raise Error(_('Cannot build package due to unsatisfied build dependencies'))
|
||||
|
||||
if ctx.config.get_option('no_install'):
|
||||
fail()
|
||||
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
for dep in dep_unsatis:
|
||||
if not dependency.repo_satisfies_dep(dep):
|
||||
raise Error(_('Build dependency %s cannot be satisfied') % str(dep))
|
||||
if ctx.ui.confirm(
|
||||
_('Do you want to install the unsatisfied build dependencies')):
|
||||
ctx.ui.info(_('Installing build dependencies.'))
|
||||
operations.install([dep.package for dep in dep_unsatis])
|
||||
else:
|
||||
fail()
|
||||
else:
|
||||
ctx.ui.warning(_('Ignoring build dependencies.'))
|
||||
|
||||
def patch_exists(self):
|
||||
"""check existence of patch files declared in PSPEC"""
|
||||
|
||||
files_dir = os.path.abspath(util.join_path(self.specdir,
|
||||
ctx.const.files_dir))
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = util.join_path(files_dir, patch.filename)
|
||||
if not os.access(patchFile, os.F_OK):
|
||||
raise Error(_("Patch file is missing: %s\n") % patch.filename)
|
||||
|
||||
def apply_patches(self):
|
||||
files_dir = os.path.abspath(util.join_path(self.specdir,
|
||||
ctx.const.files_dir))
|
||||
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = util.join_path(files_dir, patch.filename)
|
||||
if patch.compressionType:
|
||||
patchFile = util.uncompress(patchFile,
|
||||
compressType=patch.compressionType,
|
||||
targetDir=ctx.config.tmp_dir())
|
||||
|
||||
ctx.ui.action(_("* Applying patch: %s") % patch.filename)
|
||||
util.do_patch(self.srcDir, patchFile, level=patch.level, target=patch.target)
|
||||
|
||||
def generate_static_package_object(self):
|
||||
ar_files = []
|
||||
for root, dirs, files in os.walk(self.pkg_install_dir()):
|
||||
for f in files:
|
||||
if f.endswith(ctx.const.ar_file_suffix) and util.is_ar_file(util.join_path(root, f)):
|
||||
ar_files.append(util.join_path(root, f))
|
||||
|
||||
if not len(ar_files):
|
||||
return None
|
||||
|
||||
static_package_obj = pisi.specfile.Package()
|
||||
static_package_obj.name = self.spec.source.name + ctx.const.static_name_suffix
|
||||
# FIXME: find a better way to deal with the summary and description constants.
|
||||
static_package_obj.summary['en'] = u'Ar files for %s' % (self.spec.source.name)
|
||||
static_package_obj.description['en'] = u'Ar files for %s' % (self.spec.source.name)
|
||||
static_package_obj.partOf = self.spec.source.partOf
|
||||
for f in ar_files:
|
||||
static_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_install_dir()):], fileType = "library"))
|
||||
|
||||
# append all generated packages to dependencies
|
||||
for p in self.spec.packages:
|
||||
static_package_obj.packageDependencies.append(
|
||||
pisi.dependency.Dependency(package = p.name))
|
||||
|
||||
return static_package_obj
|
||||
|
||||
def generate_debug_package_object(self):
|
||||
debug_files = []
|
||||
for root, dirs, files in os.walk(self.pkg_debug_dir()):
|
||||
for f in files:
|
||||
if f.endswith(ctx.const.debug_file_suffix):
|
||||
debug_files.append(util.join_path(root, f))
|
||||
|
||||
if not len(debug_files):
|
||||
return None
|
||||
|
||||
debug_package_obj = pisi.specfile.Package()
|
||||
debug_package_obj.debug_package = True
|
||||
debug_package_obj.name = self.spec.source.name + ctx.const.debug_name_suffix
|
||||
# FIXME: find a better way to deal with the summary and description constants.
|
||||
debug_package_obj.summary['en'] = u'Debug files for %s' % (self.spec.source.name)
|
||||
debug_package_obj.description['en'] = u'Debug files for %s' % (self.spec.source.name)
|
||||
debug_package_obj.partOf = self.spec.source.partOf + '-debug'
|
||||
for f in debug_files:
|
||||
debug_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_debug_dir()):], fileType = "debug"))
|
||||
|
||||
# append all generated packages to dependencies
|
||||
for p in self.spec.packages:
|
||||
debug_package_obj.packageDependencies.append(
|
||||
pisi.dependency.Dependency(package = p.name))
|
||||
|
||||
return debug_package_obj
|
||||
|
||||
def strip_install_dir(self):
|
||||
"""strip install directory"""
|
||||
ctx.ui.action(_("Stripping files.."))
|
||||
install_dir = self.pkg_install_dir()
|
||||
try:
|
||||
nostrip = self.actionGlobals['NoStrip']
|
||||
util.strip_directory(install_dir, nostrip)
|
||||
except KeyError:
|
||||
util.strip_directory(install_dir)
|
||||
|
||||
def gen_metadata_xml(self, package, build_no=None):
|
||||
"""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"
|
||||
metadata.package.packageFormat = ctx.get_option('package_format')
|
||||
|
||||
size = 0
|
||||
if package.debug_package:
|
||||
d = self.pkg_debug_dir()
|
||||
else:
|
||||
d = self.pkg_install_dir()
|
||||
|
||||
for path in package.files:
|
||||
for p in glob.glob(util.join_path(d, path.path)):
|
||||
size += util.dir_size(p)
|
||||
|
||||
metadata.package.installedSize = size
|
||||
|
||||
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()
|
||||
|
||||
if package.debug_package:
|
||||
install_dir = self.pkg_debug_dir()
|
||||
else:
|
||||
install_dir = self.pkg_install_dir()
|
||||
|
||||
# FIXME: We need to expand globs before trying to calculate hashes
|
||||
# Not on the fly like now.
|
||||
|
||||
# we'll exclude collisions in get_file_hashes. Having a
|
||||
# collisions list is not wrong, we must just handle it :).
|
||||
collisions = check_path_collision(package, self.spec.packages)
|
||||
# FIXME: material collisions after expanding globs could be
|
||||
# reported as errors
|
||||
|
||||
d = {}
|
||||
def add_path(path):
|
||||
# add the files under material path
|
||||
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
|
||||
if ctx.get_option('create_static') \
|
||||
and fpath.endswith(ctx.const.ar_file_suffix) \
|
||||
and not package.name.endswith(ctx.const.static_name_suffix) \
|
||||
and util.is_ar_file(fpath):
|
||||
# if this is an ar file, and this package is not a static package,
|
||||
# don't include this file into the package.
|
||||
continue
|
||||
frpath = util.removepathprefix(install_dir, fpath) # relative path
|
||||
ftype, permanent = get_file_type(frpath, package.files, install_dir)
|
||||
fsize = util.dir_size(fpath)
|
||||
d[frpath] = FileInfo(path=frpath, type=ftype, permanent=permanent,
|
||||
size=fsize, hash=fhash)
|
||||
|
||||
for pinfo in package.files:
|
||||
wildcard_path = util.join_path(install_dir, pinfo.path)
|
||||
for path in glob.glob(wildcard_path):
|
||||
add_path(path)
|
||||
|
||||
for (p, fileinfo) in d.iteritems():
|
||||
files.append(fileinfo)
|
||||
|
||||
files_xml_path = util.join_path(self.pkg_dir(), ctx.const.files_xml)
|
||||
files.write(files_xml_path)
|
||||
self.files = files
|
||||
|
||||
def calc_build_no(self, package_name):
|
||||
"""Calculate build number"""
|
||||
|
||||
def metadata_changed(old_metadata, new_metadata):
|
||||
for key in old_metadata.package.__dict__.keys():
|
||||
if old_metadata.package.__dict__[key] != new_metadata.package.__dict__[key]:
|
||||
if key != "build":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# find previous build in packages dir
|
||||
found = []
|
||||
def locate_old_package(old_package_fn):
|
||||
if util.is_package_name(os.path.basename(old_package_fn), package_name):
|
||||
try:
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
ctx.ui.info(_('(found old version %s)') % old_package_fn)
|
||||
if str(old_pkg.metadata.package.name) != package_name:
|
||||
ctx.ui.warning(_('Skipping %s with wrong pkg name ') %
|
||||
old_package_fn)
|
||||
return
|
||||
old_build = old_pkg.metadata.package.build
|
||||
found.append( (old_package_fn, old_build) )
|
||||
except Error:
|
||||
ctx.ui.warning('Package file %s may be corrupt. Skipping.' % old_package_fn)
|
||||
|
||||
for root, dirs, files in os.walk(ctx.config.packages_dir()):
|
||||
for file in files:
|
||||
locate_old_package(join(root,file))
|
||||
|
||||
outdir=ctx.get_option('output_dir')
|
||||
if not outdir:
|
||||
outdir = '.'
|
||||
for file in [join(outdir,entry) for entry in os.listdir(outdir)]:
|
||||
if os.path.isfile(file):
|
||||
locate_old_package(file)
|
||||
|
||||
if not found:
|
||||
return (1, None)
|
||||
ctx.ui.warning(_('(no previous build found, setting build no to 1.)'))
|
||||
else:
|
||||
a = filter(lambda (x,y): y != None, found)
|
||||
ctx.ui.debug(str(a))
|
||||
if a:
|
||||
# sort in order of increasing build number
|
||||
a.sort(lambda x,y : cmp(x[1],y[1]))
|
||||
old_package_fn = a[-1][0] # get the last one
|
||||
old_build = a[-1][1]
|
||||
|
||||
# compare old files.xml with the new one..
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
|
||||
changed = False
|
||||
fnew = self.files.list
|
||||
fold = old_pkg.files.list
|
||||
fold.sort(lambda x,y : cmp(x.path,y.path))
|
||||
fnew.sort(lambda x,y : cmp(x.path,y.path))
|
||||
|
||||
if len(fnew) != len(fold):
|
||||
changed = True
|
||||
else:
|
||||
for i in range(len(fold)):
|
||||
fo = fold.pop(0)
|
||||
fn = fnew.pop(0)
|
||||
if fo.path != fn.path:
|
||||
changed = True
|
||||
break
|
||||
else:
|
||||
if fo.hash != fn.hash:
|
||||
changed = True
|
||||
break
|
||||
|
||||
if metadata_changed(old_pkg.metadata, self.metadata):
|
||||
changed = True
|
||||
|
||||
self.old_packages.append(os.path.basename(old_package_fn))
|
||||
else: # no old build had a build number
|
||||
old_build = None
|
||||
|
||||
ctx.ui.debug('old build number: %s' % old_build)
|
||||
|
||||
# set build number
|
||||
if old_build is None:
|
||||
ctx.ui.warning(_('(old package lacks a build no, setting build no to 1.)'))
|
||||
return (1, None)
|
||||
elif changed:
|
||||
ctx.ui.info(_('There are changes, incrementing build no to %d') % (old_build + 1))
|
||||
return (old_build + 1, old_build)
|
||||
else:
|
||||
ctx.ui.info(_('There is no change from previous build %d') % old_build)
|
||||
return (old_build, old_build)
|
||||
|
||||
def build_packages(self):
|
||||
"""Build each package defined in PSPEC file. After this process there
|
||||
will be .pisi files hanging around, AS INTENDED ;)"""
|
||||
|
||||
self.fetch_component() # bug 856
|
||||
|
||||
# Strip install directory before building .pisi packages.
|
||||
self.strip_install_dir()
|
||||
|
||||
if ctx.get_option('create_static'):
|
||||
obj = self.generate_static_package_object()
|
||||
if obj:
|
||||
self.spec.packages.append(obj)
|
||||
|
||||
if ctx.config.values.build.generatedebug:
|
||||
obj = self.generate_debug_package_object()
|
||||
if obj:
|
||||
self.spec.packages.append(obj)
|
||||
|
||||
self.new_packages = []
|
||||
self.old_packages = []
|
||||
|
||||
for package in self.spec.packages:
|
||||
old_package_name = None
|
||||
# store additional files
|
||||
c = os.getcwd()
|
||||
os.chdir(self.specdir)
|
||||
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
for afile in package.additionalFiles:
|
||||
src = os.path.join(ctx.const.files_dir, afile.filename)
|
||||
dest = os.path.join(install_dir + os.path.dirname(afile.target), os.path.basename(afile.target))
|
||||
util.copy_file(src, dest)
|
||||
if afile.permission:
|
||||
# mode is octal!
|
||||
os.chmod(dest, int(afile.permission, 8))
|
||||
os.chdir(c)
|
||||
|
||||
ctx.ui.action(_("** Building package %s") % package.name);
|
||||
|
||||
ctx.ui.info(_("Generating %s,") % ctx.const.files_xml)
|
||||
self.gen_files_xml(package)
|
||||
|
||||
ctx.ui.info(_("Generating %s,") % ctx.const.metadata_xml)
|
||||
self.gen_metadata_xml(package)
|
||||
|
||||
# build number
|
||||
if ctx.config.options.ignore_build_no or not ctx.config.values.build.buildno:
|
||||
build_no = old_build_no = None
|
||||
ctx.ui.warning(_('Build number is not available. For repo builds you must enable buildno in pisi.conf.'))
|
||||
else:
|
||||
build_no, old_build_no = self.calc_build_no(package.name)
|
||||
|
||||
self.metadata.package.build = build_no
|
||||
self.metadata.write(util.join_path(self.pkg_dir(), ctx.const.metadata_xml))
|
||||
|
||||
# Calculate new and oldpackage names for buildfarm
|
||||
name = util.package_name(package.name,
|
||||
self.spec.source.version,
|
||||
self.spec.source.release,
|
||||
self.metadata.package.build)
|
||||
|
||||
outdir = ctx.get_option('output_dir')
|
||||
if outdir:
|
||||
name = pisi.util.join_path(outdir, name)
|
||||
self.new_packages.append(name)
|
||||
|
||||
ctx.ui.info(_("Creating PiSi package %s.") % name)
|
||||
|
||||
pkg = Package(name, 'w')
|
||||
|
||||
# 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)
|
||||
|
||||
if ctx.get_option('package_format') == "1.0":
|
||||
for finfo in files.list:
|
||||
orgname = arcname = join("install", finfo.path)
|
||||
if package.debug_package:
|
||||
orgname = join("debug", finfo.path)
|
||||
pkg.add_to_package(orgname, arcname)
|
||||
pkg.close()
|
||||
else: # default package format is 1.1, so make it fallback.
|
||||
ctx.build_leftover = join(self.pkg_dir(), ctx.const.install_tar_lzma)
|
||||
tar = archive.ArchiveTar(ctx.const.install_tar_lzma, "tarlzma")
|
||||
for finfo in files.list:
|
||||
orgname = arcname = join("install", finfo.path)
|
||||
if package.debug_package:
|
||||
orgname = join("debug", finfo.path)
|
||||
tar.add_to_archive(orgname, arcname.lstrip("install"))
|
||||
tar.close()
|
||||
pkg.add_to_package(ctx.const.install_tar_lzma)
|
||||
pkg.close()
|
||||
os.unlink(ctx.const.install_tar_lzma)
|
||||
ctx.build_leftover = None
|
||||
|
||||
os.chdir(c)
|
||||
self.set_state("buildpackages")
|
||||
ctx.ui.info(_("Done."))
|
||||
|
||||
#show the files those are not collected from the install dir
|
||||
if ctx.get_option('show_abandoned_files') or ctx.get_option('debug'):
|
||||
abandoned_files = self.get_abandoned_files()
|
||||
if abandoned_files:
|
||||
ctx.ui.warning(_('Abandoned files under the install dir (%s):') % (install_dir))
|
||||
for f in abandoned_files:
|
||||
ctx.ui.info(' - %s' % (f))
|
||||
else:
|
||||
ctx.ui.warning(_('All of the files under the install dir (%s) has been collected by package(s)')
|
||||
% (install_dir))
|
||||
|
||||
if ctx.config.values.general.autoclean is True:
|
||||
ctx.ui.info(_("Cleaning Build Directory..."))
|
||||
util.clean_dir(self.pkg_dir())
|
||||
else:
|
||||
ctx.ui.info(_("Keeping Build Directory"))
|
||||
|
||||
|
||||
# reset environment variables after build. this one is for
|
||||
# buildfarm actually. buildfarm re-inits pisi for each build
|
||||
# and left environment variables go directly into initial dict
|
||||
# making actionsapi.variables.exportFlags() useless...
|
||||
os.environ = {}
|
||||
os.environ = deepcopy(ctx.config.environ)
|
||||
|
||||
return self.new_packages, self.old_packages
|
||||
|
||||
|
||||
# build functions...
|
||||
|
||||
def build(pspec):
|
||||
if pspec.endswith('.xml'):
|
||||
pb = Builder(pspec)
|
||||
else:
|
||||
pb = Builder.from_name(pspec)
|
||||
return pb.build()
|
||||
|
||||
order = {"none": 0,
|
||||
"fetch": 1,
|
||||
"unpack": 2,
|
||||
"setupaction": 3,
|
||||
"buildaction": 4,
|
||||
"installaction": 5,
|
||||
"buildpackages": 6}
|
||||
|
||||
def __buildState_fetch(pb):
|
||||
# fetch is the first state to run.
|
||||
pb.patch_exists()
|
||||
pb.fetch_source_archive()
|
||||
|
||||
def __buildState_unpack(pb, last):
|
||||
if order[last] < order["fetch"]:
|
||||
__buildState_fetch(pb)
|
||||
pb.unpack_source_archive()
|
||||
pb.apply_patches()
|
||||
|
||||
def __buildState_setupaction(pb, last):
|
||||
if order[last] < order["unpack"]:
|
||||
__buildState_unpack(pb, last)
|
||||
pb.run_setup_action()
|
||||
|
||||
def __buildState_buildaction(pb, last):
|
||||
|
||||
if order[last] < order["setupaction"]:
|
||||
__buildState_setupaction(pb, last)
|
||||
pb.run_build_action()
|
||||
|
||||
def __buildState_installaction(pb, last):
|
||||
|
||||
if order[last] < order["buildaction"]:
|
||||
__buildState_buildaction(pb, last)
|
||||
pb.run_install_action()
|
||||
|
||||
def __buildState_buildpackages(pb, last):
|
||||
|
||||
if order[last] < order["installaction"]:
|
||||
__buildState_installaction(pb, last)
|
||||
pb.build_packages()
|
||||
|
||||
def build_until(pspec, state):
|
||||
if pspec.endswith('.xml'):
|
||||
pb = Builder(pspec)
|
||||
else:
|
||||
pb = Builder.from_name(pspec)
|
||||
|
||||
pb.compile_action_script()
|
||||
|
||||
last = pb.get_state()
|
||||
ctx.ui.info("Last state was %s"%last)
|
||||
|
||||
if not last: last = "none"
|
||||
|
||||
if state == "fetch":
|
||||
__buildState_fetch(pb)
|
||||
return
|
||||
|
||||
if state == "unpack":
|
||||
__buildState_unpack(pb, last)
|
||||
return
|
||||
|
||||
if state == "setup":
|
||||
__buildState_setupaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "build":
|
||||
__buildState_buildaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "install":
|
||||
__buildState_installaction(pb, last)
|
||||
return
|
||||
|
||||
__buildState_buildpackages(pb, last)
|
||||
@@ -1,51 +0,0 @@
|
||||
Index: build.py
|
||||
===================================================================
|
||||
--- build.py (revision 8935)
|
||||
+++ build.py (working copy)
|
||||
@@ -197,13 +197,30 @@
|
||||
# apply the patches and prepare a source directory for build.
|
||||
self.apply_patches()
|
||||
|
||||
+ if ctx.get_option('use_catbox'):
|
||||
+ import catbox
|
||||
+ ret = catbox.run(
|
||||
+ self.sandboxed,
|
||||
+ ["/var/tmp/pisi", "/tmp", "/dev/tty", "/dev/null"],
|
||||
+ logger = self.sandbox_logger
|
||||
+ )
|
||||
+ if ret != 0:
|
||||
+ raise Error, "actions.py borked"
|
||||
+ else:
|
||||
+ self.sandboxed()
|
||||
+
|
||||
+ # after all, we are ready to build/prepare the packages
|
||||
+ return self.build_packages()
|
||||
+
|
||||
+ def sandbox_logger(self, event, data):
|
||||
+ if event == "denied":
|
||||
+ ctx.ui.error("Access denied to '%s'" % data)
|
||||
+
|
||||
+ def sandboxed(self):
|
||||
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"""
|
||||
|
||||
Index: cli/commands.py
|
||||
===================================================================
|
||||
--- cli/commands.py (revision 8935)
|
||||
+++ cli/commands.py (working copy)
|
||||
@@ -396,6 +396,8 @@
|
||||
default=False, help=_("don't install build dependencies, fail if a build dependency is present"))
|
||||
self.parser.add_option("-F", "--package-format", action="store", default='1.1',
|
||||
help=_("pisi package format"))
|
||||
+ self.parser.add_option("", "--use-catbox", action="store_true",
|
||||
+ default=False, help=_("use catbox"))
|
||||
|
||||
def run(self):
|
||||
if not self.args:
|
||||
@@ -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.
|
||||
@@ -1,158 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import locale
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.ui import UI
|
||||
import pisi.ui as ui
|
||||
import pisi.util as util
|
||||
from pisi.cli.colors import colorize
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
|
||||
def printu(obj, err = False):
|
||||
if not isinstance(obj, unicode):
|
||||
obj = unicode(obj)
|
||||
if err:
|
||||
out = sys.stderr
|
||||
else:
|
||||
out = sys.stdout
|
||||
out.write(obj.encode('utf-8'))
|
||||
out.flush()
|
||||
|
||||
class CLI(UI):
|
||||
"Command Line Interface"
|
||||
|
||||
def __init__(self, show_debug = False, show_verbose = False):
|
||||
super(CLI, self).__init__(show_debug, show_verbose)
|
||||
|
||||
def close(self):
|
||||
util.xterm_title_reset()
|
||||
|
||||
def output(self, msg, err = False, verbose = False):
|
||||
if (verbose and self.show_verbose) or (not verbose):
|
||||
if type(msg)==type(unicode()):
|
||||
msg = msg.encode('utf-8')
|
||||
if err:
|
||||
out = sys.stderr
|
||||
else:
|
||||
out = sys.stdout
|
||||
out.write(msg)
|
||||
out.flush()
|
||||
|
||||
def info(self, msg, verbose = False, noln = False):
|
||||
# TODO: need to look at more kinds of info messages
|
||||
# let's cheat from KDE :)
|
||||
if not noln:
|
||||
msg = '%s\n' % msg
|
||||
self.output(unicode(msg), verbose=verbose)
|
||||
|
||||
def warning(self, msg, verbose = False):
|
||||
msg = unicode(msg)
|
||||
if ctx.log:
|
||||
ctx.log.warning(msg)
|
||||
if ctx.get_option('no_color'):
|
||||
self.output(_('Warning: ') + msg + '\n', err=True, verbose=verbose)
|
||||
else:
|
||||
self.output(colorize(msg + '\n', 'purple'), err=True, verbose=verbose)
|
||||
|
||||
def error(self, msg):
|
||||
msg = unicode(msg)
|
||||
if ctx.log:
|
||||
ctx.log.error(msg)
|
||||
if ctx.get_option('no_color'):
|
||||
self.output(_('Error: ') + msg + '\n', err=True)
|
||||
else:
|
||||
self.output(colorize(msg + '\n', 'red'), err=True)
|
||||
|
||||
def action(self, msg, verbose = False):
|
||||
#TODO: this seems quite redundant?
|
||||
msg = unicode(msg)
|
||||
if ctx.log:
|
||||
ctx.log.info(msg)
|
||||
self.output(colorize(msg + '\n', 'green'))
|
||||
|
||||
def choose(self, msg, opts):
|
||||
print msg
|
||||
for i in range(0,len(opts)):
|
||||
print i + 1, opts(i)
|
||||
while True:
|
||||
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
|
||||
try:
|
||||
opt = int(s)
|
||||
if 1 <= opt and opt <= len(opts):
|
||||
return opts(opt-1)
|
||||
except (Exception,e):
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
msg = unicode(msg)
|
||||
if ctx.config.options and ctx.config.options.yes_all:
|
||||
return True
|
||||
while True:
|
||||
import re
|
||||
yesexpr = re.compile(locale.nl_langinfo(locale.YESEXPR))
|
||||
|
||||
prompt = msg + colorize(_(' (yes/no)'), 'red')
|
||||
s = raw_input(prompt.encode('utf-8'))
|
||||
if yesexpr.search(s):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def display_progress(self, **ka):
|
||||
totalsize = '%.1f %s' % pisi.util.human_readable_size(ka['total_size'])
|
||||
out = '\r%-30.30s (%s)%3d%% %9.2f %s [%s]' % \
|
||||
(ka['filename'], totalsize, ka['percent'],
|
||||
ka['rate'], ka['symbol'], ka['eta'])
|
||||
self.output(out)
|
||||
if ka['percent'] == 100:
|
||||
self.output(colorize(_(' [complete]\n'), 'gray'))
|
||||
|
||||
def status(self, msg = None):
|
||||
if msg:
|
||||
msg = unicode(msg)
|
||||
self.output(colorize(msg + '\n', 'purple'))
|
||||
util.xterm_title(msg)
|
||||
|
||||
def notify(self, event, **keywords):
|
||||
if event == ui.installed:
|
||||
msg = _('Installed %s') % keywords['package'].name
|
||||
elif event == ui.removed:
|
||||
msg = _('Removed %s') % keywords['package'].name
|
||||
elif event == ui.upgraded:
|
||||
msg = _('Upgraded %s') % keywords['package'].name
|
||||
elif event == ui.configured:
|
||||
msg = _('Configured %s') % keywords['package'].name
|
||||
elif event == ui.extracting:
|
||||
msg = _('Extracting the files of %s') % keywords['package'].name
|
||||
else:
|
||||
msg = None
|
||||
if msg:
|
||||
self.output(colorize(msg + '\n', 'cyan'))
|
||||
if ctx.log:
|
||||
ctx.log.info(msg)
|
||||
@@ -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
@@ -1,111 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.cli
|
||||
from pisi.cli import printu
|
||||
from pisi.uri import URI
|
||||
from pisi.cli.commands import *
|
||||
|
||||
class ParserError(pisi.Exception):
|
||||
pass
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class PreParser(OptionParser):
|
||||
"""consumes any options, and finds arguments from command line"""
|
||||
|
||||
def __init__(self, version):
|
||||
OptionParser.__init__(self, usage=usage_text, version=version)
|
||||
|
||||
def error(self, msg):
|
||||
raise ParserError, msg
|
||||
|
||||
def parse_args(self, args=None):
|
||||
self.opts = []
|
||||
self.rargs = self._get_args(args)
|
||||
self._process_args()
|
||||
return (self.opts, self.args)
|
||||
|
||||
def _process_args(self):
|
||||
args = []
|
||||
rargs = self.rargs
|
||||
if not self.allow_interspersed_args:
|
||||
first_arg = False
|
||||
while rargs:
|
||||
arg = rargs[0]
|
||||
def option():
|
||||
if not self.allow_interspersed_args and first_arg:
|
||||
self.error(_('Options must precede non-option arguments'))
|
||||
arg = rargs[0]
|
||||
if arg.startswith('--'):
|
||||
self.opts.append(arg[2:])
|
||||
else:
|
||||
self.opts.append(arg[1:])
|
||||
del rargs[0]
|
||||
return
|
||||
# We handle bare "--" explicitly, and bare "-" is handled by the
|
||||
# standard arg handler since the short arg case ensures that the
|
||||
# len of the opt string is greater than 1.
|
||||
if arg == "--":
|
||||
del rargs[0]
|
||||
break
|
||||
elif arg[0:2] == "--":
|
||||
# process a single long option (possibly with value(s))
|
||||
option()
|
||||
elif arg[:1] == "-" and len(arg) > 1:
|
||||
# process a cluster of short options (possibly with
|
||||
# value(s) for the last one only)
|
||||
option()
|
||||
else: # then it must be an argument
|
||||
args.append(arg)
|
||||
del rargs[0]
|
||||
self.args = args
|
||||
|
||||
|
||||
class PisiCLI(object):
|
||||
|
||||
def __init__(self, orig_args=None):
|
||||
# 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(args=orig_args)
|
||||
if len(args)==0: # more explicit than using IndexError
|
||||
if 'version' in opts:
|
||||
self.parser.print_version()
|
||||
sys.exit(0)
|
||||
elif 'help' in opts or 'h' in opts:
|
||||
self.die()
|
||||
raise Error(_('No command given'))
|
||||
cmd_name = args[0]
|
||||
except ParserError:
|
||||
raise Error(_('Command line parsing error'))
|
||||
|
||||
self.command = Command.get_command(cmd_name, args=orig_args)
|
||||
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()
|
||||
@@ -1,127 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005-2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
def make_com():
|
||||
try:
|
||||
import comar
|
||||
if not wait_comar():
|
||||
raise comar.Error
|
||||
if ctx.comar_sockname:
|
||||
com = comar.Link(sockname=ctx.comar_sockname)
|
||||
else:
|
||||
com = comar.Link()
|
||||
return com
|
||||
except ImportError:
|
||||
raise Error(_("comar package is not fully installed"))
|
||||
except comar.Error:
|
||||
raise Error(_("cannot connect to comar"))
|
||||
|
||||
def wait_comar():
|
||||
import socket, time
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
timeout = 5
|
||||
while timeout > 0:
|
||||
try:
|
||||
if ctx.comar_sockname:
|
||||
sock.connect(ctx.comar_sockname)
|
||||
else:
|
||||
sock.connect("/var/run/comar.socket")
|
||||
return True
|
||||
except:
|
||||
timeout -= 0.2
|
||||
time.sleep(0.2)
|
||||
return False
|
||||
|
||||
def wait_for_result(com, package_name=None):
|
||||
multiple = False
|
||||
while 1:
|
||||
try:
|
||||
reply = com.read_cmd()
|
||||
except:
|
||||
# Comar postInstall does a "service comar restart" which cuts
|
||||
# our precious communication link, so we waitsss
|
||||
if package_name == "comar":
|
||||
if not wait_comar():
|
||||
raise Error, _("Could not restart comar")
|
||||
return
|
||||
else:
|
||||
raise Error, _("connection with comar unexpectedly closed")
|
||||
|
||||
cmd = reply[0]
|
||||
if cmd == com.RESULT and not multiple:
|
||||
return
|
||||
elif cmd == com.NONE and not multiple:
|
||||
# no post/pre function, that is ok
|
||||
return
|
||||
elif cmd == com.RESULT_START:
|
||||
multiple = True
|
||||
elif cmd == com.RESULT_END:
|
||||
return
|
||||
elif cmd == com.FAIL:
|
||||
e = _("Configuration error: %s") % reply[2]
|
||||
raise Error, e
|
||||
elif cmd == com.ERROR:
|
||||
e = _("Script error: %s") % reply[2]
|
||||
raise Error, e
|
||||
elif cmd == com.DENIED:
|
||||
raise Error, _("comar denied our access")
|
||||
|
||||
def post_install(package_name, provided_scripts, scriptpath, metapath, filepath):
|
||||
ctx.ui.info(_("Configuring package"))
|
||||
self_post = False
|
||||
com = make_com()
|
||||
|
||||
for script in provided_scripts:
|
||||
ctx.ui.debug(_("Registering %s comar script") % script.om)
|
||||
if script.om == "System.Package":
|
||||
self_post = True
|
||||
com.register(script.om, package_name, os.path.join(scriptpath, script.script))
|
||||
wait_for_result(com)
|
||||
|
||||
ctx.ui.debug(_("Calling post install handlers"))
|
||||
com.call("System.PackageHandler.setupPackage", [ "metapath", metapath, "filepath", filepath ])
|
||||
wait_for_result(com)
|
||||
|
||||
if self_post:
|
||||
ctx.ui.debug(_("Running package's post install script"))
|
||||
com.call_package("System.Package.postInstall", package_name)
|
||||
wait_for_result(com, package_name)
|
||||
|
||||
def pre_remove(package_name, metapath, filepath):
|
||||
ctx.ui.info(_("Configuring package for removal"))
|
||||
com = make_com()
|
||||
|
||||
ctx.ui.debug(_("Running package's pre remove script"))
|
||||
com.call_package("System.Package.preRemove", package_name)
|
||||
wait_for_result(com)
|
||||
|
||||
ctx.ui.debug(_("Calling pre remove handlers"))
|
||||
com.call("System.PackageHandler.cleanupPackage", [ "metapath", metapath, "filepath", filepath ])
|
||||
wait_for_result(com)
|
||||
|
||||
ctx.ui.debug(_("Unregistering comar scripts"))
|
||||
com.remove(package_name)
|
||||
wait_for_result(com)
|
||||
@@ -1,210 +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 gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.pxml.xmlfile as xmlfile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.itembyrepodb import ItemByRepoDB
|
||||
import pisi.itembyrepodb as itembyrepodb
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
|
||||
class Distribution(xmlfile.XmlFile):
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_SourceName = [autoxml.Text, autoxml.mandatory] # name of distribution (source)
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Version = [autoxml.Text, autoxml.optional]
|
||||
t_Type = [autoxml.Text, autoxml.mandatory]
|
||||
t_Dependencies = [ [autoxml.Text], autoxml.optional, "Dependencies/Distribution"]
|
||||
|
||||
t_BinaryName = [autoxml.Text, autoxml.optional] # name of repository (binary distro)
|
||||
t_Architecture = [autoxml.Text, autoxml.optional] # architecture identifier
|
||||
|
||||
|
||||
class Component(xmlfile.XmlFile):
|
||||
"representation for component declarations"
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory] # fully qualified name
|
||||
|
||||
# component name in other languages, for instance in Turkish
|
||||
# LocalName for system.base could be sistem.taban or "Taban Sistem",
|
||||
# this could be useful for GUIs
|
||||
|
||||
t_LocalName = [autoxml.LocalText, autoxml.mandatory]
|
||||
|
||||
# Information about the component
|
||||
t_Summary = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
#t_Icon = [autoxml.Binary, autoxml.mandatory]
|
||||
|
||||
# Dependencies to other components
|
||||
t_Dependencies = [ [autoxml.String], autoxml.optional, "Dependencies/Component"]
|
||||
|
||||
# the parts of this component.
|
||||
# to be filled by the component database, thus it is optional.
|
||||
t_Packages = [ [autoxml.String], autoxml.optional, "Parts/Package"]
|
||||
|
||||
t_Sources = [ [autoxml.String], autoxml.optional, "Parts/Source"]
|
||||
|
||||
# TODO: this is probably not necessary since we use fully qualified
|
||||
# module names (like in Java)
|
||||
#t_PartOf = [autoxml.Text, autoxml.mandatory]
|
||||
|
||||
#FIXME: recursive declarations do not work!
|
||||
#class ComponentTree(xmlfile.XmlFile):
|
||||
# "index representation for the component structure"
|
||||
#
|
||||
# __metaclass__ = autoxml.autoxml
|
||||
#
|
||||
# tag = "Component"
|
||||
#
|
||||
# t_Name = [autoxml.Text, autoxml.mandatory] # fully qualified name
|
||||
# #t_Icon = [autoxml.Binary, autoxml.mandatory]
|
||||
# t_Dependencies = [ [autoxml.Text], autoxml.optional, "Component"]
|
||||
# #t_Parts = [ [pisi.component.ComponentTree], autoxml.optional, "Component"]
|
||||
|
||||
class ComponentDB(object):
|
||||
"""a database of components"""
|
||||
|
||||
#FIXME: we might need a database per repo in the future
|
||||
def __init__(self):
|
||||
self.d = ItemByRepoDB('component')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def destroy(self):
|
||||
self.d.destroy()
|
||||
|
||||
def has_component(self, name, repo = pisi.itembyrepodb.repos, txn = None):
|
||||
#name = shelve.LockedDBShelf.encodekey(name)
|
||||
name = str(name)
|
||||
return self.d.has_key(name, repo, txn)
|
||||
|
||||
def get_component(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item(name, repo, txn=txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise Error(_('Component %s not found') % name)
|
||||
|
||||
def get_component_repo(self, name, repo=None, txn = None):
|
||||
#name = shelve.LockedDBShelf.encodekey(name)
|
||||
try:
|
||||
return self.d.get_item_repo(name, repo, txn=txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise Error(_('Component %s not found') % name)
|
||||
|
||||
def get_union_comp(self, name, txn = None, repo = pisi.itembyrepodb.repos ):
|
||||
"""get a union of all repository components packages, not just the first repo in order.
|
||||
get only basic repo info from the first repo"""
|
||||
def proc(txn):
|
||||
s = self.d.d.get(name, txn=txn)
|
||||
pkgs = set()
|
||||
srcs = set()
|
||||
for repostr in self.d.order(repo = repo):
|
||||
if s.has_key(repostr):
|
||||
pkgs |= set(s[repostr].packages)
|
||||
srcs |= set(s[repostr].sources)
|
||||
comp = self.get_component(name)
|
||||
comp.packages = list(pkgs)
|
||||
comp.sources = list(srcs)
|
||||
return comp
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def list_components(self, repo=None):
|
||||
return self.d.list(repo)
|
||||
|
||||
def update_component(self, component, repo, txn = None):
|
||||
def proc(txn):
|
||||
if self.has_component(component.name, repo, txn):
|
||||
# preserve the list of packages
|
||||
component.packages = self.d.get_item(component.name, repo, txn).packages
|
||||
self.d.add_item(component.name, component, repo, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def add_package(self, component_name, package, repo, txn = None):
|
||||
def proc(txn):
|
||||
assert component_name
|
||||
if self.has_component(component_name, repo, txn):
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
else:
|
||||
component = Component( name = component_name )
|
||||
if not package in component.packages:
|
||||
component.packages.append(package)
|
||||
self.d.add_item(component_name, component, repo, txn) # update
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_package(self, component_name, package, repo = None, txn = None):
|
||||
def proc(txn, repo):
|
||||
if not self.has_component(component_name, repo, txn):
|
||||
raise Error(_('Information for component %s not available') % component_name)
|
||||
if not repo:
|
||||
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
if package in component.packages:
|
||||
component.packages.remove(package)
|
||||
self.d.add_item(component_name, component, repo, txn) # update
|
||||
|
||||
ctx.txn_proc(lambda x: proc(txn, repo), txn)
|
||||
|
||||
def add_spec(self, component_name, spec, repo, txn = None):
|
||||
def proc(txn):
|
||||
assert component_name
|
||||
if self.has_component(component_name, repo, txn):
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
else:
|
||||
component = Component( name = component_name )
|
||||
if not spec in component.sources:
|
||||
component.sources.append(spec)
|
||||
self.d.add_item(component_name, component, repo, txn) # update
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_spec(self, component_name, spec, repo = None, txn = None):
|
||||
def proc(txn, repo):
|
||||
if not self.has_component(component_name, repo, txn):
|
||||
raise Error(_('Information for component %s not available') % component_name)
|
||||
if not repo:
|
||||
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
if spec in component.sources:
|
||||
component.sources.remove(spec)
|
||||
self.d.add_item(component_name, component, repo, txn) # update
|
||||
|
||||
ctx.txn_proc(lambda x: proc(txn, repo), txn)
|
||||
|
||||
def clear(self, txn = None):
|
||||
self.d.clear(txn)
|
||||
|
||||
def remove_component(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
self.d.remove_item(name, repo, txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
self.d.remove_repo(repo, txn=txn)
|
||||
@@ -1,119 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
PiSi Configuration module is used for gathering and providing
|
||||
regular PiSi configurations.
|
||||
"""
|
||||
|
||||
import os
|
||||
from copy import deepcopy
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.configfile import ConfigurationFile
|
||||
import pisi.util
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Options(object):
|
||||
def __getattr__(self, name):
|
||||
if not self.__dict__.has_key(name):
|
||||
return None
|
||||
else:
|
||||
return self.__dict__[name]
|
||||
|
||||
class Config(object):
|
||||
"""Config Singleton"""
|
||||
|
||||
def __init__(self, options = Options()):
|
||||
self.options = options
|
||||
self.values = ConfigurationFile("/etc/pisi/pisi.conf")
|
||||
|
||||
destdir = self.get_option('destdir')
|
||||
if destdir:
|
||||
if destdir.strip().startswith('/'):
|
||||
self.destdir = destdir
|
||||
else:
|
||||
self.destdir = join(os.getcwd(), destdir)
|
||||
else:
|
||||
self.destdir = self.values.general.destinationdirectory
|
||||
|
||||
if not os.path.exists(self.destdir):
|
||||
ctx.ui.warning( _('Destination directory %s does not exist. Creating it.') % self.destdir)
|
||||
os.makedirs(self.destdir)
|
||||
|
||||
# get the initial environment variables. this is needed for
|
||||
# build process.
|
||||
self.environ = deepcopy(os.environ)
|
||||
|
||||
|
||||
def get_option(self, opt):
|
||||
if self.options:
|
||||
if hasattr(self.options, opt):
|
||||
return getattr(self.options, opt)
|
||||
return None
|
||||
|
||||
# directory accessor functions
|
||||
# here is how it goes
|
||||
# x_dir: system wide directory for storing info type x
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def dest_dir(self):
|
||||
return self.destdir
|
||||
|
||||
def subdir(self, path):
|
||||
dir = join(self.dest_dir(), path)
|
||||
pisi.util.check_dir(dir)
|
||||
return dir
|
||||
|
||||
def lib_dir(self):
|
||||
return self.subdir(self.values.dirs.lib_dir)
|
||||
|
||||
def db_dir(self):
|
||||
return self.subdir(self.values.dirs.db_dir)
|
||||
|
||||
def archives_dir(self):
|
||||
return self.subdir(self.values.dirs.archives_dir)
|
||||
|
||||
def packages_dir(self):
|
||||
return self.subdir(self.values.dirs.packages_dir)
|
||||
|
||||
def index_dir(self):
|
||||
return self.subdir(self.values.dirs.index_dir)
|
||||
|
||||
def tmp_dir(self):
|
||||
sysdir = self.subdir(self.values.dirs.tmp_dir)
|
||||
if os.environ.has_key('USER'):
|
||||
userdir = self.subdir('/tmp/pisi-' + os.environ['USER'])
|
||||
else:
|
||||
userdir = self.subdir('/tmp/pisi-root')
|
||||
# check write access
|
||||
if os.access(sysdir, os.W_OK):
|
||||
return sysdir
|
||||
else:
|
||||
return userdir
|
||||
|
||||
# bu dizini neden kullanıyoruz? Yalnızca index.py içerisinde
|
||||
# kullanılıyor ama /var/tmp/pisi/install gibi bir dizine niye
|
||||
# ihtiyacımız var? (baris)
|
||||
def install_dir(self):
|
||||
return self.tmp_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
#TODO: remove this
|
||||
config = Config()
|
||||
@@ -1,160 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# 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 = /
|
||||
#autoclean = False
|
||||
#
|
||||
#[build]
|
||||
#host = i686-pc-linux-gnu
|
||||
#generateDebug = False
|
||||
#jobs = "-j1"
|
||||
#CFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#CXXFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#LDFLAGS=
|
||||
#buildno=True # necessary for generating build nos
|
||||
#buildhelper = None / ccache / icecream
|
||||
#compressionlevel = 7
|
||||
#fallback = "ftp://ftp.pardus.org.tr/pub/pisi/source"
|
||||
#
|
||||
#[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
|
||||
#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 = "1.1"
|
||||
http_proxy = os.getenv("HTTP_PROXY") or None
|
||||
https_proxy = os.getenv("HTTPS_PROXY") or None
|
||||
ftp_proxy = os.getenv("FTP_PROXY") or None
|
||||
|
||||
class BuildDefaults:
|
||||
"""Default values for [build] section"""
|
||||
host = "i686-pc-linux-gnu"
|
||||
jobs = "-j1"
|
||||
generateDebug = False
|
||||
cflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
cxxflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
ldflags = ""
|
||||
buildno = False
|
||||
buildhelper = None
|
||||
compressionlevel = 7
|
||||
fallback = "ftp://ftp.pardus.org.tr/pub/pisi/source"
|
||||
|
||||
class DirectoriesDefaults:
|
||||
"Default values for [directories] section"
|
||||
lib_dir = "/var/lib/pisi"
|
||||
db_dir = "/var/db/pisi"
|
||||
archives_dir = "/var/cache/pisi/archives"
|
||||
packages_dir = "/var/cache/pisi/packages"
|
||||
index_dir = "/var/lib/pisi/index"
|
||||
tmp_dir = "/var/tmp/pisi"
|
||||
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 = DirectoriesDefaults
|
||||
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:
|
||||
# all values are returned as string types by ConfigParser.
|
||||
# evaluate "True" or "False" strings to boolean.
|
||||
if item[1] in ["True", "False", "None"]:
|
||||
return eval(item[1])
|
||||
else:
|
||||
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)
|
||||
@@ -1,110 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""PiSi constants.
|
||||
If you have a "magic" constant value this is where it should be
|
||||
defined."""
|
||||
|
||||
# Author: Baris Metin <baris@pardus.org.tr
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
class _constant:
|
||||
"Constant members implementation"
|
||||
class ConstError(TypeError):
|
||||
pass
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, _("Can't rebind constant: %s") % name
|
||||
# Binding an attribute once to a const is available
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __delattr__(self, name):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, _("Can't unbind constant: %s") % name
|
||||
# we don't have an attribute by this name
|
||||
raise NameError, name
|
||||
|
||||
class Constants:
|
||||
"Pisi Constants Singleton"
|
||||
|
||||
__c = _constant()
|
||||
|
||||
def __init__(self):
|
||||
# suffix for package names
|
||||
self.__c.package_suffix = ".pisi"
|
||||
|
||||
# suffix for lzma
|
||||
self.__c.lzma_suffix = ".lzma"
|
||||
|
||||
# suffix for auto generated debug packages
|
||||
self.__c.debug_name_suffix = "-debug"
|
||||
self.__c.debug_file_suffix = ".debug"
|
||||
|
||||
# suffix for auto generated ar packages
|
||||
self.__c.static_name_suffix = "-static" # an admissible use of constant
|
||||
self.__c.ar_file_suffix = ".a"
|
||||
|
||||
# directory suffixes for build
|
||||
self.__c.work_dir_suffix = "/work" # these, too, because we might wanna change 'em
|
||||
self.__c.install_dir_suffix = "/install"
|
||||
self.__c.debug_dir_suffix = "/debug"
|
||||
|
||||
# file/directory names
|
||||
#note: these don't seem very well, constants are used
|
||||
#when it is easier/more meaningful to write the constant name, or
|
||||
#when the constant is bound to change later on.
|
||||
#in some places literals are just as good, for instance
|
||||
#when constant is the same as string. readability is important...
|
||||
self.__c.actions_file = "actions.py"
|
||||
self.__c.files_dir = "files"
|
||||
self.__c.metadata_dir = "metadata"
|
||||
self.__c.comar_dir = "comar"
|
||||
self.__c.files_xml = "files.xml"
|
||||
self.__c.metadata_xml = "metadata.xml"
|
||||
self.__c.install_tar = "install.tar"
|
||||
self.__c.install_tar_lzma = "install.tar.lzma"
|
||||
self.__c.mirrors_conf = "/etc/pisi/mirrors.conf"
|
||||
|
||||
#file/directory permissions
|
||||
self.__c.umask = 0022
|
||||
|
||||
# functions in actions_file
|
||||
self.__c.setup_func = "setup"
|
||||
self.__c.build_func = "build"
|
||||
self.__c.install_func = "install"
|
||||
|
||||
# file types
|
||||
# FIXME: these seem redundant
|
||||
self.__c.doc = "doc"
|
||||
self.__c.man = "man"
|
||||
self.__c.info = "info"
|
||||
self.__c.conf = "config"
|
||||
self.__c.header = "header"
|
||||
self.__c.library = "library"
|
||||
self.__c.executable = "executable"
|
||||
self.__c.data = "data"
|
||||
self.__c.localedata = "localedata"
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.__c, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
setattr(self.__c, attr, value)
|
||||
|
||||
def __delattr__(self, attr):
|
||||
delattr(self.__c, attr)
|
||||
@@ -1,78 +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.
|
||||
#
|
||||
|
||||
# global variables here
|
||||
|
||||
import pisi.constants
|
||||
|
||||
const = pisi.constants.Constants()
|
||||
|
||||
config = None
|
||||
|
||||
log = None
|
||||
|
||||
use_mdom = False
|
||||
|
||||
def get_option(opt):
|
||||
return config and config.get_option(opt)
|
||||
|
||||
# default UI is CLI
|
||||
ui = None # not now
|
||||
|
||||
# stdout, stderr for PiSi API
|
||||
stdout = None
|
||||
stderr = None
|
||||
|
||||
dbenv = None
|
||||
installdb = None
|
||||
packagedb = None
|
||||
repodb = None
|
||||
invidx = None
|
||||
|
||||
comar = None
|
||||
comar_sockname = None
|
||||
|
||||
initialized = False
|
||||
|
||||
# Bug #2879
|
||||
# FIXME: Maybe we can create a simple rollback mechanism. There are other
|
||||
# places which need this, too.
|
||||
# this is needed in build process to clean after if something goes wrong.
|
||||
build_leftover = None
|
||||
|
||||
#def register(_impl):
|
||||
# """ Register a UI implementation"""
|
||||
# ui = _impl
|
||||
|
||||
import bsddb3.db as db
|
||||
|
||||
# copy of DBShelve.txn_proc, the only difference is it doesn't need a shelf object
|
||||
#FIXME: remove this redundancy, and move all this stuff to database.py
|
||||
def txn_proc(proc, txn = None):
|
||||
# can be used to txn protect a method automatically
|
||||
if not txn:
|
||||
if dbenv:
|
||||
autotxn = dbenv.txn_begin()
|
||||
try:
|
||||
retval = proc(autotxn)
|
||||
except db.DBError, e:
|
||||
autotxn.abort()
|
||||
raise e
|
||||
except Exception, e:
|
||||
autotxn.abort()
|
||||
raise e
|
||||
autotxn.commit()
|
||||
else:
|
||||
retval = proc(None)
|
||||
return retval
|
||||
else:
|
||||
return proc(txn)
|
||||
@@ -1,321 +0,0 @@
|
||||
#------------------------------------------------------------------------
|
||||
# Copyright (c) 1997-2001 by Total Control Software
|
||||
# All Rights Reserved
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Module Name: dbShelve.py
|
||||
#
|
||||
# Description: A reimplementation of the standard shelve.py that
|
||||
# forces the use of cPickle, and DB.
|
||||
#
|
||||
# Creation Date: 11/3/97 3:39:04PM
|
||||
#
|
||||
# License: This is free software. You may use this software for any
|
||||
# purpose including modification/redistribution, so long as
|
||||
# this header remains intact and that you do not claim any
|
||||
# rights of ownership or authorship of this software. This
|
||||
# software has been tested, but no warranty is expressed or
|
||||
# implied.
|
||||
#
|
||||
# 13-Dec-2000: Updated to be used with the new bsddb3 package.
|
||||
# Added DBShelfCursor class.
|
||||
#
|
||||
# 13-Dec-2005: Minor hacking by exa to make it work better with PiSi
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
"""Manage shelves of pickled objects using bsddb database files for the
|
||||
storage.
|
||||
|
||||
Add transaction processing by default to dictionary ops also
|
||||
Also other minor improvements -- exa
|
||||
|
||||
Now added support for overriding the marshalling method
|
||||
"""
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
import cPickle
|
||||
import bsddb3.db as db
|
||||
import bsddb3.dbobj as dbobj
|
||||
import string
|
||||
import sys
|
||||
import traceback
|
||||
import pisi
|
||||
|
||||
class CodingError(pisi.Error):
|
||||
pass
|
||||
|
||||
class DBShelf:
|
||||
"""A shelf to hold pickled objects, built upon a bsddb DB object. It
|
||||
automatically pickles/unpickles data objects going to/from the DB.
|
||||
"""
|
||||
def __init__(self, dbenv = None):
|
||||
self.dbenv = dbenv
|
||||
# how lame is bsddb3?
|
||||
if self.dbenv:
|
||||
self.db = dbobj.DB(dbenv)
|
||||
else:
|
||||
self.db = db.DB(None)
|
||||
|
||||
# it is better to explicitly close a shelf
|
||||
#def __del__(self):
|
||||
# self.close()
|
||||
|
||||
allowed_chars = string.letters + string.digits + '-'
|
||||
def check_key(key):
|
||||
return pisi.util.all(lambda x: x in allowed_chars, key)
|
||||
|
||||
def has_key(self, key, txn = None):
|
||||
if txn:
|
||||
return self.db.has_key(key, txn)
|
||||
else:
|
||||
return self.db.has_key(key)
|
||||
|
||||
def txn_proc(self, proc, txn):
|
||||
# can be used to txn protect a method automatically
|
||||
if not txn:
|
||||
if self.dbenv:
|
||||
autotxn = self.dbenv.txn_begin()
|
||||
try:
|
||||
retval = proc(autotxn)
|
||||
except db.DBError, e:
|
||||
autotxn.abort()
|
||||
raise e
|
||||
except Exception, e:
|
||||
autotxn.abort()
|
||||
#e.args += tuple(traceback.format_tb(sys.exc_traceback))
|
||||
raise e
|
||||
autotxn.commit()
|
||||
else: # execute without transactions
|
||||
retval = proc(None)
|
||||
return retval
|
||||
else:
|
||||
return proc(txn)
|
||||
|
||||
def decode(self, data):
|
||||
try:
|
||||
return cPickle.loads(data)
|
||||
except cPickle.UnpicklingError:
|
||||
raise CodingError()
|
||||
|
||||
def encode(self, obj):
|
||||
return cPickle.dumps(obj, 1)
|
||||
|
||||
def clear(self, txn = None):
|
||||
def proc(txn):
|
||||
for x in self.keys(txn):
|
||||
self.db.delete(x, txn)
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
def delete(self, x, txn):
|
||||
def proc(txn):
|
||||
self.db.delete(x, txn)
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
# another lame pythonic implementation method:
|
||||
#def __getattr__(self, name):
|
||||
# """Many methods we can just pass through to the DB object.
|
||||
# (See below)
|
||||
# """
|
||||
# print 'aptal bsddb3', name
|
||||
# return getattr(self.db, name)
|
||||
|
||||
#-----------------------------------
|
||||
# Dictionary access methods
|
||||
|
||||
def __len__(self):
|
||||
return len(self.db)
|
||||
|
||||
def __getitem__(self, key):
|
||||
def proc(txn):
|
||||
data = self.db.get(key)
|
||||
return self.decode(data)
|
||||
return self.txn_proc(proc, None)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# hyperdandik transactions
|
||||
def proc(txn):
|
||||
data = self.encode(value)
|
||||
self.db.put(key,data,txn)
|
||||
return self.txn_proc(proc, None)
|
||||
|
||||
def __delitem__(self, key):
|
||||
txn = self.dbenv.txn_begin()
|
||||
try:
|
||||
self.db.delete(key, txn)
|
||||
except db.DBError, e:
|
||||
txn.abort()
|
||||
raise e
|
||||
txn.commit()
|
||||
|
||||
def keys(self, txn=None):
|
||||
if txn != None:
|
||||
return self.db.keys(txn)
|
||||
else:
|
||||
return self.db.keys()
|
||||
|
||||
def items(self, txn=None):
|
||||
if txn != None:
|
||||
items = self.db.items(txn)
|
||||
else:
|
||||
items = self.db.items()
|
||||
newitems = []
|
||||
|
||||
for k, v in items:
|
||||
newitems.append( (k, self.decode(v) ) )
|
||||
return newitems
|
||||
|
||||
def values(self, txn=None):
|
||||
if txn != None:
|
||||
values = self.db.values(txn)
|
||||
else:
|
||||
values = self.db.values()
|
||||
|
||||
return map(lambda x : self.decode(x), values)
|
||||
|
||||
#-----------------------------------
|
||||
# Other methods
|
||||
|
||||
def __append(self, value, txn=None):
|
||||
data = self.encode(value)
|
||||
return self.db.append(data, txn)
|
||||
|
||||
def append(self, value, txn=None):
|
||||
if self.get_type() != db.DB_RECNO:
|
||||
self.append = self.__append
|
||||
return self.append(value, txn=txn)
|
||||
raise db.DBError, "append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO"
|
||||
|
||||
|
||||
def associate(self, secondaryDB, callback, flags=0):
|
||||
def _shelf_callback(priKey, priData, realCallback=callback):
|
||||
data = self.decode(priData)
|
||||
return realCallback(priKey, data)
|
||||
return self.db.associate(secondaryDB, _shelf_callback, flags)
|
||||
|
||||
|
||||
#def get(self, key, default=None, txn=None, flags=0):
|
||||
def get(self, *args, **kw):
|
||||
# We do it with *args and **kw so if the default value wasn't
|
||||
# given nothing is passed to the extension module. That way
|
||||
# an exception can be raised if set_get_returns_none is turned
|
||||
# off.
|
||||
data = apply(self.db.get, args, kw)
|
||||
try:
|
||||
return self.decode(data)
|
||||
except (TypeError, CodingError):
|
||||
return data # we may be getting the default value, or None,
|
||||
# so it doesn't need unpickled.
|
||||
|
||||
def get_both(self, key, value, txn=None, flags=0):
|
||||
data = self.encode(value)
|
||||
data = self.db.get(key, data, txn, flags)
|
||||
return self.decode(data)
|
||||
|
||||
def cursor(self, txn=None, flags=0):
|
||||
c = DBShelfCursor(self.db.cursor(txn, flags))
|
||||
c.binary = self.binary
|
||||
return c
|
||||
|
||||
def put(self, key, value, txn=None, flags=0):
|
||||
data = self.encode(value)
|
||||
return self.db.put(key, data, txn, flags)
|
||||
|
||||
def join(self, cursorList, flags=0):
|
||||
raise NotImplementedError
|
||||
|
||||
#----------------------------------------------
|
||||
# Methods allowed to pass-through to self.db
|
||||
#
|
||||
# close, delete, fd, get_byteswapped, get_type, has_key,
|
||||
# key_range, open, remove, rename, stat, sync,
|
||||
# upgrade, verify, and all set_* methods.
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class DBShelfCursor:
|
||||
"""
|
||||
"""
|
||||
def __init__(self, cursor):
|
||||
self.dbc = cursor
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Some methods we can just pass through to the cursor object. (See below)"""
|
||||
return getattr(self.dbc, name)
|
||||
|
||||
#----------------------------------------------
|
||||
|
||||
def dup(self, flags=0):
|
||||
return DBShelfCursor(self.dbc.dup(flags))
|
||||
|
||||
def put(self, key, value, flags=0):
|
||||
data = self.encode(value)
|
||||
return self.dbc.put(key, data, flags)
|
||||
|
||||
|
||||
def get(self, *args):
|
||||
count = len(args) # a method overloading hack
|
||||
method = getattr(self, 'get_%d' % count)
|
||||
apply(method, args)
|
||||
|
||||
def get_1(self, flags):
|
||||
rec = self.dbc.get(flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def get_2(self, key, flags):
|
||||
rec = self.dbc.get(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def get_3(self, key, value, flags):
|
||||
data = self.encode(value)
|
||||
rec = self.dbc.get(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
|
||||
def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT)
|
||||
def first(self, flags=0): return self.get_1(flags|db.DB_FIRST)
|
||||
def last(self, flags=0): return self.get_1(flags|db.DB_LAST)
|
||||
def next(self, flags=0): return self.get_1(flags|db.DB_NEXT)
|
||||
def prev(self, flags=0): return self.get_1(flags|db.DB_PREV)
|
||||
def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME)
|
||||
def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP)
|
||||
def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP)
|
||||
def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP)
|
||||
|
||||
def get_both(self, key, value, flags=0):
|
||||
data = self.encode(value)
|
||||
rec = self.dbc.get_both(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def set(self, key, flags=0):
|
||||
rec = self.dbc.set(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def set_range(self, key, flags=0):
|
||||
rec = self.dbc.set_range(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def set_recno(self, recno, flags=0):
|
||||
rec = self.dbc.set_recno(recno, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
set_both = get_both
|
||||
|
||||
def _extract(self, rec):
|
||||
if rec is None:
|
||||
return None
|
||||
else:
|
||||
key, data = rec
|
||||
return key, self.decode(data)
|
||||
|
||||
#----------------------------------------------
|
||||
# Methods allowed to pass-through to self.dbc
|
||||
#
|
||||
# close, count, delete, get_recno, join_item
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1,122 +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.
|
||||
#
|
||||
|
||||
"""dependency analyzer"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.context as ctx
|
||||
#import pisi.packagedb as packagedb
|
||||
from pisi.version import Version
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.util import Checks
|
||||
import pisi.itembyrepodb
|
||||
|
||||
class Dependency:
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
s_Package = [autoxml.String, autoxml.mandatory]
|
||||
a_version = [autoxml.String, autoxml.optional]
|
||||
a_versionFrom = [autoxml.String, autoxml.optional]
|
||||
a_versionTo = [autoxml.String, autoxml.optional]
|
||||
a_release = [autoxml.String, autoxml.optional]
|
||||
a_releaseFrom = [autoxml.String, autoxml.optional]
|
||||
a_releaseTo = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.package
|
||||
if self.versionFrom:
|
||||
s += _(" version >= ") + self.versionFrom
|
||||
if self.versionTo:
|
||||
s += _(" version <= ") + self.versionTo
|
||||
if self.releaseFrom:
|
||||
s += _(" release >= ") + self.releaseFrom
|
||||
if self.releaseTo:
|
||||
s += _(" release <= ") + self.releaseTo
|
||||
return s
|
||||
|
||||
def satisfies(self, pkg_name, version, release):
|
||||
"""determine if a package ver. satisfies given dependency spec"""
|
||||
ret = True
|
||||
v = Version(version)
|
||||
if self.version:
|
||||
ret &= v == Version(self.version)
|
||||
if self.versionFrom:
|
||||
ret &= v >= Version(self.versionFrom)
|
||||
if self.versionTo:
|
||||
ret &= v <= Version(self.versionTo)
|
||||
r = Version(release)
|
||||
if self.release:
|
||||
ret &= r == Version(self.release)
|
||||
if self.releaseFrom:
|
||||
ret &= r >= Version(self.releaseFrom)
|
||||
if self.releaseTo:
|
||||
ret &= r <= Version(self.releaseTo)
|
||||
return ret
|
||||
|
||||
def dict_satisfies_dep(dict, depinfo):
|
||||
"""determine if a package in a dictionary satisfies given dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not dict.has_key(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = dict[pkg_name]
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def installed_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not ctx.installdb.is_installed(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = ctx.packagedb.get_package(pkg_name, pisi.itembyrepodb.installed)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def repo_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not ctx.packagedb.has_package(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = ctx.packagedb.get_package(pkg_name)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
|
||||
for dep in deps:
|
||||
if not sat(dep):
|
||||
ctx.ui.error(_('Package %s does not satisfy dependency %s') %
|
||||
(pkg,dep))
|
||||
return False
|
||||
return True
|
||||
|
||||
def satisfies_runtime_deps(pkg):
|
||||
deps = ctx.packagedb.get_package(pkg).runtimeDependencies()
|
||||
return satisfies_dependencies(pkg, deps)
|
||||
|
||||
def installable(pkg):
|
||||
"""calculate if pkg name is installable currently
|
||||
which means it has to satisfy both install and runtime dependencies"""
|
||||
if not ctx.packagedb.has_package(pkg):
|
||||
ctx.ui.info(_("Package %s is not present in the package database") % pkg);
|
||||
return False
|
||||
elif satisfies_runtime_deps(pkg):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -1,369 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""Yet another Pisi module for fetching files from various sources. Of
|
||||
course, this is not limited to just fetching source files. We fetch
|
||||
all kinds of things: source tarballs, index files, packages, and God
|
||||
knows what."""
|
||||
|
||||
# python standard library modules
|
||||
import urllib2
|
||||
import urllib
|
||||
import ftplib
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from mimetypes import guess_type
|
||||
from mimetools import Message
|
||||
from base64 import encodestring
|
||||
from shutil import move
|
||||
from time import time
|
||||
from time import gmtime
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
|
||||
class FetchError(pisi.Error):
|
||||
pass
|
||||
|
||||
class RangeError(pisi.Error):
|
||||
pass
|
||||
|
||||
# helper functions
|
||||
def fetch_url(url, destdir, progress=None, resume=True):
|
||||
fetch = Fetcher(url, destdir)
|
||||
if not resume:
|
||||
fetch.resume = False
|
||||
fetch.progress = progress
|
||||
fetch.fetch()
|
||||
|
||||
class Fetcher:
|
||||
"""Fetcher can fetch a file from various sources using various
|
||||
protocols."""
|
||||
def __init__(self, url, destdir, resume = True):
|
||||
if not isinstance(url, URI):
|
||||
url = URI(url)
|
||||
|
||||
if ctx.config.get_option("authinfo"):
|
||||
url.set_auth_info(ctx.config.get_option("authinfo"))
|
||||
|
||||
self.resume = resume
|
||||
self.scheme = url.scheme()
|
||||
self.url = url
|
||||
self.destdir = destdir
|
||||
util.check_dir(self.destdir)
|
||||
self.eta = '??:??:??'
|
||||
self.percent = 0
|
||||
self.rate = 0.0
|
||||
self.progress = None
|
||||
self.exist_size = 0
|
||||
|
||||
def fetch (self):
|
||||
"""Return value: Fetched file's full path.."""
|
||||
|
||||
if not self.url.filename():
|
||||
self.err(_('Filename error'))
|
||||
|
||||
if not os.access(self.destdir, os.W_OK):
|
||||
self.err(_('Access denied to write to destination directory: "%s"') % (self.destdir))
|
||||
|
||||
archive_file = os.path.join(self.destdir, self.url.filename())
|
||||
|
||||
if os.path.exists(archive_file) and not os.access(archive_file, os.W_OK):
|
||||
self.err(_('Access denied to destination file: "%s"') % (archive_file))
|
||||
|
||||
partial_file = archive_file + '.part'
|
||||
|
||||
if self.url.is_local_file():
|
||||
self.fetchLocalFile(partial_file)
|
||||
else:
|
||||
self.fetchRemoteFile(partial_file)
|
||||
|
||||
if os.stat(partial_file).st_size == 0:
|
||||
os.remove(partial_file)
|
||||
self.err(_('A problem occured. Please check the archive address and/or permissions again.'))
|
||||
|
||||
move(partial_file, archive_file)
|
||||
|
||||
return archive_file
|
||||
|
||||
def _do_grab(self, fileURI, dest, total_size):
|
||||
ctx.ui.notify(pisi.ui.downloading, url = fileURI)
|
||||
bs, tt, = 1024, int(time())
|
||||
s_time = time()
|
||||
Tdiff = lambda: time() - s_time
|
||||
downloaded_size = exist_size = self.exist_size
|
||||
symbol = 'B/s'
|
||||
st = time()
|
||||
chunk = fileURI.read(bs)
|
||||
downloaded_size += len(chunk)
|
||||
|
||||
if self.progress:
|
||||
p = self.progress(total_size, exist_size)
|
||||
self.percent = p.update(downloaded_size)
|
||||
self.complete = False
|
||||
|
||||
while chunk:
|
||||
dest.write(chunk)
|
||||
chunk = fileURI.read(bs)
|
||||
downloaded_size += len(chunk)
|
||||
ct = time()
|
||||
if int(tt) != int(ct):
|
||||
self.rate = (downloaded_size - exist_size) / (ct - st)
|
||||
|
||||
if self.percent:
|
||||
self.eta = '%02d:%02d:%02d' %\
|
||||
tuple([i for i in gmtime((Tdiff() * (100 - self.percent)) / self.percent)[3:6]])
|
||||
|
||||
self.rate, symbol = util.human_readable_rate(self.rate)
|
||||
|
||||
tt = time()
|
||||
|
||||
if self.progress:
|
||||
if p.update(downloaded_size):
|
||||
self.percent = p.percent
|
||||
if not self.complete:
|
||||
ctx.ui.display_progress(filename = self.url.filename(),
|
||||
percent = self.percent,
|
||||
total_size = total_size,
|
||||
downloaded_size = downloaded_size,
|
||||
rate = self.rate,
|
||||
eta = self.eta,
|
||||
symbol = symbol)
|
||||
if self.percent == 100: #FIXME: will be superseded by a
|
||||
self.complete = True # working progress interface
|
||||
|
||||
dest.close()
|
||||
|
||||
def fetchLocalFile (self, archive_file):
|
||||
url = self.url
|
||||
|
||||
if not os.access(url.path(), os.F_OK):
|
||||
self.err(_('No such file or no permission to read'))
|
||||
|
||||
dest = open(archive_file, 'w')
|
||||
total_size = os.path.getsize(url.path())
|
||||
fileObj = open(url.path())
|
||||
self._do_grab(fileObj, dest, total_size)
|
||||
|
||||
def fetchRemoteFile (self, archive_file):
|
||||
from httplib import HTTPException
|
||||
|
||||
if os.path.exists(archive_file) and self.resume:
|
||||
if self.scheme == 'http' or self.scheme == 'https' or self.scheme == 'ftp':
|
||||
self.exist_size = os.path.getsize(archive_file)
|
||||
dest = open(archive_file, 'ab')
|
||||
else:
|
||||
dest = open(archive_file, 'wb')
|
||||
|
||||
uri = self.url.get_uri()
|
||||
|
||||
flag = 1
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
|
||||
except RangeError:
|
||||
ctx.ui.info(_('Requested range not satisfiable, starting again.'))
|
||||
dest = open(archive_file, 'wb')
|
||||
self.exist_size = 0
|
||||
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
|
||||
headers = fileObj.info()
|
||||
flag = 0
|
||||
except ValueError, e:
|
||||
self.err(_('Cannot fetch %s; value error: %s') % (uri, e))
|
||||
except urllib2.HTTPError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e))
|
||||
except urllib2.URLError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e[-1][-1]))
|
||||
except OSError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e))
|
||||
except HTTPException, e:
|
||||
self.err(_('Cannot fetch %s; (%s): %s') % (uri, e.__class__.__name__, e))
|
||||
finally:
|
||||
if flag:
|
||||
if os.stat(archive_file).st_size == 0:
|
||||
os.remove(archive_file)
|
||||
|
||||
try:
|
||||
total_size = int(headers['Content-Length']) + self.exist_size
|
||||
except:
|
||||
total_size = 0
|
||||
|
||||
self._do_grab(fileObj, dest, total_size)
|
||||
|
||||
def formatRequest(self, request):
|
||||
if self.url.auth_info():
|
||||
enc = encodestring('%s:%s' % self.url.auth_info())
|
||||
request.add_header('Authorization', 'Basic %s' % enc)
|
||||
|
||||
range_handlers = {
|
||||
'http' : HTTPRangeHandler,
|
||||
'https': HTTPRangeHandler,
|
||||
'ftp' : FTPRangeHandler
|
||||
}
|
||||
|
||||
if self.exist_size and range_handlers.has_key(self.scheme):
|
||||
opener = urllib2.build_opener(range_handlers.get(self.scheme)())
|
||||
urllib2.install_opener(opener)
|
||||
request.add_header('Range', 'bytes=%d-' % self.exist_size)
|
||||
|
||||
proxy_handler = None
|
||||
|
||||
if ctx.config.values.general.http_proxy and self.url.scheme() == "http":
|
||||
http_proxy = ctx.config.values.general.http_proxy
|
||||
proxy_handler = urllib2.ProxyHandler({URI(http_proxy).scheme(): http_proxy})
|
||||
|
||||
elif ctx.config.values.general.https_proxy and self.url.scheme() == "https":
|
||||
https_proxy = ctx.config.values.general.https_proxy
|
||||
proxy_handler = urllib2.ProxyHandler({URI(https_proxy): https_proxy})
|
||||
|
||||
elif ctx.config.values.general.ftp_proxy and self.url.scheme() == "ftp":
|
||||
ftp_proxy = ctx.config.values.general.ftp_proxy
|
||||
proxy_handler = urllib2.ProxyHandler({URI(http_proxy): ftp_proxy})
|
||||
|
||||
if proxy_handler:
|
||||
ctx.ui.info(_("Proxy configuration has been found for '%s' protocol") % self.url.scheme())
|
||||
opener = urllib2.build_opener(proxy_handler)
|
||||
urllib2.install_opener(opener)
|
||||
|
||||
return request
|
||||
|
||||
def err (self, error):
|
||||
raise FetchError(error)
|
||||
|
||||
class HTTPRangeHandler(urllib2.BaseHandler):
|
||||
"""
|
||||
to override the urllib2 error: 'Error 206: Partial Content'
|
||||
this reponse from the HTTP server is already what we expected to get.
|
||||
Don't give up, resume downloading..
|
||||
"""
|
||||
|
||||
def http_error_206(self, request, fp, errcode, msg, headers):
|
||||
return urllib.addinfourl(fp, headers, request.get_full_url())
|
||||
|
||||
def http_error_416(self, request, fp, errcode, msg, headers):
|
||||
# HTTP 1.1's 'Range Not Satisfiable' error..
|
||||
raise RangeError
|
||||
|
||||
|
||||
class FTPRangeHandler(urllib2.FTPHandler):
|
||||
"""
|
||||
FTP Range support..
|
||||
"""
|
||||
def ftp_open(self, req):
|
||||
host = req.get_host()
|
||||
host, port = urllib.splitport(host)
|
||||
if port is None:
|
||||
port = ftplib.FTP_PORT
|
||||
|
||||
try:
|
||||
host = socket.gethostbyname(host)
|
||||
except socket.error, msg:
|
||||
raise FetchError(msg)
|
||||
|
||||
path, attrs = urllib.splitattr(req.get_selector())
|
||||
dirs = path.split('/')
|
||||
dirs = map(urllib.unquote, dirs)
|
||||
dirs, file = dirs[:-1], dirs[-1]
|
||||
if dirs and not dirs[0]:
|
||||
dirs = dirs[1:]
|
||||
try:
|
||||
fw = self.connect_ftp('', '', host, port, dirs)
|
||||
type = file and 'I' or 'D'
|
||||
for attr in attrs:
|
||||
attr, value = urllib.splitattr(attr)
|
||||
if attr.lower() == 'type' and \
|
||||
value in ('a', 'A', 'i', 'I', 'd', 'D'):
|
||||
type = value.upper()
|
||||
|
||||
rawr = req.headers.get('Range', None)
|
||||
if rawr:
|
||||
rest = int(rawr.split("=")[1].rstrip("-"))
|
||||
else:
|
||||
rest = 0
|
||||
|
||||
fp, retrlen = fw.retrfile(file, type, rest)
|
||||
|
||||
fb, lb = rest, retrlen
|
||||
if retrlen is None or retrlen == 0:
|
||||
raise RangeError
|
||||
retrlen = lb - fb
|
||||
if retrlen < 0:
|
||||
# beginning of range is larger than file
|
||||
raise RangeError
|
||||
|
||||
headers = ''
|
||||
mtype = guess_type(req.get_full_url())[0]
|
||||
if mtype:
|
||||
headers += 'Content-Type: %s\n' % mtype
|
||||
if retrlen is not None and retrlen >= 0:
|
||||
headers += 'Content-Length: %d\n' % retrlen
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError, msg:
|
||||
from StringIO import StringIO
|
||||
|
||||
return urllib.addinfourl(fp, Message(StringIO(headers)), req.get_full_url())
|
||||
except ftplib.all_errors, msg:
|
||||
raise IOError, (_('ftp error'), msg), sys.exc_info()[2]
|
||||
|
||||
def connect_ftp(self, user, passwd, host, port, dirs):
|
||||
fw = ftpwrapper('', '', host, port, dirs)
|
||||
return fw
|
||||
|
||||
class ftpwrapper(urllib.ftpwrapper):
|
||||
def retrfile(self, file, type, rest=None):
|
||||
self.endtransfer()
|
||||
if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
|
||||
else: cmd = 'TYPE ' + type; isdir = 0
|
||||
try:
|
||||
self.ftp.voidcmd(cmd)
|
||||
except ftplib.all_errors:
|
||||
self.init()
|
||||
self.ftp.voidcmd(cmd)
|
||||
conn = None
|
||||
if file and not isdir:
|
||||
try:
|
||||
self.ftp.nlst(file)
|
||||
except ftplib.error_perm, reason:
|
||||
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||
# Restore the transfer mode!
|
||||
self.ftp.voidcmd(cmd)
|
||||
try:
|
||||
cmd = 'RETR ' + file
|
||||
conn = self.ftp.ntransfercmd(cmd, rest)
|
||||
except ftplib.error_perm, reason:
|
||||
if str(reason)[:3] == '501':
|
||||
# workaround for REST not suported error
|
||||
fp, retrlen = self.retrfile(file, type)
|
||||
fp = RangeableFileObject(fp, (rest,''))
|
||||
return (fp, retrlen)
|
||||
elif str(reason)[:3] != '550':
|
||||
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||
if not conn:
|
||||
self.ftp.voidcmd('TYPE A')
|
||||
if file: cmd = 'LIST ' + file
|
||||
else: cmd = 'LIST'
|
||||
conn = self.ftp.ntransfercmd(cmd)
|
||||
self.busy = 1
|
||||
return (urllib.addclosehook(conn[0].makefile('rb'),
|
||||
self.endtransfer), conn[1])
|
||||
|
||||
@@ -1,265 +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 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-restrictive way
|
||||
"""
|
||||
|
||||
import os
|
||||
import types
|
||||
import bz2
|
||||
import shutil
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
from pisi.uri import URI
|
||||
from pisi.util import join_path as join
|
||||
from pisi.fetcher import fetch_url
|
||||
import pisi.context as ctx
|
||||
|
||||
class AlreadyHaveException(pisi.Exception):
|
||||
def __init__(self, url, localfile):
|
||||
pisi.Exception.__init__(self, "URL %s already downloaded as %s" % (url, localfile))
|
||||
self.url = url
|
||||
self.localfile = localfile
|
||||
|
||||
class NoSignatureFound(pisi.Exception):
|
||||
def __init__(self, url):
|
||||
pisi.Exception.__init__(self, "No signature found for %s" % url)
|
||||
self.url = url
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class InvalidSignature(pisi.Error):
|
||||
def __init__(self, url):
|
||||
pisi.Exception.__init__(self, " invalid for %s" % url)
|
||||
self.url = url
|
||||
|
||||
class File:
|
||||
|
||||
(read, write) = range(2) # modes
|
||||
(bz2, gzip, auto) = range(3) # compress enums
|
||||
(detached, whatelse) = range(2)
|
||||
|
||||
@staticmethod
|
||||
def make_uri(uri):
|
||||
"handle URI arg"
|
||||
if type(uri) == types.StringType or type(uri) == types.UnicodeType:
|
||||
uri = URI(uri)
|
||||
elif not isinstance(uri, URI):
|
||||
raise Error(_("uri must have type either URI or string"))
|
||||
return uri
|
||||
|
||||
@staticmethod
|
||||
def choose_method(filename, compress):
|
||||
# this is really simple (^_^) -- exa
|
||||
if compress == File.auto:
|
||||
if filename.endswith('.bz2'):
|
||||
return File.bz2
|
||||
elif filename.endswith('.gz'):
|
||||
return File.gzip
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return compress
|
||||
|
||||
@staticmethod
|
||||
def decompress(localfile, compress):
|
||||
compress = File.choose_method(localfile, compress)
|
||||
if compress == File.bz2:
|
||||
open(localfile[:-4], "w").write(bz2.BZ2File(localfile).read())
|
||||
localfile = localfile[:-4]
|
||||
elif compress == File.gzip:
|
||||
raise Error(_("zip compression not supported yet"))
|
||||
return localfile
|
||||
|
||||
@staticmethod
|
||||
def download(uri, transfer_dir = "/tmp", sha1sum = False,
|
||||
compress = None, sign = None, copylocal = False):
|
||||
|
||||
assert isinstance(uri, URI)
|
||||
|
||||
if sha1sum:
|
||||
sha1filename = File.download(URI(uri.get_uri() + '.sha1sum'), transfer_dir)
|
||||
sha1f = file(sha1filename)
|
||||
newsha1 = sha1f.readlines()[0]
|
||||
|
||||
if uri.is_remote_file() or copylocal:
|
||||
localfile = join(transfer_dir, uri.filename())
|
||||
|
||||
# TODO: code to use old .sha1sum file, is this a necessary optimization?
|
||||
#oldsha1fn = localfile + '.sha1sum'
|
||||
#if os.exists(oldsha1fn):
|
||||
#oldsha1 = file(oldsha1fn).readlines()[0]
|
||||
if sha1sum and os.path.exists(localfile):
|
||||
oldsha1 = pisi.util.sha1_file(localfile)
|
||||
if (newsha1 == oldsha1):
|
||||
# early terminate, we already got it ;)
|
||||
raise AlreadyHaveException(uri, localfile)
|
||||
|
||||
if uri.is_remote_file():
|
||||
ctx.ui.info(_("Fetching %s") % uri.get_uri(), verbose=True)
|
||||
fetch_url(uri, transfer_dir, ctx.ui.Progress)
|
||||
else:
|
||||
# copy to transfer dir,
|
||||
localfile = join(transfer_dir, uri.filename())
|
||||
ctx.ui.info(_("Copying %s to transfer dir") % uri.get_uri(), verbose=True)
|
||||
shutil.copy(uri.get_uri(), transfer_dir)
|
||||
else:
|
||||
localfile = uri.get_uri() #TODO: use a special function here?
|
||||
if not os.path.exists(localfile):
|
||||
raise IOError(_("File '%s' not found.") % localfile)
|
||||
if not os.access(localfile, os.W_OK):
|
||||
oldfn = localfile
|
||||
localfile = join(transfer_dir, os.path.basename(localfile))
|
||||
shutil.copy(oldfn, localfile)
|
||||
|
||||
if sha1sum:
|
||||
if (pisi.util.sha1_file(localfile) != newsha1):
|
||||
raise Error(_("File integrity of %s compromised.") % uri)
|
||||
|
||||
localfile = File.decompress(localfile, compress)
|
||||
|
||||
return localfile
|
||||
|
||||
|
||||
def __init__(self, uri, mode, transfer_dir = "/tmp",
|
||||
sha1sum = False, compress = None, sign = None):
|
||||
"it is pointless to open a file without a URI and a mode"
|
||||
|
||||
self.transfer_dir = transfer_dir
|
||||
self.sha1sum = sha1sum
|
||||
self.compress = compress
|
||||
self.sign = sign
|
||||
|
||||
|
||||
uri = File.make_uri(uri)
|
||||
if mode==File.read or mode==File.write:
|
||||
self.mode = mode
|
||||
else:
|
||||
raise Error(_("File mode must be either File.read or File.write"))
|
||||
if uri.is_remote_file():
|
||||
if self.mode == File.read:
|
||||
localfile = File.download(uri, transfer_dir, sha1sum, compress, sign)
|
||||
else:
|
||||
raise Error(_("Remote write not implemented"))
|
||||
else:
|
||||
localfile = uri.get_uri()
|
||||
if self.mode == File.read:
|
||||
localfile = File.decompress(localfile, self.compress)
|
||||
|
||||
if self.mode == File.read:
|
||||
access = 'r'
|
||||
else:
|
||||
access = 'w'
|
||||
self.__file__ = file(localfile, access)
|
||||
self.localfile = localfile
|
||||
|
||||
def local_file(self):
|
||||
"returns the underlying file object"
|
||||
return self.__file__
|
||||
|
||||
def close(self, delete_transfer = False):
|
||||
"this method must be called at the end of operation"
|
||||
self.__file__.close()
|
||||
if self.mode == File.write:
|
||||
compressed_file = None
|
||||
if self.compress == File.bz2:
|
||||
compressed_file = self.localfile + ".bz2"
|
||||
bz2.BZ2File(compressed_file, "w").write(open(self.localfile, "r").read())
|
||||
|
||||
elif self.compress == File.gzip:
|
||||
raise Error(_("gzip compression not supported yet"))
|
||||
|
||||
if self.sha1sum:
|
||||
sha1 = pisi.util.sha1_file(self.localfile)
|
||||
cs = file(self.localfile + '.sha1sum', 'w')
|
||||
cs.write(sha1)
|
||||
cs.close()
|
||||
if compressed_file:
|
||||
sha1 = pisi.util.sha1_file(compressed_file)
|
||||
cs = file(compressed_file + '.sha1sum', 'w')
|
||||
cs.write(sha1)
|
||||
cs.close()
|
||||
|
||||
if self.sign==File.detached:
|
||||
if pisi.util.run_batch('gpg --detach-sig ' + self.localfile)[0]:
|
||||
raise Error(_("ERROR: gpg --detach-sig %s failed") % self.localfile)
|
||||
if compressed_file:
|
||||
if pisi.util.run_batch('gpg --detach-sig ' + compressed_file)[0]:
|
||||
raise Error(_("ERROR: gpg --detach-sig %s failed") % compressed_file)
|
||||
|
||||
@staticmethod
|
||||
def check_signature(uri, transfer_dir, sign=detached):
|
||||
if sign==File.detached:
|
||||
try:
|
||||
sigfilename = File.download(URI(uri + '.sig'), transfer_dir)
|
||||
except:
|
||||
raise NoSignatureFound(uri)
|
||||
if os.system('gpg --verify ' + sigfilename) != 0:
|
||||
raise InvalidSignature(uri)
|
||||
# everything is all right here
|
||||
|
||||
def flush(self):
|
||||
self.__file__.flush()
|
||||
|
||||
def fileno(self):
|
||||
return self.__file__.fileno()
|
||||
|
||||
def isatty(self):
|
||||
return self.__file__.isatty()
|
||||
|
||||
def next(self):
|
||||
return self.__file__.next()
|
||||
|
||||
def read(self, size = None):
|
||||
if size:
|
||||
return self.__file__.read(size)
|
||||
else:
|
||||
return self.__file__.read()
|
||||
|
||||
def readline(self, size = None):
|
||||
if size:
|
||||
return self.__file__.readline(size)
|
||||
else:
|
||||
return self.__file__.readline()
|
||||
|
||||
def readlines(self, size = None):
|
||||
if size:
|
||||
return self.__file__.readlines(size)
|
||||
else:
|
||||
return self.__file__.readlines()
|
||||
|
||||
def xreadlines(self):
|
||||
return self.__file__.xreadlines()
|
||||
|
||||
def seek(self, offset, whence=0):
|
||||
self.__file__.seek(offset, whence)
|
||||
|
||||
def tell(self):
|
||||
return self.__file__.tell()
|
||||
|
||||
def truncate(self):
|
||||
self.__file__.truncate()
|
||||
|
||||
def write(self, str):
|
||||
self.__file__.write(str)
|
||||
|
||||
def writelines(self, sequence):
|
||||
self.__file__.writelines(sequence)
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
'''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
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
|
||||
class FileInfo:
|
||||
"""File holds the information for a File node/tag in files.xml"""
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
t_Path = [ autoxml.String, autoxml.mandatory ]
|
||||
t_Type = [ autoxml.String, autoxml.mandatory ]
|
||||
t_Size = [ autoxml.Long, autoxml.optional ]
|
||||
t_Hash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
|
||||
t_Permanent = [ autoxml.String, autoxml.optional ]
|
||||
|
||||
def __str__(self):
|
||||
s = "/%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type,
|
||||
self.size, self.hash)
|
||||
return s
|
||||
|
||||
|
||||
class Files(autoxml.XmlFile):
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "Files"
|
||||
|
||||
t_List = [ [FileInfo], autoxml.optional, "File"]
|
||||
|
||||
def append(self, fileinfo):
|
||||
self.list.append(fileinfo)
|
||||
|
||||
|
||||
class FilesDB(shelve.LockedDBShelf):
|
||||
|
||||
def __init__(self):
|
||||
shelve.LockedDBShelf.__init__(self, 'files')
|
||||
|
||||
def add_files(self, pkg_name, files, txn = None):
|
||||
def proc(txn):
|
||||
for x in files.list:
|
||||
path = x.path
|
||||
del x.path # don't store redundant attribute in db
|
||||
self.put(path, (pkg_name, x), txn)
|
||||
x.path = path # store it back in
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
def remove_files(self, files, txn = None):
|
||||
def proc(txn):
|
||||
for x in files.list:
|
||||
if self.has_key(x.path):
|
||||
self.delete(x.path, txn)
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
def has_file(self, path, txn = None):
|
||||
return self.has_key(str(path), txn)
|
||||
|
||||
def get_file(self, path, txn = None):
|
||||
path = str(path)
|
||||
def proc(txn):
|
||||
if not self.has_key(path, txn):
|
||||
return None
|
||||
else:
|
||||
(name, fileinfo) = self.get(path, txn)
|
||||
fileinfo.path = path
|
||||
return (name, fileinfo)
|
||||
return self.txn_proc(proc, txn)
|
||||
|
||||
def match_files(self, glob):
|
||||
# NB: avoid using, this reads the entire db
|
||||
import fnmatch
|
||||
glob = str(glob)
|
||||
infos = []
|
||||
for key in self.keys():
|
||||
if fnmatch.fnmatch(key, glob):
|
||||
|
||||
# FIXME: Why should we assign path attribute manually
|
||||
# in fileinfo? This is also done in get_file(), seems
|
||||
# like a dirty workaround... - baris
|
||||
name = self[key][0]
|
||||
fileinfo = self[key][1]
|
||||
fileinfo.path = key
|
||||
infos.append((name, fileinfo))
|
||||
return infos
|
||||
@@ -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.
|
||||
|
||||
# the most simple minded digraph class ever
|
||||
|
||||
|
||||
import sys
|
||||
import string
|
||||
|
||||
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):
|
||||
def repl(char):
|
||||
if char in string.punctuation:
|
||||
return '_'
|
||||
else:
|
||||
return char
|
||||
return pisi.util.concat(map(repl, str(u)))
|
||||
|
||||
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
|
||||
|
||||
@@ -1,168 +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 source/package index"""
|
||||
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile as specfile
|
||||
import pisi.metadata as metadata
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.sourcedb as sourcedb
|
||||
import pisi.util as util
|
||||
from pisi.package import Package
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
from pisi.file import File
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.uri import URI
|
||||
import pisi.component as component
|
||||
import pisi.specfile as specfile
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Index(XmlFile):
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Distribution = [ component.Distribution, autoxml.optional ]
|
||||
t_Specs = [ [specfile.SpecFile], autoxml.optional, "SpecFile"]
|
||||
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
|
||||
#t_Metadatas = [ [metadata.MetaData], autoxml.optional, "MetaData"]
|
||||
t_Components = [ [component.Component], autoxml.optional, "Component"]
|
||||
|
||||
def name():
|
||||
return self.distribution.name + self.distribution.repositoryname
|
||||
|
||||
def read_uri(self, uri, tmpdir, force = False):
|
||||
self.read(uri, tmpDir=tmpdir, sha1sum=not force,
|
||||
compress=File.auto, sign=File.detached, copylocal = True)
|
||||
|
||||
# read index for a given repo, force means download even if remote not updated
|
||||
def read_uri_of_repo(self, uri, repo = None, force = False):
|
||||
"""Read PSPEC file"""
|
||||
if repo:
|
||||
tmpdir = os.path.join(ctx.config.index_dir(), repo)
|
||||
else:
|
||||
tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
|
||||
pisi.util.clean_dir(tmpdir)
|
||||
pisi.util.check_dir(tmpdir)
|
||||
|
||||
# write uri
|
||||
urlfile = file(pisi.util.join_path(tmpdir, 'uri'), 'w')
|
||||
urlfile.write(uri) # uri
|
||||
urlfile.close()
|
||||
|
||||
self.read_uri(uri, tmpdir, force)
|
||||
|
||||
if not repo:
|
||||
repo = self.distribution.name()
|
||||
# and what do we do with it? move it to index dir properly
|
||||
newtmpdir = os.path.join(ctx.config.index_dir(), repo)
|
||||
pisi.util.clean_dir(newtmpdir) # replace newtmpdir
|
||||
shutil.move(tmpdir, newtmpdir)
|
||||
|
||||
def check_signature(self, filename, repo):
|
||||
tmpdir = os.path.join(ctx.config.index_dir(), repo)
|
||||
File.check_signature(filename, tmpdir)
|
||||
|
||||
def index(self, repo_uri, skip_sources=False):
|
||||
self.repo_dir = repo_uri
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn.endswith(ctx.const.package_suffix):
|
||||
ctx.ui.info(_('Adding %s to package index') % fn)
|
||||
self.add_package(os.path.join(root, fn), repo_uri)
|
||||
if fn == 'component.xml':
|
||||
ctx.ui.info(_('Adding %s to component index') % fn)
|
||||
self.add_component(os.path.join(root, fn))
|
||||
if fn == 'pspec.xml' and not skip_sources:
|
||||
self.add_spec(os.path.join(root, fn), repo_uri)
|
||||
if fn == 'distribution.xml':
|
||||
self.add_distro(os.path.join(root, fn))
|
||||
|
||||
def update_db(self, repo, txn = None):
|
||||
ctx.componentdb.remove_repo(repo, txn=txn)
|
||||
for comp in self.components:
|
||||
ctx.componentdb.update_component(comp, repo, txn)
|
||||
ctx.packagedb.remove_repo(repo, txn=txn)
|
||||
for pkg in self.packages:
|
||||
ctx.packagedb.add_package(pkg, repo, txn=txn)
|
||||
ctx.sourcedb.remove_repo(repo, txn=txn)
|
||||
for sf in self.specs:
|
||||
ctx.sourcedb.add_spec(sf, repo, txn=txn)
|
||||
|
||||
def add_package(self, path, repo_uri):
|
||||
package = Package(path, 'r')
|
||||
# extract control files
|
||||
util.clean_dir(ctx.config.install_dir())
|
||||
package.extract_PISI_files(ctx.config.install_dir())
|
||||
|
||||
md = metadata.MetaData()
|
||||
md.read(os.path.join(ctx.config.install_dir(), ctx.const.metadata_xml))
|
||||
md.package.packageSize = os.path.getsize(path)
|
||||
if ctx.config.options and ctx.config.options.absolute_urls:
|
||||
md.package.packageURI = os.path.realpath(path)
|
||||
else: # create relative path by default
|
||||
# TODO: in the future well do all of this with purl/pfile/&helpers
|
||||
# really? heheh -- future exa
|
||||
md.package.packageURI = util.removepathprefix(repo_uri, path)
|
||||
# check package semantics
|
||||
errs = md.errors()
|
||||
if md.errors():
|
||||
ctx.ui.error(_('Package %s: metadata corrupt, skipping...') % md.package.name)
|
||||
ctx.ui.error(unicode(Error(*errs)))
|
||||
else:
|
||||
self.packages.append(md.package)
|
||||
|
||||
def add_component(self, path):
|
||||
comp = component.Component()
|
||||
#try:
|
||||
comp.read(path)
|
||||
self.components.append(comp)
|
||||
#except:
|
||||
# raise Error(_('Component in %s is corrupt') % path)
|
||||
#ctx.ui.error(str(Error(*errs)))
|
||||
|
||||
def add_distro(self, path):
|
||||
distro = component.Distribution()
|
||||
#try:
|
||||
distro.read(path)
|
||||
self.distribution = distro
|
||||
#except:
|
||||
# raise Error(_('Distribution in %s is corrupt') % path)
|
||||
#ctx.ui.error(str(Error(*errs)))
|
||||
|
||||
def add_spec(self, path, repo_uri):
|
||||
import pisi.build
|
||||
ctx.ui.info(_('Adding %s to source index') % path)
|
||||
#TODO: may use try/except to handle this
|
||||
builder = pisi.build.Builder(path)
|
||||
#ctx.ui.error(_('SpecFile in %s is corrupt, skipping...') % path)
|
||||
#ctx.ui.error(str(Error(*errs)))
|
||||
builder.fetch_component()
|
||||
sf = builder.spec
|
||||
if ctx.config.options and ctx.config.options.absolute_urls:
|
||||
sf.source.sourceURI = os.path.realpath(path)
|
||||
else: # create relative path by default
|
||||
sf.source.sourceURI = util.removepathprefix(repo_uri, path)
|
||||
# check component
|
||||
self.specs.append(sf)
|
||||
@@ -1,215 +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
|
||||
#
|
||||
|
||||
# System
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
from pisi.util import join_path as join
|
||||
|
||||
|
||||
class InstallDBError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class InstallInfo:
|
||||
# some data is replicated from packagedb
|
||||
# we store as an object, hey, we can waste O(1) space.
|
||||
# this is also easier to modify in the future, without
|
||||
# requiring database upgrades! wow!
|
||||
def __init__(self, state, version, release, build, distribution, time):
|
||||
self.state = state
|
||||
self.version = version
|
||||
self.release = release
|
||||
self.build = build
|
||||
self.distribution = distribution
|
||||
self.time = time
|
||||
|
||||
def one_liner(self):
|
||||
import time
|
||||
time_str = time.strftime("%d %b %Y %H:%M", self.time)
|
||||
s = '%2s|%10s|%6s|%6s|%8s|%12s' % (self.state, self.version, self.release,
|
||||
self.build, self.distribution,
|
||||
time_str)
|
||||
return s
|
||||
|
||||
state_map = { 'i': _('installed'), 'ip':_('installed-pending'),
|
||||
'r':_('removed'), 'p': _('purged') }
|
||||
|
||||
def __str__(self):
|
||||
s = _("State: %s\nVersion: %s, Release: %s, Build: %s\n") % \
|
||||
(InstallInfo.state_map[self.state], self.version,
|
||||
self.release, self.build)
|
||||
import time
|
||||
time_str = time.strftime("%d %b %Y %H:%M", self.time)
|
||||
s += _('Distribution: %s, Install Time: %s\n') % (self.distribution,
|
||||
time_str)
|
||||
return s
|
||||
|
||||
|
||||
class InstallDB:
|
||||
|
||||
def __init__(self):
|
||||
self.d = shelve.LockedDBShelf('install')
|
||||
self.dp = shelve.LockedDBShelf('configpending')
|
||||
self.files_dir = join(ctx.config.db_dir(), 'files')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dp.close()
|
||||
|
||||
def files_name(self, pkg, version, release):
|
||||
pkg_dir = self.pkg_dir(pkg, version, release)
|
||||
return join(pkg_dir, ctx.const.files_xml)
|
||||
|
||||
def files(self, pkg):
|
||||
pkg = str(pkg)
|
||||
pkginfo = self.d[pkg]
|
||||
files = Files()
|
||||
files.read(self.files_name(pkg,pkginfo.version,pkginfo.release))
|
||||
return files
|
||||
|
||||
def pkg_dir(self, pkg, version, release):
|
||||
return join(ctx.config.lib_dir(), 'package',
|
||||
pkg + '-' + version + '-' + release)
|
||||
|
||||
def is_recorded(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
return self.d.has_key(pkg)
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def is_installed(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
if self.is_recorded(pkg, txn):
|
||||
info = self.d.get(pkg, txn)
|
||||
return info.state=='i' or info.state=='ip'
|
||||
else:
|
||||
return False
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def list_installed(self, txn = None):
|
||||
def proc(txn):
|
||||
list = []
|
||||
for (pkg, info) in self.d.items(txn):
|
||||
if info.state=='i' or info.state=='ip':
|
||||
list.append(pkg)
|
||||
return list
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def list_pending(self):
|
||||
# warning: reads the entire db
|
||||
dict = {}
|
||||
for (pkg, x) in self.dp.items():
|
||||
pkginfo = self.d[pkg]
|
||||
dict[pkg] = pkginfo
|
||||
return dict
|
||||
|
||||
def get_info(self, pkg):
|
||||
pkg = str(pkg)
|
||||
return self.d[pkg]
|
||||
|
||||
def get_version(self, pkg):
|
||||
pkg = str(pkg)
|
||||
info = self.d[pkg]
|
||||
return (info.version, info.release, info.build)
|
||||
|
||||
def is_removed(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.is_recorded(pkg):
|
||||
info = self.d[pkg]
|
||||
return info.state=='r'
|
||||
else:
|
||||
return False
|
||||
|
||||
def install(self, pkg, version, release, build, distro = "",
|
||||
config_later = False, rebuild=False, txn = None):
|
||||
"""install package with specific version, release, build"""
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
if self.is_installed(pkg, txn):
|
||||
raise InstallDBError(_("Already installed"))
|
||||
if config_later:
|
||||
state = 'ip'
|
||||
self.dp.put(pkg, True, txn)
|
||||
else:
|
||||
state = 'i'
|
||||
|
||||
# FIXME: it might be more appropriate to pass date
|
||||
# as an argument, or installation data afterwards
|
||||
# to do this -- exa
|
||||
if not rebuild:
|
||||
import time
|
||||
ctime = time.localtime()
|
||||
else:
|
||||
files_xml = self.files_name(pkg, version, release)
|
||||
ctime = util.creation_time(files_xml)
|
||||
|
||||
self.d.put(pkg, InstallInfo(state, version, release, build, distro, ctime), txn)
|
||||
|
||||
self.d.txn_proc(proc,txn)
|
||||
|
||||
def clear_pending(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
info = self.d.get(pkg, txn)
|
||||
if self.is_installed(pkg, txn):
|
||||
assert info.state == 'ip'
|
||||
info.state = 'i'
|
||||
self.d.put(pkg, info, txn)
|
||||
self.dp.delete(pkg, txn)
|
||||
self.d.txn_proc(proc,txn)
|
||||
|
||||
def remove(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
info = self.d.get(pkg, txn)
|
||||
info.state = 'r'
|
||||
self.d.put(pkg, info, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def purge(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
if self.d.has_key(pkg, txn):
|
||||
self.d.delete(pkg, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
db = None
|
||||
|
||||
def init():
|
||||
global db
|
||||
if db:
|
||||
return db
|
||||
|
||||
db = InstallDB()
|
||||
return db
|
||||
|
||||
def finalize():
|
||||
global db
|
||||
if db:
|
||||
db.close()
|
||||
db = None
|
||||
@@ -1,220 +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 os
|
||||
import fcntl
|
||||
import types
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
installed, thirdparty, repos, all = range(1, 5)
|
||||
|
||||
"""installed and thirdparty are special databases to keep track
|
||||
of already installed stuff and third party stuff not in any real repository.
|
||||
repos means search in repositories only, and all means search in
|
||||
repositories and special databases (called tracking databases)
|
||||
"""
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
pass
|
||||
|
||||
class ItemByRepoDB(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.d = shelve.LockedDBShelf(name)
|
||||
#self.dbyrepo = shelve.LockedDBShelf(name + '-byrepo')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def clear(self, txn = None):
|
||||
self.d.clear(txn=txn)
|
||||
|
||||
def txn_proc(self, proc, txn):
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def items(self):
|
||||
return self.d.items()
|
||||
|
||||
@staticmethod
|
||||
def not_just_tracking(data):
|
||||
keys = data.keys()
|
||||
if len(keys)==1:
|
||||
if 'trdparty' in keys or 'inst' in keys:
|
||||
return False
|
||||
elif len(keys)==2:
|
||||
if 'trdparty' in keys and 'inst' in keys:
|
||||
return False
|
||||
return True
|
||||
#below is a slower way
|
||||
#for x in data.keys():
|
||||
# if x.startsWith('repo-'):
|
||||
# return True
|
||||
#return False
|
||||
|
||||
def list_if(self, pred):
|
||||
return [ k for k,data in self.d.items() if pred(k, data)]
|
||||
|
||||
def list(self, repo = None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
if repo not in [repos, all]:
|
||||
return [ k for k,data in self.d.items() if data.has_key(self.repo_str(repo))]
|
||||
else:
|
||||
if repo == all:
|
||||
return [ pkg for pkg in self.d.keys() ]
|
||||
else:
|
||||
return self.list_if( lambda k,data: ItemByRepoDB.not_just_tracking(data) )
|
||||
|
||||
# TODO: carry this to repodb, really :/
|
||||
def order(self, repo = None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
assert repo in [all, repos]
|
||||
import pisi.repodb
|
||||
order = [ 'repo-' + x for x in ctx.repodb.list() ]
|
||||
if repo == all:
|
||||
order += ['trdparty', 'inst']
|
||||
return order
|
||||
|
||||
# def list_repo(self, repo):
|
||||
# return self.dbyrepo[repo]
|
||||
|
||||
def repo_str(self, repo):
|
||||
if repo==thirdparty:
|
||||
repo='trdparty'
|
||||
elif repo==installed:
|
||||
repo='inst'
|
||||
else:
|
||||
assert type(repo) == type("")
|
||||
repo='repo-'+repo
|
||||
return repo
|
||||
|
||||
def str_repo(self, str):
|
||||
if str.startswith('repo-'):
|
||||
return str[5:]
|
||||
elif str=='trdparty':
|
||||
return thirdparty
|
||||
elif str=='inst':
|
||||
return installed
|
||||
else:
|
||||
raise Error(_('Invalid repository string'))
|
||||
|
||||
def has_key(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
if repo == None:
|
||||
repo = repos
|
||||
haskey = self.d.has_key(name, txn)
|
||||
if repo == all:
|
||||
return haskey
|
||||
elif repo == repos:
|
||||
data = self.d.get(name, txn)
|
||||
return haskey and ItemByRepoDB.not_just_tracking(data)
|
||||
else:
|
||||
repostr = self.repo_str(repo)
|
||||
return haskey and self.d.get(name, txn).has_key(repostr)
|
||||
|
||||
def get_item_repo(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
if repo == None:
|
||||
repo = repos
|
||||
def proc(txn):
|
||||
if not self.d.has_key(name, txn=txn):
|
||||
raise NotfoundError(_('Key %s not found') % name)
|
||||
s = self.d.get(name, txn=txn)
|
||||
if repo in [repos, all]:
|
||||
for repostr in self.order(repo):
|
||||
if s.has_key(repostr):
|
||||
return (s[repostr], self.str_repo(repostr))
|
||||
else:
|
||||
repostr = self.repo_str(repo)
|
||||
if s.has_key(repostr):
|
||||
return (s[repostr], repo)
|
||||
raise NotfoundError(_('Key %s in repo %s not found') % (name, repo))
|
||||
#return None
|
||||
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def get_item(self, name, repo = None, txn = None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
x = self.get_item_repo(name, repo, txn)
|
||||
if x:
|
||||
item, repo = x
|
||||
# discard repo, not always needed
|
||||
return item
|
||||
else:
|
||||
return None
|
||||
|
||||
def which_repo(self, name, txn = None):
|
||||
x = self.get_item_repo(name, txn=txn)
|
||||
if x:
|
||||
item, repo = x
|
||||
return repo
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_item(self, name, obj, repo, txn = None):
|
||||
assert not repo in [all, repos]
|
||||
repostr = self.repo_str(repo)
|
||||
def proc(txn):
|
||||
if not self.d.has_key(name):
|
||||
s = dict()
|
||||
else:
|
||||
s = self.d.get(name, txn)
|
||||
s[ repostr ] = obj
|
||||
self.d.put(name, s, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_item_repo(self, name, repo, txn = None):
|
||||
assert not repo in [all, repos]
|
||||
name = str(name)
|
||||
def p(txn):
|
||||
s = self.d.get(name, txn)
|
||||
repostr = self.repo_str(repo)
|
||||
if s.has_key(repostr):
|
||||
del s[repostr]
|
||||
if (len(s)==0):
|
||||
self.d.delete(name, txn)
|
||||
else:
|
||||
self.d.put(name, s, txn)
|
||||
self.d.txn_proc(p, txn)
|
||||
|
||||
def remove_item_only(self, name, txn = None):
|
||||
def p(txn):
|
||||
repo = self.which_repo(name, txn=txn)
|
||||
self.remove_item_repo(name, repo, txn=txn)
|
||||
self.d.txn_proc(p, txn)
|
||||
|
||||
def remove_item(self, name, repo=None, txn=None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
if not repo in [all, repos]:
|
||||
self.remove_item_repo(name, repo,txn=txn)
|
||||
else:
|
||||
self.remove_item_only(name,txn=txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
def proc(txn):
|
||||
for key in self.d.keys():
|
||||
self.remove_item_repo(key, repo, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
@@ -1,184 +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 os
|
||||
import fcntl
|
||||
import types
|
||||
import cPickle
|
||||
|
||||
import bsddb3.db as db
|
||||
import bsddb3.dbobj as dbobj
|
||||
#import bsddb3.dbshelve as shelve
|
||||
import pisi.dbshelve as shelve
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# check database version
|
||||
# if write is given it knows it has write access
|
||||
# if force is given it updates the specified db version
|
||||
def check_dbversion(versionfile, ver, write=False, update=False):
|
||||
verfn = join_path(pisi.context.config.db_dir(), versionfile)
|
||||
firsttime = False
|
||||
if os.path.exists(verfn):
|
||||
verfile = file(verfn, 'r')
|
||||
ls = verfile.readlines()
|
||||
currver = Version(ls[0])
|
||||
dbver = Version(ver)
|
||||
if currver < dbver:
|
||||
if not update:
|
||||
raise Error(_('Database version for %s insufficient. Please run rebuild-db command.') % versionfile)
|
||||
else:
|
||||
pass # continue to update, then
|
||||
elif currver > dbver:
|
||||
raise Error(_('Database version for %s greater than PiSi version. You need a newer PiSi.') % versionfile)
|
||||
elif not update:
|
||||
return True # db version is OK
|
||||
else:
|
||||
firsttime = True
|
||||
if write and (update or firsttime):
|
||||
if os.access(pisi.context.config.db_dir(), os.W_OK):
|
||||
verfile = file(verfn, 'w')
|
||||
verfile.write(ver)
|
||||
verfile.close()
|
||||
else:
|
||||
raise Error(_('Cannot attain write access to database environment'))
|
||||
else:
|
||||
raise Error(_('Database version %s not present.') % versionfile)
|
||||
|
||||
def lock_dbenv():
|
||||
ctx.dbenv_lock = file(join_path(pisi.context.config.db_dir(), 'dbenv.lock'), 'w')
|
||||
try:
|
||||
fcntl.flock(ctx.dbenv_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
raise Error(_("Another instance of PiSi is running. Only one instance is allowed to modify the PiSi database at a time."))
|
||||
|
||||
# write: write access to database environment
|
||||
# writeversion: would you like to be able
|
||||
def init_dbenv(write=False, writeversion=False):
|
||||
if os.access(pisi.context.config.db_dir(), os.R_OK):
|
||||
# try to read version
|
||||
check_dbversion('dbversion', pisi.__dbversion__, write=write, update=writeversion)
|
||||
check_dbversion('filesdbversion', pisi.__filesdbversion__, write=write, update=writeversion)
|
||||
else:
|
||||
raise Error(_('Cannot attain read access to database environment'))
|
||||
if write:
|
||||
if os.access(pisi.context.config.db_dir(), os.W_OK):
|
||||
lock_dbenv()
|
||||
ctx.dbenv = dbobj.DBEnv()
|
||||
flags = (db.DB_INIT_MPOOL | # cache
|
||||
db.DB_INIT_TXN | # transaction subsystem
|
||||
db.DB_INIT_LOG | # logging subsystem
|
||||
db.DB_RECOVER | # run normal recovery
|
||||
db.DB_CREATE) # allow db to create files
|
||||
ctx.dbenv.set_cachesize(0, 4*1024*1024)
|
||||
ctx.dbenv.open(pisi.context.config.db_dir(), flags)
|
||||
ctx.dbenv.set_flags(db.DB_LOG_AUTOREMOVE, 1) # clear inactive logs automatically
|
||||
else:
|
||||
raise Error(_("Cannot write to PiSi database."))
|
||||
else:
|
||||
ctx.dbenv = None # read-only access to database
|
||||
|
||||
#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 dbenv and os.access(os.path.dirname(filename), os.W_OK):
|
||||
flags = 'w'
|
||||
elif os.access(filename, os.R_OK):
|
||||
flags = 'r'
|
||||
else:
|
||||
raise Error(_('Cannot attain read or write access to database %s') % dbname)
|
||||
self.open(filename, dbname, filetype, flags, mode)
|
||||
|
||||
def destroy(self):
|
||||
os.unlink(self.filename)
|
||||
#self.close()
|
||||
#self.db.remove(self.filename)
|
||||
|
||||
def __del__(self):
|
||||
# superclass does something funky, we don't need that
|
||||
pass
|
||||
|
||||
def open(self, filename, dbname, filetype, flags=db.DB_CREATE, mode=0644):
|
||||
self.filename = filename
|
||||
self.closed = False
|
||||
if type(flags) == type(''):
|
||||
sflag = flags
|
||||
if sflag == 'r':
|
||||
flags = db.DB_RDONLY
|
||||
elif sflag == 'rw':
|
||||
flags = 0
|
||||
elif sflag == 'w':
|
||||
flags = db.DB_CREATE
|
||||
elif sflag == 'c':
|
||||
flags = db.DB_CREATE
|
||||
elif sflag == 'n':
|
||||
flags = db.DB_TRUNCATE | db.DB_CREATE
|
||||
else:
|
||||
raise Error, _("Flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags")
|
||||
self.flags = flags
|
||||
if self.flags & db.DB_RDONLY == 0:
|
||||
flags |= db.DB_AUTO_COMMIT # use txn subsystem in write mode
|
||||
self.lock()
|
||||
filename = os.path.realpath(filename) # we give absolute path due to dbenv
|
||||
#print 'opening', filename, filetype, flags, mode
|
||||
return self.db.open(filename, None, filetype, flags, mode)
|
||||
|
||||
def lock(self):
|
||||
self.lockfile = file(self.filename + '.lock', 'w')
|
||||
try:
|
||||
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
raise Error(_("Another instance of PiSi is running. Only one instance is allowed to modify the PiSi database at a time."))
|
||||
|
||||
def close(self):
|
||||
if self.closed:
|
||||
return
|
||||
self.db.close()
|
||||
if self.flags & db.DB_RDONLY == 0:
|
||||
self.unlock()
|
||||
self.closed = True
|
||||
|
||||
def unlock(self):
|
||||
self.lockfile.close()
|
||||
os.unlink(self.filename + '.lock')
|
||||
|
||||
@staticmethod
|
||||
def encodekey(key):
|
||||
'''utility method for dbs that must store unicodes in keys'''
|
||||
if type(key)==types.UnicodeType:
|
||||
return key.encode('utf-8')
|
||||
elif type(key)==types.StringType:
|
||||
return key
|
||||
else:
|
||||
raise Error('Key must be either string or unicode')
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
t_Build = [ autoxml.Integer, autoxml.optional]
|
||||
t_Distribution = [ autoxml.String, autoxml.mandatory]
|
||||
t_DistributionRelease = [ autoxml.String, autoxml.mandatory]
|
||||
t_Architecture = [ autoxml.String, autoxml.mandatory]
|
||||
t_InstalledSize = [ autoxml.Long, autoxml.mandatory]
|
||||
t_PackageSize = [ autoxml.Long, autoxml.optional]
|
||||
t_PackageURI = [ autoxml.String, autoxml.optional]
|
||||
t_PackageFormat = [ autoxml.String, autoxml.optional]
|
||||
|
||||
t_Source = [ Source, autoxml.optional]
|
||||
|
||||
def decode_hook(self, node, errs, where):
|
||||
self.version = self.history[0].version
|
||||
self.release = self.history[0].release
|
||||
|
||||
def __str__(self):
|
||||
s = specfile.Package.__str__(self)
|
||||
s += _('Distribution: %s, Dist. Release: %s\n') % \
|
||||
(self.distribution, self.distributionRelease)
|
||||
s += _('Architecture: %s, Installed Size: %s') % \
|
||||
(self.architecture, self.installedSize)
|
||||
return s
|
||||
|
||||
class MetaData(xmlfile.XmlFile):
|
||||
"""Package metadata. Metadata is composed of Specfile and various
|
||||
other information. A metadata has two parts, Source and Package."""
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Source = [ Source, autoxml.mandatory]
|
||||
t_Package = [ Package, autoxml.mandatory]
|
||||
#t_History = [ [Update], autoxml.mandatory]
|
||||
|
||||
def from_spec(self, src, pkg):
|
||||
# this just copies fields, it doesn't fix every necessary field
|
||||
self.source.name = src.name
|
||||
self.source.homepage = src.homepage
|
||||
self.source.packager = src.packager
|
||||
self.package.source = self.source # FIXME: I know that replication sucks here, but this is the easiest for now-- exa
|
||||
self.package.name = pkg.name
|
||||
self.package.summary = pkg.summary
|
||||
self.package.description = pkg.description
|
||||
self.package.icon = pkg.icon
|
||||
self.package.isA = pkg.isA
|
||||
self.package.partOf = pkg.partOf
|
||||
self.package.license = pkg.license
|
||||
self.package.packageDependencies = pkg.packageDependencies
|
||||
self.package.componentDependencies = pkg.componentDependencies
|
||||
self.package.files = pkg.files
|
||||
# FIXME: no need to copy full history with comments
|
||||
self.package.history = src.history
|
||||
self.package.conflicts = pkg.conflicts
|
||||
self.package.providesComar = pkg.providesComar
|
||||
#self.package.requiresComar = pkg.requiresComar
|
||||
self.package.additionalFiles = pkg.additionalFiles
|
||||
|
||||
# FIXME: right way to do it?
|
||||
self.source.version = src.version
|
||||
self.source.release = src.release
|
||||
self.package.version = src.version
|
||||
self.package.release = src.release
|
||||
@@ -1,47 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
import os.path
|
||||
import pisi.context as ctx
|
||||
from pisi import Error
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
class Mirrors:
|
||||
def __init__(self, config=ctx.const.mirrors_conf):
|
||||
self.mirrors = {}
|
||||
self._parse(config)
|
||||
|
||||
def get_mirrors(self, name):
|
||||
if self.mirrors.has_key(name):
|
||||
return list(self.mirrors[name])
|
||||
|
||||
return None
|
||||
|
||||
def _add_mirror(self, name, url):
|
||||
if self.mirrors.has_key(name):
|
||||
self.mirrors[name].append(url)
|
||||
else:
|
||||
self.mirrors[name] = [url]
|
||||
|
||||
def _parse(self, config):
|
||||
if os.path.exists(config):
|
||||
for line in open(config, "r").readlines():
|
||||
if not line.startswith('#') and not line == '\n':
|
||||
mirror = line.strip().split()
|
||||
if len(mirror) == 2:
|
||||
(name, url) = mirror
|
||||
self._add_mirror(name, url)
|
||||
else:
|
||||
raise Error(_('Mirrors file %s does not exist. Could not resolve mirrors://') % config)
|
||||
|
||||
@@ -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)
|
||||
@@ -1,839 +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 Operations: install/remove/upgrade"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
import pisi.util as util
|
||||
import pisi.dependency as dependency
|
||||
import pisi.pgraph as pgraph
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
import pisi.atomicoperations as atomicoperations
|
||||
import pisi.ui as ui
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
def upgrade_pisi():
|
||||
"""forces to reload pisi modules and runs rebuild-db if needed."""
|
||||
|
||||
old_filesdbversion = pisi.__filesdbversion__
|
||||
old_dbversion = pisi.__dbversion__
|
||||
|
||||
def rebuild_db():
|
||||
"""rebuild_db is necessary if database structures has changed."""
|
||||
from pisi.version import Version
|
||||
import pisi.context as ctx
|
||||
|
||||
if Version(pisi.__filesdbversion__) > Version(old_filesdbversion) or \
|
||||
Version(pisi.__dbversion__) > Version(old_dbversion):
|
||||
pisi.api.init(database=False)
|
||||
ctx.ui.info(_("* PiSi database version has changed. Rebuilding database..."))
|
||||
pisi.api.rebuild_db()
|
||||
ctx.ui.info(_("* Database rebuild operation is completed succesfully."))
|
||||
pisi.api.finalize()
|
||||
|
||||
def reload_pisi():
|
||||
for module in sys.modules.keys():
|
||||
if module.startswith("pisi."):
|
||||
"""removal from sys.modules forces reload via import"""
|
||||
del(sys.modules[module])
|
||||
reload(pisi)
|
||||
|
||||
pisi.api.finalize()
|
||||
reload_pisi()
|
||||
rebuild_db()
|
||||
pisi.api.init()
|
||||
|
||||
# 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, repo = None):
|
||||
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()
|
||||
if not ctx.get_option('ignore_file_conflicts'):
|
||||
check_conflicts(order, packagedb)
|
||||
order.reverse()
|
||||
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
for x in order:
|
||||
atomicoperations.install_single_file(dfn[x])
|
||||
|
||||
pisi_installed = ctx.installdb.is_installed('pisi')
|
||||
|
||||
if 'pisi' in order and pisi_installed:
|
||||
upgrade_pisi()
|
||||
|
||||
def check_conflict(pkg):
|
||||
conflicts = []
|
||||
|
||||
for c in pkg.conflicts:
|
||||
if ctx.installdb.is_installed(c):
|
||||
conflicts.append(c)
|
||||
|
||||
return conflicts
|
||||
|
||||
def calculate_conflicts(order, packagedb):
|
||||
B_0 = set(order)
|
||||
C = D = set()
|
||||
pkg_conflicts = {}
|
||||
|
||||
for x in order:
|
||||
pkg = packagedb.get_package(x)
|
||||
B_p = set(check_conflict(pkg))
|
||||
if B_p:
|
||||
pkg_conflicts[x] = B_p
|
||||
C = C.union(B_p)
|
||||
|
||||
B_i = B_0.intersection(set(pkg.conflicts))
|
||||
# check if there are any conflicts within the packages that are
|
||||
# going to be installed
|
||||
if B_i:
|
||||
D = D.union(B_i)
|
||||
D.add(pkg.name)
|
||||
|
||||
return (C, D, pkg_conflicts)
|
||||
|
||||
def check_conflicts(order, packagedb):
|
||||
"""check if upgrading to the latest versions will cause havoc
|
||||
done in a simple minded way without regard for dependencies of
|
||||
conflicts, etc."""
|
||||
|
||||
(C, D, pkg_conflicts) = calculate_conflicts(order, packagedb)
|
||||
|
||||
if D:
|
||||
raise Error(_("Selected packages [%s] are in conflict with each other.") %
|
||||
util.strlist(list(D)))
|
||||
|
||||
if pkg_conflicts:
|
||||
conflicts = ""
|
||||
for pkg in pkg_conflicts.keys():
|
||||
conflicts += _("[%s conflicts with: %s]") % (pkg, util.strlist(pkg_conflicts[pkg]))
|
||||
|
||||
ctx.ui.info(_("The following packages have conflicts: %s") %
|
||||
conflicts)
|
||||
|
||||
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
|
||||
raise Error(_("Conflicts remain"))
|
||||
|
||||
if remove(list(C), ignore_dep=True, bypass_safety=True) == False:
|
||||
raise Error(_("Conflicts remain"))
|
||||
|
||||
def expand_components(A):
|
||||
Ap = set()
|
||||
for x in A:
|
||||
if ctx.componentdb.has_component(x):
|
||||
Ap = Ap.union(ctx.componentdb.get_union_comp(x).packages)
|
||||
else:
|
||||
Ap.add(x)
|
||||
return Ap
|
||||
|
||||
def is_upgradable(name, ignore_build = False):
|
||||
if not ctx.installdb.is_installed(name):
|
||||
return False
|
||||
(version, release, build) = ctx.installdb.get_version(name)
|
||||
try:
|
||||
pkg = ctx.packagedb.get_package(name)
|
||||
except:
|
||||
return False
|
||||
if ignore_build or (not build) or (not pkg.build):
|
||||
return Version(release) < Version(pkg.release)
|
||||
else:
|
||||
return build < pkg.build
|
||||
|
||||
def upgrade_base(A = set()):
|
||||
ignore_build = ctx.get_option('ignore_build_no')
|
||||
if not ctx.get_option('bypass_safety'):
|
||||
if ctx.componentdb.has_component('system.base'):
|
||||
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
|
||||
extra_installs = filter(lambda x: not ctx.installdb.is_installed(x), systembase - A)
|
||||
if extra_installs:
|
||||
ctx.ui.warning(_('Safety switch: Following packages in system.base will be installed: ') +
|
||||
util.strlist(extra_installs))
|
||||
G_f, install_order = plan_install_pkg_names(extra_installs)
|
||||
extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), systembase - set(install_order))
|
||||
upgrade_order = []
|
||||
if extra_upgrades:
|
||||
ctx.ui.warning(_('Safety switch: Following packages in system.base will be upgraded: ') +
|
||||
util.strlist(extra_upgrades))
|
||||
G_f, upgrade_order = plan_upgrade(extra_upgrades, ignore_build)
|
||||
# return packages that must be added to any installation
|
||||
return set(install_order + upgrade_order)
|
||||
else:
|
||||
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
|
||||
return set()
|
||||
|
||||
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 = [str(x) for x in A] #FIXME: why do we still get unicode input here? :/ -- exa
|
||||
# A was a list, remove duplicates and expand components
|
||||
A_0 = A = expand_components(set(A))
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# 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(_("The following package(s) are already installed and are not going to be installed again:\n") +
|
||||
util.strlist(d))
|
||||
A = Ap
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to install.'))
|
||||
return
|
||||
|
||||
A |= upgrade_base(A)
|
||||
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
G_f, order = plan_install_pkg_names(A)
|
||||
else:
|
||||
G_f = None
|
||||
order = list(A)
|
||||
|
||||
if len(order) > 1:
|
||||
ctx.ui.info(_("Following packages will be installed in the respective "
|
||||
"order to satisfy dependencies:\n") + util.strlist(order))
|
||||
|
||||
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
|
||||
total_size, symbol = util.human_readable_size(total_size)
|
||||
ctx.ui.info(_('Total size of package(s): %.2f %s') % (total_size, symbol))
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
if set(order) - A_0:
|
||||
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
|
||||
return False
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
pisi_installed = ctx.installdb.is_installed('pisi')
|
||||
|
||||
for x in order:
|
||||
atomicoperations.install_single_name(x, True) # allow reinstalls here
|
||||
|
||||
if 'pisi' in order and pisi_installed:
|
||||
upgrade_pisi()
|
||||
|
||||
def plan_install_pkg_names(A):
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = ctx.packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies():
|
||||
ctx.ui.debug('checking %s' % str(dep))
|
||||
# we don't deal with already *satisfied* dependencies
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
if not ctx.get_option('ignore_file_conflicts'):
|
||||
check_conflicts(order, ctx.packagedb)
|
||||
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 minimum or maximum number of upgrades according to options."""
|
||||
|
||||
ignore_build = ctx.get_option('ignore_build_no')
|
||||
security_only = ctx.get_option('security_only')
|
||||
|
||||
if not A:
|
||||
# if A is empty, then upgrade all packages
|
||||
A = ctx.installdb.list_installed()
|
||||
|
||||
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, True)
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
if ctx.packagedb.has_package(x):
|
||||
pkg = ctx.packagedb.get_package(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is not available in repositories.') % x, True)
|
||||
continue
|
||||
|
||||
if security_only:
|
||||
updates = [i for i in pkg.history if Version(i.release) > Version(release)]
|
||||
if not pisi.util.any(lambda i:i.type == 'security', updates):
|
||||
continue
|
||||
|
||||
if ignore_build or (not build) or (not pkg.build):
|
||||
if Version(release) < Version(pkg.release):
|
||||
Ap.append(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is already at the latest release %s.')
|
||||
% (pkg.name, pkg.release), True)
|
||||
else:
|
||||
if build < pkg.build:
|
||||
Ap.append(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is already at the latest build %s.')
|
||||
% (pkg.name, pkg.build), True)
|
||||
|
||||
|
||||
A = set(Ap)
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to upgrade.'))
|
||||
return True
|
||||
|
||||
A |= upgrade_base(A)
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
G_f, order = plan_upgrade(A, ignore_build)
|
||||
else:
|
||||
G_f = None
|
||||
order = list(A)
|
||||
|
||||
ctx.ui.info(_('The following packages will be upgraded: ') +
|
||||
util.strlist(order))
|
||||
|
||||
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
|
||||
total_size, symbol = util.human_readable_size(total_size)
|
||||
ctx.ui.info(_('Total size of packages: %.2f %s') % (total_size, symbol))
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
if set(order) - A_0:
|
||||
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
|
||||
return False
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
paths = []
|
||||
for x in order:
|
||||
install_op = atomicoperations.Install.from_name(x)
|
||||
paths.append(install_op.package_fname)
|
||||
|
||||
# fetch to be upgraded packages but do not install them.
|
||||
if ctx.get_option('fetch_only'):
|
||||
return
|
||||
|
||||
for path in paths:
|
||||
install_op = atomicoperations.Install(path, ignore_file_conflicts = True)
|
||||
install_op.install(True)
|
||||
|
||||
if 'pisi' in order:
|
||||
upgrade_pisi()
|
||||
|
||||
def plan_upgrade(A, ignore_build = False):
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
packagedb = ctx.packagedb
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
|
||||
# TODO: conflicts
|
||||
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies():
|
||||
# add packages that can be upgraded
|
||||
if dependency.repo_satisfies_dep(dep):
|
||||
if ctx.installdb.is_installed(dep.package):
|
||||
if ctx.get_option('eager'):
|
||||
if not is_upgradable(dep.package):
|
||||
continue
|
||||
else:
|
||||
if dependency.installed_satisfies_dep(dep):
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
else:
|
||||
ctx.ui.error(_('Dependency %s of %s cannot be satisfied') % (dep, x))
|
||||
raise Error(_("Upgrade is not possible."))
|
||||
B = Bp
|
||||
# now, search reverse dependencies to see if anything
|
||||
# should be upgraded
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
rev_deps = packagedb.get_rev_deps(x)
|
||||
for (rev_dep, depinfo) in rev_deps:
|
||||
if ctx.get_option('eager'):
|
||||
# add all upgradable reverse deps
|
||||
if is_upgradable(rev_dep):
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
else:
|
||||
# add only installed but unsatisfied reverse dependencies
|
||||
if ctx.installdb.is_installed(rev_dep) and \
|
||||
(not dependency.installed_satisfies_dep(depinfo)):
|
||||
if not dependency.repo_satisfies_dep(depinfo):
|
||||
raise Error(_('Reverse dependency %s of %s cannot be satisfied') % (rev_dep, x))
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
B = Bp
|
||||
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
if not ctx.get_option('ignore_file_conflicts'):
|
||||
check_conflicts(order, ctx.packagedb)
|
||||
return G_f, order
|
||||
|
||||
def remove(A, ignore_dep = False, bypass_safety = False):
|
||||
"""remove set A of packages from system (A is a list of package names)"""
|
||||
|
||||
A = [str(x) for x in A]
|
||||
|
||||
# filter packages that are not installed
|
||||
A_0 = A = expand_components(set(A))
|
||||
|
||||
if not ctx.get_option('bypass_safety') and not bypass_safety:
|
||||
if ctx.componentdb.has_component('system.base'):
|
||||
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
|
||||
refused = A.intersection(systembase)
|
||||
if refused:
|
||||
ctx.ui.warning(_('Safety switch: cannot remove the following packages in system.base: ') +
|
||||
util.strlist(refused))
|
||||
A = A - systembase
|
||||
else:
|
||||
ctx.ui.warning(_('Safety switch: the component system.base cannot be found'))
|
||||
|
||||
Ap = []
|
||||
for x in A:
|
||||
if ctx.installdb.is_installed(x):
|
||||
Ap.append(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s does not exist. Cannot remove.') % x)
|
||||
A = set(Ap)
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to remove.'))
|
||||
return False
|
||||
|
||||
if not ctx.config.get_option('ignore_dependency') and not ignore_dep:
|
||||
G_f, order = plan_remove(A)
|
||||
else:
|
||||
G_f = None
|
||||
order = A
|
||||
|
||||
ctx.ui.info(_("""The following minimal list of packages will be removed
|
||||
in the respective order to satisfy dependencies:
|
||||
""") + util.strlist(order))
|
||||
if len(order) > len(A_0):
|
||||
if not ctx.ui.confirm(_('Do you want to continue?')):
|
||||
ctx.ui.warning(_('Package removal declined'))
|
||||
return False
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
for x in order:
|
||||
if ctx.installdb.is_installed(x):
|
||||
atomicoperations.remove_single(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is not installed. Cannot remove.') % x)
|
||||
|
||||
def plan_remove(A):
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(ctx.packagedb, pisi.itembyrepodb.installed) # construct G_f
|
||||
|
||||
# find the (install closure) graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = ctx.packagedb.get_package(x, pisi.itembyrepodb.installed)
|
||||
rev_deps = ctx.packagedb.get_rev_deps(x, pisi.itembyrepodb.installed)
|
||||
for (rev_dep, depinfo) in rev_deps:
|
||||
# we don't deal with uninstalled rev deps
|
||||
# and unsatisfied dependencies (this is important, too)
|
||||
if ctx.packagedb.has_package(rev_dep, pisi.itembyrepodb.installed) and \
|
||||
dependency.installed_satisfies_dep(depinfo):
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
B = Bp
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
return G_f, order
|
||||
|
||||
def expand_src_components(A):
|
||||
Ap = set()
|
||||
for x in A:
|
||||
if ctx.componentdb.has_component(x):
|
||||
Ap = Ap.union(ctx.componentdb.get_union_comp(x).sources)
|
||||
else:
|
||||
Ap.add(x)
|
||||
return Ap
|
||||
|
||||
##def build_names(A, rebuild = true):
|
||||
##
|
||||
## # A was a list, remove duplicates and expand components
|
||||
## A_0 = A = expand_src_components(set(A))
|
||||
## ctx.ui.debug('A = %s' % str(A))
|
||||
##
|
||||
## # filter packages that are already installed
|
||||
## if not rebuild:
|
||||
## Ap = set(filter(lambda x: not ctx.installdb.is_installed(x), A))
|
||||
## d = A - Ap
|
||||
## if len(d) > 0:
|
||||
## ctx.ui.warning(_('Not re-building the following packages: ') +
|
||||
## util.strlist(d))
|
||||
## A = Ap
|
||||
##
|
||||
## if len(A)==0:
|
||||
## ctx.ui.info(_('No packages to build.'))
|
||||
## return
|
||||
##
|
||||
## if not ctx.config.get_option('ignore_dependency'):
|
||||
## G_f, order_inst = plan_build_pkg_names(A)
|
||||
## else:
|
||||
## G_f = None
|
||||
## order_inst = []
|
||||
##
|
||||
## ctx.ui.info(_("""The following minimal list of packages will be installed
|
||||
##in the respective order to satisfy dependencies:
|
||||
##""") + util.strlist(order))
|
||||
##
|
||||
## if ctx.get_option('dry_run'):
|
||||
## return
|
||||
##
|
||||
## if len(order) > len(A_0):
|
||||
## if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
|
||||
## return False
|
||||
##
|
||||
## ctx.ui.notify(ui.packagestogo, order = order)
|
||||
##
|
||||
## for x in order:
|
||||
## atomicoperations.install_single_name(x)
|
||||
##
|
||||
##def plan_build_names(A):
|
||||
## # try to construct a pisi graph of packages to
|
||||
## # install / reinstall
|
||||
##
|
||||
## G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
##
|
||||
## # find the "install closure" graph of G_f by package
|
||||
## # set A using packagedb
|
||||
## for x in A:
|
||||
## G_f.add_package(x)
|
||||
## B = A
|
||||
##
|
||||
## while len(B) > 0:
|
||||
## Bp = set()
|
||||
## for x in B:
|
||||
## pkg = packagedb.get_package(x)
|
||||
## for dep in pkg.runtimeDependencies():
|
||||
## ctx.ui.debug('checking %s' % str(dep))
|
||||
## # we don't deal with already *satisfied* dependencies
|
||||
## if not dependency.installed_satisfies_dep(dep):
|
||||
## if not dep.package in G_f.vertices():
|
||||
## Bp.add(str(dep.package))
|
||||
## G_f.add_dep(x, dep)
|
||||
## B = Bp
|
||||
## if ctx.config.get_option('debug'):
|
||||
## G_f.write_graphviz(sys.stdout)
|
||||
## order = G_f.topological_sort()
|
||||
## order.reverse()
|
||||
## check_conflicts(order)
|
||||
## return G_f, order
|
||||
|
||||
def emerge(A, rebuild_all = False):
|
||||
|
||||
# A was a list, remove duplicates and expand components
|
||||
A = [str(x) for x in A]
|
||||
A_0 = A = expand_src_components(set(A))
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to emerge.'))
|
||||
return
|
||||
|
||||
#A |= upgrade_base(A)
|
||||
|
||||
# FIXME: Errr... order_build changes type conditionally and this
|
||||
# is not good. - baris
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
G_f, order_inst, order_build = plan_emerge(A, rebuild_all)
|
||||
else:
|
||||
G_f = None
|
||||
order_inst = []
|
||||
order_build = A
|
||||
|
||||
if order_inst:
|
||||
ctx.ui.info(_("""The following minimal list of packages will be installed
|
||||
from repository in the respective order to satisfy dependencies:
|
||||
""") + util.strlist(order_inst))
|
||||
ctx.ui.info(_("""The following minimal list of packages will be built and
|
||||
installed in the respective order to satisfy dependencies:
|
||||
""") + util.strlist(order_build))
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
if len(order_inst) + len(order_build) > len(A_0):
|
||||
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
|
||||
return False
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order_inst)
|
||||
|
||||
pisi_installed = ctx.installdb.is_installed('pisi')
|
||||
|
||||
for x in order_inst:
|
||||
atomicoperations.install_single_name(x)
|
||||
|
||||
#ctx.ui.notify(ui.packagestogo, order = order_build)
|
||||
|
||||
for x in order_build:
|
||||
package_names = atomicoperations.build(x)[0]
|
||||
install_pkg_files(package_names) # handle inter-package deps here
|
||||
|
||||
# FIXME: take a look at the fixme above :(, we have to be sure
|
||||
# that order_build is a known type...
|
||||
U = set(order_build)
|
||||
U.update(order_inst)
|
||||
if 'pisi' in order_build or (('pisi' in U) and pisi_installed):
|
||||
upgrade_pisi()
|
||||
|
||||
def plan_emerge(A, rebuild_all):
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pisi.graph.Digraph()
|
||||
|
||||
def get_spec(name):
|
||||
if ctx.sourcedb.has_spec(name):
|
||||
return ctx.sourcedb.get_spec(name)
|
||||
else:
|
||||
raise Error(_('Cannot find source package: %s') % name)
|
||||
def get_src(name):
|
||||
return get_spec(name).source
|
||||
def add_src(src):
|
||||
if not str(src.name) in G_f.vertices():
|
||||
G_f.add_vertex(str(src.name), (src.version, src.release))
|
||||
def pkgtosrc(pkg):
|
||||
return ctx.sourcedb.pkgtosrc(pkg)
|
||||
|
||||
# setup first
|
||||
#specfiles = [ ctx.sourcedb.get_source(x)[1] for x in A ]
|
||||
#pkgtosrc = {}
|
||||
B = A
|
||||
|
||||
install_list = set()
|
||||
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
sf = get_spec(x)
|
||||
src = sf.source
|
||||
add_src(src)
|
||||
|
||||
# add dependencies
|
||||
|
||||
def process_dep(dep):
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
if dependency.repo_satisfies_dep(dep):
|
||||
install_list.add(dep.package)
|
||||
return
|
||||
srcdep = pkgtosrc(dep.package)
|
||||
if not srcdep in G_f.vertices():
|
||||
Bp.add(srcdep)
|
||||
add_src(get_src(srcdep))
|
||||
if not src.name == srcdep: # firefox - firefox-devel thing
|
||||
G_f.add_edge(src.name, srcdep)
|
||||
|
||||
for builddep in src.buildDependencies:
|
||||
process_dep(builddep)
|
||||
|
||||
for pkg in sf.packages:
|
||||
for rtdep in pkg.packageDependencies:
|
||||
process_dep(rtdep)
|
||||
B = Bp
|
||||
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order_build = G_f.topological_sort()
|
||||
order_build.reverse()
|
||||
|
||||
G_f2, order_inst = plan_install_pkg_names(install_list)
|
||||
|
||||
return G_f, order_inst, order_build
|
||||
@@ -1,135 +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 methods to add/remove files, extract control files"""
|
||||
|
||||
from os.path import join, exists, basename, dirname
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.archive as archive
|
||||
from pisi.uri import URI
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Package:
|
||||
"""PiSi Package Class provides access to a pisi package (.pisi
|
||||
file)."""
|
||||
def __init__(self, packagefn, mode='r'):
|
||||
self.filepath = packagefn
|
||||
url = URI(packagefn)
|
||||
|
||||
if url.is_remote_file():
|
||||
self.fetch_remote_file(url)
|
||||
|
||||
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
|
||||
|
||||
def fetch_remote_file(self, url):
|
||||
from fetcher import fetch_url
|
||||
dest = ctx.config.packages_dir()
|
||||
self.filepath = join(dest, url.filename())
|
||||
|
||||
#FIXME: also check sha1sum
|
||||
if not exists(self.filepath):
|
||||
try:
|
||||
fetch_url(url, dest, ctx.ui.Progress)
|
||||
except pisi.fetcher.FetchError:
|
||||
# Bug 3465
|
||||
if ctx.get_option('reinstall'):
|
||||
raise Error(_("There was a problem while fetching '%s'.\nThe package "
|
||||
"may have been upgraded. Please try to upgrade the package.") % url);
|
||||
raise
|
||||
else:
|
||||
ctx.ui.info(_('%s [cached]') % url.filename())
|
||||
|
||||
def add_to_package(self, fn, an=None):
|
||||
"""Add a file or directory to package"""
|
||||
self.impl.add_to_archive(fn, an)
|
||||
|
||||
def close(self):
|
||||
"""Close the package archive"""
|
||||
self.impl.close()
|
||||
|
||||
def extract(self, outdir):
|
||||
"""Extract entire package contents to directory"""
|
||||
self.extract_dir('', outdir) # means package root
|
||||
|
||||
def extract_files(self, paths, outdir):
|
||||
"""Extract paths to outdir"""
|
||||
self.impl.unpack_files(paths, outdir)
|
||||
|
||||
def extract_file(self, path, outdir):
|
||||
"""Extract file with path to outdir"""
|
||||
self.extract_files([path], outdir)
|
||||
|
||||
def extract_dir(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
copies the directory archiveroot/dir to outdir"""
|
||||
self.impl.unpack_dir(dir, outdir)
|
||||
|
||||
def extract_install(self, outdir):
|
||||
if self.impl.has_file(ctx.const.install_tar_lzma):
|
||||
self.extract_file(ctx.const.install_tar_lzma, ctx.config.tmp_dir())
|
||||
tar = archive.ArchiveTar(join(ctx.config.tmp_dir(), ctx.const.install_tar_lzma), 'tarlzma', False)
|
||||
tar.unpack_dir(outdir)
|
||||
else:
|
||||
self.extract_dir_flat('install', 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)
|
||||
@@ -1,172 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""package database
|
||||
interface for update/query to local package repository
|
||||
|
||||
we basically store everything in PackageInfo class
|
||||
yes, we are cheap
|
||||
"""
|
||||
|
||||
import os
|
||||
import fcntl
|
||||
import types
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.itembyrepodb import ItemByRepoDB
|
||||
import pisi.itembyrepodb as itembyrepodb
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
def __init__(self, pkg):
|
||||
pisi.Error.__init__("Package %s not found" % pkg)
|
||||
self.pkg = pkg
|
||||
|
||||
class PackageDB(object):
|
||||
"""PackageDB class provides an interface to the package database
|
||||
using shelf objects"""
|
||||
|
||||
def __init__(self):
|
||||
self.d = ItemByRepoDB('package')
|
||||
self.dr = ItemByRepoDB('revdep')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dr.close()
|
||||
|
||||
def destroy(self):
|
||||
self.d.destroy()
|
||||
self.dr.destroy()
|
||||
|
||||
def has_package(self, name, repo=None, txn = None):
|
||||
return self.d.has_key(name, repo, txn=txn)
|
||||
|
||||
def get_package(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item(name, repo, txn=txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise Error(_('Package %s not found') % name)
|
||||
|
||||
def get_package_repo(self, name, repo=None, txn = None):
|
||||
return self.d.get_item_repo(name, repo, txn=txn)
|
||||
|
||||
def which_repo(self, name, txn = None):
|
||||
return self.d.which_repo(name, txn=txn)
|
||||
|
||||
def get_rev_deps(self, name, repo = None, txn = None):
|
||||
if self.dr.has_key(name, repo, txn=txn):
|
||||
return self.dr.get_item(name, repo, txn=txn)
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_deps(self, name, repo = None, txn = None):
|
||||
if self.d.has_key(name, repo, txn=txn):
|
||||
pinfo = self.d.get_item(name, repo, txn=txn)
|
||||
return pinfo.packageDependencies
|
||||
else:
|
||||
return []
|
||||
|
||||
def list_packages(self, repo=None):
|
||||
return self.d.list(repo)
|
||||
|
||||
def add_package(self, package_info, repo, txn = None):
|
||||
name = str(package_info.name)
|
||||
|
||||
def proc(txn):
|
||||
self.d.add_item(name, package_info, repo, txn)
|
||||
for dep in package_info.runtimeDependencies():
|
||||
dep_name = str(dep.package)
|
||||
if self.dr.has_key(dep_name, repo, txn):
|
||||
revdep = self.dr.get_item(dep_name, repo, txn)
|
||||
revdep = filter(lambda (n,d):n!=name, revdep)
|
||||
revdep.append( (name, dep) )
|
||||
self.dr.add_item(dep_name, revdep, repo, txn)
|
||||
else:
|
||||
self.dr.add_item(dep_name, [ (name, dep) ], repo, txn)
|
||||
# add component
|
||||
ctx.componentdb.add_package(package_info.partOf, package_info.name, repo, txn)
|
||||
# index summary and description
|
||||
for (lang, doc) in package_info.summary.iteritems():
|
||||
if lang in ['en', 'tr']:
|
||||
pisi.search.add_doc('summary', lang, package_info.name, doc, repo=repo, txn=txn)
|
||||
for (lang, doc) in package_info.description.iteritems():
|
||||
if lang in ['en', 'tr']:
|
||||
pisi.search.add_doc('description', lang, package_info.name, doc, repo=repo, txn=txn)
|
||||
|
||||
ctx.txn_proc(proc, txn)
|
||||
|
||||
def clear(self, txn = None):
|
||||
self.d.clear()
|
||||
self.dr.clear()
|
||||
|
||||
def remove_package(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
def proc(txn):
|
||||
package_info = self.d.get_item(name, repo, txn=txn)
|
||||
self.d.remove_item(name, repo, txn=txn)
|
||||
for dep in package_info.runtimeDependencies():
|
||||
dep_name = str(dep.package)
|
||||
if self.dr.has_key(dep_name, repo, txn):
|
||||
revdep = self.dr.get_item(dep_name, repo, txn)
|
||||
revdep = filter(lambda (n,d):n!=name, revdep)
|
||||
if revdep:
|
||||
self.dr.add_item(dep_name, revdep, repo, txn)
|
||||
else:
|
||||
# Bug 3558: removal of revdep list of a package from revdepdb
|
||||
# should only be done by the list members (dep. packages), not
|
||||
# the package itself. So if a package is removed, it is removed
|
||||
# from packagedb but its revdepdb part may still exist, until
|
||||
# all the list members are removed.
|
||||
self.dr.remove_item(dep_name, repo, txn=txn)
|
||||
|
||||
ctx.componentdb.remove_package(package_info.partOf, package_info.name, repo, txn)
|
||||
for (lang, doc) in package_info.summary.iteritems():
|
||||
if lang in ['en', 'tr']:
|
||||
pisi.search.remove_doc('summary', lang, package_info.name, doc, repo=repo, txn=txn)
|
||||
for (lang, doc) in package_info.description.iteritems():
|
||||
if lang in ['en', 'tr']:
|
||||
pisi.search.remove_doc('description', lang, package_info.name, doc, repo=repo, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
def proc(txn):
|
||||
self.d.remove_repo(repo, txn=txn)
|
||||
self.dr.remove_repo(repo, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
pkgdb = None
|
||||
|
||||
def remove_tracking_package(name, txn = None):
|
||||
# remove the guy from the tracking databases
|
||||
if pkgdb.has_package(name, itembyrepodb.installed, txn=txn):
|
||||
pkgdb.remove_package(name, itembyrepodb.installed, txn=txn)
|
||||
if pkgdb.has_package(name, itembyrepodb.thirdparty, txn=txn):
|
||||
pkgdb.remove_package(name, itembyrepodb.thirdparty, txn=txn)
|
||||
|
||||
def init_db():
|
||||
global pkgdb
|
||||
pkgdb = PackageDB()
|
||||
return pkgdb
|
||||
|
||||
def finalize_db():
|
||||
global pkgdb
|
||||
if pkgdb:
|
||||
pkgdb.close()
|
||||
@@ -1,84 +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"""
|
||||
|
||||
import string
|
||||
|
||||
from graph import *
|
||||
|
||||
# Cache the results from packagedb queries in a graph
|
||||
|
||||
class PGraph(Digraph):
|
||||
|
||||
def __init__(self, packagedb, repo = pisi.itembyrepodb.repos):
|
||||
super(PGraph, self).__init__()
|
||||
self.packagedb = packagedb
|
||||
self.repo = repo
|
||||
|
||||
def add_package(self, pkg):
|
||||
pkg1 = self.packagedb.get_package(pkg, self.repo)
|
||||
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, self.repo)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not pkg2name in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(pkg2name, self.repo)
|
||||
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, self.repo)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not depinfo.package in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(depinfo.package, self.repo)
|
||||
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, self.repo)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not depinfo.package in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(pkg, self.repo)
|
||||
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, self.repo)
|
||||
pkg1data = (pkg1.version, pkg1.release)
|
||||
pkg2data = None
|
||||
if not pkg in self.vertices():
|
||||
pkg2 = self.packagedb.get_package(conflinfo.package, self.repo)
|
||||
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) + ')\" ]')
|
||||
|
||||
@@ -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
|
||||
@@ -1,827 +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.
|
||||
#
|
||||
|
||||
"""
|
||||
autoxml is a metaclass for automatic XML translation, using
|
||||
a miniature type system. (w00t!) This is based on an excellent
|
||||
high-level XML processing prototype that Gurer prepared.
|
||||
|
||||
Method names are mixedCase for compatibility with minidom,
|
||||
an old library.
|
||||
"""
|
||||
|
||||
# System
|
||||
import locale
|
||||
import codecs
|
||||
import types
|
||||
import formatter
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
from pisi.pxml.xmlext import *
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
import pisi.context as ctx
|
||||
import pisi.util as util
|
||||
import pisi.oo as oo
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# requirement specs
|
||||
|
||||
mandatory, optional = range(2) # poor man's enum
|
||||
|
||||
# basic types
|
||||
|
||||
String = types.StringType
|
||||
Text = types.UnicodeType
|
||||
Integer = types.IntType
|
||||
Long = types.LongType
|
||||
Float = types.FloatType
|
||||
|
||||
#class datatype(type):
|
||||
# def __init__(cls, name, bases, dict):
|
||||
# """entry point for metaclass code"""
|
||||
# # standard initialization
|
||||
# super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
class LocalText(dict):
|
||||
"""Handles XML tags with localized text"""
|
||||
|
||||
def __init__(self, tag = "", req = optional):
|
||||
self.tag = tag
|
||||
self.req = req
|
||||
dict.__init__(self)
|
||||
|
||||
def decode(self, node, errs, where = ""):
|
||||
# flags, tag name, instance attribute
|
||||
assert self.tag != ''
|
||||
nodes = getAllNodes(node, self.tag)
|
||||
if not nodes:
|
||||
if self.req == mandatory:
|
||||
errs.append(where + ': ' + _("At least one '%s' tag should have local text") %
|
||||
self.tag )
|
||||
else:
|
||||
for node in nodes:
|
||||
lang = getNodeAttribute(node, 'xml:lang')
|
||||
c = getNodeText(node)
|
||||
if not c:
|
||||
errs.append(where + ': ' + _("'%s' language of tag '%s' is empty") %
|
||||
(lang, self.tag))
|
||||
# FIXME: check for dups and 'en'
|
||||
if not lang:
|
||||
lang = 'en'
|
||||
self[lang] = c
|
||||
|
||||
def encode(self, node, errs):
|
||||
assert self.tag != ''
|
||||
for key in self.iterkeys():
|
||||
newnode = addNode(node, self.tag)
|
||||
setNodeAttribute(newnode, 'xml:lang', key)
|
||||
addText(newnode, '', self[key])
|
||||
|
||||
#FIXME: maybe more appropriate for pisi.util
|
||||
@staticmethod
|
||||
def get_lang():
|
||||
try:
|
||||
(lang, encoding) = locale.getlocale()
|
||||
if not lang:
|
||||
(lang, encoding) = locale.getdefaultlocale()
|
||||
if lang==None: # stupid python means it is C locale
|
||||
return 'en'
|
||||
else:
|
||||
return lang[0:2]
|
||||
except:
|
||||
raise Error(_('LocalText: unable to get either current or default locale'))
|
||||
|
||||
def errors(self, where = unicode()):
|
||||
errs = []
|
||||
langs = [ LocalText.get_lang(), 'en', 'tr', ]
|
||||
if self.keys() and not util.any(lambda x : self.has_key(x), langs):
|
||||
errs.append( where + ': ' + _("Tag should have at least the current locale, or failing that an English or Turkish version"))
|
||||
#FIXME: check if all entries are unicode
|
||||
return errs
|
||||
|
||||
def format(self, f, errs):
|
||||
L = LocalText.get_lang()
|
||||
if self.has_key(L):
|
||||
f.add_flowing_data(self[L])
|
||||
elif self.has_key('en'):
|
||||
# fallback to English, blah
|
||||
f.add_flowing_data(self['en'])
|
||||
elif self.has_key('tr'):
|
||||
# fallback to Turkish
|
||||
f.add_flowing_data(self['tr'])
|
||||
else:
|
||||
errs.append(_("Tag should have at least the current locale, or failing that an English or Turkish version"))
|
||||
|
||||
#FIXME: factor out these common routines
|
||||
def print_text(self, file = sys.stdout):
|
||||
w = Writer(file) # plain text
|
||||
f = formatter.AbstractFormatter(w)
|
||||
errs = []
|
||||
self.format(f, errs)
|
||||
if errs:
|
||||
for x in errs:
|
||||
ctx.ui.warning(x)
|
||||
|
||||
def __str__(self):
|
||||
L = LocalText.get_lang()
|
||||
if self.has_key(L):
|
||||
return unicode(self[L])
|
||||
elif self.has_key('en'):
|
||||
# fallback to English, blah
|
||||
return unicode(self['en'])
|
||||
elif self.has_key('tr'):
|
||||
# fallback to Turkish
|
||||
return unicode(self['tr'])
|
||||
else:
|
||||
return unicode()
|
||||
|
||||
class Writer(formatter.DumbWriter):
|
||||
"""adds unicode support"""
|
||||
|
||||
def __init__(self, file=None, maxcol=78):
|
||||
formatter.DumbWriter.__init__(self, file, maxcol)
|
||||
|
||||
def send_literal_data(self, data):
|
||||
self.file.write(data.encode("utf-8"))
|
||||
i = data.rfind('\n')
|
||||
if i >= 0:
|
||||
self.col = 0
|
||||
data = data[i+1:]
|
||||
data = data.expandtabs()
|
||||
self.col = self.col + len(data)
|
||||
self.atbreak = 0
|
||||
|
||||
|
||||
class autoxml(oo.autosuper, oo.autoprop):
|
||||
"""High-level automatic XML transformation interface for xmlfile.
|
||||
The idea is to declare a class for each XML tag. Inside the
|
||||
class the tags and attributes nested in the tag are further
|
||||
elaborated. A simple example follows:
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
|
||||
This class defines a tag and an attribute nested in Employee
|
||||
class. Name is a string and type is an integer, called basic
|
||||
types.
|
||||
While the tag is mandatory, the attribute may be left out.
|
||||
|
||||
Other basic types supported are: xmlfile.Float, xmlfile.Double
|
||||
and (not implemented yet): xmlfile.Binary
|
||||
|
||||
By default, the class name is taken as the corresponding tag,
|
||||
which may be overridden by defining a tag attribute. Thus,
|
||||
the same tag may also be written as:
|
||||
|
||||
class EmployeeXML:
|
||||
...
|
||||
tag = 'Employee'
|
||||
...
|
||||
|
||||
In addition to basic types, we allow for two kinds of complex
|
||||
types: class types and list types.
|
||||
|
||||
A declared class can be nested in another class as follows
|
||||
|
||||
class Position:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Description = [xmlfile.Text, xmlfile.optional]
|
||||
|
||||
which we can add to our Employee class.
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
t_Position = [Position, xmlfile.mandatory]
|
||||
|
||||
Note some unfortunate redundancy here with Position; this is
|
||||
justified by the implementation (kidding). Still, you might
|
||||
want to assign a different name than the class name that
|
||||
goes in there, which may be fully qualified.
|
||||
|
||||
There is more! Suppose we want to define a company, with
|
||||
of course many employees.
|
||||
|
||||
class Company:
|
||||
__metaclass__ = autoxml
|
||||
t_Employees = [ [Employee], xmlfile.mandatory, 'Employees/Employee']
|
||||
|
||||
Logically, inside the Company/Employees tag, we will have several
|
||||
Employee tags, which are inserted to the Employees instance variable of
|
||||
Company in order of appearance. We can define lists of any other valid
|
||||
type. Here we used a list of an autoxml class defined above.
|
||||
|
||||
The mandatory flag here asserts that at least one such record
|
||||
is to be found.
|
||||
|
||||
You see, it works like magic, when it works of course. All of it
|
||||
done without a single brain exploding.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def __init__(cls, name, bases, dict):
|
||||
"""entry point for metaclass code"""
|
||||
#print 'generating class', name
|
||||
|
||||
# standard initialization
|
||||
super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
xmlfile_support = XmlFile in bases
|
||||
|
||||
cls.autoxml_bases = filter(lambda base: isinstance(base, autoxml), bases)
|
||||
|
||||
#TODO: initialize class attribute __xml_tags
|
||||
#setattr(cls, 'xml_variables', [])
|
||||
|
||||
# default class tag is class name
|
||||
if not dict.has_key('tag'):
|
||||
cls.tag = name
|
||||
|
||||
# generate helper routines, for each XML component
|
||||
names = []
|
||||
inits = []
|
||||
decoders = []
|
||||
encoders = []
|
||||
errorss = []
|
||||
formatters = []
|
||||
|
||||
# read declaration order from source
|
||||
# code contributed by bahadir kandemir
|
||||
from inspect import getsourcelines
|
||||
from itertools import ifilter
|
||||
import re
|
||||
|
||||
fn = re.compile('\s*([tas]_[a-zA-Z]+).*').findall
|
||||
|
||||
lines = filter(fn, getsourcelines(cls)[0])
|
||||
decl_order = map(lambda x:x.split()[0], lines)
|
||||
|
||||
# there should be at most one str member, and it should be
|
||||
# the first to process
|
||||
|
||||
order = filter(lambda x: not x.startswith('s_'), decl_order)
|
||||
|
||||
# find string member
|
||||
str_members = filter(lambda x:x.startswith('s_'), decl_order)
|
||||
if len(str_members)>1:
|
||||
raise Error('Only one str member can be defined')
|
||||
elif len(str_members)==1:
|
||||
order.insert(0, str_members[0])
|
||||
|
||||
for var in order:
|
||||
if var.startswith('t_') or var.startswith('a_') or var.startswith('s_'):
|
||||
name = var[2:]
|
||||
if var.startswith('a_'):
|
||||
x = autoxml.gen_attr_member(cls, name)
|
||||
elif var.startswith('t_'):
|
||||
x = autoxml.gen_tag_member(cls, name)
|
||||
elif var.startswith('s_'):
|
||||
x = autoxml.gen_str_member(cls, name)
|
||||
(name, init, decoder, encoder, errors, format_x) = x
|
||||
names.append(name)
|
||||
inits.append(init)
|
||||
decoders.append(decoder)
|
||||
encoders.append(encoder)
|
||||
errorss.append(errors)
|
||||
formatters.append(format_x)
|
||||
|
||||
# generate top-level helper functions
|
||||
cls.initializers = inits
|
||||
def initialize(self, uri = None, keepDoc = False, tmpDir = '/tmp',
|
||||
**args):
|
||||
if xmlfile_support:
|
||||
if args.has_key('tag'):
|
||||
XmlFile.__init__(self, tag = args['tag'])
|
||||
else:
|
||||
XmlFile.__init__(self, tag = cls.tag)
|
||||
for base in cls.autoxml_bases:
|
||||
base.__init__(self)
|
||||
for init in inits:
|
||||
init(self)
|
||||
for x in args.iterkeys():
|
||||
setattr(self, x, args[x])
|
||||
# init hook
|
||||
if hasattr(self, 'init'):
|
||||
self.init(tag)
|
||||
if xmlfile_support and uri:
|
||||
self.read(uri, keepDoc, tmpDir)
|
||||
|
||||
cls.__init__ = initialize
|
||||
|
||||
cls.decoders = decoders
|
||||
def decode(self, node, errs, where = unicode(cls.tag)):
|
||||
for base in cls.autoxml_bases:
|
||||
base.decode(self, node, errs, where)
|
||||
for decode_member in decoders:#self.__class__.decoders:
|
||||
decode_member(self, node, errs, where)
|
||||
if hasattr(self, 'decode_hook'):
|
||||
self.decode_hook(node, errs, where)
|
||||
cls.decode = decode
|
||||
|
||||
cls.encoders = encoders
|
||||
def encode(self, node, errs):
|
||||
for base in cls.autoxml_bases:
|
||||
base.encode(self, node, errs)
|
||||
for encode_member in encoders:#self.__class__.encoders:
|
||||
encode_member(self, node, errs)
|
||||
if hasattr(self, 'encode_hook'):
|
||||
self.encode_hook(node, errs)
|
||||
cls.encode = encode
|
||||
|
||||
cls.errorss = errorss
|
||||
def errors(self, where = unicode(name)):
|
||||
errs = []
|
||||
for base in cls.autoxml_bases:
|
||||
errs.extend(base.errors(self, where))
|
||||
for errors in errorss:#self.__class__.errorss:
|
||||
errs.extend(errors(self, where))
|
||||
if hasattr(self, 'errors_hook'):
|
||||
errs.extend(self.errors_hook(where))
|
||||
return errs
|
||||
cls.errors = errors
|
||||
def check(self):
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.check: '%s' errors") % len(errs))
|
||||
raise Error(*errs)
|
||||
cls.check = check
|
||||
|
||||
cls.formatters = formatters
|
||||
def format(self, f, errs):
|
||||
for base in cls.autoxml_bases:
|
||||
base.format(self, f, errs)
|
||||
for formatter in formatters:#self.__class__.formatters:
|
||||
formatter(self, f, errs)
|
||||
cls.format = format
|
||||
def print_text(self, file = sys.stdout):
|
||||
w = Writer(file) # plain text
|
||||
f = formatter.AbstractFormatter(w)
|
||||
errs = []
|
||||
self.format(f, errs)
|
||||
if errs:
|
||||
for x in errs:
|
||||
ctx.ui.warning(x)
|
||||
cls.print_text = print_text
|
||||
if not dict.has_key('__str__'):
|
||||
def str(self):
|
||||
strfile = StringIO()
|
||||
self.print_text(strfile)
|
||||
str = strfile.getvalue()
|
||||
strfile.close()
|
||||
return str
|
||||
cls.__str__ = str
|
||||
|
||||
if not dict.has_key('__eq__'):
|
||||
def equal(self, other):
|
||||
# handle None
|
||||
if other ==None:
|
||||
return False # well, must be False at this point :)
|
||||
for name in names:
|
||||
try:
|
||||
if getattr(self, name) != getattr(other, name):
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
def notequal(self, other):
|
||||
return not self.__eq__(other)
|
||||
cls.__eq__ = equal
|
||||
cls.__ne__ = notequal
|
||||
|
||||
if xmlfile_support:
|
||||
def read(self, uri, keepDoc = False, tmpDir = '/tmp',
|
||||
sha1sum = False, compress = None, sign = None, copylocal = False):
|
||||
"read XML file and decode it into a python object"
|
||||
self.readxml(uri, tmpDir, sha1sum=sha1sum,
|
||||
compress=compress, sign=sign, copylocal=copylocal)
|
||||
errs = []
|
||||
self.decode(self.rootNode(), errs)
|
||||
if errs:
|
||||
errs.append(_("autoxml.read: File '%s' has errors") % uri)
|
||||
raise Error(*errs)
|
||||
if hasattr(self, 'read_hook'):
|
||||
self.read_hook(errs)
|
||||
|
||||
if not keepDoc:
|
||||
self.unlink() # get rid of the tree
|
||||
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.read: File '%s' has errors") % uri)
|
||||
raise Error(*errs)
|
||||
|
||||
def write(self, uri, keepDoc = False, tmpDir = '/tmp',
|
||||
sha1sum = False, compress = None, sign = None):
|
||||
"encode the contents of the python object into an XML file"
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.write: object validation has failed"))
|
||||
raise Error(*errs)
|
||||
errs = []
|
||||
self.newDocument()
|
||||
self.encode(self.rootNode(), errs)
|
||||
if hasattr(self, 'write_hook'):
|
||||
self.write_hook(errs)
|
||||
if errs:
|
||||
errs.append(_("autoxml.write: File encoding '%s' has errors") % uri)
|
||||
raise Error(*errs)
|
||||
self.writexml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign)
|
||||
if not keepDoc:
|
||||
self.unlink() # get rid of the tree
|
||||
|
||||
cls.read = read
|
||||
cls.write = write
|
||||
|
||||
|
||||
def gen_attr_member(cls, attr):
|
||||
"""generate readers and writers for an attribute member"""
|
||||
#print 'attr:', attr
|
||||
spec = getattr(cls, 'a_' + attr)
|
||||
tag_type = spec[0]
|
||||
assert type(tag_type) == type(type)
|
||||
def readtext(node, attr):
|
||||
return getNodeAttribute(node, attr)
|
||||
def writetext(node, attr, text):
|
||||
#print 'write attr', attr, text
|
||||
setNodeAttribute(node, attr, text)
|
||||
anonfuns = cls.gen_anon_basic(attr, spec, readtext, writetext)
|
||||
return cls.gen_named_comp(attr, spec, anonfuns)
|
||||
|
||||
def gen_tag_member(cls, tag):
|
||||
"""generate helper funs for tag member of class"""
|
||||
#print 'tag:', tag
|
||||
spec = getattr(cls, 't_' + tag)
|
||||
anonfuns = cls.gen_tag(tag, spec)
|
||||
return cls.gen_named_comp(tag, spec, anonfuns)
|
||||
|
||||
def gen_tag(cls, tag, spec):
|
||||
"""generate readers and writers for the tag"""
|
||||
tag_type = spec[0]
|
||||
if type(tag_type) is types.TypeType and \
|
||||
autoxml.basic_cons_map.has_key(tag_type):
|
||||
def readtext(node, tagpath):
|
||||
#print 'read tag', node, tagpath
|
||||
return getNodeText(node, tagpath)
|
||||
def writetext(node, tagpath, text):
|
||||
#print 'write tag', node, tagpath, text
|
||||
addText(node, tagpath, text)
|
||||
return cls.gen_anon_basic(tag, spec, readtext, writetext)
|
||||
elif type(tag_type) is types.ListType:
|
||||
return cls.gen_list_tag(tag, spec)
|
||||
elif tag_type is LocalText:
|
||||
return cls.gen_insetclass_tag(tag, spec)
|
||||
elif type(tag_type) is autoxml or type(tag_type) is types.TypeType:
|
||||
return cls.gen_class_tag(tag, spec)
|
||||
else:
|
||||
raise Error(_('gen_tag: unrecognized tag type %s in spec') %
|
||||
str(tag_type))
|
||||
|
||||
def gen_str_member(cls, token):
|
||||
"""generate readers and writers for a string member"""
|
||||
spec = getattr(cls, 's_' + token)
|
||||
tag_type = spec[0]
|
||||
assert type(tag_type) == type(type)
|
||||
def readtext(node, blah):
|
||||
#node.normalize() # piksemel doesn't have this
|
||||
return getNodeText(node)
|
||||
def writetext(node, blah, text):
|
||||
addText(node, "", text)
|
||||
anonfuns = cls.gen_anon_basic(token, spec, readtext, writetext)
|
||||
return cls.gen_named_comp(token, spec, anonfuns)
|
||||
|
||||
def gen_named_comp(cls, token, spec, anonfuns):
|
||||
"""generate a named component tag/attr. a decoration of
|
||||
anonymous functions that do not bind to variable names"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = spec[0]
|
||||
req = spec[1]
|
||||
(init_a, decode_a, encode_a, errors_a, format_a) = anonfuns
|
||||
|
||||
def init(self):
|
||||
"""initialize component"""
|
||||
setattr(self, name, init_a())
|
||||
|
||||
def decode(self, node, errs, where):
|
||||
"""decode component from DOM node"""
|
||||
setattr(self, name, decode_a(node, errs, where + '.' + unicode(name)))
|
||||
|
||||
def encode(self, node, errs):
|
||||
"""encode self inside, possibly new, DOM node using xml"""
|
||||
if hasattr(self, name):
|
||||
value = getattr(self, name)
|
||||
else:
|
||||
value = None
|
||||
encode_a(node, value, errs)
|
||||
|
||||
def errors(self, where):
|
||||
"""return errors in the object"""
|
||||
errs = []
|
||||
if hasattr(self, name) and getattr(self, name) != None:
|
||||
value = getattr(self,name)
|
||||
errs.extend(errors_a(value, where + '.' + name))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory variable %s not available') % name)
|
||||
return errs
|
||||
|
||||
def format(self, f, errs):
|
||||
if hasattr(self, name):
|
||||
value = getattr(self,name)
|
||||
f.add_literal_data(token + ': ')
|
||||
format_a(value, f, errs)
|
||||
f.add_line_break()
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory variable %s not available') % name)
|
||||
|
||||
return (name, init, decode, encode, errors, format)
|
||||
|
||||
def mixed_case(cls, identifier):
|
||||
"""helper function to turn token name into mixed case"""
|
||||
if identifier is "":
|
||||
return ""
|
||||
else:
|
||||
if identifier[0]=='I':
|
||||
lowly = 'i' # because of pythonic idiots we can't choose locale in lower
|
||||
else:
|
||||
lowly = identifier[0].lower()
|
||||
return lowly + identifier[1:]
|
||||
|
||||
def tagpath_head_last(cls, tagpath):
|
||||
"returns split of the tag path into last tag and the rest"
|
||||
try:
|
||||
lastsep = tagpath.rindex('/')
|
||||
except ValueError, e:
|
||||
return ('', tagpath)
|
||||
return (tagpath[:lastsep], tagpath[lastsep+1:])
|
||||
|
||||
def parse_spec(cls, token, spec):
|
||||
"""decompose member specification"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = spec[0]
|
||||
req = spec[1]
|
||||
|
||||
if len(spec)>=3:
|
||||
path = spec[2] # an alternative path specified
|
||||
elif type(token_type) is type([]):
|
||||
if type(token_type[0]) is autoxml:
|
||||
# if list of class, by default nested like in most PSPEC
|
||||
path = token + '/' + token_type[0].tag
|
||||
else:
|
||||
# if list of ordinary type, just take the name for
|
||||
path = token
|
||||
elif type(token_type) is autoxml:
|
||||
# if a class, by default its tag
|
||||
path = token_type.tag
|
||||
else:
|
||||
path = token # otherwise it's the same name as
|
||||
# the token
|
||||
return name, token_type, req, path
|
||||
|
||||
def gen_anon_basic(cls, token, spec, readtext, writetext):
|
||||
"""Generate a tag or attribute with one of the basic
|
||||
types like integer. This has got to be pretty generic
|
||||
so that we can invoke it from the complex types such as Class
|
||||
and List. The readtext and writetext arguments achieve
|
||||
the DOM text access for this datatype."""
|
||||
|
||||
name, token_type, req, tagpath = cls.parse_spec(token, spec)
|
||||
|
||||
def initialize():
|
||||
"""default value for all basic types is None"""
|
||||
return None
|
||||
|
||||
def decode(node, errs, where):
|
||||
"""decode from DOM node, the value, watching the spec"""
|
||||
text = readtext(node, token)
|
||||
#print 'read text ', text
|
||||
if text:
|
||||
try:
|
||||
value = autoxml.basic_cons_map[token_type](text)
|
||||
except:
|
||||
value = None
|
||||
errs.append(where + ': ' + _('Type mismatch: read text cannot be decoded'))
|
||||
return value
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory token %s not available') % token)
|
||||
return None
|
||||
|
||||
def encode(node, value, errs):
|
||||
"""encode given value inside DOM node"""
|
||||
if value:
|
||||
writetext(node, token, unicode(value))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory token %s not available') % token)
|
||||
|
||||
def errors(value, where):
|
||||
errs = []
|
||||
if value and not isinstance(value, token_type):
|
||||
errs.append(where + ': ' + _('Type mismatch. Expected %s, got %s') %
|
||||
(token_type, type(value)) )
|
||||
return errs
|
||||
|
||||
def format(value, f, errs):
|
||||
"""format value for pretty printing"""
|
||||
f.add_literal_data(unicode(value))
|
||||
|
||||
return initialize, decode, encode, errors, format
|
||||
|
||||
def gen_class_tag(cls, tag, spec):
|
||||
"""generate a class datatype"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
def make_object():
|
||||
obj = tag_type.__new__(tag_type)
|
||||
obj.__init__(tag=tag, req=req)
|
||||
return obj
|
||||
|
||||
def init():
|
||||
return make_object()
|
||||
|
||||
def decode(node, errs, where):
|
||||
node = getNode(node, tag)
|
||||
if node:
|
||||
try:
|
||||
obj = make_object()
|
||||
obj.decode(node, errs, where)
|
||||
return obj
|
||||
except Error:
|
||||
errs.append(where + ': '+ _('Type mismatch: DOM cannot be decoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory argument not available'))
|
||||
return None
|
||||
|
||||
def encode(node, obj, errs):
|
||||
if node and obj:
|
||||
try:
|
||||
#FIXME: this doesn't look pretty
|
||||
classnode = newNode(node, tag)
|
||||
obj.encode(classnode, errs)
|
||||
addNode(node, '', classnode)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
# note: we can receive an error if obj has no content
|
||||
errs.append(_('Object cannot be encoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
def errors(obj, where):
|
||||
return obj.errors(where)
|
||||
|
||||
def format(obj, f, errs):
|
||||
try:
|
||||
obj.format(f, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
def gen_list_tag(cls, tag, spec):
|
||||
"""generate a list datatype. stores comps in tag/comp_tag"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
pathcomps = path.split('/')
|
||||
comp_tag = pathcomps.pop()
|
||||
list_tagpath = util.makepath(pathcomps, sep='/', relative=True)
|
||||
|
||||
if len(tag_type) != 1:
|
||||
raise Error(_('List type must contain only one element'))
|
||||
|
||||
x = cls.gen_tag(comp_tag, [tag_type[0], mandatory])
|
||||
(init_item, decode_item, encode_item, errors_item, format_item) = x
|
||||
|
||||
def init():
|
||||
return []
|
||||
|
||||
def decode(node, errs, where):
|
||||
l = []
|
||||
nodes = getAllNodes(node, path)
|
||||
#print node, tag + '/' + comp_tag, nodes
|
||||
if len(nodes)==0 and req==mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory list "%s" under "%s" node is empty.') % (path, node.name()))
|
||||
ix = 1
|
||||
for node in nodes:
|
||||
dummy = newNode(node, "Dummy")
|
||||
addNode(dummy, '', node)
|
||||
l.append(decode_item(dummy, errs, where + unicode("[%s]" % ix)))
|
||||
#l.append(decode_item(node, errs, where + unicode("[%s]" % ix)))
|
||||
ix += 1
|
||||
return l
|
||||
|
||||
def encode(node, l, errs):
|
||||
if l and len(l) > 0:
|
||||
for item in l:
|
||||
if list_tagpath:
|
||||
listnode = addNode(node, list_tagpath, branch = False)
|
||||
else:
|
||||
listnode = node
|
||||
encode_item(listnode, item, errs)
|
||||
#encode_item(node, item, errs)
|
||||
else:
|
||||
if req is mandatory:
|
||||
errs.append(_('Mandatory list "%s" under "%s" node is empty.') % (path, node.name()))
|
||||
|
||||
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):
|
||||
l.sort()
|
||||
for node in l:
|
||||
format_item(node, f, errs)
|
||||
f.add_literal_data(' ')
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
try:
|
||||
from xmlextpiks import *
|
||||
except:
|
||||
try:
|
||||
#if pisi.context.use_mdom:
|
||||
# gibidi
|
||||
from xmlextcdom import *
|
||||
except:
|
||||
#raise Error('cannot find 4suite implementation')
|
||||
print 'xmlext: piksemel or cDomlette implementation cannot be loaded, falling back to minidom'
|
||||
from xmlextmdom import *
|
||||
@@ -1,214 +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.
|
||||
#
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses cDomlette from 4suite.org
|
||||
adapted from the minidom implementation by Faik Uygur and fixed by Eray
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import Ft
|
||||
from Ft.Xml.Domlette import implementation
|
||||
from Ft.Xml.Domlette import NoExtDtdReader
|
||||
import Ft.Lib
|
||||
import pisi
|
||||
|
||||
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
|
||||
XHTML_NS = unicode(XHTML_NAMESPACE)
|
||||
XML_NS = unicode(XML_NAMESPACE)
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
# Document wrappers
|
||||
|
||||
def newDocument(tag):
|
||||
# create a document and return document root
|
||||
doc = implementation.createDocument(None, tag, None)
|
||||
return doc.documentElement
|
||||
|
||||
def parse(filename):
|
||||
# parse file and return document root
|
||||
try:
|
||||
doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(filename))
|
||||
return doc.documentElement
|
||||
except Ft.FtException, e:
|
||||
raise Error(_("File '%s' has invalid XML: %s") % (filename, str(e)))
|
||||
|
||||
def newNode(node, tag):
|
||||
return node.ownerDocument.createElementNS(None, tag)
|
||||
|
||||
def newTextNode(node, text):
|
||||
return node.ownerDocument.createTextNode(text)
|
||||
|
||||
# Node related wrappers
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if attrname.startswith('xml:'):
|
||||
if node.hasAttributeNS(XML_NS, attrname[4:]):
|
||||
return node.getAttributeNS(XML_NS, attrname[4:])
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
if not node.hasAttributeNS(None, attrname):
|
||||
return None
|
||||
else:
|
||||
return node.getAttributeNS(None, attrname)
|
||||
|
||||
def setNodeAttribute(node, attrname, value):
|
||||
"""get named attribute from DOM node"""
|
||||
if attrname.startswith('xml:'):
|
||||
node.setAttributeNS(XML_NS, attrname, value)
|
||||
else:
|
||||
node.setAttributeNS(None, attrname, value)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
dom = node.ownerDocument
|
||||
for tag in tags:
|
||||
node = node.appendChild(dom.createElementNS(None, tag))
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
newnode = newTextNode(node, text)
|
||||
return addNode(node, tagPath, newnode, branch = branch)
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
note: this is a particularly inefficient implementation of xmlext.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
import pisi
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
# Document wrappers
|
||||
|
||||
def newDocument(tag):
|
||||
impl = mdom.getDOMImplementation()
|
||||
dom = impl.createDocument(None, tag, None)
|
||||
return dom.documentElement
|
||||
|
||||
def parse(filename):
|
||||
try:
|
||||
dom = mdom.parse(filename)
|
||||
return dom.documentElement
|
||||
except ExpatError, inst:
|
||||
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def newNode(node, tag):
|
||||
return node.ownerDocument.createElement(tag)
|
||||
|
||||
def newTextNode(node, text):
|
||||
return node.ownerDocument.createTextNode(text)
|
||||
|
||||
# Node related wrappers
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if not node.hasAttribute(attrname):
|
||||
return None
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
dom = node.ownerDocument
|
||||
for tag in tags:
|
||||
node = node.appendChild(dom.createElement(tag))
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
newnode = newTextNode(node, text)
|
||||
return addNode(node, tagPath, newnode, branch = branch)
|
||||
@@ -1,167 +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.
|
||||
#
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses piksemel
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import piksemel as iks
|
||||
|
||||
parse = iks.parse
|
||||
newDocument = iks.newDocument
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
tags = tagPath.split('/')
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
nodeList = [node] # basis case
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
return nodeList
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def setNodeAttribute(node, attrname, value):
|
||||
"""get named attribute from DOM node"""
|
||||
return node.setAttribute(attrname, value)
|
||||
|
||||
def getChildElts(parent):
|
||||
"""get only child elements"""
|
||||
return [x for x in parent.tags()]
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.tags(childName)]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
child = node.firstChild()
|
||||
if not child:
|
||||
return None
|
||||
if child.type() == iks.DATA:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data().strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
if tagpath == "":
|
||||
return node
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.tags():
|
||||
if child.name() == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
for tag in tags:
|
||||
node = node.insertTag(tag)
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.insertNode(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagpath, text):
|
||||
node = addNode(node, tagpath)
|
||||
node.insertData(text)
|
||||
|
||||
def newNode(node, tag):
|
||||
return iks.newDocument(tag)
|
||||
@@ -1,22 +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.context
|
||||
|
||||
try:
|
||||
from xmlfilepiks import *
|
||||
except:
|
||||
try:
|
||||
from xmlfilecdom import *
|
||||
except:
|
||||
print 'xmlext: cDomlette/piksemel implementation cannot be loaded, falling back to minidom'
|
||||
from xmlfilemdom import *
|
||||
@@ -1,168 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses cDomlette from 4suite.org
|
||||
adapted from the minidom implementation by Faik Uygur and fixed by Eray
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import codecs
|
||||
import exceptions
|
||||
|
||||
import Ft
|
||||
from Ft.Xml.Domlette import implementation
|
||||
from Ft.Xml.Domlette import NoExtDtdReader
|
||||
from Ft.Xml.Domlette import Print, PrettyPrint
|
||||
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
|
||||
|
||||
XHTML_NS = unicode(XHTML_NAMESPACE)
|
||||
XML_NS = unicode(XML_NAMESPACE)
|
||||
|
||||
import pisi
|
||||
from pisi.pxml.xmlextcdom import *
|
||||
from pisi.file import File
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
self.doc = implementation.createDocument(None, self.rootTag, None)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
del self.doc
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc.documentElement
|
||||
|
||||
def readxml(self, uri, tmpDir='/tmp', sha1sum=False, compress=None, sign=None):
|
||||
uri = File.make_uri(uri)
|
||||
localpath = File.download(uri, tmpDir,sha1sum=sha1sum,compress=compress,sign=sign)
|
||||
try:
|
||||
self.doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(localpath))
|
||||
return self.doc.documentElement
|
||||
except Ft.FtException, e:
|
||||
raise Error(_("File '%s' has invalid XML: %s") % (localpath, str(e)) )
|
||||
except exceptions.ValueError, e:
|
||||
raise Error(_("File '%s' not found") % localpath )
|
||||
|
||||
def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None):
|
||||
f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign)
|
||||
PrettyPrint(self.rootNode(), stream = f)
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.rootNode().tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error(_("Root tagname %s not identical to %s as expected") %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
def newNode(self, tag):
|
||||
return self.doc.createElementNS(None, tag)
|
||||
|
||||
def newTextNode(self, text):
|
||||
return self.doc.createTextNode(text)
|
||||
|
||||
def newAttribute(self, attr):
|
||||
return self.doc.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.doc.documentElement, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.doc.documentElement, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return node.childNodes
|
||||
|
||||
# get only elements of a given type
|
||||
#FIXME: this doesn't work
|
||||
def getChildrenWithType(self, tagpath, type):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
return filter(lambda x:x.nodeType == type, node.childNodes)
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
try:
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
|
||||
node.childNodes)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.doc.documentElement, tagPath, newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.doc.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
node = self.addNode(tagPath, self.newTextNode(text))
|
||||
return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -1,159 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
note: this is a particularly inefficient implementation of xmlfile
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
import codecs
|
||||
|
||||
import pisi
|
||||
from pisi.file import File
|
||||
|
||||
class Error(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
impl = mdom.getDOMImplementation()
|
||||
self.doc = impl.createDocument(None, self.rootTag, None)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
self.doc.unlink()
|
||||
del self.doc
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc.documentElement
|
||||
|
||||
def readxml(self, uri, tmpDir = '/tmp'):
|
||||
uri = File.make_uri(uri)
|
||||
localpath = File.download(uri, tmpDir)
|
||||
|
||||
try:
|
||||
self.doc = mdom.parse(localpath)
|
||||
except ExpatError, inst:
|
||||
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def writexml(self, fileName):
|
||||
f = codecs.open(fileName,'w', "utf-8")
|
||||
f.write(self.doc.toprettyxml())
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.rootNode().tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error(_("Root tagname %s not identical to %s as expected") %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
def newNode(self, tag):
|
||||
return self.doc.createElement(tag)
|
||||
|
||||
def newTextNode(self, text):
|
||||
return self.doc.createTextNode(text)
|
||||
|
||||
def newAttribute(self, attr):
|
||||
return self.doc.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.doc.documentElement, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.doc.documentElement, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return node.childNodes
|
||||
|
||||
# get only elements of a given type
|
||||
#FIXME: this doesn't work
|
||||
def getChildrenWithType(self, tagpath, type):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
return filter(lambda x:x.nodeType == type, node.childNodes)
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
try:
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
|
||||
node.childNodes)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.doc.documentElement, tagPath, newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.doc.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
node = self.addNode(tagPath, self.newTextNode(text))
|
||||
return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses piksemel
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import codecs
|
||||
import exceptions
|
||||
|
||||
import piksemel as iks
|
||||
|
||||
import pisi
|
||||
from pisi.file import File
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
self.doc = iks.newDocument(self.rootTag)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
del self.doc
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc
|
||||
|
||||
def readxmlfile(self, file):
|
||||
raise Exception("not implemented")
|
||||
try:
|
||||
self.doc = iks.parse(file)
|
||||
return self.doc
|
||||
except Exception, e:
|
||||
raise Error(_("File '%s' has invalid XML") % (localpath) )
|
||||
|
||||
|
||||
def readxml(self, uri, tmpDir='/tmp', sha1sum=False,
|
||||
compress=None, sign=None, copylocal = False):
|
||||
uri = File.make_uri(uri)
|
||||
#try:
|
||||
localpath = File.download(uri, tmpDir, sha1sum=sha1sum,
|
||||
compress=compress,sign=sign, copylocal=copylocal)
|
||||
#except IOError, e:
|
||||
# raise Error(_("Cannot read URI %s: %s") % (uri, unicode(e)) )
|
||||
try:
|
||||
self.doc = iks.parse(localpath)
|
||||
return self.doc
|
||||
except Exception, e:
|
||||
raise Error(_("File '%s' has invalid XML") % (localpath) )
|
||||
|
||||
def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None):
|
||||
f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign)
|
||||
f.write(self.doc.toPrettyString())
|
||||
f.close()
|
||||
|
||||
def writexmlfile(self, f):
|
||||
f.write(self.doc.toPrettyString())
|
||||
@@ -1,126 +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 os, fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.lockeddbshelve as shelve
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.util as util
|
||||
from pisi.uri import URI
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Repo:
|
||||
def __init__(self, indexuri):
|
||||
self.indexuri = indexuri
|
||||
|
||||
#class HttpRepo
|
||||
|
||||
#class FtpRepo
|
||||
|
||||
#class RemovableRepo
|
||||
|
||||
|
||||
class RepoDB(object):
|
||||
"""RepoDB maps repo ids to repository information"""
|
||||
|
||||
def __init__(self, txn = None):
|
||||
self.d = shelve.LockedDBShelf("repo")
|
||||
def proc(txn):
|
||||
if not self.d.has_key("order", txn):
|
||||
self.d.put("order", [], txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def repo_name(self, ix):
|
||||
l = self.list()
|
||||
return l[ix]
|
||||
|
||||
def swap(self, x, y):
|
||||
def proc(txn):
|
||||
l = self.d.get("order", txn)
|
||||
t = l[x]
|
||||
l[x] = l[y]
|
||||
l[y] = t
|
||||
self.d.put("order", l, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def has_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key("repo-" + name)
|
||||
|
||||
def get_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d["repo-" + name]
|
||||
|
||||
def add_repo(self, name, repo_info, txn = None, at = None):
|
||||
"""add repository with name and repo_info at a given optional position"""
|
||||
name = str(name)
|
||||
assert (isinstance(repo_info,Repo))
|
||||
def proc(txn):
|
||||
if self.d.has_key("repo-" + name, txn):
|
||||
raise Error(_('Repository %s already exists') % name)
|
||||
self.d.put("repo-" + name, repo_info, txn)
|
||||
order = self.d.get("order", txn)
|
||||
if at == None:
|
||||
order.append(name)
|
||||
else:
|
||||
if at<0 or at>len(order):
|
||||
raise Error(_("Cannot add repository at position %s") % at)
|
||||
order.insert(at, name)
|
||||
self.d.put("order", order, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def list(self):
|
||||
return self.d["order"]
|
||||
|
||||
def clear(self):
|
||||
self.d.clear()
|
||||
|
||||
def remove_repo(self, name, txn = None):
|
||||
name = str(name)
|
||||
def proc(txn):
|
||||
self.d.delete("repo-" + name, txn)
|
||||
list = self.d.get("order", txn)
|
||||
list.remove(name)
|
||||
self.d.put("order", list, txn)
|
||||
ctx.packagedb.remove_repo(name, txn=txn)
|
||||
ctx.sourcedb.remove_repo(name, txn=txn)
|
||||
ctx.componentdb.remove_repo(name, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
db = None
|
||||
|
||||
def init():
|
||||
global db
|
||||
|
||||
if db:
|
||||
return db
|
||||
|
||||
db = RepoDB()
|
||||
return db
|
||||
|
||||
def finalize():
|
||||
global db
|
||||
|
||||
if db:
|
||||
db.close()
|
||||
db = None
|
||||
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
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,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
class Actions:
|
||||
|
||||
template = """
|
||||
from pisi.actionsapi import pisitools
|
||||
|
||||
WorkDir = "skeleton"
|
||||
|
||||
def install():
|
||||
pisitools.dobin("skeleton.py")
|
||||
pisitools.rename("/usr/bin/skeleton.py", "%s")
|
||||
"""
|
||||
|
||||
def __init__(self, name, filepath):
|
||||
self.name = name
|
||||
self.filepath = filepath
|
||||
|
||||
def write(self):
|
||||
open(self.filepath, "w").write(self.template % self.name)
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
import locale
|
||||
from os.path import join
|
||||
|
||||
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:
|
||||
|
||||
__c = _constant()
|
||||
|
||||
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)
|
||||
|
||||
consts = Constants()
|
||||
|
||||
consts.scenarios_path = "scenarios/"
|
||||
consts.pisi_db = "db/"
|
||||
consts.repo_name = "scenario-db"
|
||||
consts.repo_path = "repo/"
|
||||
consts.repo_url = consts.repo_path + "pisi-index.xml.bz2"
|
||||
|
||||
consts.glob_pisis = "*.pisi"
|
||||
consts.pisi_suffix = ".pisi"
|
||||
|
||||
consts.pspec_path = "/tmp/pspec.xml"
|
||||
consts.actionspy_path = "/tmp/actions.py"
|
||||
|
||||
consts.packager_name = "Faik Uygur"
|
||||
consts.packager_email = "faik@pardus.org.tr"
|
||||
|
||||
consts.homepage = "http://cekirdek.uludag.org.tr/~faik/pisi"
|
||||
consts.summary = "%s is a good application"
|
||||
consts.description = "%s is a free software that can do anything it wants"
|
||||
consts.license = ["GPL-2"]
|
||||
|
||||
consts.skel_sha1sum = "cc64dfa6e068fe1f6fb68a635878b1ea21acfac7"
|
||||
consts.skel_type = "targz"
|
||||
consts.skel_uri = "http://cekirdek.uludag.org.tr/~faik/pisi/skeleton.tar.gz"
|
||||
consts.skel_bindir = "/usr/bin"
|
||||
consts.skel_dirtype = "executable"
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
from pisi.scenarioapi.pspec import Pspec
|
||||
from pisi.scenarioapi.actions import Actions
|
||||
from pisi.scenarioapi.constants import *
|
||||
from pisi.scenarioapi.withops import *
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
class Package:
|
||||
def __init__(self, name, deps = [], cons = [], date = "2006-18-18", ver = "1.0", partOf="None"):
|
||||
self.name = name
|
||||
self.dependencies = deps
|
||||
self.conflicts = cons
|
||||
self.version = ver
|
||||
self.date = date
|
||||
self.partOf = partOf
|
||||
self.pspec = None
|
||||
self.actions = None
|
||||
self.create_package()
|
||||
|
||||
def create_pisi(self):
|
||||
os.system("pisi build %s -O %s > /dev/null 2>&1" % (consts.pspec_path, consts.repo_path))
|
||||
|
||||
def create_package(self):
|
||||
pspec = Pspec(self.name, consts.pspec_path)
|
||||
pspec.set_source(consts.homepage, consts.summary % self.name,
|
||||
consts.description % self.name, consts.license, self.partOf)
|
||||
pspec.set_packager(consts.packager_name, consts.packager_email)
|
||||
pspec.set_archive(consts.skel_sha1sum, consts.skel_type, consts.skel_uri)
|
||||
pspec.set_package(self.dependencies, self.conflicts)
|
||||
pspec.add_file_path(consts.skel_bindir, consts.skel_dirtype)
|
||||
pspec.set_history(self.date, self.version)
|
||||
|
||||
actions = Actions(self.name, consts.actionspy_path)
|
||||
|
||||
self.pspec = pspec
|
||||
self.actions = actions
|
||||
self.pspec.write()
|
||||
self.actions.write()
|
||||
|
||||
self.create_pisi()
|
||||
|
||||
def get_file_name(self):
|
||||
# use glob. there may be buildnos at the end of the package name
|
||||
pkg = consts.repo_path + self.name + "-" + \
|
||||
self.version + "-" + self.pspec.pspec.history[0].release
|
||||
|
||||
found = glob.glob(pkg + consts.glob_pisis)
|
||||
if not found:
|
||||
raise Exception(_("No pisi package: %s* found.") % pkg)
|
||||
|
||||
return os.path.basename(found[0])
|
||||
|
||||
def version_bump(self, *args):
|
||||
for with in args:
|
||||
if with.types == CONFLICT and with.action == ADDED:
|
||||
self.pspec.add_conflicts(with.data)
|
||||
|
||||
if with.types == CONFLICT and with.action == REMOVED:
|
||||
self.pspec.remove_conflicts(with.data)
|
||||
|
||||
if with.types == DEPENDENCY and with.action == ADDED:
|
||||
self.pspec.add_dependencies(with.data)
|
||||
|
||||
if with.types == DEPENDENCY and with.action == REMOVED:
|
||||
self.pspec.remove_dependencies(with.data)
|
||||
|
||||
if with.types == VERSION and with.action == INIT:
|
||||
self.version = with.data
|
||||
|
||||
self.pspec.update_history(self.date, self.version)
|
||||
self.actions.name = self.name
|
||||
self.actions.write()
|
||||
self.create_pisi()
|
||||
|
||||
if __name__ == "__main__":
|
||||
p = Package("w0rmux", [], [], "0.7")
|
||||
p.version_bump()
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import os
|
||||
import pisi.util as util
|
||||
from pisi.scenarioapi.constants import *
|
||||
|
||||
def pisi_upgraded(*args):
|
||||
packages = util.strlist(args).rstrip()
|
||||
os.system("pisi -D%s upgrade %s" % (consts.pisi_db, packages))
|
||||
|
||||
def pisi_info(package):
|
||||
os.system("pisi -D%s info %s" % (consts.pisi_db, package))
|
||||
|
||||
def pisi_removed(*args):
|
||||
packages = util.strlist(args).rstrip()
|
||||
os.system("pisi -D%s remove %s" % (consts.pisi_db, packages))
|
||||
|
||||
def pisi_added_repo(name, url):
|
||||
os.system("pisi -D%s add-repo -y %s %s" % (consts.pisi_db, name, url))
|
||||
|
||||
def pisi_updated_repo():
|
||||
os.system("pisi -D%s update-repo" % consts.pisi_db)
|
||||
|
||||
def pisi_installed(*args):
|
||||
packages = util.strlist(args).rstrip()
|
||||
os.system("pisi -D%s install %s" % (consts.pisi_db, packages))
|
||||
|
||||
def pisi_reinstalled(package):
|
||||
os.system("pisi -D%s install --reinstall %s" % (consts.pisi_db, package))
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
from pisi.specfile import SpecFile, Package, Update, Path
|
||||
from pisi.dependency import Dependency
|
||||
from pisi.pxml.autoxml import LocalText
|
||||
|
||||
class Pspec:
|
||||
def __init__(self, pkgname, filepath):
|
||||
self.pspec = SpecFile()
|
||||
self.package = Package()
|
||||
self.update = Update()
|
||||
self.filepath = filepath
|
||||
self.name = pkgname
|
||||
|
||||
@staticmethod
|
||||
def read(filepath):
|
||||
spec = SpecFile()
|
||||
spec.read(filepath)
|
||||
tmp = Pspec(spec.source.name, filepath)
|
||||
|
||||
tmp.pspec.source.packager = spec.source.packager
|
||||
tmp.pspec.source.homepage = spec.source.homepage
|
||||
tmp.pspec.source.archive = spec.source.archive
|
||||
tmp.pspec.source.name = spec.source.name
|
||||
tmp.pspec.source.license = spec.source.license
|
||||
tmp.pspec.source.partOf = spec.source.partOf
|
||||
tmp.pspec.source.summary = spec.source.summary
|
||||
tmp.pspec.source.description = spec.source.description
|
||||
|
||||
for pkg in spec.packages:
|
||||
p = Package()
|
||||
p.name = pkg.name
|
||||
p.files = pkg.files
|
||||
p.conflicts = pkg.conflicts
|
||||
p.packageDependencies = pkg.packageDependencies
|
||||
tmp.pspec.packages.append(p)
|
||||
|
||||
tmp.pspec.history = spec.history
|
||||
return tmp
|
||||
|
||||
def add_dependencies(self, dependencies):
|
||||
# special case of given one dependency package
|
||||
# with depedency versioning info [**kw, name]
|
||||
# [{"versionFrom":"0.4.2"}, "udev"]
|
||||
if type(dependencies[0]) == dict:
|
||||
dep = Dependency()
|
||||
(kw, dep.package) = dependencies
|
||||
dep.__dict__[kw.keys()[0]] = kw.values()[0]
|
||||
self.package.packageDependencies.append(dep)
|
||||
return
|
||||
|
||||
for depname in dependencies:
|
||||
dep = Dependency()
|
||||
dep.package = depname
|
||||
self.package.packageDependencies.append(dep)
|
||||
|
||||
def remove_dependencies(self, dependencies):
|
||||
for depname in dependencies:
|
||||
for dep in self.package.packageDependencies:
|
||||
if dep.package == depname:
|
||||
self.package.packageDependencies.remove(dep)
|
||||
|
||||
def add_conflicts(self, conflicts):
|
||||
for con in conflicts:
|
||||
self.package.conflicts.append(con)
|
||||
|
||||
def remove_conflicts(self, conflicts):
|
||||
for con in conflicts:
|
||||
self.package.conflicts.remove(con)
|
||||
|
||||
def update_history(self, date, version):
|
||||
new = Update()
|
||||
new.name = self.update.name
|
||||
new.email = self.update.email
|
||||
new.date = date
|
||||
new.version = version
|
||||
new.release = str(int(self.update.release) + 1)
|
||||
self.update = new
|
||||
self.pspec.history.append(self.update)
|
||||
self.pspec.history.reverse()
|
||||
self.write()
|
||||
|
||||
def set_source(self, homepage, summary, description, license, partOf):
|
||||
self.pspec.source.name = self.name
|
||||
self.pspec.source.homepage = homepage
|
||||
self.pspec.source.license = license
|
||||
self.pspec.source.partOf = partOf
|
||||
self.pspec.source.summary = LocalText("Summary")
|
||||
self.pspec.source.description = LocalText("Description")
|
||||
self.pspec.source.summary["en"] = summary
|
||||
self.pspec.source.description["en"] = description
|
||||
|
||||
def set_packager(self, name, email):
|
||||
self.pspec.source.packager.name = unicode(name)
|
||||
self.pspec.source.packager.email = email
|
||||
self.update.name = unicode(name)
|
||||
self.update.email = email
|
||||
|
||||
def set_archive(self, sha1sum, type, uri):
|
||||
self.pspec.source.archive.sha1sum = sha1sum
|
||||
self.pspec.source.archive.type = type
|
||||
self.pspec.source.archive.uri = uri
|
||||
|
||||
def add_file_path(self, path, type):
|
||||
p = Path()
|
||||
p.path = path
|
||||
p.fileType = type
|
||||
self.pspec.packages[0].files.append(p)
|
||||
|
||||
def set_package(self, dependencies, conflicts):
|
||||
self.package.name = self.name
|
||||
self.package.conflicts = conflicts
|
||||
|
||||
if dependencies:
|
||||
for depname in dependencies:
|
||||
dep = Dependency()
|
||||
dep.package = depname
|
||||
self.package.packageDependencies.append(dep)
|
||||
|
||||
self.pspec.packages.append(self.package)
|
||||
|
||||
def set_history(self, date, version, comment = "No Comment", release = "1"):
|
||||
self.update.date = date
|
||||
self.update.version = version
|
||||
self.update.comment = comment
|
||||
self.update.release = release
|
||||
self.pspec.history.append(self.update)
|
||||
|
||||
def write(self):
|
||||
self.pspec.write(self.filepath)
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
from pisi.scenarioapi.package import Package
|
||||
from pisi.scenarioapi.withops import *
|
||||
from pisi.scenarioapi.constants import *
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
repodb = {}
|
||||
|
||||
def repo_added_package(package, *args):
|
||||
if repodb.has_key(package):
|
||||
raise Exception(_("Repo already has package named %s.") % package)
|
||||
|
||||
version = "1.0"
|
||||
partOf = "None"
|
||||
dependencies = []
|
||||
conflicts = []
|
||||
|
||||
for with in args:
|
||||
if with.types == CONFLICT and with.action == INIT:
|
||||
conflicts = with.data
|
||||
|
||||
if with.types == DEPENDENCY and with.action == INIT:
|
||||
dependencies = with.data
|
||||
|
||||
if with.types == VERSION and with.action == INIT:
|
||||
version = with.data
|
||||
|
||||
if with.types == PARTOF and with.action == INIT:
|
||||
partOf = with.data
|
||||
|
||||
repodb[package] = Package(package, dependencies, conflicts, ver=version, partOf=partOf)
|
||||
|
||||
def repo_removed_package(package):
|
||||
if not repodb.has_key(package):
|
||||
raise Exception(_("Repo does not have package named %s.") % package)
|
||||
|
||||
os.unlink(os.path.join(consts.repo_path, repodb[package].get_file_name()))
|
||||
del repodb[package]
|
||||
|
||||
def repo_version_bumped(package, *args):
|
||||
if not repodb.has_key(package):
|
||||
raise Exception(_("Repo does not have package named %s.") % package)
|
||||
|
||||
old_file = repodb[package].get_file_name()
|
||||
repodb[package].version_bump(*args)
|
||||
os.unlink(os.path.join(consts.repo_path, old_file))
|
||||
|
||||
def repo_updated_index():
|
||||
cur = os.getcwd()
|
||||
path = os.path.join(cur, consts.repo_path)
|
||||
os.chdir(consts.repo_path)
|
||||
os.system("pisi index --skip-signing %s >/dev/null 2>&1" % path)
|
||||
os.chdir(cur)
|
||||
|
||||
def repo_get_url():
|
||||
return "."
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
from pisi.scenarioapi.repoops import *
|
||||
from pisi.scenarioapi.pisiops import *
|
||||
from pisi.scenarioapi.constants import *
|
||||
|
||||
def let_repo_had(package, *args):
|
||||
repo_added_package(package, *args)
|
||||
repo_updated_index()
|
||||
|
||||
def let_pisi_had(*args):
|
||||
url = os.path.join(os.getcwd(), consts.repo_url)
|
||||
pisi_added_repo(consts.repo_name, url)
|
||||
packages = util.strlist(args).rstrip()
|
||||
os.system("pisi -D%s install --ignore-dependency %s" % (consts.pisi_db, packages))
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
ADDED, REMOVED, INIT = range(3)
|
||||
PARTOF, VERSION, CONFLICT, DEPENDENCY = range(4)
|
||||
|
||||
class with:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def with_action(types, action, data):
|
||||
w = with()
|
||||
w.types = types
|
||||
w.action = action
|
||||
w.data = data
|
||||
return w
|
||||
|
||||
def with_partof(partof):
|
||||
return with_action(PARTOF, INIT, partof)
|
||||
|
||||
def with_version(version):
|
||||
return with_action(VERSION, INIT, version)
|
||||
|
||||
def with_conflicts(*cons):
|
||||
return with_action(CONFLICT, INIT, cons)
|
||||
|
||||
def with_dependencies(*deps):
|
||||
return with_action(DEPENDENCY, INIT, deps)
|
||||
|
||||
def with_added_conflicts(*cons):
|
||||
return with_action(CONFLICT, ADDED, cons)
|
||||
|
||||
def with_removed_conflicts(*cons):
|
||||
return with_action(CONFLICT, REMOVED, cons)
|
||||
|
||||
def with_added_dependencies(*deps):
|
||||
return with_action(DEPENDENCY, ADDED, deps)
|
||||
|
||||
def with_removed_dependencies(*deps):
|
||||
return with_action(DEPENDENCY, REMOVED, deps)
|
||||
|
||||
def with_added_dependency(package, **kw):
|
||||
assert len(kw) <= 1
|
||||
|
||||
if not len(kw):
|
||||
return with_action(DEPENDENCY, ADDED, [package])
|
||||
|
||||
if (kw.has_key("versionFrom") or
|
||||
kw.has_key("versionTo") or
|
||||
kw.has_key("version") or
|
||||
kw.has_key("releaseFrom") or
|
||||
kw.has_key("releaseTo") or
|
||||
kw.has_key("release")):
|
||||
return with_action(DEPENDENCY, ADDED, [kw, package])
|
||||
|
||||
# pass other keywords.
|
||||
return with_action(DEPENDENCY, ADDED, [package])
|
||||
@@ -1,62 +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
|
||||
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, repo = None, txn = None):
|
||||
terms = p.preprocess(lang, str)
|
||||
ctx.invidx[id][lang].add_doc(docid, terms, repo=repo, txn=txn)
|
||||
|
||||
def remove_doc(id, lang, docid, str, repo = None, txn = None):
|
||||
terms = p.preprocess(lang, str)
|
||||
ctx.invidx[id][lang].remove_doc(docid, terms, repo = repo, txn = txn)
|
||||
|
||||
def query_terms(id, lang, terms, repo = None, txn = None):
|
||||
terms = map(lambda x: p.lower(lang, x), terms)
|
||||
return ctx.invidx[id][lang].query(terms, repo = repo, txn = txn)
|
||||
|
||||
def query(id, lang, str, repo = None, txn = None):
|
||||
terms = p.preprocess(lang, str)
|
||||
return query_terms(id, lang, terms, repo = repo, txn = txn)
|
||||
@@ -1,83 +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 types
|
||||
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.itembyrepodb import ItemByRepoDB
|
||||
import pisi.itembyrepodb as itembyrepodb
|
||||
|
||||
class InvertedIndex(object):
|
||||
"""a database of term -> set of documents"""
|
||||
|
||||
def __init__(self, id, lang):
|
||||
self.d = ItemByRepoDB('ii-%s-%s' % (id, lang))
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def has_term(self, term, repo = None, txn = None):
|
||||
return self.d.has_key(shelve.LockedDBShelf.encodekey(term), repo=repo,txn=txn)
|
||||
|
||||
def get_term(self, term, repo = None, txn = None):
|
||||
"""get set of doc ids given term"""
|
||||
term = shelve.LockedDBShelf.encodekey(term)
|
||||
def proc(txn):
|
||||
if not self.has_term(term, repo=repo, txn=txn):
|
||||
return set()
|
||||
return self.d.get_item(term, repo=repo, txn=txn)
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def get_union_term(self, name, txn = None, repo = itembyrepodb.repos ):
|
||||
"""get a union of all repository terms, not just the first repo in order.
|
||||
get only basic repo info from the first repo"""
|
||||
name = shelve.LockedDBShelf.encodekey(name)
|
||||
def proc(txn):
|
||||
terms= set()
|
||||
if self.d.d.has_key(name):
|
||||
s = self.d.d.get(name, txn=txn)
|
||||
for repostr in self.d.order(repo = repo):
|
||||
if s.has_key(repostr):
|
||||
terms |= s[repostr]
|
||||
return terms
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def query(self, terms, repo = None, txn = None):
|
||||
def proc(txn):
|
||||
docs = [ self.get_union_term(x, repo=repo, txn=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, repo = None, txn= None):
|
||||
return self.d.list(f, repo=repo, txn=txn)
|
||||
|
||||
def add_doc(self, doc, terms, repo = None, 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, repo=repo, txn=txn)
|
||||
term_i_docs.add(doc)
|
||||
self.d.add_item(term_i, term_i_docs, repo=repo, txn=txn) # update
|
||||
return self.d.txn_proc(f, txn)
|
||||
|
||||
def remove_doc(self, doc, terms,repo=None, 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,repo=repo, txn=txn)
|
||||
if doc in term_i_docs:
|
||||
term_i_docs.remove(doc)
|
||||
self.d.add_item(term_i, term_i_docs, repo=repo, txn=txn) # update
|
||||
return self.d.txn_proc(f, txn)
|
||||
@@ -1,40 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import tokenize
|
||||
|
||||
def lowly_python(str):
|
||||
def lowly_char(c):
|
||||
if c=='I':
|
||||
lowly = 'i' # because of some fools we can't choose locale in lower
|
||||
else:
|
||||
lowly = c.lower()
|
||||
return c
|
||||
|
||||
r = ""
|
||||
for c in str:
|
||||
r += lowly_char(c)
|
||||
return r
|
||||
|
||||
def lower(lang, str):
|
||||
if lang=='tr':
|
||||
return lowly_python(str)
|
||||
else:
|
||||
return str.lower()
|
||||
|
||||
def preprocess(lang, str):
|
||||
terms = tokenize.tokenize(lang, str)
|
||||
|
||||
# normalize
|
||||
terms = map(lambda x: lower(lang, x), terms)
|
||||
|
||||
return terms
|
||||
@@ -1,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.
|
||||
#
|
||||
|
||||
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
|
||||
@@ -1,106 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# python standard library
|
||||
|
||||
from os.path import join, basename
|
||||
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
|
||||
from pisi.mirrors import Mirrors
|
||||
|
||||
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:
|
||||
self.progress = ctx.ui.Progress
|
||||
else:
|
||||
self.progress = None
|
||||
|
||||
try:
|
||||
if self.url.get_uri().startswith("mirrors://"):
|
||||
self.fetch_from_mirror()
|
||||
else:
|
||||
fetch_url(self.url, ctx.config.archives_dir(), self.progress)
|
||||
except pisi.fetcher.FetchError:
|
||||
if ctx.config.values.build.fallback:
|
||||
self.fetch_from_fallback()
|
||||
else:
|
||||
raise
|
||||
|
||||
def fetch_from_fallback(self):
|
||||
archive = basename(self.url.get_uri())
|
||||
src = join(ctx.config.values.build.fallback, archive)
|
||||
ctx.ui.warning(_('Trying fallback address: %s') % src)
|
||||
fetch_url(src, ctx.config.archives_dir(), self.progress)
|
||||
|
||||
def fetch_from_mirror(self):
|
||||
uri = self.url.get_uri()
|
||||
sep = uri[len("mirrors://"):].split("/")
|
||||
name = sep.pop(0)
|
||||
archive = "/".join(sep)
|
||||
|
||||
mirrors = Mirrors().get_mirrors(name)
|
||||
if not mirrors:
|
||||
raise Error(_("%s mirrors are not defined.") % name)
|
||||
|
||||
for mirror in mirrors:
|
||||
try:
|
||||
url = join(mirror, archive)
|
||||
ctx.ui.warning(_('Fetching source from mirror: %s') % url)
|
||||
fetch_url(url, ctx.config.archives_dir(), self.progress)
|
||||
return
|
||||
except pisi.fetcher.FetchError:
|
||||
pass
|
||||
|
||||
raise pisi.fetcher.FetchError(_('Could not fetch source from %s mirrors.') % name);
|
||||
|
||||
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)
|
||||
@@ -1,112 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
package source database
|
||||
interface for update/query to local package repository
|
||||
we basically store everything in sourceinfo class
|
||||
yes, we are cheap
|
||||
to handle multiple repositories, for sources, we
|
||||
store a set of repositories in which the source appears.
|
||||
the actual guy to take is determined from the repo order.
|
||||
"""
|
||||
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
import pisi.repodb
|
||||
from pisi.itembyrepodb import ItemByRepoDB
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
pass
|
||||
|
||||
class SourceDB(object):
|
||||
|
||||
def __init__(self):
|
||||
self.d = ItemByRepoDB('source')
|
||||
self.dpkgtosrc = ItemByRepoDB('pkgtosrc')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dpkgtosrc.close()
|
||||
|
||||
def list(self):
|
||||
return self.d.list()
|
||||
|
||||
def has_spec(self, name, repo=None, txn=None):
|
||||
return self.d.has_key(name, repo, txn)
|
||||
|
||||
def get_spec(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item(name, repo, txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise NotfoundError(_("Source package %s not found") % name)
|
||||
|
||||
def get_spec_repo(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item_repo(name, repo, txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise NotfoundError(_("Source package %s not found") % name)
|
||||
|
||||
def pkgtosrc(self, name, txn = None):
|
||||
return self.dpkgtosrc.get_item(name, txn=txn)
|
||||
|
||||
def add_spec(self, spec, repo, txn = None):
|
||||
assert not spec.errors()
|
||||
name = str(spec.source.name)
|
||||
def proc(txn):
|
||||
self.d.add_item(name, spec, repo, txn)
|
||||
for pkg in spec.packages:
|
||||
self.dpkgtosrc.add_item(pkg.name, name, repo, txn)
|
||||
ctx.componentdb.add_spec(spec.source.partOf, spec.source.name, repo, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_spec(self, name, repo, txn = None):
|
||||
name = str(name)
|
||||
def proc(txn):
|
||||
assert self.has_spec(name, txn=txn)
|
||||
spec = self.d.get_item(name, repo, txn)
|
||||
self.d.remove_item(name, txn=txn)
|
||||
for pkg in spec.packages:
|
||||
self.dpkgtosrc.remove_item_repo(pkg.name, repo, txn)
|
||||
ctx.componentdb.remove_spec(spec.source.partOf, spec.source.name, repo, txn)
|
||||
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
def proc(txn):
|
||||
self.d.remove_repo(repo, txn=txn)
|
||||
self.dpkgtosrc.remove_repo(repo, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
sourcedb = None
|
||||
|
||||
def init():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
return sourcedb
|
||||
|
||||
sourcedb = SourceDB()
|
||||
return sourcedb
|
||||
|
||||
def finalize():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
sourcedb.close()
|
||||
sourcedb = None
|
||||
@@ -1,282 +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.
|
||||
#
|
||||
|
||||
"""
|
||||
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]
|
||||
a_type = [autoxml.String, autoxml.optional]
|
||||
t_Date = [autoxml.String, autoxml.mandatory]
|
||||
t_Version = [autoxml.String, autoxml.mandatory]
|
||||
t_Name = [autoxml.Text, autoxml.optional]
|
||||
t_Email = [autoxml.String, autoxml.optional]
|
||||
t_Comment = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.date
|
||||
s += ", ver=" + self.version
|
||||
s += ", rel=" + self.release
|
||||
if self.type:
|
||||
s += ", type=" + self.type
|
||||
return s
|
||||
|
||||
|
||||
class Path:
|
||||
|
||||
s_Path = [autoxml.String, autoxml.mandatory]
|
||||
a_fileType = [autoxml.String, autoxml.optional]
|
||||
a_permanent = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.path
|
||||
s += ", type=" + self.fileType
|
||||
return s
|
||||
|
||||
|
||||
class ComarProvide:
|
||||
|
||||
s_om = [autoxml.String, autoxml.mandatory]
|
||||
a_script = [autoxml.String, autoxml.mandatory]
|
||||
|
||||
def __str__(self):
|
||||
# FIXME: descriptive enough?
|
||||
s = self.script
|
||||
s += ' (' + self.om + ')'
|
||||
return s
|
||||
|
||||
|
||||
class Archive:
|
||||
|
||||
s_uri = [ autoxml.String, autoxml.mandatory ]
|
||||
a_type =[ autoxml.String, autoxml.mandatory ]
|
||||
a_sha1sum =[ autoxml.String, autoxml.mandatory ]
|
||||
|
||||
def decode_hook(self, node, errs, where):
|
||||
self.name = basename(self.uri)
|
||||
|
||||
def __str__(self):
|
||||
s = _('URI: %s, type: %s, sha1sum: %s') % (self.uri, self.type, self.sha1sum)
|
||||
return s
|
||||
|
||||
|
||||
class Source:
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory]
|
||||
t_Homepage = [autoxml.String, autoxml.optional]
|
||||
t_Packager = [Packager, autoxml.mandatory]
|
||||
t_Summary = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_IsA = [ [autoxml.String], autoxml.optional]
|
||||
t_PartOf = [autoxml.String, autoxml.optional]
|
||||
t_Icon = [ autoxml.String, autoxml.optional]
|
||||
t_License = [ [autoxml.String], autoxml.mandatory]
|
||||
t_Archive = [Archive, autoxml.mandatory ]
|
||||
t_Patches = [ [Patch], autoxml.optional]
|
||||
t_BuildDependencies = [ [Dependency], autoxml.optional]
|
||||
t_Version = [ autoxml.String, autoxml.optional]
|
||||
t_Release = [ autoxml.String, autoxml.optional]
|
||||
t_SourceURI = [ autoxml.String, autoxml.optional ] # used in index
|
||||
|
||||
|
||||
class Package:
|
||||
|
||||
t_Name = [ autoxml.String, autoxml.mandatory ]
|
||||
t_Summary = [ autoxml.LocalText, autoxml.optional ]
|
||||
t_Description = [ autoxml.LocalText, autoxml.optional ]
|
||||
t_IsA = [ [autoxml.String], autoxml.optional]
|
||||
t_PartOf = [autoxml.String, autoxml.optional]
|
||||
t_License = [ [autoxml.String], autoxml.optional]
|
||||
t_Icon = [ autoxml.String, autoxml.optional]
|
||||
t_PackageDependencies = [ [Dependency], autoxml.optional, "RuntimeDependencies/Dependency"]
|
||||
t_ComponentDependencies = [ [autoxml.String], autoxml.optional, "RuntimeDependencies/Component"]
|
||||
t_Files = [ [Path], autoxml.optional]
|
||||
t_Conflicts = [ [autoxml.String], autoxml.optional, "Conflicts/Package"]
|
||||
t_ProvidesComar = [ [ComarProvide], autoxml.optional, "Provides/COMAR"]
|
||||
#t_RequiresComar = [ [autoxml.String], autoxml.mandatory, "Requires/COMAR"]
|
||||
t_AdditionalFiles = [ [AdditionalFile], autoxml.optional]
|
||||
t_History = [ [Update], autoxml.optional]
|
||||
|
||||
# FIXME: needed in build process, to distinguish dynamically generated debug packages.
|
||||
# find a better way to do this.
|
||||
debug_package = False
|
||||
|
||||
def runtimeDependencies(self):
|
||||
deps = self.packageDependencies
|
||||
deps += [ ctx.componentdb.get_component[x].packages for x in self.componentDependencies ]
|
||||
return deps
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.name + '-' \
|
||||
+ self.version + '-' \
|
||||
+ self.release
|
||||
|
||||
return util.join_path( ctx.config.lib_dir(), 'package', packageDir)
|
||||
|
||||
def installable(self):
|
||||
"""calculate if pkg is installable currently"""
|
||||
deps = self.runtimeDependencies()
|
||||
return pisi.dependency.satisfies_dependencies(self.name, deps)
|
||||
|
||||
def __str__(self):
|
||||
if self.build:
|
||||
build = self.build
|
||||
else:
|
||||
build = '--'
|
||||
s = _('Name: %s, version: %s, release: %s, build %s\n') % (
|
||||
self.name, self.version, self.release, 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 + ' '
|
||||
s += '\n'
|
||||
s += _('Dependencies: ')
|
||||
for x in self.componentDependencies:
|
||||
s += x.package + ' '
|
||||
for x in self.packageDependencies:
|
||||
s += x.package + ' '
|
||||
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
|
||||
@@ -1,101 +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
|
||||
#
|
||||
|
||||
import sys
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
(installed, upgraded, removed, installing, removing,
|
||||
configuring, configured, extracting, downloading, packagestogo) = range(10)
|
||||
|
||||
class UI(object):
|
||||
"Abstract class for UI operations, derive from this."
|
||||
|
||||
class Progress:
|
||||
def __init__(self, totalsize, existsize = 0):
|
||||
self.totalsize = totalsize
|
||||
try:
|
||||
self.percent = (existsize * 100) / totalsize
|
||||
except:
|
||||
self.percent = 0
|
||||
|
||||
def update(self, size):
|
||||
if not self.totalsize:
|
||||
return 100
|
||||
try:
|
||||
self.percent = (size * 100) / self.totalsize
|
||||
except:
|
||||
self.percent = 0
|
||||
return self.percent
|
||||
|
||||
def __init__(self, debuggy = False, verbose = False):
|
||||
self.show_debug = debuggy
|
||||
self.show_verbose = verbose
|
||||
|
||||
def close(self):
|
||||
"cleanup stuff here"
|
||||
pass
|
||||
|
||||
def set_verbose(self, flag):
|
||||
self.show_verbose = flag
|
||||
|
||||
def set_debug(self, flag):
|
||||
self.show_debug = flag
|
||||
|
||||
def info(self, msg, verbose = False, noln = False):
|
||||
"give an informative message"
|
||||
pass
|
||||
|
||||
def ack(self, msg):
|
||||
"inform the user of an important event and wait for acknowledgement"
|
||||
pass
|
||||
|
||||
def debug(self, msg):
|
||||
"show debugging info"
|
||||
if self.show_debug:
|
||||
self.info('DEBUG: ' + msg)
|
||||
|
||||
def warning(self,msg):
|
||||
"warn the user"
|
||||
pass
|
||||
|
||||
def error(self,msg):
|
||||
"inform a (possibly fatal) error"
|
||||
pass
|
||||
|
||||
#FIXME: merge this with info, this just means "important message"
|
||||
def action(self,msg):
|
||||
"uh?"
|
||||
pass
|
||||
|
||||
def choose(self, msg, list):
|
||||
"ask the user to choose from a list of alternatives"
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
"ask a yes/no question"
|
||||
pass
|
||||
|
||||
def display_progress(self, pd):
|
||||
"display progress"
|
||||
pass
|
||||
|
||||
def status(self, msg = None):
|
||||
"set status, if not given clear it"
|
||||
pass
|
||||
|
||||
def notify(self, event, **keywords):
|
||||
"notify UI of a significant event"
|
||||
pass
|
||||
@@ -1,110 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""Simplifies working with URLs, purl module provides common URL
|
||||
parsing and processing"""
|
||||
|
||||
from urlparse import urlparse
|
||||
from os.path import basename
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
class URI(object):
|
||||
"""URI class provides a URL parser and simplifies working with
|
||||
URLs."""
|
||||
|
||||
def __init__(self, uri=None):
|
||||
if uri:
|
||||
self.set_uri(str(uri))
|
||||
else:
|
||||
self.__scheme = None
|
||||
self.__location = None
|
||||
self.__path = None
|
||||
self.__filename = None
|
||||
self.__params = None
|
||||
self.__query = None
|
||||
self.__fragment = None
|
||||
self.__uri = None
|
||||
|
||||
self.__authinfo = None
|
||||
|
||||
def get_uri(self):
|
||||
if self.__uri:
|
||||
return self.__uri
|
||||
return None
|
||||
|
||||
def set_uri(self, uri):
|
||||
# (scheme, location, path, params, query, fragment)
|
||||
uri = str(uri)
|
||||
u = urlparse(uri, "file")
|
||||
self.__scheme = u[0]
|
||||
self.__location = u[1]
|
||||
self.__path = u[2]
|
||||
self.__filename = basename(self.__path)
|
||||
self.__params = u[3]
|
||||
self.__query = u[4]
|
||||
self.__fragment = u[5]
|
||||
|
||||
self.__uri = uri
|
||||
|
||||
def is_local_file(self):
|
||||
if self.scheme() == "file":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_remote_file(self):
|
||||
return not self.is_local_file()
|
||||
|
||||
def is_absolute_path(self):
|
||||
return util.absolute_path(self.__path)
|
||||
|
||||
def is_relative_path(self):
|
||||
return not self.is_absolute_path()
|
||||
|
||||
def set_auth_info(self, authTuple):
|
||||
if not isinstance(authTuple, tuple):
|
||||
raise Exception, _("setAuthInfo needs a tuple (user, pass)")
|
||||
self.__authinfo = authTuple
|
||||
|
||||
def auth_info(self):
|
||||
return self.__authinfo
|
||||
|
||||
def scheme(self):
|
||||
return self.__scheme
|
||||
|
||||
def location(self):
|
||||
return self.__location
|
||||
|
||||
def path(self):
|
||||
return self.__path
|
||||
|
||||
def filename(self):
|
||||
return self.__filename
|
||||
|
||||
def params(self):
|
||||
return self.__params
|
||||
|
||||
def query(self):
|
||||
return self.__query
|
||||
|
||||
def fragment(self):
|
||||
return self.__fragment
|
||||
|
||||
def __str__(self):
|
||||
return self.get_uri()
|
||||
|
||||
uri = property(get_uri, set_uri)
|
||||
@@ -1,654 +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"""
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import sha
|
||||
import shutil
|
||||
import string
|
||||
import statvfs
|
||||
import operator
|
||||
import subprocess
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class FileError(Error):
|
||||
pass
|
||||
|
||||
|
||||
#########################
|
||||
# spec validation utility #
|
||||
#########################
|
||||
|
||||
class Checks:
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
|
||||
def add(self, err):
|
||||
self.list.append(err)
|
||||
|
||||
def join(self, list):
|
||||
self.list.extend(list)
|
||||
|
||||
def has_tag(self, var, section, name):
|
||||
if not var:
|
||||
self.list.append(_("%s section should have a '%s' tag") % (section, name))
|
||||
|
||||
def has_error():
|
||||
return len(self.list)>0
|
||||
|
||||
def print_errors(list):
|
||||
for x in list:
|
||||
ctx.ui.error(x)
|
||||
print_errors = staticmethod(print_errors)
|
||||
|
||||
#########################
|
||||
# string/list/functional#
|
||||
#########################
|
||||
|
||||
def every(pred, seq):
|
||||
return reduce(operator.and_, map(pred, seq), True)
|
||||
|
||||
def any(pred, seq):
|
||||
return reduce(operator.or_, map(pred, seq), False)
|
||||
|
||||
def unzip(seq):
|
||||
return zip(*seq)
|
||||
|
||||
def concat(l):
|
||||
'''concatenate a list of lists'''
|
||||
return reduce( operator.concat, l )
|
||||
|
||||
def strlist(l):
|
||||
"""concatenate string reps of l's elements"""
|
||||
return "".join(map(lambda x: str(x) + ' ', l))
|
||||
|
||||
def multisplit(str, chars):
|
||||
""" split str with any of chars"""
|
||||
l = [str]
|
||||
for c in chars:
|
||||
l = concat(map(lambda x:x.split(c), l))
|
||||
return l
|
||||
|
||||
def same(l):
|
||||
'''check if all elements of a sequence are equal'''
|
||||
if len(l)==0:
|
||||
return True
|
||||
else:
|
||||
last = l.pop()
|
||||
for x in l:
|
||||
if x!=last:
|
||||
return False
|
||||
return True
|
||||
|
||||
def prefix(a, b):
|
||||
'''check if sequence a is a prefix of sequence b'''
|
||||
if len(a)>len(b):
|
||||
return False
|
||||
for i in range(0,len(a)):
|
||||
if a[i]!=b[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def remove_prefix(a,b):
|
||||
"remove prefix a from sequence b"
|
||||
assert prefix(a,b)
|
||||
return b[len(a):]
|
||||
|
||||
def human_readable_size(size = 0):
|
||||
symbols, depth = [' B', 'KB', 'MB', 'GB'], 0
|
||||
|
||||
while size > 1000 and depth < 3:
|
||||
size = float(size / 1024)
|
||||
depth += 1
|
||||
|
||||
return size, symbols[depth]
|
||||
|
||||
def human_readable_rate(size = 0):
|
||||
x = human_readable_size(size)
|
||||
return x[0], x[1] + '/s'
|
||||
|
||||
##############################
|
||||
# Process Releated Functions #
|
||||
##############################
|
||||
|
||||
def run_batch(cmd):
|
||||
"""run command and report return value and output"""
|
||||
ctx.ui.info(_('Running ') + cmd, verbose=True)
|
||||
p = subprocess.Popen(cmd, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = p.communicate()
|
||||
ctx.ui.debug(_('return value for "%s" is %s') % (cmd, p.returncode))
|
||||
return (p.returncode, out, err)
|
||||
|
||||
# you can't use the following for Popen, oops
|
||||
class TeeOutFile:
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
|
||||
def write(self, str):
|
||||
self.write(str)
|
||||
ctx.ui.debug(str)
|
||||
|
||||
# TODO: it might be worthwhile to try to remove the
|
||||
# use of ctx.stdout, and use run_batch()'s return
|
||||
# values instead. but this is good enough :)
|
||||
def run_logged(cmd):
|
||||
"""run command and get return value"""
|
||||
ctx.ui.info(_('Running ') + cmd, verbose=True)
|
||||
if ctx.stdout:
|
||||
stdout = ctx.stdout
|
||||
else:
|
||||
if ctx.get_option('debug'):
|
||||
stdout = None
|
||||
else:
|
||||
stdout = subprocess.PIPE
|
||||
if ctx.stderr:
|
||||
stderr = ctx.stderr
|
||||
else:
|
||||
if ctx.get_option('debug'):
|
||||
stderr = None
|
||||
else:
|
||||
stderr = subprocess.STDOUT
|
||||
|
||||
p = subprocess.Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
|
||||
out, err = p.communicate()
|
||||
ctx.ui.debug(_('return value for "%s" is %s') % (cmd, p.returncode))
|
||||
|
||||
return p.returncode
|
||||
|
||||
|
||||
######################
|
||||
# Terminal functions #
|
||||
######################
|
||||
|
||||
def has_xterm():
|
||||
return os.environ.has_key("TERM") and sys.stderr.isatty()
|
||||
|
||||
def xterm_title(message):
|
||||
"""sets message as a console window's title"""
|
||||
if os.environ.has_key("TERM") and sys.stderr.isatty():
|
||||
terminalType = os.environ["TERM"]
|
||||
for term in ["xterm", "Eterm", "aterm", "rxvt", "screen", "kterm", "rxvt-unicode"]:
|
||||
if terminalType.startswith(term):
|
||||
sys.stderr.write("\x1b]2;"+str(message)+"\x07")
|
||||
sys.stderr.flush()
|
||||
break
|
||||
|
||||
def xterm_title_reset():
|
||||
"""resets console window's title"""
|
||||
if os.environ.has_key("TERM"):
|
||||
terminalType = os.environ["TERM"]
|
||||
xterm_title(os.environ["TERM"])
|
||||
|
||||
|
||||
#############################
|
||||
# Path Processing Functions #
|
||||
#############################
|
||||
|
||||
def splitpath(a):
|
||||
"""split path into components and return as a list
|
||||
os.path.split doesn't do what I want like removing trailing /"""
|
||||
comps = a.split(os.path.sep)
|
||||
if comps[len(comps)-1]=='':
|
||||
comps.pop()
|
||||
return comps
|
||||
|
||||
def makepath(comps, relative = False, sep = os.path.sep):
|
||||
"""reconstruct a path from components"""
|
||||
path = reduce(lambda x,y: x + sep + y, comps, '')
|
||||
if relative:
|
||||
return path[len(sep):]
|
||||
else:
|
||||
return path
|
||||
|
||||
def parentpath(a, sep = os.path.sep):
|
||||
# remove trailing '/'
|
||||
a = a.rstrip(sep)
|
||||
return a[:a.rfind(sep)]
|
||||
|
||||
def parenturi(a):
|
||||
return parentpath(a, '/')
|
||||
|
||||
# I'm not sure how necessary this is. Ahem.
|
||||
def commonprefix(l):
|
||||
"""an improved version of os.path.commonprefix,
|
||||
returns a list of path components"""
|
||||
common = []
|
||||
comps = map(splitpath, l)
|
||||
for i in range(0, min(len,l)):
|
||||
compi = map(lambda x: x[i], comps) # get ith slice
|
||||
if same(compi):
|
||||
common.append(compi[0])
|
||||
return common
|
||||
|
||||
# but this one is necessary
|
||||
def subpath(a, b):
|
||||
"find if path a is before b in the directory tree"
|
||||
return prefix(splitpath(a), splitpath(b))
|
||||
|
||||
def removepathprefix(prefix, path):
|
||||
"remove path prefix a from b, finding the pathname rooted at a"
|
||||
comps = remove_prefix(splitpath(prefix), splitpath(path))
|
||||
if len(comps) > 0:
|
||||
return join_path(*tuple(comps))
|
||||
else:
|
||||
return ""
|
||||
|
||||
def absolute_path(path):
|
||||
"determine if given @path is absolute"
|
||||
comps = splitpath(path)
|
||||
return comps[0] == ''
|
||||
|
||||
def join_path(a, *p):
|
||||
"""Join two or more pathname components, inserting '/' as needed"""
|
||||
"""The python original version has a silly logic"""
|
||||
path = a
|
||||
for b in p:
|
||||
b = b.lstrip('/')
|
||||
if path == '' or path.endswith('/'):
|
||||
path += b
|
||||
else:
|
||||
path += '/' + b
|
||||
return path
|
||||
|
||||
####################################
|
||||
# File/Directory Related Functions #
|
||||
####################################
|
||||
|
||||
def check_file(file, mode = os.F_OK):
|
||||
"shorthand to check if a file exists"
|
||||
if not os.access(file, mode):
|
||||
raise FileError("File " + file + " not found")
|
||||
return True
|
||||
|
||||
def check_dir(dir):
|
||||
"""check if directory exists, and create if it doesn't.
|
||||
works recursively"""
|
||||
dir = dir.strip().rstrip("/")
|
||||
if not os.access(dir, os.F_OK):
|
||||
os.makedirs(dir)
|
||||
|
||||
def clean_dir(path):
|
||||
"Remove all content of a directory (top)"
|
||||
# don't reimplement the wheel
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
def creation_time(file):
|
||||
"""returns the creation time of the given file"""
|
||||
if check_file(file):
|
||||
import time
|
||||
st = os.stat(file)
|
||||
return time.localtime(st.st_ctime)
|
||||
|
||||
def dir_size(dir):
|
||||
""" calculate the size of files under a dir
|
||||
based on the os module example"""
|
||||
# It's really hard to give an approximate value for package's
|
||||
# installed size. Gettin a sum of all files' sizes if far from
|
||||
# being true. Using 'du' command (like Debian does) can be a
|
||||
# better solution :(.
|
||||
# Not really, du calculates size on disk, this is much better -- exa
|
||||
from os.path import getsize, islink, isdir, exists
|
||||
join = join_path
|
||||
|
||||
if exists(dir) and (not isdir(dir) and not islink(dir)):
|
||||
#so, this is not a directory but file..
|
||||
return getsize(dir)
|
||||
|
||||
if islink(dir):
|
||||
return long(len(os.readlink(dir)))
|
||||
|
||||
def sizes():
|
||||
for root, dirs, files in os.walk(dir):
|
||||
yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))])
|
||||
yield sum([long(len(os.readlink((join(root, name))))) for name in files if islink(join(root,name))])
|
||||
return sum( sizes() )
|
||||
|
||||
def copy_file(src,dest):
|
||||
"""copy source file to destination file"""
|
||||
check_file(src)
|
||||
check_dir(os.path.dirname(dest))
|
||||
shutil.copyfile(src, dest)
|
||||
|
||||
def is_ar_file(file_path):
|
||||
return open(file_path).readline().strip() == '!<arch>'
|
||||
|
||||
def clean_ar_timestamps(ar_file):
|
||||
"""clean the timestamps of the ar files"""
|
||||
if not is_ar_file(ar_file):
|
||||
return
|
||||
content = open(ar_file).readlines()
|
||||
fp = open(ar_file, 'w')
|
||||
for line in content:
|
||||
pos = line.rfind(chr(32) + chr(96))
|
||||
if pos > -1 and line[pos - 57:pos + 2].find(chr(47)) > -1:
|
||||
line = line[:pos - 41] + '0000000000' + line[pos - 31:]
|
||||
fp.write(line)
|
||||
fp.close()
|
||||
|
||||
# FIXME: this should be done in a much much simpler way
|
||||
# as it stands, it seems to be a kludge to solve
|
||||
# an unrelated problem
|
||||
def get_file_hashes(top, excludePrefix=None, removePrefix=None):
|
||||
"""Generator function iterates over a toplevel path and returns the
|
||||
(filePath, sha1Hash) tuples for all files. If excludePrefixes list
|
||||
is given as a parameter, function will exclude the filePaths
|
||||
matching those prefixes. The removePrefix string parameter will be
|
||||
used to remove prefix from filePath while matching excludes, if
|
||||
given."""
|
||||
|
||||
def sha1_sum(f, data=False):
|
||||
if not data and f.endswith('.a'):
|
||||
#workaround for .a issue..
|
||||
#don't skip .a files,
|
||||
#but pad their timestamps with '0'..
|
||||
clean_ar_timestamps(f)
|
||||
|
||||
func = None
|
||||
|
||||
if data:
|
||||
func = sha1_data
|
||||
else:
|
||||
func = sha1_file
|
||||
|
||||
try:
|
||||
return func(f)
|
||||
except FileError, e:
|
||||
if os.path.islink(f):
|
||||
ctx.ui.info(_("Including external link '%s'") % f)
|
||||
elif os.path.isdir(f):
|
||||
ctx.ui.info(_("Including directory '%s'") % f)
|
||||
else:
|
||||
raise e
|
||||
return None
|
||||
|
||||
def has_excluded_prefix(filename):
|
||||
if excludePrefix and removePrefix:
|
||||
tempfnam = remove_prefix(removePrefix, filename)
|
||||
for p in excludePrefix:
|
||||
if tempfnam.startswith(p):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# handle single file
|
||||
if os.path.isfile(top):
|
||||
yield (top, sha1_sum(top))
|
||||
return
|
||||
|
||||
# handle single symlink declaration here.
|
||||
if os.path.islink(top):
|
||||
yield (top, sha1_sum(os.readlink(top), True))
|
||||
return
|
||||
|
||||
for root, dirs, files in os.walk(top, topdown=False):
|
||||
#bug 339
|
||||
if os.path.islink(root) and not has_excluded_prefix(root):
|
||||
#yield the symlink..
|
||||
#bug 373
|
||||
yield (root, sha1_sum(os.readlink(root), True))
|
||||
excludePrefix.append(remove_prefix(removePrefix, root) + "/")
|
||||
continue
|
||||
|
||||
#bug 397
|
||||
for dir in dirs:
|
||||
d = join_path(root, dir)
|
||||
if os.path.islink(d) and not has_excluded_prefix(d):
|
||||
yield (d, sha1_sum(os.readlink(d), True))
|
||||
excludePrefix.append(remove_prefix(removePrefix, d) + "/")
|
||||
|
||||
#bug 340
|
||||
if os.path.isdir(root) and not has_excluded_prefix(root):
|
||||
parent, r, d, f = root, '', '', ''
|
||||
for r, d, f in os.walk(parent, topdown=False): pass
|
||||
if not f and not d:
|
||||
yield (parent, sha1_sum(parent))
|
||||
|
||||
for fname in files:
|
||||
f = join_path(root, fname)
|
||||
if has_excluded_prefix(f):
|
||||
continue
|
||||
#bug 373
|
||||
elif os.path.islink(f):
|
||||
yield (f, sha1_sum(os.readlink(f), True))
|
||||
else:
|
||||
yield (f, sha1_sum(f))
|
||||
|
||||
def copy_dir(src, dest):
|
||||
"""copy source dir to destination dir recursively"""
|
||||
shutil.copytree(src, dest)
|
||||
|
||||
def check_file_hash(filename, hash):
|
||||
"""Check the files integrity with a given hash"""
|
||||
return sha1_file(filename) == hash
|
||||
|
||||
def sha1_file(filename):
|
||||
"""calculate sha1 hash of filename"""
|
||||
# Broken links can cause problem!
|
||||
try:
|
||||
m = sha.new()
|
||||
f = file(filename, 'rb')
|
||||
for line in f:
|
||||
m.update(line)
|
||||
return m.hexdigest()
|
||||
except IOError:
|
||||
raise FileError(_("I/O Error: Cannot calculate SHA1 hash of %s") % filename)
|
||||
|
||||
def sha1_data(data):
|
||||
"""calculate sha1 hash of given data"""
|
||||
try:
|
||||
m = sha.new()
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
except:
|
||||
raise Error(_("Cannot calculate SHA1 hash of given data"))
|
||||
|
||||
def uncompress(patchFile, compressType="gz", targetDir=None):
|
||||
"""uncompresses a file and returns the path of the uncompressed
|
||||
file"""
|
||||
if targetDir:
|
||||
filePath = join_path(targetDir,
|
||||
os.path.basename(patchFile))
|
||||
else:
|
||||
filePath = os.path.basename(patchFile)
|
||||
|
||||
if compressType == "gz":
|
||||
from gzip import GzipFile
|
||||
obj = GzipFile(patchFile)
|
||||
elif compressType == "bz2":
|
||||
from bz2 import BZ2File
|
||||
obj = BZ2File(patchFile)
|
||||
|
||||
open(filePath, "w").write(obj.read())
|
||||
return filePath
|
||||
|
||||
|
||||
def do_patch(sourceDir, patchFile, level = 0, target = ''):
|
||||
"""simple function to apply patches.."""
|
||||
cwd = os.getcwd()
|
||||
os.chdir(sourceDir)
|
||||
|
||||
if level == None:
|
||||
level = 0
|
||||
if target == None:
|
||||
target = ''
|
||||
|
||||
check_file(patchFile)
|
||||
(ret, out, err) = run_batch("patch -p%d %s < %s" %
|
||||
(level, target, patchFile))
|
||||
if ret:
|
||||
if out is None and err is None:
|
||||
# Which means stderr and stdout directed so they are None
|
||||
raise Error(_("ERROR: patch (%s) failed") % (patchFile))
|
||||
else:
|
||||
raise Error(_("ERROR: patch (%s) failed: %s") % (patchFile, out))
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def strip_directory(top, excludelist=[]):
|
||||
for root, dirs, files in os.walk(top):
|
||||
for fn in files:
|
||||
frpath = join_path(root, fn)
|
||||
drpath = join_path(os.path.dirname(top),
|
||||
ctx.const.debug_dir_suffix,
|
||||
remove_prefix(top, frpath))
|
||||
|
||||
# Some upstream sources have buggy libtool and ltmain.sh with them,
|
||||
# which causes wrong path entries in *.la files. And these wrong path
|
||||
# entries sometimes triggers compile-time errors or linkage problems.
|
||||
# Instead of patching all these buggy sources and maintain these patches,
|
||||
# PiSi removes wrong paths...
|
||||
extension = os.path.splitext(frpath)[1]
|
||||
if extension == ".la":
|
||||
# FIXME: I'm regular expr. idiot, so one can convert this to python...
|
||||
os.system("sed -i -e 's~-L/var/tmp/pisi/[[:graph:]]*~~g' %s" % frpath)
|
||||
os.system("sed -i -e 's~/var/tmp/pisi/[[:graph:]]*/install/~/~g' %s" % frpath)
|
||||
|
||||
# real path in .pisi package
|
||||
p = '/' + removepathprefix(top, frpath)
|
||||
strip = True
|
||||
for exclude in excludelist:
|
||||
if p.startswith(exclude):
|
||||
strip = False
|
||||
ctx.ui.debug("%s [%s]" %(p, "NoStrip"))
|
||||
|
||||
if strip:
|
||||
if strip_file(frpath, drpath):
|
||||
ctx.ui.debug("%s [%s]" %(p, "stripped"))
|
||||
|
||||
|
||||
def strip_file(filepath, outpath):
|
||||
"""strip a file"""
|
||||
p = os.popen("file \"%s\"" % filepath)
|
||||
o = p.read()
|
||||
|
||||
def run_strip(f, flags=""):
|
||||
p = os.popen("strip %s %s" %(flags, f))
|
||||
ret = p.close()
|
||||
if ret:
|
||||
ctx.ui.warning(_("strip command failed for file '%s'!") % f)
|
||||
|
||||
def save_elf_debug(f, o):
|
||||
"""copy debug info into file.debug file"""
|
||||
p = os.popen("objcopy --only-keep-debug %s %s%s" % (f, o, ctx.const.debug_file_suffix))
|
||||
ret = p.close()
|
||||
if ret:
|
||||
ctx.ui.warning(_("objcopy (keep-debug) command failed for file '%s'!") % f)
|
||||
|
||||
"""mark binary/shared objects to use file.debug"""
|
||||
p = os.popen("objcopy --add-gnu-debuglink=%s%s %s" % (o, ctx.const.debug_file_suffix, f))
|
||||
ret = p.close()
|
||||
if ret:
|
||||
ctx.ui.warning(_("objcopy (add-debuglink) command failed for file '%s'!") % f)
|
||||
|
||||
if "current ar archive" in o:
|
||||
run_strip(filepath, "-g")
|
||||
return True
|
||||
|
||||
elif "SB executable" in o:
|
||||
if ctx.config.values.build.generatedebug:
|
||||
check_dir(os.path.dirname(outpath))
|
||||
save_elf_debug(filepath, outpath)
|
||||
run_strip(filepath)
|
||||
return True
|
||||
|
||||
elif "SB shared object" in o:
|
||||
if ctx.config.values.build.generatedebug:
|
||||
check_dir(os.path.dirname(outpath))
|
||||
save_elf_debug(filepath, outpath)
|
||||
run_strip(filepath, "--strip-unneeded")
|
||||
# FIXME: warn for TEXTREL
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def partition_freespace(directory):
|
||||
""" returns free space of given directory's partition """
|
||||
st = os.statvfs(directory)
|
||||
return st[statvfs.F_BSIZE] * st[statvfs.F_BFREE]
|
||||
|
||||
def clean_locks(top = '.'):
|
||||
for root, dirs, files in os.walk(top):
|
||||
for fn in files:
|
||||
if fn.endswith('.lock'):
|
||||
path = join_path(root, fn)
|
||||
ctx.ui.info(_('Removing lock %s'), path)
|
||||
os.unlink(path)
|
||||
|
||||
########################################
|
||||
# Package/Repository Related Functions #
|
||||
########################################
|
||||
|
||||
def package_name(name, version, release, build, prependSuffix=True):
|
||||
fn = name + '-' + version + '-' + release
|
||||
if build:
|
||||
fn += '-' + str(build)
|
||||
if prependSuffix:
|
||||
fn += ctx.const.package_suffix
|
||||
return fn
|
||||
|
||||
def is_package_name(fn, package_name = None):
|
||||
"check if fn is a valid filename for given package_name"
|
||||
"if not given a package name, see if fn fits the package name rules"
|
||||
if (package_name==None) or fn.startswith(package_name + '-'):
|
||||
if fn.endswith(ctx.const.package_suffix):
|
||||
# get version string, skip separator '-'
|
||||
verstr = fn[len(package_name) + 1:
|
||||
len(fn)-len(ctx.const.package_suffix)]
|
||||
import string
|
||||
for x in verstr.split('-'):
|
||||
# weak rule: version components after '-' start with a digit
|
||||
if x is '' or (not x[0] in string.digits):
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def env_update():
|
||||
import pisi.environment
|
||||
ctx.ui.info(_('Updating environment...'))
|
||||
|
||||
env_dir = join_path(ctx.config.dest_dir(), "/etc/env.d")
|
||||
if not os.path.exists(env_dir):
|
||||
os.makedirs(env_dir, 0755)
|
||||
|
||||
pisi.environment.update_environment(ctx.config.dest_dir())
|
||||
|
||||
def parse_package_name(package_name):
|
||||
"return package name and version string"
|
||||
"ex: package_name=tasma-1.0.3-5-2, returns (tasma, 1.0.3-5-2)"
|
||||
|
||||
# but we should handle package names like 855resolution
|
||||
name = []
|
||||
for part in package_name.split("-"):
|
||||
if name != [] and part[0] in string.digits:
|
||||
break
|
||||
else:
|
||||
name.append(part)
|
||||
name = "-".join(name)
|
||||
version = package_name[len(name) + 1:]
|
||||
|
||||
return (name, version)
|
||||
@@ -1,190 +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.
|
||||
#
|
||||
|
||||
"""version structure"""
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
# Basic rule is:
|
||||
# p > (no suffix) > m > rc > pre > beta > alpha
|
||||
# m: milestone. this was added for OO.o
|
||||
# p: patch-level
|
||||
keywords = {"alpha": 0,
|
||||
"beta" : 1,
|
||||
"pre" : 2,
|
||||
"rc" : 3,
|
||||
"m" : 4,
|
||||
"NOKEY" : 5,
|
||||
"p" : 6}
|
||||
|
||||
# helper functions
|
||||
def has_keyword(versionitem):
|
||||
if versionitem._keyword != "NOKEY":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class VersionItem:
|
||||
_keyword = "NOKEY"
|
||||
_value = 0
|
||||
|
||||
def __init__(self, itemstring):
|
||||
|
||||
# special-case: 1.5p
|
||||
# here "p" comes as a separate itemstring and conflicts with
|
||||
# keyword "p". But keyword should allways contain an integer prefix.
|
||||
# So, skipping looking for keywords if the length() is 1 makes the
|
||||
# trick.
|
||||
if len(itemstring) > 1:
|
||||
|
||||
for keyword in keywords.keys():
|
||||
if itemstring.startswith(keyword):
|
||||
|
||||
if self._keyword == "NOKEY":
|
||||
self._keyword = keyword
|
||||
else:
|
||||
# longer match is correct
|
||||
if len(keyword) > len(self._keyword):
|
||||
self._keyword = keyword
|
||||
|
||||
if self._keyword == "NOKEY":
|
||||
if len(itemstring) == 1 and itemstring in string.ascii_letters:
|
||||
# single letter version item ('a' to 'Z')
|
||||
self._value = itemstring
|
||||
else:
|
||||
self._value = int(itemstring)
|
||||
else:
|
||||
# rest is the version item's value. And each must have
|
||||
# one!
|
||||
self._value = int(itemstring[len(self._keyword):])
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __lt__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l < r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value < rhs._value
|
||||
else: # l > r
|
||||
return False
|
||||
|
||||
def __le__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l < r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value <= rhs._value
|
||||
else: # l > r
|
||||
return False
|
||||
|
||||
def __gt__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l > r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value > rhs._value
|
||||
else: # l < r
|
||||
return False
|
||||
|
||||
def __ge__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l > r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value >= rhs._value
|
||||
else: # l < r
|
||||
return False
|
||||
|
||||
def __eq__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l == r and self._value == rhs._value:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class Version:
|
||||
|
||||
def __init__(self, verstring):
|
||||
self.comps = []
|
||||
for i in util.multisplit(verstring,'.-_'):
|
||||
# some version strings can contain ascii chars at the
|
||||
# back. As an example: 2.11a
|
||||
# We split '11a' as two items like '11' and 'a'
|
||||
s = re.compile("[a-z-A-Z]$").search(i)
|
||||
if s:
|
||||
head = i[:s.start()]
|
||||
tail = s.group()
|
||||
self.comps.append(VersionItem(head))
|
||||
self.comps.append(VersionItem(tail))
|
||||
else:
|
||||
self.comps.append(VersionItem(i))
|
||||
|
||||
self.verstring = verstring
|
||||
|
||||
def string(self):
|
||||
return self.verstring
|
||||
|
||||
def compare(self, rhs):
|
||||
"""this comparison routine is essentially a comparison routine
|
||||
for two rationals in (0,1) interval. we compare two sequences
|
||||
of digits one by one. We start with the leftmost digit
|
||||
in the expansion. If they are equal, we proceed to the next. If
|
||||
not we use the comparison operator. And we iterate to the left.
|
||||
The result is, 0 if two are equal, -1 if self < rhs, and +1
|
||||
if self>rhs"""
|
||||
lhs = self.comps
|
||||
rhs = rhs.comps
|
||||
# pad the short version string with zeros
|
||||
if len(lhs) < len(rhs):
|
||||
lhs.extend( [VersionItem('0')] * (len(rhs) - len(lhs)) )
|
||||
elif len(lhs) > len(rhs):
|
||||
rhs.extend( [VersionItem('0')] * (len(lhs) - len(rhs)) )
|
||||
# now let's iterate from left to right
|
||||
for (litem, ritem) in zip(lhs, rhs):
|
||||
if litem < ritem:
|
||||
return -1
|
||||
elif litem > ritem:
|
||||
return +1
|
||||
return 0
|
||||
|
||||
# premature optimization is the root of all evil
|
||||
|
||||
def __lt__(self,rhs):
|
||||
return self.compare(rhs) < 0
|
||||
|
||||
def __le__(self,rhs):
|
||||
return self.compare(rhs) <= 0
|
||||
|
||||
def __gt__(self,rhs):
|
||||
return self.compare(rhs) > 0
|
||||
|
||||
def __ge__(self,rhs):
|
||||
return self.compare(rhs) >= 0
|
||||
|
||||
def __eq__(self,rhs):
|
||||
return self.compare(rhs) == 0
|
||||
|
||||
def __str__(self):
|
||||
return self.verstring
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user