Move pisi-1.1_rc4 tag
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
Current maintainer
|
||||
------------------
|
||||
Faik Uygur <faik [at] pardus.org.tr>
|
||||
|
||||
Authors and contributors
|
||||
------------------------
|
||||
|
||||
Ahmet Aygun <ahmet [at] pardusman.org>
|
||||
A. Murat Eren <meren [at] pardus.org.tr>
|
||||
Bahadır Kandemir <bahadir [at] pardus.org.tr>
|
||||
Barış Metin <baris [at] pardus.org.tr>
|
||||
Faik Uygur <faik [at] pardus.org.tr>
|
||||
Furkan Duman <coderlord [at] gmail.com>
|
||||
Görkem Çetin <gorkem [at] pardus.org.tr>
|
||||
Gürer Özen <gurer [at] pardus.org.tr>
|
||||
İsmail Dönmez <ismail [at] pardus.org.tr>
|
||||
Onur Küçük <onur [at] pardus.org.tr>
|
||||
S. Çağlar Onur <caglar [at] pardus.org.tr>
|
||||
|
||||
Inactive contributors
|
||||
---------------------------------
|
||||
Eray Özkural < erayo@cs.bilkent.edu.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,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,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.
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,532 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XMI xmlns:UML="http://schema.omg.org/spec/UML/1.3" verified="false" timestamp="2006-09-29T21:06:38" xmi.version="1.2" >
|
||||
<XMI.header>
|
||||
<XMI.documentation>
|
||||
<XMI.exporter>umbrello uml modeller http://uml.sf.net</XMI.exporter>
|
||||
<XMI.exporterVersion>1.5.4</XMI.exporterVersion>
|
||||
<XMI.exporterEncoding>UnicodeUTF8</XMI.exporterEncoding>
|
||||
</XMI.documentation>
|
||||
<XMI.metamodel xmi.name="UML" href="UML.xml" xmi.version="1.3" />
|
||||
</XMI.header>
|
||||
<XMI.content>
|
||||
<UML:Model isSpecification="false" isLeaf="false" isRoot="false" xmi.id="m1" isAbstract="false" name="UML Model" >
|
||||
<UML:Namespace.ownedElement>
|
||||
<UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="3" isRoot="false" isAbstract="false" name="datatype" />
|
||||
<UML:Stereotype isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="108" isRoot="false" isAbstract="false" name="enum" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="2" isRoot="false" isAbstract="false" name="int" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="4" isRoot="false" isAbstract="false" name="char" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="5" isRoot="false" isAbstract="false" name="bool" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="6" isRoot="false" isAbstract="false" name="float" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="7" isRoot="false" isAbstract="false" name="double" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="8" isRoot="false" isAbstract="false" name="short" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="9" isRoot="false" isAbstract="false" name="long" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="10" isRoot="false" isAbstract="false" name="unsigned int" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="11" isRoot="false" isAbstract="false" name="unsigned short" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="12" isRoot="false" isAbstract="false" name="unsigned long" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="13" isRoot="false" isAbstract="false" name="string" />
|
||||
<UML:DataType stereotype="3" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="89" isRoot="false" isAbstract="false" name="data" />
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="24" isRoot="false" isAbstract="false" name="InstallDB" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="25" type="76" name="d" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="77" type="76" name="db" />
|
||||
</UML:Classifier.feature>
|
||||
<UML:Namespace.ownedElement>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="24" xmi.id="14" isRoot="false" isAbstract="false" name="InstallInfo" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="79" type="13" name="state" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="80" type="13" name="version" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="81" type="13" name="release" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="82" type="13" name="build" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="83" type="13" name="distribution" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="84" type="13" name="time" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
</UML:Namespace.ownedElement>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="76" isRoot="false" isAbstract="false" name="LockedDBShelf" />
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="86" isRoot="false" isAbstract="false" name="RepoDB" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="87" type="76" name="d" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="98" isRoot="false" isAbstract="false" name="Repo" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="99" type="13" name="indexuri" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="111" isRoot="false" isAbstract="false" name="FilesDB" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="112" type="76" name="d" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="114" isRoot="false" isAbstract="false" name="FileInfo" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="115" type="13" name="path" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="116" type="13" name="type" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="117" type="9" name="size" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="118" type="13" name="hash" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="119" type="5" name="permanent" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Enumeration stereotype="108" isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="141" isRoot="false" isAbstract="false" name="repo" >
|
||||
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="142" isRoot="false" isAbstract="false" name="installed" />
|
||||
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="143" isRoot="false" isAbstract="false" name="thirdparty" />
|
||||
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="144" isRoot="false" isAbstract="false" name="repos" />
|
||||
<UML:EnumerationLiteral isSpecification="false" isLeaf="false" visibility="public" namespace="141" xmi.id="145" isRoot="false" isAbstract="false" name="all" />
|
||||
</UML:Enumeration>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="146" isRoot="false" isAbstract="false" name="ItemByRepo" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="147" type="76" name="d" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="155" isRoot="false" isAbstract="false" name="ComponentDB" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="156" type="146" name="d" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="163" isRoot="false" isAbstract="false" name="Component" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="164" type="13" name="name" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="165" type="13" name="localname" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="166" type="13" name="summary" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="167" type="13" name="description" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="168" type="177" name="dependencies" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="169" type="177" name="packages" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="170" type="177" name="sources" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="177" isRoot="false" isAbstract="false" name="list" />
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="186" isRoot="false" isAbstract="false" name="PackageDB" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="187" type="146" name="d" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="188" type="146" name="dr" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="190" isRoot="false" isAbstract="false" name="Package" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="191" type="13" name="name" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="192" type="13" name="summary" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="193" type="13" name="description" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="194" type="13" name="partof" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="195" type="13" name="license" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="196" type="177" name="packageDependencies" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="197" type="177" name="componentDependencies" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="198" type="177" name="files" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="199" type="177" name="conflicts" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="200" type="177" name="providesComar" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="201" type="177" name="additionalFiles" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="202" type="177" name="history" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="211" isRoot="false" isAbstract="false" name="SourceDB" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="212" type="146" name="d" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="213" type="146" name="dpkgtosrc" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="215" isRoot="false" isAbstract="false" name="SpecFile" >
|
||||
<UML:Classifier.feature>
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="216" type="217" name="source" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="218" type="177" name="packages" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="219" type="177" name="history" />
|
||||
<UML:Attribute isSpecification="false" visibility="private" xmi.id="221" type="177" name="components" />
|
||||
</UML:Classifier.feature>
|
||||
</UML:Class>
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="217" isRoot="false" isAbstract="false" name="Source" />
|
||||
<UML:Class isSpecification="false" isLeaf="false" visibility="public" namespace="m1" xmi.id="220" isRoot="false" isAbstract="false" name="History" />
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="26" name="" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="27" aggregation="none" type="24" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="28" aggregation="none" type="14" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="35" name="" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="36" aggregation="none" type="24" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="37" aggregation="none" type="14" name="" multiplicity="n" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="40" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="41" aggregation="none" type="24" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="42" aggregation="none" type="14" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="64" name="" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="65" aggregation="none" type="24" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="66" aggregation="none" type="14" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="73" name="" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="false" xmi.id="74" aggregation="none" type="24" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="75" aggregation="none" type="14" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="90" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="91" aggregation="composite" type="24" name="name" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="92" aggregation="none" type="14" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="100" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="101" aggregation="composite" type="86" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="102" aggregation="none" type="98" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="120" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="121" aggregation="composite" type="111" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="122" aggregation="none" type="114" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Dependency isSpecification="false" visibility="public" namespace="m1" xmi.id="149" client="146" name="" supplier="141" />
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="171" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="172" aggregation="composite" type="155" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="173" aggregation="none" type="163" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="203" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="204" aggregation="composite" type="186" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="205" aggregation="none" type="190" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
<UML:Association isSpecification="false" visibility="public" namespace="m1" xmi.id="222" name="1..*" >
|
||||
<UML:Association.connection>
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="223" aggregation="composite" type="211" name="" />
|
||||
<UML:AssociationEnd isSpecification="false" visibility="public" changeability="changeable" isNavigable="true" xmi.id="224" aggregation="none" type="215" name="" />
|
||||
</UML:Association.connection>
|
||||
</UML:Association>
|
||||
</UML:Namespace.ownedElement>
|
||||
</UML:Model>
|
||||
</XMI.content>
|
||||
<XMI.extensions xmi.extender="umbrello" >
|
||||
<docsettings viewid="209" documentation="" uniqueid="234" />
|
||||
<diagrams>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="1" documentation="" type="402" showops="1" showpackage="0" name="InstallDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
|
||||
<widgets>
|
||||
<classwidget usesdiagramfillcolour="0" width="142" showattsigs="601" usesdiagramusefillcolour="0" x="543" y="273" showopsigs="601" linewidth="none" fillcolour="#ffffc0" height="135" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="14" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="#ff0000" />
|
||||
<classwidget usesdiagramfillcolour="0" width="204" showattsigs="601" usesdiagramusefillcolour="0" x="35" y="355" showopsigs="601" linewidth="none" fillcolour="#ffffc0" height="63" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="24" showoperations="1" showpackage="1" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="#ff0000" />
|
||||
<notewidget usesdiagramfillcolour="1" width="413" usesdiagramusefillcolour="1" x="27" y="149" linewidth="none" fillcolour="none" height="185" usefillcolor="1" isinstance="0" xmi.id="46" showstereotype="1" text="@d: is a bsddb dict which holds package related data.
|
||||
key:"package name" value:InstallInfo
|
||||
|
||||
"/var/db/pisi/install.bdb"
|
||||
|
||||
@dp: is a bsddb dict which holds pending packages data.
|
||||
key:"package name" value:bool
|
||||
|
||||
"/var/db/pisi/configpending.bdb"
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="411" usesdiagramusefillcolour="1" x="15" y="14" linewidth="none" fillcolour="none" height="113" usefillcolor="1" isinstance="0" xmi.id="47" text="installdb.py
|
||||
|
||||
The main purpose of the installdb is to hold the packages' version and status infos. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="313" usesdiagramusefillcolour="1" x="678" y="50" linewidth="none" fillcolour="none" height="245" usefillcolor="1" isinstance="0" xmi.id="49" showstereotype="1" text="@state: holds the status of the package
|
||||
'i' : 'installed'
|
||||
'ip' : 'installed-pending'
|
||||
'r' : 'removed'
|
||||
'p' : 'purged'
|
||||
@version: version of the package
|
||||
@release: release number of the package
|
||||
@build: build number of the package
|
||||
@distribution: distribution of the package. (exp: "Pardus")
|
||||
@time: the time when the package is installed
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="14" widgetaid="24" xmi.id="90" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="239" starty="386" />
|
||||
<endpoint endx="543" endy="340" />
|
||||
</linepath>
|
||||
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="391" y="363" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="227" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<floatingtext usesdiagramfillcolour="1" width="56" usesdiagramusefillcolour="1" x="241" y="362" linewidth="none" posttext="" role="709" fillcolour="none" height="22" usefillcolor="1" pretext="+" isinstance="0" xmi.id="228" text="name" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="52" documentation="" type="402" showops="1" showpackage="0" name="RepoDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
|
||||
<widgets>
|
||||
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="13" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="54" text="repodb.py
|
||||
|
||||
The main purpose of the repodb is to hold the repositories' infos (currently we only have the repository's url).
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="139" showattsigs="601" usesdiagramusefillcolour="1" x="35" y="299" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="86" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="403" usesdiagramusefillcolour="1" x="22" y="165" linewidth="none" fillcolour="none" height="101" usefillcolor="1" isinstance="0" xmi.id="88" showstereotype="1" text="@d: is a bsddb dict which holds repositories' infos
|
||||
key:"repository name" value:Repo
|
||||
|
||||
"/var/db/pisi/repodb.bdb"
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="121" showattsigs="601" usesdiagramusefillcolour="1" x="538" y="259" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="98" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="243" usesdiagramusefillcolour="1" x="615" y="211" linewidth="none" fillcolour="none" height="58" usefillcolor="1" isinstance="0" xmi.id="106" showstereotype="1" text="@indexuri: url of the repository. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="98" widgetaid="86" xmi.id="100" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="174" starty="321" />
|
||||
<endpoint endx="538" endy="281" />
|
||||
</linepath>
|
||||
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="356" y="301" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="229" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="109" documentation="" type="402" showops="1" showpackage="0" name="FilesDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
|
||||
<widgets>
|
||||
<notewidget usesdiagramfillcolour="1" width="404" usesdiagramusefillcolour="1" x="14" y="14" linewidth="none" fillcolour="none" height="113" usefillcolor="1" isinstance="0" xmi.id="110" text="filesdb.py
|
||||
|
||||
The main purpose of the filesdb is to hold all the packages' files' information.
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="139" showattsigs="601" usesdiagramusefillcolour="1" x="43" y="338" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="111" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="420" usesdiagramusefillcolour="1" x="15" y="188" linewidth="none" fillcolour="none" height="115" usefillcolor="1" isinstance="0" xmi.id="113" showstereotype="1" text="@d: is a bsddb dict which holds file infos that all the installed packages have.
|
||||
|
||||
key:"file path" value: (packagename, FileInfo) tuple
|
||||
"/var/db/pisi/filesdb.bdb"
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="140" showattsigs="601" usesdiagramusefillcolour="1" x="538" y="244" showopsigs="601" linewidth="none" fillcolour="none" height="122" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="114" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="335" usesdiagramusefillcolour="1" x="640" y="81" linewidth="none" fillcolour="none" height="223" usefillcolor="1" isinstance="0" xmi.id="133" showstereotype="1" text="@path: path of the file
|
||||
@type: type of the file. ("doc", "man", "info", "config", "header", "library", "executable", "data", "localedata")
|
||||
@size: size of the file
|
||||
@hash: sha1sum of the file
|
||||
@permanent: If true, file is not removed when the package is removed. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="114" widgetaid="111" xmi.id="120" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="182" starty="360" />
|
||||
<endpoint endx="538" endy="305" />
|
||||
</linepath>
|
||||
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="360" y="332" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="230" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="136" documentation="" type="402" showops="1" showpackage="0" name="ItemByRepo" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
|
||||
<widgets>
|
||||
<enumwidget usesdiagramfillcolour="0" width="85" usesdiagramusefillcolour="0" x="507" y="249" linewidth="none" fillcolour="#ffffc0" height="126" usefillcolor="1" isinstance="0" xmi.id="141" showpackage="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" />
|
||||
<classwidget usesdiagramfillcolour="1" width="139" showattsigs="601" usesdiagramusefillcolour="1" x="52" y="262" showopsigs="601" linewidth="none" fillcolour="none" height="45" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="146" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="394" usesdiagramusefillcolour="1" x="38" y="178" linewidth="none" fillcolour="none" height="69" usefillcolor="1" isinstance="0" xmi.id="148" showstereotype="1" text="@d: is a bsddb dict which holds another key,value dict
|
||||
key: objkey value:dict("repo name", objvalue)
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="20" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="152" text="itembydb.py
|
||||
|
||||
The main purpose of the itembyrepo class is make it easier for holding datas by repository." font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="141" widgetaid="146" xmi.id="149" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="191" starty="284" />
|
||||
<endpoint endx="507" endy="312" />
|
||||
</linepath>
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="153" documentation="" type="402" showops="1" showpackage="0" name="ComponentDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
|
||||
<widgets>
|
||||
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="20" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="154" text="component.py
|
||||
|
||||
The main purpose of the componentdb is to hold the repositories component infos. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="130" showattsigs="601" usesdiagramusefillcolour="1" x="31" y="332" showopsigs="601" linewidth="none" fillcolour="none" height="47" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="155" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="401" usesdiagramusefillcolour="1" x="30" y="203" linewidth="none" fillcolour="none" height="112" usefillcolor="1" isinstance="0" xmi.id="157" showstereotype="1" text="@d: is a ItemByRepo object which contains component infos by repos.
|
||||
|
||||
key: "component name" value: Component
|
||||
"/var/db/pisi/component.bdb"
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="160" showattsigs="601" usesdiagramusefillcolour="1" x="549" y="317" showopsigs="601" linewidth="none" fillcolour="none" height="153" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="163" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="335" usesdiagramusefillcolour="1" x="673" y="106" linewidth="none" fillcolour="none" height="223" usefillcolor="1" isinstance="0" xmi.id="176" showstereotype="1" text="@name: component name
|
||||
@localname: components local name (system.base) -> (sistem.taban)
|
||||
@summary: component information
|
||||
@component: component description
|
||||
@dependencies: string list of other components this component depends on.
|
||||
@packages: string list of packages under this component
|
||||
@sources: string list of source packages under this component" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="163" widgetaid="155" xmi.id="171" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="161" starty="355" />
|
||||
<endpoint endx="549" endy="393" />
|
||||
</linepath>
|
||||
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="355" y="374" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="231" showstereotype="1" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1021" snapy="10" showatts="1" xmi.id="184" documentation="" type="402" showops="1" showpackage="0" name="PackageDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="579" >
|
||||
<widgets>
|
||||
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="16" y="20" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="185" text="packagedb.py
|
||||
|
||||
The main purpose of the packagedb is to hold package infos of the repositories and reverse dependency informations." font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="125" showattsigs="601" usesdiagramusefillcolour="1" x="36" y="428" showopsigs="601" linewidth="none" fillcolour="none" height="63" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="186" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="418" usesdiagramusefillcolour="1" x="32" y="177" linewidth="none" fillcolour="none" height="235" usefillcolor="1" isinstance="0" xmi.id="189" showstereotype="1" text="@d: is a ItemByRepo object which holds the package informations of repos.
|
||||
key:"package name" value:Package
|
||||
|
||||
"/var/db/pisi/package.bdb"
|
||||
|
||||
@d: is a ItemByRepo object which holds reverse dependency information of packages.
|
||||
key:"dependency name" value: [dependent package names list]
|
||||
|
||||
"/var/db/pisi/revdep.bdb" " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="217" showattsigs="601" usesdiagramusefillcolour="1" x="557" y="296" showopsigs="601" linewidth="none" fillcolour="none" height="243" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="190" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="317" usesdiagramusefillcolour="1" x="700" y="187" linewidth="none" fillcolour="none" height="120" usefillcolor="1" isinstance="0" xmi.id="206" showstereotype="1" text="@name: package name
|
||||
@summary: package's summary
|
||||
@description: description of the package
|
||||
@partof: package's component
|
||||
@license: license of the package
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="190" widgetaid="186" xmi.id="203" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="161" starty="459" />
|
||||
<endpoint endx="557" endy="417" />
|
||||
</linepath>
|
||||
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="359" y="438" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="232" showstereotype="1" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
<diagram snapgrid="0" showattsig="1" fillcolor="#ffffc0" linewidth="0" zoom="100" showgrid="0" showopsig="1" usefillcolor="1" snapx="10" canvaswidth="1012" snapy="10" showatts="1" xmi.id="209" documentation="" type="402" showops="1" showpackage="0" name="SourceDB" localid="900000" showstereotype="0" showscope="1" snapcsgrid="0" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="#ff0000" canvasheight="593" >
|
||||
<widgets>
|
||||
<notewidget usesdiagramfillcolour="1" width="453" usesdiagramusefillcolour="1" x="27" y="22" linewidth="none" fillcolour="none" height="108" usefillcolor="1" isinstance="0" xmi.id="210" text="sourcedb.py
|
||||
|
||||
The main purpose of the sourcedb is for emerge operation. " font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="175" showattsigs="601" usesdiagramusefillcolour="1" x="33" y="433" showopsigs="601" linewidth="none" fillcolour="none" height="63" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="211" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
<notewidget usesdiagramfillcolour="1" width="414" usesdiagramusefillcolour="1" x="31" y="174" linewidth="none" fillcolour="none" height="242" usefillcolor="1" isinstance="0" xmi.id="214" showstereotype="1" text="@d: is a ItemByRepo object which contains source packages building information of the package for emerge.
|
||||
key: "source package name" value: SpecFile
|
||||
|
||||
"/var/db/pisi/source.bdb"
|
||||
|
||||
@d: is a ItemByRepo object which contains package name information and that package's source package name information
|
||||
key: "package name" value:"source package name"
|
||||
|
||||
"/var/db/pisi/pkgtosrc.bdb"
|
||||
" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
<classwidget usesdiagramfillcolour="1" width="133" showattsigs="601" usesdiagramusefillcolour="1" x="666" y="365" showopsigs="601" linewidth="none" fillcolour="none" height="99" usefillcolor="1" showpubliconly="0" showattributes="1" isinstance="0" xmi.id="215" showoperations="1" showpackage="0" showscope="1" font="DejaVu Sans,9,-1,5,75,0,0,0,0,0" linecolor="none" />
|
||||
</widgets>
|
||||
<messages/>
|
||||
<associations>
|
||||
<assocwidget totalcounta="2" indexa="1" totalcountb="2" indexb="1" linewidth="none" widgetbid="215" widgetaid="211" xmi.id="222" linecolor="none" >
|
||||
<linepath>
|
||||
<startpoint startx="208" starty="464" />
|
||||
<endpoint endx="666" endy="414" />
|
||||
</linepath>
|
||||
<floatingtext usesdiagramfillcolour="1" width="32" usesdiagramusefillcolour="1" x="437" y="439" linewidth="none" posttext="" role="703" fillcolour="none" height="32" usefillcolor="1" pretext="" isinstance="0" xmi.id="233" showstereotype="1" text="1..*" font="DejaVu Sans,9,-1,5,50,0,0,0,0,0" linecolor="none" />
|
||||
</assocwidget>
|
||||
</associations>
|
||||
</diagram>
|
||||
</diagrams>
|
||||
<listview>
|
||||
<listitem open="1" type="800" label="Görünümler" >
|
||||
<listitem open="1" type="801" label="Mantıksal Görünüm" >
|
||||
<listitem open="1" type="813" id="163" >
|
||||
<listitem open="0" type="814" id="164" />
|
||||
<listitem open="0" type="814" id="165" />
|
||||
<listitem open="0" type="814" id="166" />
|
||||
<listitem open="0" type="814" id="167" />
|
||||
<listitem open="0" type="814" id="168" />
|
||||
<listitem open="0" type="814" id="169" />
|
||||
<listitem open="0" type="814" id="170" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="155" >
|
||||
<listitem open="0" type="814" id="156" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="114" >
|
||||
<listitem open="0" type="814" id="115" />
|
||||
<listitem open="0" type="814" id="116" />
|
||||
<listitem open="0" type="814" id="117" />
|
||||
<listitem open="0" type="814" id="118" />
|
||||
<listitem open="0" type="814" id="119" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="111" >
|
||||
<listitem open="0" type="814" id="112" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="220" />
|
||||
<listitem open="1" type="813" id="24" >
|
||||
<listitem open="1" type="813" id="14" >
|
||||
<listitem open="0" type="814" id="79" />
|
||||
<listitem open="0" type="814" id="80" />
|
||||
<listitem open="0" type="814" id="81" />
|
||||
<listitem open="0" type="814" id="82" />
|
||||
<listitem open="0" type="814" id="83" />
|
||||
<listitem open="0" type="814" id="84" />
|
||||
</listitem>
|
||||
<listitem open="0" type="814" id="25" />
|
||||
<listitem open="0" type="814" id="77" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="146" >
|
||||
<listitem open="0" type="814" id="147" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="76" />
|
||||
<listitem open="1" type="813" id="190" >
|
||||
<listitem open="0" type="814" id="191" />
|
||||
<listitem open="0" type="814" id="192" />
|
||||
<listitem open="0" type="814" id="193" />
|
||||
<listitem open="0" type="814" id="194" />
|
||||
<listitem open="0" type="814" id="195" />
|
||||
<listitem open="0" type="814" id="196" />
|
||||
<listitem open="0" type="814" id="197" />
|
||||
<listitem open="0" type="814" id="198" />
|
||||
<listitem open="0" type="814" id="199" />
|
||||
<listitem open="0" type="814" id="200" />
|
||||
<listitem open="0" type="814" id="201" />
|
||||
<listitem open="0" type="814" id="202" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="186" >
|
||||
<listitem open="0" type="814" id="187" />
|
||||
<listitem open="0" type="814" id="188" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="98" >
|
||||
<listitem open="0" type="814" id="99" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="86" >
|
||||
<listitem open="0" type="814" id="87" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="217" />
|
||||
<listitem open="1" type="813" id="211" >
|
||||
<listitem open="0" type="814" id="212" />
|
||||
<listitem open="0" type="814" id="213" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="215" >
|
||||
<listitem open="0" type="814" id="216" />
|
||||
<listitem open="0" type="814" id="218" />
|
||||
<listitem open="0" type="814" id="219" />
|
||||
<listitem open="0" type="814" id="221" />
|
||||
</listitem>
|
||||
<listitem open="1" type="813" id="177" />
|
||||
<listitem open="1" type="830" label="Veri türleri" >
|
||||
<listitem open="1" type="829" id="5" />
|
||||
<listitem open="1" type="829" id="4" />
|
||||
<listitem open="1" type="829" id="89" />
|
||||
<listitem open="1" type="829" id="7" />
|
||||
<listitem open="1" type="829" id="6" />
|
||||
<listitem open="1" type="829" id="2" />
|
||||
<listitem open="1" type="829" id="9" />
|
||||
<listitem open="1" type="829" id="8" />
|
||||
<listitem open="1" type="829" id="13" />
|
||||
<listitem open="1" type="829" id="10" />
|
||||
<listitem open="1" type="829" id="12" />
|
||||
<listitem open="1" type="829" id="11" />
|
||||
</listitem>
|
||||
<listitem open="1" type="831" id="141" >
|
||||
<listitem open="0" type="839" id="142" />
|
||||
<listitem open="0" type="839" id="143" />
|
||||
<listitem open="0" type="839" id="144" />
|
||||
<listitem open="0" type="839" id="145" />
|
||||
</listitem>
|
||||
</listitem>
|
||||
<listitem open="1" type="802" label="Use Case Görünümü" />
|
||||
<listitem open="1" type="821" label="Bileşen Görünümü" />
|
||||
<listitem open="1" type="827" label="Deployment View" />
|
||||
<listitem open="1" type="836" label="Entity Relationship Model" />
|
||||
</listitem>
|
||||
</listview>
|
||||
<codegeneration>
|
||||
<codegenerator language="C++" />
|
||||
</codegeneration>
|
||||
</XMI.extensions>
|
||||
</XMI>
|
||||
@@ -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,86 +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
|
||||
gettext.bindtextdomain('pisi', "/usr/share/locale")
|
||||
gettext.textdomain('pisi')
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
def sig_handler(sig, frame):
|
||||
if sig == signal.SIGTERM:
|
||||
exit()
|
||||
|
||||
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
|
||||
|
||||
signal.signal(signal.SIGTERM, sig_handler)
|
||||
|
||||
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,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.
|
||||
#
|
||||
|
||||
# PiSi version
|
||||
|
||||
__version__ = "1.1_rc4"
|
||||
|
||||
__dbversion__ = "1.1_beta8"
|
||||
__filesdbversion__ = "1.0.5" # yes, this is the real bottleneck
|
||||
|
||||
__all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ]
|
||||
|
||||
# FIXME: Exception shadows builtin Exception. This is no good.
|
||||
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,515 +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
|
||||
|
||||
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.operations import plan_install_pkg_names as plan_install
|
||||
from pisi.operations import plan_remove, plan_upgrade, upgrade_base
|
||||
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 generate_install_order(A):
|
||||
# returns the install order of the given install package list with any extra
|
||||
# dependency that is also going to be installed
|
||||
G_f, order = plan_install(A, ignore_conflicts = True)
|
||||
return order
|
||||
|
||||
def generate_remove_order(A):
|
||||
# returns the remove order of the given removal package list with any extra
|
||||
# reverse dependency that is also going to be removed
|
||||
G_f, order = plan_remove(A)
|
||||
return order
|
||||
|
||||
def generate_upgrade_order(A):
|
||||
# returns the upgrade order of the given upgrade package list with any needed extra
|
||||
# dependency
|
||||
G_f, order = plan_upgrade(A, ignore_conflicts = True)
|
||||
return order
|
||||
|
||||
def generate_base_upgrade(A):
|
||||
# all the packages of the system.base must be installed on the system.
|
||||
# method returns the currently needed system.base component install and
|
||||
# upgrade needs
|
||||
base = upgrade_base(A)
|
||||
return list(base)
|
||||
|
||||
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, name = pkginfo.name)
|
||||
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)
|
||||
ctx.ui.notify(pisi.ui.updatingrepo, name = 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)
|
||||
|
||||
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))
|
||||
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,411 +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 read_file(self, file_path):
|
||||
return self.zip_obj.read(file_path)
|
||||
|
||||
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,571 +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
|
||||
|
||||
ctx.ui.notify(pisi.ui.installing, name=self.pkginfo.name)
|
||||
|
||||
def install(self, ask_reinstall = True):
|
||||
if ctx.get_option('fetch_only'):
|
||||
return
|
||||
|
||||
ctx.ui.status(_('Installing %s, version %s, release %s, build %s') %
|
||||
(self.pkginfo.name, self.pkginfo.version,
|
||||
self.pkginfo.release, self.pkginfo.build))
|
||||
|
||||
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, name = self.pkginfo.name)
|
||||
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, name = self.pkginfo.name)
|
||||
|
||||
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()
|
||||
|
||||
ctx.ui.notify(pisi.ui.removing, name=self.package_name)
|
||||
|
||||
def run(self):
|
||||
"""Remove a single package"""
|
||||
|
||||
ctx.ui.status(_('Removing package %s') % self.package_name)
|
||||
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()
|
||||
progress = ctx.ui.Progress(len(self.files.list))
|
||||
removed = 0
|
||||
for fileinfo in self.files.list:
|
||||
self.remove_file(fileinfo)
|
||||
removed += 1
|
||||
ctx.ui.display_progress(operation = "removing",
|
||||
percent = progress.update(removed),
|
||||
info = _("Removing package %s") % self.package_name)
|
||||
|
||||
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(_('Installed file %s is not exists on system [Probably you manually deleted]') % 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,975 +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.getSourceVersion() + '-' + self.spec.getSourceRelease()
|
||||
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()
|
||||
self.compile_comar_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.getSourceVersion(),
|
||||
"SRC_RELEASE": self.spec.getSourceRelease()
|
||||
}
|
||||
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
|
||||
|
||||
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 given actions.py to check syntax error in it and sets the actionLocals and actionGlobals"""
|
||||
fname = util.join_path(self.specdir, ctx.const.actions_file)
|
||||
try:
|
||||
localSymbols = globalSymbols = {}
|
||||
buf = open(fname).read()
|
||||
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
|
||||
except IOError, e:
|
||||
raise Error(_("Unable to read Actions Script (%s): %s") %(fname,e))
|
||||
except SyntaxError, e:
|
||||
raise Error(_("SyntaxError in Actions Script (%s): %s") %(fname,e))
|
||||
|
||||
self.actionLocals = localSymbols
|
||||
self.actionGlobals = globalSymbols
|
||||
self.srcDir = self.pkg_src_dir()
|
||||
|
||||
def compile_comar_script(self):
|
||||
"""Compiles comar scripts to check syntax errors"""
|
||||
for package in self.spec.packages:
|
||||
for pcomar in package.providesComar:
|
||||
fname = util.join_path(self.specdir, ctx.const.comar_dir,
|
||||
pcomar.script)
|
||||
|
||||
try:
|
||||
buf = open(fname).read()
|
||||
compile(buf, "error", "exec")
|
||||
except IOError, e:
|
||||
raise Error(_("Unable to read COMAR script (%s): %s") %(fname,e))
|
||||
except SyntaxError, e:
|
||||
raise Error(_("SyntaxError in COMAR file (%s): %s") %(fname,e))
|
||||
|
||||
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.getSourceVersion()
|
||||
|
||||
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('ignore_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, self.spec.history)
|
||||
|
||||
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:
|
||||
|
||||
# removing "farce" in specfile.py:SpecFile.override_tags
|
||||
# this block of code came here... SpecFile should never
|
||||
# ever ruin the generated PSPEC file. If build process
|
||||
# needs this, we should do it in here... (bug: #3773)
|
||||
if not package.summary:
|
||||
package.summary = self.spec.source.summary
|
||||
if not package.description:
|
||||
# TODO: remove this if statement with the part in
|
||||
# specfile.py:SpecFile
|
||||
if not self.spec.source.description:
|
||||
self.spec.dirtyWorkAround()
|
||||
|
||||
package.description = self.spec.source.description
|
||||
if not package.partOf:
|
||||
package.partOf = self.spec.source.partOf
|
||||
if not package.license:
|
||||
package.license = self.spec.source.license
|
||||
if not package.icon:
|
||||
package.icon = self.spec.source.icon
|
||||
|
||||
|
||||
|
||||
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.getSourceVersion(),
|
||||
self.spec.getSourceRelease(),
|
||||
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('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()
|
||||
pb.compile_comar_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,165 +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, operation, percent, info="", **ka):
|
||||
""" display progress of any operation """
|
||||
if operation == "removing":
|
||||
return
|
||||
elif operation == "fetching":
|
||||
totalsize = '%.1f %s' % pisi.util.human_readable_size(ka['total_size'])
|
||||
out = '\r%-30.30s (%s)%3d%% %9.2f %s [%s]' % \
|
||||
(ka['filename'], totalsize, percent,
|
||||
ka['rate'], ka['symbol'], ka['eta'])
|
||||
self.output(out)
|
||||
else:
|
||||
self.output("\r%s (%d%%)" % (info, percent))
|
||||
|
||||
if 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['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,225 +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"]
|
||||
|
||||
class ComponentDB(object):
|
||||
"""a database of components"""
|
||||
|
||||
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 = 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):
|
||||
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(self.get_packages(name, True, repo, txn))
|
||||
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)
|
||||
|
||||
# Returns the package list of the component
|
||||
# walk: walks through the underlying components' packages
|
||||
def get_packages(self, component_name, walk=False, repo=None, txn = None):
|
||||
"""returns the given component's and underlying recursive components' packages"""
|
||||
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
if not walk:
|
||||
return component.packages
|
||||
|
||||
packages = []
|
||||
packages.extend(component.packages)
|
||||
for dep in component.dependencies:
|
||||
packages.extend(self.get_packages(dep))
|
||||
|
||||
return packages
|
||||
|
||||
def add_child(self, component, repo, txn = None):
|
||||
"""update component tree"""
|
||||
parent_name = ".".join(component.name.split(".")[:-1])
|
||||
if not parent_name: # root component
|
||||
return
|
||||
|
||||
if self.has_component(parent_name, repo, txn):
|
||||
parent = self.get_component(parent_name, repo, txn)
|
||||
else:
|
||||
parent = Component(name = parent_name)
|
||||
|
||||
if component.name not in parent.dependencies:
|
||||
parent.dependencies.append(component.name)
|
||||
self.d.add_item(parent_name, parent, repo, txn)
|
||||
|
||||
def update_component(self, component, repo, txn = None):
|
||||
def proc(txn):
|
||||
if self.has_component(component.name, repo, txn):
|
||||
# preserve list of packages and dependencies
|
||||
current = self.d.get_item(component.name, repo, txn)
|
||||
component.packages = current.packages
|
||||
component.dependencies = current.dependencies
|
||||
self.d.add_item(component.name, component, repo, txn)
|
||||
self.add_child(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.add_child(component, repo, txn)
|
||||
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,113 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
#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,317 +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 pisi.Error, 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):
|
||||
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(operation = "fetching",
|
||||
percent = self.percent,
|
||||
filename = self.url.filename(),
|
||||
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,178 +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):
|
||||
# FIXME: updating db takes too much time. So a notify mechanism is used to inform the status
|
||||
# of the operation.
|
||||
|
||||
self.progress = ctx.ui.Progress(len(self.packages)+len(self.specs))
|
||||
self.processed = 0
|
||||
|
||||
def update_progress():
|
||||
self.processed += 1
|
||||
ctx.ui.display_progress(operation = "updatingrepo",
|
||||
percent = self.progress.update(self.processed),
|
||||
info = _("Updating package database of %s") % repo)
|
||||
|
||||
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)
|
||||
update_progress()
|
||||
ctx.sourcedb.remove_repo(repo, txn=txn)
|
||||
for sf in self.specs:
|
||||
ctx.sourcedb.add_spec(sf, repo, txn=txn)
|
||||
update_progress()
|
||||
|
||||
def add_package(self, path, repo_uri):
|
||||
package = Package(path, 'r')
|
||||
md = package.get_metadata()
|
||||
md.package.packageSize = os.path.getsize(path)
|
||||
md.package.packageHash = util.sha1_file(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 not len(s):
|
||||
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,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.
|
||||
#
|
||||
|
||||
import os
|
||||
import fcntl
|
||||
import types
|
||||
|
||||
import bsddb3.db as db
|
||||
import bsddb3.dbobj as dbobj
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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,105 +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_PackageHash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
|
||||
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, history):
|
||||
# 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
|
||||
# merge pkg.isA with src.isA
|
||||
pkg.isA.extend(src.isA)
|
||||
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 = 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_package_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]\n") % (pkg, util.strlist(pkg_conflicts[pkg]))
|
||||
|
||||
ctx.ui.info(_("The following packages have conflicts:\n%s") %
|
||||
conflicts)
|
||||
|
||||
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
|
||||
raise Error(_("Conflicts remain"))
|
||||
|
||||
if remove(list(C), ignore_dep=True, ignore_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('ignore_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, ignore_conflicts = False):
|
||||
# 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_package_conflicts') and not ignore_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, ignore_conflicts = 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_package_conflicts') and not ignore_conflicts:
|
||||
check_conflicts(order, ctx.packagedb)
|
||||
return G_f, order
|
||||
|
||||
def remove(A, ignore_dep = False, ignore_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('ignore_safety') and not ignore_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,147 +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 get_metadata(self):
|
||||
"""reads metadata.xml from the PiSi package and returns MetaData object"""
|
||||
md = MetaData()
|
||||
md.parse(self.impl.read_file(ctx.const.metadata_xml))
|
||||
return md
|
||||
|
||||
def get_files(self):
|
||||
"""reads files.xml from the PiSi package and returns Files object"""
|
||||
files = Files()
|
||||
files.parse(self.impl.read_file(ctx.const.files_xml))
|
||||
return files
|
||||
|
||||
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,846 +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 parse(self, xml, keepDoc = False):
|
||||
"parse XML string and decode it into a python object"
|
||||
self.parsexml(xml)
|
||||
errs = []
|
||||
self.decode(self.rootNode(), errs)
|
||||
if errs:
|
||||
errs.append(_("autoxml.parse: String '%s' has errors") % xml)
|
||||
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.parse: String '%s' has errors") % xml)
|
||||
|
||||
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
|
||||
cls.parse = parse
|
||||
|
||||
|
||||
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,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,95 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
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 parsexml(self, xml):
|
||||
"""parses xml string and returns DOM"""
|
||||
try:
|
||||
self.doc = iks.parseString(xml)
|
||||
return self.doc
|
||||
except Exception, e:
|
||||
raise Error(_("String '%s' has invalid XML") % (xml))
|
||||
|
||||
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,117 +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 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,245 +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.optional]
|
||||
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 getSourceVersion(self):
|
||||
return self.history[0].version
|
||||
|
||||
def getSourceRelease(self):
|
||||
return self.history[0].release
|
||||
|
||||
def dirtyWorkAround(self):
|
||||
#TODO: Description should be mandatory. Remove this crap when repo is ready.
|
||||
#http://liste.pardus.org.tr/gelistirici/2006-September/002332.html
|
||||
self.source.description = autoxml.LocalText("Description")
|
||||
self.source.description["en"] = self.source.summary["en"]
|
||||
@@ -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, updatingrepo) = range(11)
|
||||
|
||||
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,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.
|
||||
#
|
||||
|
||||
"""version structure"""
|
||||
|
||||
import re
|
||||
import string
|
||||
import exceptions
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
|
||||
maxdashes = 2
|
||||
|
||||
# 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 VersionException(pisi.Exception):
|
||||
def __init__(self, str):
|
||||
pisi.Exception.__init__(self, str)
|
||||
|
||||
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):
|
||||
# PiSi version policy does not allow "-" in version strings.
|
||||
# They are special and used for build and release no separation.
|
||||
if verstring.count("-") > maxdashes:
|
||||
raise VersionException("%s is not a valid PiSi version format" % verstring)
|
||||
|
||||
verchunks = verstring.split("-")
|
||||
verchunks.extend("0" * (maxdashes - verstring.count("-")))
|
||||
(version, release, build) = verchunks
|
||||
|
||||
self.comps = []
|
||||
for i in util.multisplit(version,'._'):
|
||||
# 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
|
||||
self.release = VersionItem(release)
|
||||
self.build = VersionItem(build)
|
||||
|
||||
def string(self):
|
||||
return self.verstring
|
||||
|
||||
def compare(self, ver):
|
||||
"""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 = ver.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 in version items
|
||||
for (litem, ritem) in zip(lhs, rhs):
|
||||
if litem < ritem:
|
||||
return -1
|
||||
elif litem > ritem:
|
||||
return +1
|
||||
# now let's compare release and build
|
||||
lhs = (self.release, self.build)
|
||||
rhs = (ver.release, ver.build)
|
||||
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Scenario : bug3237scen.py
|
||||
#
|
||||
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3237
|
||||
#
|
||||
# Problem : If the revdep of the "upgraded only packages"' dependency is not satisfied
|
||||
# it is also added to the upgrade list and creates "confusion"
|
||||
#
|
||||
# # pisi lu
|
||||
# amarok - KDE için müzik çalıcısı
|
||||
# ksynaptics - Synaptics touchpad yapılandırma aracı
|
||||
# libwmf - Microsoft Word gibi uygulamaların kullan.....
|
||||
# tunepimp - MusicBrainz uyumluluğu olan uygulamalar.......
|
||||
#
|
||||
# pisi up tunepimp libwmf ksynaptics
|
||||
# Depolar güncelleniyor
|
||||
# * pardus-1.1 deposu güncelleniyor
|
||||
# pardus-1.1 deposu için güncelleme yok.
|
||||
# Aşağıdaki paketlerde çakışmalar bulunuyor: [amarok: lastfm-player ile çakışıyor]
|
||||
# Bu çakışan paketler kaldırılsın mı? (evet/hayır)hayır
|
||||
#
|
||||
# Problem Description:
|
||||
#
|
||||
# There appears to be some upgrades needed. We only wanted to upgrade some of the packages.
|
||||
# But the package we did not include in the list of upgrades also is tried to be upgraded.
|
||||
# The purpose to not include it in the upgrade list, was, it was conflicting with another
|
||||
# package.
|
||||
#
|
||||
# Expected:
|
||||
#
|
||||
# pisi either should not upgrade packages out of the given upgrade list or should show
|
||||
# all the "to be upgraded" list before the conflict warning.
|
||||
|
||||
from pisi.scenarioapi.scenario import *
|
||||
|
||||
AMAROK = "amarok"
|
||||
KSYNAPTICS = "ksynaptics"
|
||||
LIBWMF = "libwmf"
|
||||
TUNEPIMP = "tunepimp"
|
||||
LASTFM = "lastfm-player"
|
||||
|
||||
let_repo_had(AMAROK, with_conflicts(LASTFM))
|
||||
let_repo_had(LASTFM)
|
||||
let_repo_had(TUNEPIMP, with_version("0.3.9"))
|
||||
let_repo_had(KSYNAPTICS)
|
||||
let_repo_had(LIBWMF)
|
||||
|
||||
let_pisi_had(AMAROK, LASTFM, TUNEPIMP, KSYNAPTICS, LIBWMF)
|
||||
|
||||
def run():
|
||||
repo_version_bumped(TUNEPIMP, with_version("0.4.1"))
|
||||
repo_version_bumped(AMAROK, with_added_dependency(TUNEPIMP, versionFrom="0.4.1"))
|
||||
repo_version_bumped(KSYNAPTICS)
|
||||
repo_version_bumped(LIBWMF)
|
||||
repo_updated_index()
|
||||
pisi_upgraded(LIBWMF, TUNEPIMP, KSYNAPTICS)
|
||||
@@ -1,49 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Scenario : bug3390scen.py
|
||||
#
|
||||
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3390
|
||||
#
|
||||
# Problem : Package reverse dependencies are not updated in pisi database
|
||||
#
|
||||
# caglar@zangetsu ~ $ pisi info valgrind
|
||||
# Yüklü paket:
|
||||
# Ad: valgrind, versiyon 3.2.0, sürüm 5, inşa 4
|
||||
# Özet: Valgrind, x86-GNU/Linux ve ppc-GNU/Linux için geliştirilmiş, bellek
|
||||
# hatalarını ayıklayıcı, açık kaynaklı bir yazılımdır.
|
||||
# Açıklama: Valgrind, x86-GNU/Linux ve ppc-GNU/Linux için geliştirilmiş, bellek
|
||||
# hatalarını ayıklayıcı, açık kaynaklı bir yazılımdır.
|
||||
# Bileşen: programming.tools
|
||||
# Sağladıkları:
|
||||
# Bağımlılıklar:xorg openmpi
|
||||
# Dağıtım: Pardus, Dağıtım Sürümü: 1.1
|
||||
# Mimari: Any, Yerleşik Boyut: 19128120
|
||||
# Ters bağımlılıklar: kdesdk
|
||||
#
|
||||
# Problem Description:
|
||||
#
|
||||
# Valgrind package had a dependency of openmpi package. It is version bumped with removed
|
||||
# dependeny of openmpi at the repository. Also openmpi is version bumped. After upgrading
|
||||
# pisi repository and trying to remove openmpi, it is seen that openmpi still has a reverse
|
||||
# dependency of valgrind.
|
||||
#
|
||||
# Expected:
|
||||
#
|
||||
# Pisi should have updated the reverse dependency informations correctly and should not show
|
||||
# a reverse dependency of valgrind while removing openmpi, after a succesfull repository upgrade.
|
||||
|
||||
from pisi.scenarioapi.scenario import *
|
||||
|
||||
VALGRIND = "valgrind"
|
||||
OPENMPI = "openmpi"
|
||||
|
||||
let_repo_had(VALGRIND, with_dependencies(OPENMPI))
|
||||
let_repo_had(OPENMPI)
|
||||
let_pisi_had(VALGRIND, OPENMPI)
|
||||
|
||||
def run():
|
||||
repo_version_bumped(VALGRIND, with_removed_dependencies(OPENMPI))
|
||||
repo_version_bumped(OPENMPI)
|
||||
repo_updated_index()
|
||||
pisi_upgraded()
|
||||
pisi_removed(OPENMPI)
|
||||
@@ -1,38 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Scenario : bug3465scen.py
|
||||
#
|
||||
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3465
|
||||
#
|
||||
# Problem : Pisi does not warn the user about updated packages in the repository.
|
||||
#
|
||||
# sudo pisi it wormux --reinstall
|
||||
# Bağımlılıkları sağlamak için bu paketler verilen sırada kurulacaktır:
|
||||
# wormux
|
||||
# Paketlerin toplam boyu: 17.62 MB
|
||||
# Paket wormux, pardus-1.1 deposunda bulundu
|
||||
# Program sonlandırıldı.
|
||||
# http://paketler.pardus.org.tr/pardus-1.1/wormux-0.7-2-1.pisi indirilemiyor; HTTP
|
||||
# Error 404: Not Found
|
||||
#
|
||||
# Problem Description:
|
||||
#
|
||||
# The user had not updated pisi's repository database for some time. Then, when pisi is asked
|
||||
# to reinstall an installed package, it failed to fetch the requested version of the package.
|
||||
# Because the package had been upgraded at the repository and the old package has been removed.
|
||||
#
|
||||
# Expected:
|
||||
#
|
||||
# Pisi should warn the user to update repository.
|
||||
|
||||
from pisi.scenarioapi.scenario import *
|
||||
|
||||
WORMUX = "wormux"
|
||||
|
||||
let_repo_had(WORMUX)
|
||||
let_pisi_had(WORMUX)
|
||||
|
||||
def run():
|
||||
repo_version_bumped(WORMUX)
|
||||
repo_updated_index()
|
||||
pisi_reinstalled(WORMUX)
|
||||
@@ -1,48 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Scenario : bug3481scen.py
|
||||
#
|
||||
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3481
|
||||
#
|
||||
# Problem : PISI asks if conflicting system.base application to be removed but does not
|
||||
# allow it without -S
|
||||
#
|
||||
# Aşağıdaki paketlerde çakışmalar bulunuyor: [coreutils: hashalot ile çakışıyor]
|
||||
# Bu çakışan paketler kaldırılsın mı? (evet/hayır)evet
|
||||
# Emniyet mandalı: taban sistem system.base deki bu paketler kaldırılamıyor: hashalot
|
||||
# Kaldıracak paket yok.
|
||||
# Program sonlandırıldı.
|
||||
# pisi.operations.Error: Çakışmalar var
|
||||
# Genel yardım için lütfen 'pisi help' komutunu kullanınız.
|
||||
#
|
||||
# Problem Description:
|
||||
#
|
||||
# PISI upgrade command sees some system.base packages conflict with each other. It asks if you
|
||||
# want to remove the conflicting package but does not allow it to be removed without
|
||||
# --bypass-safety parameter.
|
||||
#
|
||||
# Expected:
|
||||
#
|
||||
# Pisi should remove the package if answered yes.
|
||||
#
|
||||
|
||||
from pisi.scenarioapi.scenario import *
|
||||
|
||||
HASHALOT="hashalot"
|
||||
COREUTILS="coreutils"
|
||||
GLIBC="glibc"
|
||||
UTIL_LINUX="util-linux"
|
||||
|
||||
let_repo_had(HASHALOT, with_partof("system.base"))
|
||||
let_repo_had(COREUTILS, with_partof("system.base"))
|
||||
let_repo_had(GLIBC, with_partof("system.base"))
|
||||
let_repo_had(UTIL_LINUX, with_partof("system.base"))
|
||||
|
||||
let_pisi_had(COREUTILS, HASHALOT, GLIBC, UTIL_LINUX)
|
||||
|
||||
def run():
|
||||
repo_version_bumped(GLIBC)
|
||||
repo_version_bumped(UTIL_LINUX)
|
||||
repo_version_bumped(COREUTILS, with_added_conflicts(HASHALOT))
|
||||
repo_updated_index()
|
||||
pisi_upgraded()
|
||||
@@ -1,47 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Scenario : bug3558scen.py
|
||||
#
|
||||
# Source : http://bugs.pardus.org.tr/show_bug.cgi?id=3558
|
||||
#
|
||||
# Problem : reverse dependency information disappears
|
||||
#
|
||||
# faik@iago scenarios $ pisi info kdelibs
|
||||
# Yüklü paket:
|
||||
# Ad: kdelibs, versiyon 3.5.4, sürüm 35, inşa 17
|
||||
# Özet: Tüm KDE programlarının ihtiyaç duyduğu KDE kütüphaneleri
|
||||
# Açıklama: Tüm KDE programlarının ihtiyaç duyduğu KDE kütüphaneleri
|
||||
# Bileşen: desktop.kde.base
|
||||
# Sağladıkları:
|
||||
# Bağımlılıklar:qt arts freetype fontconfig libxslt libxml2 libpcre libart_lgpl libidn utempter alsa-lib cups tiff aspell
|
||||
# jasper mDNSResponder ghostscript acl zpspell openexr mit-kerberos tulliana2
|
||||
# Dağıtım: Pardus, Dağıtım Sürümü: 1.1
|
||||
# Mimari: Any, Yerleşik Boyut: 222414685
|
||||
# Ters bağımlılıklar: kdebase gwenview
|
||||
#
|
||||
# Problem Description:
|
||||
#
|
||||
# Package's reverse dependencies disappears unexpectedly. If a package which has reverse dependencies has been upgraded.
|
||||
# The revdep list is removed from revdep db. So upgraded package will have an empty revdep list.
|
||||
#
|
||||
# Expected:
|
||||
#
|
||||
# They should not. :)
|
||||
#
|
||||
|
||||
from pisi.scenarioapi.scenario import *
|
||||
|
||||
FLIGHTGEAR = "flightgear"
|
||||
FLIGHTGEAR_DATA = "flightgear-data"
|
||||
|
||||
let_repo_had(FLIGHTGEAR, with_dependencies(FLIGHTGEAR_DATA))
|
||||
let_repo_had(FLIGHTGEAR_DATA)
|
||||
|
||||
let_pisi_had(FLIGHTGEAR, FLIGHTGEAR_DATA)
|
||||
|
||||
def run():
|
||||
pisi_info(FLIGHTGEAR_DATA)
|
||||
repo_version_bumped(FLIGHTGEAR_DATA)
|
||||
repo_updated_index()
|
||||
pisi_upgraded()
|
||||
pisi_info(FLIGHTGEAR_DATA)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user