Move pisi-1.1_beta8 tag
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
Authors:
|
||||
|
||||
Eray Özkural <eray [at] pardus.org.tr>
|
||||
Barış Metin <baris [at] pardus.org.tr>
|
||||
A. Murat Eren <meren [at] pardus.org.tr>
|
||||
S. Çağlar Onur <caglar [at] pardus.org.tr>
|
||||
Gürer Özen <gurer [at] pardus.org.tr>
|
||||
İsmail Dönmez <ismail [at] pardus.org.tr>
|
||||
Faik Uygur <faik [at] pardus.org.tr>
|
||||
Bahadır Kandemir <bahadir [at] pardus.org.tr>
|
||||
@@ -1,129 +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. :)
|
||||
|
||||
|
||||
Unit testing
|
||||
------------
|
||||
|
||||
Unit tests are located in unittests directory. Running the tests is
|
||||
trivial. But you must synchronize your code and data with the test
|
||||
code, which can be a tedious work if you lose discipline.
|
||||
|
||||
Sample data files are located in the same directory with test modules.
|
||||
|
||||
For running the entire test suite, use the following command:
|
||||
|
||||
$ ./tests/run.py
|
||||
|
||||
The following command will run tests in specfiletests and archivetests
|
||||
in unittests dir:
|
||||
|
||||
$ ./tests/run.py specfile archive
|
||||
|
||||
Do not depend on the output of unittests. Instead of producing an
|
||||
output message/data in your tests, check the data internally. By
|
||||
definition, unittest should just report succeeding and failing cases.
|
||||
|
||||
If you didn't, take a look at the links below for having an idea of
|
||||
unit testing.
|
||||
http://www.extremeprogramming.org/rules/unittests.html
|
||||
http://www.extremeprogramming.org/rules/unittests2.html
|
||||
|
||||
|
||||
Misc. Suggestions
|
||||
-----------------
|
||||
|
||||
1. Demeter's Law
|
||||
|
||||
In OO programming, try to invoke Demeter's law.
|
||||
One of the "rules" there is not directly accessing any
|
||||
objects that are further than, 2/3 refs, away. So the
|
||||
following code is OK.
|
||||
destroy_system(a.system().name())
|
||||
but the following isn't as robust
|
||||
destroy_system(object_store.root().a.system.name())
|
||||
As you can tell, this introduces too many implementation
|
||||
dependencies. The rule of thumb is that, in these cases
|
||||
this statement must have been elsewhere.... It may be a
|
||||
good idea to not count the object scope in this case,
|
||||
so in Python self.a means only one level of reference,
|
||||
not two.
|
||||
|
||||
One quibble with this: it may be preferable not to insist
|
||||
on this where it would be inefficient. So if everything
|
||||
is neatly packed into one object contained in another
|
||||
object, why replicate everything in the upper level? If
|
||||
the semantics prevents dependency changes, then chains
|
||||
of 3 or even 4 could be acceptable.
|
||||
|
||||
OTOH, in Python and C++, it's not always good to implement
|
||||
accessor/modifier pairs for every property of an object.
|
||||
It would be much simpler if you are not doing any special
|
||||
processing on the property (e.g. if what the type system
|
||||
does is sufficient).
|
||||
|
||||
The main rule of thumb in Demeter's Law is avoiding
|
||||
putting more than, say, 10 methods in a class. That works
|
||||
really well in practice, forcing refactoring every now
|
||||
and then.
|
||||
|
||||
2. We all know, you're using LISP but didn't want to tell
|
||||
us. Don't be scared, as a success story and for your encouragment
|
||||
there are tens of people somewhere with LISP releated jobs.
|
||||
|
||||
3. If you are studying Data structures and Algorithms, and if
|
||||
your first assignment is to implement a basic FIFO queue,
|
||||
don't implement it. Just show your teacher the syntax of LISP,
|
||||
tell him how beautiful it is, and show how an autistic person
|
||||
can count lots of parenthesis with a "one second" look, you'll
|
||||
probably get A+.
|
||||
|
||||
4. If you are interested in "Playstation 2 Linux Games Programming"
|
||||
or "How to extend C programs with Guile", please don't exercise
|
||||
your valuable skills in this project.
|
||||
@@ -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 4suite XML processing library, the newest version
|
||||
bsd bindings called "bsddb3" and Pardus configuration manager COMAR
|
||||
in order to run.
|
||||
|
||||
If you are upgrading from an old PiSi release you may have to
|
||||
run the following command as root:
|
||||
|
||||
# pisi rebuild-db
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
recursive-include po *
|
||||
recursive-include tests *.py
|
||||
recursive-include tools *
|
||||
recursive-include doc *
|
||||
include . *.dtd
|
||||
include . README NEWS CODING COPYING
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
PISI 1.0.9 25 April 2006:
|
||||
|
||||
* source index/database and remote builds by name
|
||||
* "emerge" command, now we are just like gentoo :)
|
||||
* performance and usability improvements. faster database access and
|
||||
faster updates. index integrity, authenticity and compression.
|
||||
smaller db files and simplification in db schema / less seeks.
|
||||
correct use of berkeley DB cache.
|
||||
* several other improvements and bugfixes
|
||||
|
||||
PISI 1.0.2 25 December 2005:
|
||||
|
||||
* some minor bugs were corrected
|
||||
|
||||
PISI 1.0.1 24 December 2005:
|
||||
|
||||
* bugfix release. one important improvement is preventing the accumulation
|
||||
of inactive database logs (we handle catastrophic failure in
|
||||
another way, by making all critical data available as xml)
|
||||
|
||||
PISI 1.0 21 December 2005:
|
||||
|
||||
* new command "check"
|
||||
* a new tool "calc-build-order.py" to calculate build order
|
||||
for the entire distro
|
||||
* various bugfixes and improvements, no significant issues remain
|
||||
|
||||
PISI release candidate 2 12 December 2005:
|
||||
|
||||
* maintenance release for Pardus 1.0 RC1, various bugfixes
|
||||
|
||||
PISI release candidate 1 9 December 2005:
|
||||
|
||||
* a command to search package text
|
||||
* better reliability through transaction support for berkeley db
|
||||
* db version file
|
||||
* loads of bugfixes
|
||||
|
||||
PISI Beta 1/2 release 17/18 November 2005:
|
||||
|
||||
* support for components, see the completed items in TODO
|
||||
* a tool to find library dependencies
|
||||
* various improvements and bugfixes
|
||||
|
||||
PISI Alpha 9 release 13 November 2005:
|
||||
|
||||
* new package: pisi.pxml
|
||||
* first release of autoxml, a metaclass for high-level xml processing
|
||||
* uses 4suite.org for fast xml processing
|
||||
* supports multiple parsers: minidom, 4suite and in the future: piksemel
|
||||
* all xml structures in PISI have been updated to use autoxml
|
||||
* improvements in the ui interface
|
||||
* significant fixes for Turkish translations
|
||||
|
||||
previous PISI releases had too many things to mention :)
|
||||
@@ -1,16 +0,0 @@
|
||||
PISI - Packages Installed Succesfully as Intended
|
||||
|
||||
PISI is a new package manager for the PARDUS
|
||||
distribution. In Turkish PISI means "kitty", and
|
||||
like a kitty, it is featureful and small.
|
||||
|
||||
Some of its distinctive features:
|
||||
|
||||
- Implemented in python
|
||||
- Efficient and small
|
||||
- Package sources are written in XML and python
|
||||
- Fast database access implemented with berkeley DB
|
||||
- Integrates low-level and high-level package operations (dependency resolution)
|
||||
- Framework approach to build applications and tools upon
|
||||
- Comprehensive CLI and a user-friendly qt GUI (distributed separately)
|
||||
- Extremely simple package construction
|
||||
@@ -1,5 +0,0 @@
|
||||
For upgrading from pisi 1.0 and before
|
||||
|
||||
You have to move the packages in /var/lib/pisi under /var/lib/pisi/package and remove /var/db/pisi/ directory.
|
||||
|
||||
db version has changed, so a rebuild-db will be unfortunately necessary.
|
||||
@@ -1,247 +0,0 @@
|
||||
|
||||
|
||||
PiSi TODO List
|
||||
==============
|
||||
|
||||
A list of tasks to accomplish, organized into priority sections
|
||||
|
||||
Legend:
|
||||
|
||||
- Todo
|
||||
? Not determined if/how we have to do
|
||||
/ In progress
|
||||
+ Accomplished
|
||||
|
||||
1. Pre-Alpha
|
||||
|
||||
+ reading spec file
|
||||
+ writing
|
||||
+ files
|
||||
+ metadata
|
||||
+ Actions API framework
|
||||
+ sample api modules (autotools)
|
||||
+ unit test
|
||||
+ source building
|
||||
+ package module
|
||||
+ extraction
|
||||
+ add file / compression
|
||||
+ package creation
|
||||
+ install database
|
||||
+ package database
|
||||
+ command line interface
|
||||
+ pisi-build
|
||||
+ pisi-install
|
||||
+ pisi-index
|
||||
+ pisi-updatedb
|
||||
+ single repository index
|
||||
+ simple dependency checking
|
||||
+ Package/Files
|
||||
|
||||
2. Alpha
|
||||
|
||||
+ multi-package dependency analysis (eray)
|
||||
+ design a package operation planner
|
||||
+ install/remove operations (eray)
|
||||
+ command line interface:
|
||||
+ pisi remove
|
||||
+ svn-like CLI
|
||||
+ configuration file
|
||||
+ define the format of the configuration file
|
||||
+ extend Config module (config.py) accordingly
|
||||
+ define configuration keys (baris - meren)
|
||||
+ query
|
||||
+ list of installed packages (eray)
|
||||
+ refactor actionsAPI (caglar)
|
||||
+ fix xml indentation (meren)
|
||||
+ define file types (doc, executable, conf, etc...) (baris - meren)
|
||||
+ write specfile (eray)
|
||||
+ file locking for database access [not tested!] (eray)
|
||||
+ implement file uri
|
||||
+ extend the modules dealing with files accordingly
|
||||
+ ui module improvements
|
||||
+ methods for interaction (yes, no questions, etc.)
|
||||
+ implement source database (eray)
|
||||
+ easy package preparation
|
||||
+ convert ebuild to pisi
|
||||
+ COMAR interface
|
||||
+ what do we need to specify in a package.
|
||||
+ package install: register config script
|
||||
+ package remove: unregister config script
|
||||
+ internet installation
|
||||
+ support URI's whereever a filename is supported
|
||||
+ http server
|
||||
+ pisi updatedb over internet
|
||||
|
||||
3. Beta
|
||||
|
||||
+ build cmd: installl build deps
|
||||
+ improve url support, it's a little dumb at the moment
|
||||
+ devise a File class that works with URLs
|
||||
+ add URL support to autoxml
|
||||
+ info cmd: reverse dependencies of a package
|
||||
+ a helper script for finding dependencies (eray)
|
||||
+ autoxml: automated xml processing (eray)
|
||||
+ design
|
||||
+ basic types
|
||||
+ list type
|
||||
+ class type
|
||||
+ localtext/text types
|
||||
+ revised interface
|
||||
+ improved error handling, check routines, hooks, format/print routines
|
||||
+ xmlfile support (read/write)
|
||||
+ string member, in addition to tag/attribute members
|
||||
+ replace specfile with new specfile using autoxml
|
||||
+ autoxmlize: metadata, index, dependency and related code paths
|
||||
+ inheritance support
|
||||
+ revise writing, error reporting, tests
|
||||
+ components (eray)
|
||||
+ requirements
|
||||
+ xml format, add to PSPEC
|
||||
+ index component.xml's and make use of them in build
|
||||
+ a database of components:
|
||||
+ query components by name
|
||||
+ query list of packages in a component
|
||||
+ index: update component database(s)
|
||||
+ bug fixing
|
||||
+ install/remove/upgrade components
|
||||
+ safety switch: preserve system.base, install system.devel for build, etc.
|
||||
+ info <component>
|
||||
+ started documentation
|
||||
+ component dependencies
|
||||
+ multiple package repository (eray)
|
||||
+ decide how to implement
|
||||
+ support repo order
|
||||
+ refactor xml support and implement multiple backends
|
||||
+ 4suite support (initial patch by faik, completed by eray)
|
||||
+ piksemel support (eray)
|
||||
+ fix autoxml implementation to use 4suite by default
|
||||
+ partial caching and automatic resume for file download (meren)
|
||||
+ use a '.part' extension
|
||||
+ search-file command (eray)
|
||||
+ exception handling (eray)
|
||||
+ uniform exception hierarchy
|
||||
+ proper handling
|
||||
+ API: we even have application users (eray)
|
||||
+ a facade module pisi.api (eray)
|
||||
+ a library init command (eray)
|
||||
+ high-level package ops, index (eray)
|
||||
+ info/util commands (eray)
|
||||
+ build commands (baris)
|
||||
+ documentation
|
||||
+ actionsAPI documentation, unittests (caglar - meren)
|
||||
+ versioning information document (eray - baris)
|
||||
+ UI
|
||||
+ PISIMAT
|
||||
+ pykde GUI (cartman)
|
||||
+ improve interface (eray)
|
||||
+ refactor UI, reintroduce base class
|
||||
+ add an ack interface, start cleaning up a bit
|
||||
+ cli (eray)
|
||||
+ ask alternatives to choose from
|
||||
+ eliminate \n's from infos, what's the point?
|
||||
+ metaclass coolness for CLI command framework (eray)
|
||||
+ i18n support
|
||||
+ translations and correct language for summary, description (packagers)
|
||||
+ make pisi messages translatable (eray)
|
||||
+ .pot support for dist script (gurer)
|
||||
+ translate messages mesajların çevirisinin yapılması (pisi tr.po) (eray)
|
||||
+ proper unicode handling in code - tons of fixes (eray - baris)
|
||||
+ configure-pending (eray)
|
||||
+ proper commar connection (baris - gurer)
|
||||
+ ignore-deps (eray)
|
||||
+ more robust upgrade (eray)
|
||||
+ query (eray)
|
||||
+ comar OM information (Provides)
|
||||
+ find package by name, display summary/description
|
||||
+ info --files option (like rpm -ql, but better of course)
|
||||
+ implement missing unit tests
|
||||
+ sourcedb
|
||||
+ package (baris)
|
||||
+ configuration file (baris)
|
||||
+ upgrade (eray)
|
||||
+ upgrade operation
|
||||
+ test upgrade op
|
||||
+ system-wide upgrade (upgrade-all) komutu
|
||||
+ incremental build (eray)
|
||||
+ generate binary release number by comparing MD5s (eray)
|
||||
+ keep track of successfully completed configure, make, install
|
||||
steps (necessary for large-scale builds?) (meren)
|
||||
+ database locking bugs (eray)
|
||||
|
||||
4. Release
|
||||
|
||||
* high priority
|
||||
|
||||
+ transaction stuff for database (eray)
|
||||
+ database version file
|
||||
|
||||
* low priority
|
||||
|
||||
+ search command: (eray)
|
||||
+ implement an inverted index
|
||||
+ search in summary and description
|
||||
|
||||
5. Post Release
|
||||
|
||||
* high priority
|
||||
|
||||
- package signing
|
||||
- a flag to sign package after building
|
||||
- code to check if the package has a valid signature
|
||||
- cli improvements
|
||||
- non interactive mode (similar to --auto in urpmi)
|
||||
/ high-level build commands
|
||||
+ an emerge command, just for fun
|
||||
+ make use of binary repo to satisfy dependencies in emerge
|
||||
/ build command enhancements:
|
||||
+ aware of repo sources
|
||||
? full dependency analysis like install/upgrade etc.
|
||||
? build-all (or build-distro) command, or as a separate tool
|
||||
+ make a package and sourcedb for each repo
|
||||
+ packagedb
|
||||
+ sourceb
|
||||
? improve url support: consider adding "other" protocols
|
||||
- removable media: media://
|
||||
|
||||
* medium priority
|
||||
|
||||
- UI impl: non-interactive use that selects defaults and logs output
|
||||
- UI impl: a general purpose Qt UI
|
||||
/ multi-architecture support
|
||||
+ design decisions
|
||||
+ extend XML specs to support that
|
||||
- cross-platform building support
|
||||
- incremental build/fetch for repository index (pisi-index.xml)
|
||||
/ xdelta support (caglar)
|
||||
- diffsets
|
||||
- a command (check-repo) to check if repos are consistent wrt dep relations
|
||||
- component improvements (optional)
|
||||
- maintain a list of components each component has
|
||||
- maintain a list of packages
|
||||
- more support for categories:
|
||||
- put categories into a database
|
||||
- fast search for categories
|
||||
|
||||
* low priority
|
||||
|
||||
/ overhaul installdb (try to merge its use with packagedb) (eray)
|
||||
/ verify metodlari
|
||||
+ SpecFile
|
||||
+ MetaData
|
||||
+ Files
|
||||
- Index (somebody should write one :} )
|
||||
- autoxml:
|
||||
- revise error checking, printing and tests
|
||||
- uri type
|
||||
- date type
|
||||
- enum type
|
||||
- make autoxml dependent on only xmlext and xmlfile interfaces
|
||||
- search command: (eray)
|
||||
- search components
|
||||
- english stemming
|
||||
- turkish stemming
|
||||
- phonetic/syntactic fault tolerance (whichever works!)
|
||||
- in particular evaluate soundex and metaphone
|
||||
- research if there are language independent phonetic encodings
|
||||
- implement as an option (or make it possible to turn it off if it works well :))
|
||||
- fault tolerant search in package names (somehow)
|
||||
@@ -1,79 +0,0 @@
|
||||
% ALGORITHM STYLE -- Released 8 April 1996
|
||||
% for LaTeX-2e
|
||||
% Copyright -- 1994 Peter Williams
|
||||
% E-mail Peter.Williams@dsto.defence.gov.au
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithm}
|
||||
\typeout{Document Style `algorithm' - floating environment}
|
||||
|
||||
\RequirePackage{float}
|
||||
\RequirePackage{ifthen}
|
||||
\newcommand{\ALG@within}{nothing}
|
||||
\newboolean{ALG@within}
|
||||
\setboolean{ALG@within}{false}
|
||||
\newcommand{\ALG@floatstyle}{ruled}
|
||||
\newcommand{\ALG@name}{Algorithm}
|
||||
\newcommand{\listalgorithmname}{List of \ALG@name s}
|
||||
|
||||
% Declare Options
|
||||
% first appearance
|
||||
\DeclareOption{plain}{
|
||||
\renewcommand{\ALG@floatstyle}{plain}
|
||||
}
|
||||
\DeclareOption{ruled}{
|
||||
\renewcommand{\ALG@floatstyle}{ruled}
|
||||
}
|
||||
\DeclareOption{boxed}{
|
||||
\renewcommand{\ALG@floatstyle}{boxed}
|
||||
}
|
||||
% then numbering convention
|
||||
\DeclareOption{part}{
|
||||
\renewcommand{\ALG@within}{part}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{chapter}{
|
||||
\renewcommand{\ALG@within}{chapter}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{section}{
|
||||
\renewcommand{\ALG@within}{section}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{subsection}{
|
||||
\renewcommand{\ALG@within}{subsection}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{subsubsection}{
|
||||
\renewcommand{\ALG@within}{subsubsection}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption{nothing}{
|
||||
\renewcommand{\ALG@within}{nothing}
|
||||
\setboolean{ALG@within}{true}
|
||||
}
|
||||
\DeclareOption*{\edef\ALG@name{\CurrentOption}}
|
||||
|
||||
% ALGORITHM
|
||||
%
|
||||
\ProcessOptions
|
||||
\floatstyle{\ALG@floatstyle}
|
||||
\ifthenelse{\boolean{ALG@within}}{
|
||||
\ifthenelse{\equal{\ALG@within}{part}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[part]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{chapter}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[chapter]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{section}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[section]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{subsection}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[subsection]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{subsubsection}}
|
||||
{\newfloat{algorithm}{htbp}{loa}[subsubsection]}{}
|
||||
\ifthenelse{\equal{\ALG@within}{nothing}}
|
||||
{\newfloat{algorithm}{htbp}{loa}}{}
|
||||
}{
|
||||
\newfloat{algorithm}{htbp}{loa}
|
||||
}
|
||||
\floatname{algorithm}{\ALG@name}
|
||||
|
||||
\newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
% ALGORITHMIC STYLE -- Released 8 APRIL 1996
|
||||
% for LaTeX version 2e
|
||||
% Copyright -- 1994 Peter Williams
|
||||
% E-mail PeterWilliams@dsto.defence.gov.au
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithmic}
|
||||
\typeout{Document Style `algorithmic' - environment}
|
||||
%
|
||||
\RequirePackage{ifthen}
|
||||
\RequirePackage{calc}
|
||||
\newboolean{ALC@noend}
|
||||
\setboolean{ALC@noend}{false}
|
||||
\newcounter{ALC@line}
|
||||
\newcounter{ALC@rem}
|
||||
\newlength{\ALC@tlm}
|
||||
%
|
||||
\DeclareOption{noend}{\setboolean{ALC@noend}{true}}
|
||||
%
|
||||
\ProcessOptions
|
||||
%
|
||||
% ALGORITHMIC
|
||||
\newcommand{\algorithmicrequire}{\textbf{Require:}}
|
||||
\newcommand{\algorithmicensure}{\textbf{Ensure:}}
|
||||
\newcommand{\algorithmiccomment}[1]{\{#1\}}
|
||||
\newcommand{\algorithmicend}{\textbf{end}}
|
||||
\newcommand{\algorithmicif}{\textbf{if}}
|
||||
\newcommand{\algorithmicthen}{\textbf{then}}
|
||||
\newcommand{\algorithmicelse}{\textbf{else}}
|
||||
\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif}
|
||||
\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif}
|
||||
\newcommand{\algorithmicfor}{\textbf{for}}
|
||||
\newcommand{\algorithmicforall}{\textbf{for all}}
|
||||
\newcommand{\algorithmicdo}{\textbf{do}}
|
||||
\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor}
|
||||
\newcommand{\algorithmicwhile}{\textbf{while}}
|
||||
\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile}
|
||||
\newcommand{\algorithmicloop}{\textbf{loop}}
|
||||
\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop}
|
||||
\newcommand{\algorithmicrepeat}{\textbf{repeat}}
|
||||
\newcommand{\algorithmicuntil}{\textbf{until}}
|
||||
\def\ALC@item[#1]{%
|
||||
\if@noparitem \@donoparitem
|
||||
\else \if@inlabel \indent \par \fi
|
||||
\ifhmode \unskip\unskip \par \fi
|
||||
\if@newlist \if@nobreak \@nbitem \else
|
||||
\addpenalty\@beginparpenalty
|
||||
\addvspace\@topsep \addvspace{-\parskip}\fi
|
||||
\else \addpenalty\@itempenalty \addvspace\itemsep
|
||||
\fi
|
||||
\global\@inlabeltrue
|
||||
\fi
|
||||
\everypar{\global\@minipagefalse\global\@newlistfalse
|
||||
\if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels
|
||||
\penalty\z@ \fi
|
||||
\everypar{}}\global\@nobreakfalse
|
||||
\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi
|
||||
\sbox\@tempboxa{\makelabel{#1}}%
|
||||
\global\setbox\@labels
|
||||
\hbox{\unhbox\@labels \hskip \itemindent
|
||||
\hskip -\labelwidth \hskip -\ALC@tlm
|
||||
\ifdim \wd\@tempboxa >\labelwidth
|
||||
\box\@tempboxa
|
||||
\else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
|
||||
\hskip \ALC@tlm}\ignorespaces}
|
||||
%
|
||||
\newenvironment{algorithmic}[1][0]{
|
||||
\let\@item\ALC@item
|
||||
\newcommand{\ALC@lno}{%
|
||||
\ifthenelse{\equal{\arabic{ALC@rem}}{0}}
|
||||
{{\footnotesize \arabic{ALC@line}:}}{}%
|
||||
}
|
||||
\let\@listii\@listi
|
||||
\let\@listiii\@listi
|
||||
\let\@listiv\@listi
|
||||
\let\@listv\@listi
|
||||
\let\@listvi\@listi
|
||||
\let\@listvii\@listi
|
||||
\newenvironment{ALC@g}{
|
||||
\begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@
|
||||
\listparindent\z@ \rightmargin\z@
|
||||
\topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@
|
||||
\leftmargin 1em
|
||||
\addtolength{\ALC@tlm}{\leftmargin}
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
\newcommand{\ALC@it}{\addtocounter{ALC@line}{1}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item}
|
||||
\newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}%
|
||||
{}{\ \algorithmiccomment{##1}}}
|
||||
\newcommand{\REQUIRE}{\item[\algorithmicrequire]}
|
||||
\newcommand{\ENSURE}{\item[\algorithmicensure]}
|
||||
\newcommand{\STATE}{\ALC@it}
|
||||
\newcommand{\COMMENT}[1]{\algorithmiccomment{##1}}
|
||||
\newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\renewcommand{\\}{\@centercr}
|
||||
\newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSIF}[2][default]%
|
||||
{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@whl}}
|
||||
\newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop%
|
||||
\ALC@com{##1}\begin{ALC@loop}}
|
||||
\newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat%
|
||||
\ALC@com{##1}\begin{ALC@rpt}}
|
||||
\newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1}
|
||||
\ifthenelse{\boolean{ALC@noend}}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}}
|
||||
}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop}
|
||||
}
|
||||
\renewcommand{\@toodeep}{}
|
||||
\begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}%
|
||||
\itemsep\z@ \itemindent\z@ \listparindent\z@%
|
||||
\partopsep\z@ \parskip\z@ \parsep\z@%
|
||||
\labelsep 0.5em \topsep 0.2em%
|
||||
\ifthenelse{\equal{#1}{0}}
|
||||
{\labelwidth 0.5em }
|
||||
{\labelwidth 1.2em }
|
||||
\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep}
|
||||
\ALC@tlm\labelsep
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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}
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
From eray@uludag.org.tr Wed Sep 7 20:15:04 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id BE4C284D16;
|
||||
Wed, 7 Sep 2005 20:15:15 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 128A11878DE;
|
||||
Wed, 7 Sep 2005 20:09:27 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 8A1AD1878D6
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Wed, 7 Sep 2005 20:09:13 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 56F5B908AE; Wed, 7 Sep 2005 20:15:02 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from [192.168.1.5] (unknown [85.97.87.38])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 0057B84D16
|
||||
for <cekirdek@uludag.org.tr>; Wed, 7 Sep 2005 20:15:01 +0300 (EEST)
|
||||
From: Eray Ozkural <eray@uludag.org.tr>
|
||||
Organization: Pardus
|
||||
To: cekirdek@uludag.org.tr
|
||||
Date: Wed, 7 Sep 2005 20:15:04 +0300
|
||||
User-Agent: KMail/1.7.2
|
||||
MIME-Version: 1.0
|
||||
Message-Id: <200509072015.04792.eray@uludag.org.tr>
|
||||
Cc:
|
||||
Subject: [Cekirdek] [PISI Design] Dandik Fedora'nin component treesi
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============1420878072=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 3643
|
||||
X-Length: 4136
|
||||
|
||||
--===============1420878072==
|
||||
Content-Type: text/plain;
|
||||
charset="us-ascii"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Disposition: inline
|
||||
|
||||
Bu aslinda mandrake'ye gore biraz daha akli basinda ve daha
|
||||
onemlisi component mantigina uygun bir liste, bir iki degisiklik
|
||||
disinda bu listeyle baslamakta fayda var. Bir de tabii gentoo'nun
|
||||
kendi listesi var, onu da gentoo'cular gonderebilir mi listeye lutfen?
|
||||
|
||||
Bu dedigim gibi iki seviyeli bir tree, oldukca iyi aslinda.... sadece
|
||||
dedigim gibi component/category karisimi biraz onun disinda gayet
|
||||
guzel... Bir de baktim "group" demisler, group da iyi bir sozcuk aslinda
|
||||
degistirmeye gerek yok degil mi component lafini?
|
||||
|
||||
Saygilar,
|
||||
|
||||
Desktop
|
||||
X11
|
||||
GNOME
|
||||
KDE
|
||||
Applications
|
||||
Editors
|
||||
Engineering and Scientific
|
||||
Graphical Internet
|
||||
Text-based Internet
|
||||
Office/Productivity
|
||||
Sound and Video
|
||||
Authoring and Publishing
|
||||
Graphics
|
||||
Games and Entertainment
|
||||
Servers
|
||||
Server Configuration Tools
|
||||
Web Server
|
||||
Mail Server
|
||||
Windows File Server
|
||||
DNS Name Server
|
||||
FTP Server
|
||||
PostgreSQL Database
|
||||
MySQL Database
|
||||
News Server
|
||||
Network Servers
|
||||
Legacy Network Server
|
||||
Development
|
||||
Development Tools
|
||||
X Software Development
|
||||
GNOME ""
|
||||
KDE ""
|
||||
Legacy ""
|
||||
Java Development
|
||||
Eclipse
|
||||
System
|
||||
Language Support (includes fonts and input methods)
|
||||
Administration Tools
|
||||
System Tools
|
||||
Printing Support
|
||||
|
||||
--
|
||||
Eray Ozkural (exa), Uludag Gelistiricisi
|
||||
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
|
||||
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
|
||||
|
||||
--===============1420878072==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============1420878072==--
|
||||
|
||||
From gurer@uludag.org.tr Wed Sep 7 22:04:45 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 00FBF380AA;
|
||||
Wed, 7 Sep 2005 22:04:23 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 4BEDB1878F8;
|
||||
Wed, 7 Sep 2005 21:58:34 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id CBC0E159028
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Wed, 7 Sep 2005 21:58:32 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 5EB758654F; Wed, 7 Sep 2005 22:04:21 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from cable73-96.anadolu.kablonet.com.tr (unknown [195.174.73.96])
|
||||
(using SSLv3 with cipher RC4-MD5 (128/128 bits))
|
||||
(No client certificate requested)
|
||||
by uludag.org.tr (Postfix) with ESMTP id D92BD380AA
|
||||
for <cekirdek@uludag.org.tr>; Wed, 7 Sep 2005 22:04:20 +0300 (EEST)
|
||||
From: =?ISO-8859-1?Q?G=FCrer_=D6zen?= <gurer@uludag.org.tr>
|
||||
To: cekirdek@uludag.org.tr
|
||||
In-Reply-To: <200509072015.04792.eray@uludag.org.tr>
|
||||
References: <200509072015.04792.eray@uludag.org.tr>
|
||||
Organization: TUBITAK - UEKAE
|
||||
Date: Wed, 07 Sep 2005 22:04:45 +0300
|
||||
Message-Id: <1126119885.10981.13.camel@localhost>
|
||||
Mime-Version: 1.0
|
||||
X-Mailer: Evolution 2.2.3
|
||||
Cc:
|
||||
Subject: [Cekirdek] component hadisesi
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============1294830247=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 3648
|
||||
X-Length: 4903
|
||||
|
||||
--===============1294830247==
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Kanalda meren, exa, çağlar, ben biraz fikir teatisinde bulunduk. Teati
|
||||
dedim de çay çekti canım. Özetlen:
|
||||
|
||||
Component bir görev için bir araya gelmiş ve görevin ayrı parçalarını
|
||||
yapan paketler dedik. Kullanım amacı kullanıcının bir alt sistemi (kde,
|
||||
tex) bir hamlede kurabilmesini sağlamak, gerektiğinde dependency olarak
|
||||
kullanılmak.
|
||||
|
||||
Aynı işi yapan farklı programların bir grubu (library, editor, archiver,
|
||||
vs) kesinlikle component olmuyor.
|
||||
|
||||
Temel olarak iki tipi var:
|
||||
|
||||
KDE (kicker, kwm, kdm, kopete, vs)
|
||||
burda aynı koddan türemiş ve bize masaüstü sağlayan paketlerin bir
|
||||
componenti var.
|
||||
|
||||
Base (kernel, grub, libc, bash, vs)
|
||||
burda ayrı kodlardan gelen ve bize açılıp çalışabilen bir sistem
|
||||
sağlayan paketlerin bir componenti var.
|
||||
|
||||
Aynı paket birden fazla componente dahil olmuyor, bu component değil
|
||||
bağımlılık ilişkisi. Örnek:
|
||||
libxml2 is a part of Gnome
|
||||
kopete (a partof KDE) depends on libxml2 (bunu uydurdum, kopete
|
||||
libxml2 kullanmıyor olabilir)
|
||||
|
||||
Yani componenti *asıl* olarak kod ilişkisi belirliyor, ama örneğin base
|
||||
paketinde direk kod ilişkisi yoksa da, birbirleriyle çok içiçe iş yapan
|
||||
programları bir component altında grupladık. İkincil kriter birlikte
|
||||
çalışma.
|
||||
|
||||
Karma bi örnek:
|
||||
Tex componenti içinde aynı koddan gelen kpathsea, tetex,vs paketleri var
|
||||
diyelim, ama farklı kodu olan hevea da buraya girebilir, çünkü html
|
||||
generate edebilen bir tex sistemi için gerekli.
|
||||
|
||||
Hiçbir componente ait olmayan, sadece dependency ve category ilişkileri
|
||||
olan paketler olabilir. Bunlar mesela other diye bi dizinde
|
||||
(componentler aynı zamanda paket deposundaki dizinler, bu şart değil ama
|
||||
kolaylık) tutulur.
|
||||
|
||||
Componentleri hiyerarşiye koymak kötü bi fikir değil, fedoranın ana
|
||||
grupları bu konuda fena değil.
|
||||
|
||||
Kafa karışıklığı kalmadı sanırım. Kategorileri konuşmadık daha :)
|
||||
|
||||
Bu vesileyle yavaş yavaş pisi depomuzu a/ b/ c/ lerden kurtarıp
|
||||
component bazlı dizinlere geçirmeye başlayalım dedik.
|
||||
|
||||
|
||||
|
||||
--===============1294830247==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============1294830247==--
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
component'larla ilgili notlar
|
||||
|
||||
1. Temel feature'lar (Eray)
|
||||
--------------------
|
||||
|
||||
Iki yerde component tag'i tanimladik simdiye kadar, bir
|
||||
source'larda bir de binary'lerde. Binary'de tanimlanan component default
|
||||
olarak source'daki tanimi inherit ediyor ve tanimlanan component'i override
|
||||
edebiliyor.
|
||||
|
||||
Bir component temel olarak bir meta-package, icerisinde paketler olan
|
||||
bir paket. Bir component'in icerisinde bir takim source'lar ve bir takim
|
||||
binary'ler bulunuyor diye bakabiliriz. Sanirim query'leri bu sekilde yapmak
|
||||
mumkun olmali.
|
||||
|
||||
$ pisi list-components
|
||||
$ pisi info system.base
|
||||
Source packages in system.base:
|
||||
....
|
||||
....
|
||||
Binary packages in system.base:
|
||||
....
|
||||
....
|
||||
|
||||
gibi ozellikler eklemeyi umit ediyorum.
|
||||
|
||||
Source'larin component tag'leri de, gene sadece bir senaryo konusuyorum,
|
||||
direkt olarak directory structure'indan inherit alinacak. Ayni zamanda o pspec
|
||||
icin de bir tane component tanimlananack default olarak, ve bu component o
|
||||
scope'da tanimlanmis olan butun binary package'lari icerecek.
|
||||
|
||||
Ornegin diyelim ki a/b/c/pspec.xml var ve c1 c2 c3 seklinde uc tane paket
|
||||
tanimliyor. Hic bir component tanimi yapilmadigi zaman otomatik olarak bir
|
||||
a.b.c component'i olusturulacak, ve bu component'in icerisinde c1 c2 c3
|
||||
bulunacak.
|
||||
|
||||
$ pisi info a.b.c
|
||||
Source packages:
|
||||
c - oldur beni yarim sen olmazsan biterim
|
||||
Binary packages:
|
||||
c1 - bu aksam demlenmemek sonum olur benim
|
||||
c2 - sincaplarla konustum butun gece
|
||||
c3 - her gul gordugumde icim kan aglar
|
||||
|
||||
Bu varsayilan davranis, ama bunu degistirmek mumkun olacak. Burada yamuk
|
||||
gozukebilecek bir sey var, o da tek bir paket oldugunda sanki biraz
|
||||
redundancy olmasi, o takdirde bir optimizasyon olarak, diyelim ki
|
||||
a/b/c/pspec.xml'in icerisinde tek bir paket tanimli c1
|
||||
|
||||
$ pisi info a.b
|
||||
....
|
||||
c1 - dil dil dillerdeyim
|
||||
|
||||
olabilir bu durumda, bu genel agac mantiginda bir sorun yaratmayacaktir.
|
||||
|
||||
|
||||
2. Temel tanım (Barış)
|
||||
|
||||
fiziksel aitlik: kdebase'den çıkan kcontrol gibi; grup aitliği: pdf
|
||||
göstericileri gibi...
|
||||
|
||||
|
||||
3. Database XML ayrımı (Barış)
|
||||
|
||||
Component database'i ile component.xml ayrı olmalı. Component database,
|
||||
pisi'nin pisi-index.xml dosyasını okuyarak oluşturacağı bir veritabanı. Hangi
|
||||
paketler hangi componentlere dahil, vs. sorguları bu veritabanından
|
||||
yapılacak.
|
||||
|
||||
Pspec dosyasını hatırlayalım. İçerisinde bir <PartOf> diye bir tag var.
|
||||
Oluşturulacak paketin hangi component'e ait olduğunu belirtiyor. Bu bilgi
|
||||
pisi-index.xml dosyasına da koyulmalı.
|
||||
|
||||
4. PL modüllerine benzerlik (Eray)
|
||||
|
||||
component tag'leri Java ya da python'daki gibi
|
||||
directory yapisindan cikiyor. Yani pisi bir programlama dili olsaydi
|
||||
component'lar module'ler ya da package'larla es anlamli olacakti.
|
||||
|
||||
5. Mereology ve Eray'ın açıklama çabaları (Eray)
|
||||
|
||||
PartOf iliskisi hakkında su kadarini soylemek yeter: eger a b'nin bir parcasiysa,
|
||||
a'nin fonksiyonu b'nin fonksiyonunun bir parcasidir. Bu da fiziksel sistemler
|
||||
icin bir principle of compositionality'nin varligindan hareket eder [*]
|
||||
|
||||
Genel olarak da software engineering ve AI camiasinda module'un tanimi gayet
|
||||
iyi bilinir. Bir modul icerisindeki bagimliliklar yogundur. Moduller arasindaki
|
||||
bagimliliklar zayiftir.
|
||||
|
||||
Bu tanim sadece software engineering'de degil, nesneler arasindaki
|
||||
benzerliklerin incelendigi bir cok disiplinde kullanilan informal bir tanim,
|
||||
ama tabii ki formule dokulmus bir ton hali var.Sırf bu tanımı taban alarak
|
||||
yazılmış başarılı kümeleme (clustering) algoritmaları var.
|
||||
|
||||
Modulerlik tanimi verilen *fiziksel* modul ve parcasi olma iliskisiyle
|
||||
birlesince birlikte install edilip remove edilme yahut ortak bağımlılıklara
|
||||
sahip olma tanımlarına götürür.
|
||||
|
||||
Bu tanımı analiz edebilmek için parcasi olma iliskisininin anlamini
|
||||
korumamiz yeterli. Temel olarak
|
||||
|
||||
kol insanin parcasidir
|
||||
|
||||
iliskisi burada yer aliyor. Eger
|
||||
|
||||
a partof b
|
||||
|
||||
turu iliskilerde a paket ya da component, ve b component ise, a ve b'nin
|
||||
iliskisinin kol ve insan iliskisi gibi olmasini bekleriz. Eger bu parca-butun
|
||||
iliskisini ihlal ediyorsa o zaman muhtemelen yanlis bir iliski bulunmus
|
||||
demektir.
|
||||
|
||||
Bunun turlu sonuclari da onem sirasina gore soyle dizilebilir:
|
||||
|
||||
1. Fonksiyonlarin bolunebilmesi prensibinden: (basit bir sonuç)
|
||||
a's function is part-of b's function
|
||||
|
||||
orneğin kolun fonksiyonu insanın total fonksiyonunun bir parçasıdır.
|
||||
|
||||
paket ornegi: pisi'nin fonksiyonu olan paket yükleme/çıkarma system.base'in fonksiyonunun, yani temel pardus sisteminin fonksiyonunun bir parçasıdır.
|
||||
|
||||
2. Karmasik sistemlerde birbirine dayanan ufak parcalarin kararli yapilar
|
||||
meydana getirmesi prensibinden: (evrimsel sonuç)
|
||||
|
||||
if a is a part-of c, and b is a part-of c, then it follows that a and b
|
||||
may:
|
||||
a. have many interdependencies
|
||||
b. share in their origins
|
||||
(which are about the same thing)
|
||||
|
||||
2b. sonucunun bizim durumumuza uygulanması, source code'un aynı kaynaktan
|
||||
çıkması, birlikte inşa edilmeleri gibi şartları getirir. Bunun oldukça
|
||||
olasi, en azindan insa metodlarinin ve kaynaklarının birbirine benzemesini
|
||||
bekleriz. Yalniz, 2.a'daki bagimliliklar sadece insa ile degil ayni zamanda
|
||||
calisma ile de alakalidir. Kaynakları farklı parçaların birbirine bağımlı
|
||||
hale gelebilecegini unutmamak gerekir.
|
||||
|
||||
Fedora'nin yaklasiminin hos tarafi, boyle teknik ayrintilara girmeden "package
|
||||
group" mantigini kullanmasiydi, ama bizim belli bir anlami olan parca-butun
|
||||
iliskisini korumamiz daha mantikli bir yazilim ontology'si ortaya
|
||||
cikaracaktir. Onlarin yaklasimi ise "anything goes", grup ile kategori'nin
|
||||
temel bir farki yok cunku, herhangi bir bakis acisi olabilir "grup".
|
||||
|
||||
|
||||
6. Modulleri test etmek (!!) (Eray)
|
||||
------------------------------------
|
||||
|
||||
PISI'deki seçilen implementation detaylari bir tanima cok fazla commitment
|
||||
yapmiyor, implementation'ın getirdiği tek şey paketleri bir ağaca koymak.
|
||||
Component'ların seçiminin ne kadar kaliteli olduğunu belirleyemiyor.
|
||||
Paketlerin ve componentlarin secimi yapiyor, ki kritik olan o.
|
||||
Yalnız modulerlik tanimindan ve part-of iliskisinin
|
||||
if a is part of b, and b is part of c, then a is part of c.
|
||||
if a is part of b, and b is not part of c, then a is not part of c.
|
||||
gibi sonuclari getirmesinden hareketle (ki bunlar klasik computational
|
||||
ontology) kismen test edebilecegimiz bir sekil aliyor ornegin bagimlilik
|
||||
graph'ini cluster ederek, ya da her modul icin bir modulerlik sayisi
|
||||
hesaplayarak. Daha az formal olarak da bu sonuçları kafamızda yürüterek
|
||||
yaptığımız componentların ne kadar akla yatkın olduğunu bulabiliriz.
|
||||
|
||||
|
||||
7. Cagların frugalware önerisi
|
||||
------------------------------
|
||||
|
||||
http://ftp.frugalware.org/pub/frugalware/frugalware-current/source/
|
||||
adresindeki yerleşimin hem source hem de binary depo için uygulanmasını ve
|
||||
kategori, componentların da bundan çıkartılmasını öneriyorum.
|
||||
@@ -1,879 +0,0 @@
|
||||
From eray@uludag.org.tr Thu Jul 21 15:54:55 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 351D885A97;
|
||||
Thu, 21 Jul 2005 15:55:08 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 0742810A659;
|
||||
Thu, 21 Jul 2005 15:52:59 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id BABCC10A654
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Thu, 21 Jul 2005 15:52:56 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 91D0C86099; Thu, 21 Jul 2005 15:55:05 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from [192.168.1.5] (unknown [85.99.19.56])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 0143385A97
|
||||
for <cekirdek@uludag.org.tr>; Thu, 21 Jul 2005 15:55:04 +0300 (EEST)
|
||||
From: Eray Ozkural <eray@uludag.org.tr>
|
||||
Organization: Pardus
|
||||
To: cekirdek@uludag.org.tr
|
||||
Date: Thu, 21 Jul 2005 15:54:55 +0300
|
||||
User-Agent: KMail/1.7.2
|
||||
MIME-Version: 1.0
|
||||
Message-Id: <200507211554.55602.eray@uludag.org.tr>
|
||||
Cc:
|
||||
Subject: [Cekirdek] [PISI design] mandrake component / group tree
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============2130999829=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 1651
|
||||
X-Length: 4961
|
||||
|
||||
--===============2130999829==
|
||||
Content-Type: text/plain;
|
||||
charset="us-ascii"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Disposition: inline
|
||||
|
||||
Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan. Bu
|
||||
component lafi akil mi karistiriyor diye killaniyorum hala.
|
||||
|
||||
Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
|
||||
bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
|
||||
debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
|
||||
Development, Networking fena degil ama bizim daha anlamli birseyler yapmamiz
|
||||
gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice bir
|
||||
component olacak. Ya bizim componentlari en iyi python module'leri gibi
|
||||
dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
|
||||
|
||||
apps.office partof apps
|
||||
apps.office.openoffice partof apps.office
|
||||
|
||||
openoffice-libs partof apps.office.openoffice
|
||||
openoffice-bin partof apps.office.openoffice
|
||||
|
||||
ornegin
|
||||
|
||||
buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
|
||||
edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle, bu
|
||||
da guzel birsey!
|
||||
|
||||
Altlarindaki paketleri yazmiyorum.
|
||||
|
||||
Mandrake Group Tree
|
||||
--------------------------------
|
||||
|
||||
Accessibility
|
||||
Archiving
|
||||
Backup
|
||||
Cd burning
|
||||
Compression
|
||||
Other
|
||||
Books
|
||||
Computer books
|
||||
Howtos
|
||||
Literature
|
||||
Other
|
||||
Cluster
|
||||
Message Passing
|
||||
Queueing Services
|
||||
Communications
|
||||
Configuration
|
||||
Databases
|
||||
Development
|
||||
C
|
||||
C++
|
||||
Databases
|
||||
Debug
|
||||
Gnome and GTK+
|
||||
Java
|
||||
KDE and Qt
|
||||
Kernel
|
||||
Libraries
|
||||
Other
|
||||
Perl
|
||||
Python
|
||||
Documentation
|
||||
Editors
|
||||
Education
|
||||
Emulators
|
||||
File Tools
|
||||
Games
|
||||
Adventure
|
||||
Arcade
|
||||
Boards
|
||||
Cards
|
||||
Other
|
||||
Puzzles
|
||||
Sports
|
||||
Strategy
|
||||
Graphical desktop
|
||||
Enlightenment
|
||||
FVWM based
|
||||
GNOME
|
||||
Icewm
|
||||
KDE
|
||||
Other
|
||||
Sawfish
|
||||
WindowMakes
|
||||
Xfce
|
||||
Graphics
|
||||
Monitoring
|
||||
Networking
|
||||
Chat
|
||||
File transfer
|
||||
Instant messaging
|
||||
Instant Messaging
|
||||
IRC
|
||||
Mail
|
||||
News
|
||||
Other
|
||||
Remote access
|
||||
WWW
|
||||
Office
|
||||
Publishing
|
||||
Sciences
|
||||
Astronomy
|
||||
Biology
|
||||
Chemistry
|
||||
Computer science
|
||||
Geosciences
|
||||
Mathematics
|
||||
Other
|
||||
Physics
|
||||
System
|
||||
Base
|
||||
Configuration
|
||||
Cryptography
|
||||
Deployment
|
||||
Fonts
|
||||
Internationalization
|
||||
Kernel and hardware
|
||||
Libraries
|
||||
Servers
|
||||
X11
|
||||
XFree86
|
||||
Shells
|
||||
Sounds
|
||||
System
|
||||
Terminals
|
||||
Text tools
|
||||
Toys
|
||||
Video
|
||||
|
||||
--
|
||||
Eray
|
||||
|
||||
--===============2130999829==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============2130999829==--
|
||||
|
||||
From serdar@uludag.org.tr Thu Jul 21 16:10:37 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 6010F85F66;
|
||||
Thu, 21 Jul 2005 16:12:48 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 7E52910A661;
|
||||
Thu, 21 Jul 2005 16:10:39 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 2FAA4950E8
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Thu, 21 Jul 2005 16:10:37 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 5ECC486095; Thu, 21 Jul 2005 16:12:46 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from webmail.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 5801F86094
|
||||
for <cekirdek@uludag.org.tr>; Thu, 21 Jul 2005 16:12:46 +0300 (EEST)
|
||||
Received: from 193.140.74.2 (SquirrelMail authenticated user serdar)
|
||||
by webmail.uludag.org.tr with HTTP;
|
||||
Thu, 21 Jul 2005 16:10:37 +0300 (EEST)
|
||||
Message-ID: <51672.193.140.74.2.1121951437.squirrel@webmail.uludag.org.tr>
|
||||
In-Reply-To: <200507211554.55602.eray@uludag.org.tr>
|
||||
References: <200507211554.55602.eray@uludag.org.tr>
|
||||
Date: Thu, 21 Jul 2005 16:10:37 +0300 (EEST)
|
||||
Subject: Re: [Cekirdek] [PISI design] mandrake component / group tree
|
||||
From: serdar@uludag.org.tr
|
||||
To: cekirdek@uludag.org.tr
|
||||
User-Agent: SquirrelMail/1.4.4
|
||||
MIME-Version: 1.0
|
||||
X-Priority: 3 (Normal)
|
||||
Importance: Normal
|
||||
Cc:
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============1295726599=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 1655
|
||||
X-Length: 7560
|
||||
|
||||
--===============1295726599==
|
||||
Content-Type: text/plain;charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Selamlar..
|
||||
|
||||
Benim görüşüm, kullanıcının karşısına ilk anda bu kadar çok
|
||||
kategori çıkarsa, çabucak refüze olacağıdır. Kapsamları daraltıp,
|
||||
işi 2 kademe yerine 3-4 kademeye yaymak daha kullanışlı olur
|
||||
sanıyorum.
|
||||
|
||||
İyi bir örnek değil ama, freshmeat.net'te ki kategorilere bir bakabiliriz.
|
||||
|
||||
# Adaptive Technologies (51 projects)
|
||||
# Artistic Software (165 projects)
|
||||
# Communications (5684 projects)
|
||||
# Database (2022 projects)
|
||||
# Desktop Environment (2373 projects)
|
||||
# Documentation (172 projects)
|
||||
# Education (896 projects)
|
||||
# freshmeat.net (28 projects)
|
||||
# Games/Entertainment (2720 projects)
|
||||
# Home Automation (80 projects)
|
||||
# Information Management (1259 projects)
|
||||
# Internet (10220 projects)
|
||||
# Multimedia (6251 projects)
|
||||
# Office/Business (2066 projects)
|
||||
# Other/Nonlisted Topic (261 projects)
|
||||
# Printing (227 projects)
|
||||
# Religion (50 projects)
|
||||
# Scientific/Engineering (2705 projects)
|
||||
# Security (1591 projects)
|
||||
# Software Development (10758 projects)
|
||||
# System (10314 projects)
|
||||
# Terminals (321 projects)
|
||||
# Text Editors (647 projects)
|
||||
# Text Processing (2416 projects)
|
||||
# Utilities (3056 projects)
|
||||
|
||||
Bu listeden çıkarılabilecek bir sürü şey olabilir. Hatta minor
|
||||
şeyler birleştirilip bir alt seviyede detaylandırılabilir. Freshmeat
|
||||
bunları upuzun uzatıyor, böylece aradığını bulmak daha kolay
|
||||
oluyor.
|
||||
|
||||
Yukarıdaki liste çok uygun değil. Örneğin, Text Editors ile Office
|
||||
ayrımı çoğu için anlam ifade etmez. Database ile
|
||||
development/business'in ayrık durması da anlamsız bence, Databases
|
||||
development ve belki business altında listlenebilir.
|
||||
|
||||
Liste ne kadar uzun olursa, o kadar bıkkınlık verici olacaktır
|
||||
sanıyorum. Bu yüzden de laf gelimi, her kademede en fazla 10 girdi
|
||||
olacak şekilde daha derin listeler hazırlanmasının faydalı
|
||||
olacağını düşünüyorum..
|
||||
|
||||
Saygı ve sevgiler..
|
||||
|
||||
> Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan.
|
||||
> Bu
|
||||
> component lafi akil mi karistiriyor diye killaniyorum hala.
|
||||
>
|
||||
> Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
|
||||
> bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
|
||||
> debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
|
||||
> Development, Networking fena degil ama bizim daha anlamli birseyler
|
||||
> yapmamiz
|
||||
> gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice bir
|
||||
> component olacak. Ya bizim componentlari en iyi python module'leri gibi
|
||||
> dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
|
||||
>
|
||||
> apps.office partof apps
|
||||
> apps.office.openoffice partof apps.office
|
||||
>
|
||||
> openoffice-libs partof apps.office.openoffice
|
||||
> openoffice-bin partof apps.office.openoffice
|
||||
>
|
||||
> ornegin
|
||||
>
|
||||
> buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
|
||||
> edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle,
|
||||
> bu
|
||||
> da guzel birsey!
|
||||
>
|
||||
> Altlarindaki paketleri yazmiyorum.
|
||||
>
|
||||
> Mandrake Group Tree
|
||||
> --------------------------------
|
||||
>
|
||||
> Accessibility
|
||||
> Archiving
|
||||
> Backup
|
||||
> Cd burning
|
||||
> Compression
|
||||
> Other
|
||||
> Books
|
||||
> Computer books
|
||||
> Howtos
|
||||
> Literature
|
||||
> Other
|
||||
> Cluster
|
||||
> Message Passing
|
||||
> Queueing Services
|
||||
> Communications
|
||||
> Configuration
|
||||
> Databases
|
||||
> Development
|
||||
> C
|
||||
> C++
|
||||
> Databases
|
||||
> Debug
|
||||
> Gnome and GTK+
|
||||
> Java
|
||||
> KDE and Qt
|
||||
> Kernel
|
||||
> Libraries
|
||||
> Other
|
||||
> Perl
|
||||
> Python
|
||||
> Documentation
|
||||
> Editors
|
||||
> Education
|
||||
> Emulators
|
||||
> File Tools
|
||||
> Games
|
||||
> Adventure
|
||||
> Arcade
|
||||
> Boards
|
||||
> Cards
|
||||
> Other
|
||||
> Puzzles
|
||||
> Sports
|
||||
> Strategy
|
||||
> Graphical desktop
|
||||
> Enlightenment
|
||||
> FVWM based
|
||||
> GNOME
|
||||
> Icewm
|
||||
> KDE
|
||||
> Other
|
||||
> Sawfish
|
||||
> WindowMakes
|
||||
> Xfce
|
||||
> Graphics
|
||||
> Monitoring
|
||||
> Networking
|
||||
> Chat
|
||||
> File transfer
|
||||
> Instant messaging
|
||||
> Instant Messaging
|
||||
> IRC
|
||||
> Mail
|
||||
> News
|
||||
> Other
|
||||
> Remote access
|
||||
> WWW
|
||||
> Office
|
||||
> Publishing
|
||||
> Sciences
|
||||
> Astronomy
|
||||
> Biology
|
||||
> Chemistry
|
||||
> Computer science
|
||||
> Geosciences
|
||||
> Mathematics
|
||||
> Other
|
||||
> Physics
|
||||
> System
|
||||
> Base
|
||||
> Configuration
|
||||
> Cryptography
|
||||
> Deployment
|
||||
> Fonts
|
||||
> Internationalization
|
||||
> Kernel and hardware
|
||||
> Libraries
|
||||
> Servers
|
||||
> X11
|
||||
> XFree86
|
||||
> Shells
|
||||
> Sounds
|
||||
> System
|
||||
> Terminals
|
||||
> Text tools
|
||||
> Toys
|
||||
> Video
|
||||
>
|
||||
> --
|
||||
> Eray
|
||||
> _______________________________________________
|
||||
> Cekirdek mailing list
|
||||
> Cekirdek@uludag.org.tr
|
||||
> http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
>
|
||||
|
||||
|
||||
|
||||
--===============1295726599==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============1295726599==--
|
||||
|
||||
From eray@uludag.org.tr Wed Jul 27 13:02:17 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id AABE08584B;
|
||||
Wed, 27 Jul 2005 13:02:30 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id EAF71E558D;
|
||||
Wed, 27 Jul 2005 12:59:53 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id CF155E558D
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Wed, 27 Jul 2005 12:59:52 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 0B4998584E; Wed, 27 Jul 2005 13:02:29 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from [192.168.1.5] (unknown [85.99.19.56])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 719658584B
|
||||
for <cekirdek@uludag.org.tr>; Wed, 27 Jul 2005 13:02:28 +0300 (EEST)
|
||||
From: Eray Ozkural <eray@uludag.org.tr>
|
||||
Organization: Pardus
|
||||
To: cekirdek@uludag.org.tr
|
||||
Subject: Re: [Cekirdek] [PISI design] mandrake component / group tree
|
||||
Date: Wed, 27 Jul 2005 13:02:17 +0300
|
||||
User-Agent: KMail/1.7.2
|
||||
References: <200507211554.55602.eray@uludag.org.tr>
|
||||
In-Reply-To: <200507211554.55602.eray@uludag.org.tr>
|
||||
MIME-Version: 1.0
|
||||
Message-Id: <200507271302.17641.eray@uludag.org.tr>
|
||||
Cc:
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============2058226043=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 1835
|
||||
X-Length: 5701
|
||||
|
||||
--===============2058226043==
|
||||
Content-Type: text/plain;
|
||||
charset="iso-8859-1"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Disposition: inline
|
||||
|
||||
Buna bakip dusundunuz mu? Bakiniz, mandrake'nin daha cok kategorik
|
||||
bir ayrim, cok component mantigina uymuyor. Fedora daha uygun aslinda ama o da
|
||||
cok basit yararli olmak icin.
|
||||
|
||||
On Thursday 21 July 2005 03:54 pm, Eray Ozkural wrote:
|
||||
> Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan. Bu
|
||||
> component lafi akil mi karistiriyor diye killaniyorum hala.
|
||||
>
|
||||
> Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
|
||||
> bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
|
||||
> debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
|
||||
> Development, Networking fena degil ama bizim daha anlamli birseyler
|
||||
> yapmamiz gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice
|
||||
> bir component olacak. Ya bizim componentlari en iyi python module'leri gibi
|
||||
> dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
|
||||
>
|
||||
> apps.office partof apps
|
||||
> apps.office.openoffice partof apps.office
|
||||
>
|
||||
> openoffice-libs partof apps.office.openoffice
|
||||
> openoffice-bin partof apps.office.openoffice
|
||||
>
|
||||
> ornegin
|
||||
>
|
||||
> buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
|
||||
> edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle,
|
||||
> bu da guzel birsey!
|
||||
>
|
||||
> Altlarindaki paketleri yazmiyorum.
|
||||
>
|
||||
> Mandrake Group Tree
|
||||
> --------------------------------
|
||||
>
|
||||
> Accessibility
|
||||
> Archiving
|
||||
> Backup
|
||||
> Cd burning
|
||||
> Compression
|
||||
> Other
|
||||
> Books
|
||||
> Computer books
|
||||
> Howtos
|
||||
> Literature
|
||||
> Other
|
||||
> Cluster
|
||||
> Message Passing
|
||||
> Queueing Services
|
||||
> Communications
|
||||
> Configuration
|
||||
> Databases
|
||||
> Development
|
||||
> C
|
||||
> C++
|
||||
> Databases
|
||||
> Debug
|
||||
> Gnome and GTK+
|
||||
> Java
|
||||
> KDE and Qt
|
||||
> Kernel
|
||||
> Libraries
|
||||
> Other
|
||||
> Perl
|
||||
> Python
|
||||
> Documentation
|
||||
> Editors
|
||||
> Education
|
||||
> Emulators
|
||||
> File Tools
|
||||
> Games
|
||||
> Adventure
|
||||
> Arcade
|
||||
> Boards
|
||||
> Cards
|
||||
> Other
|
||||
> Puzzles
|
||||
> Sports
|
||||
> Strategy
|
||||
> Graphical desktop
|
||||
> Enlightenment
|
||||
> FVWM based
|
||||
> GNOME
|
||||
> Icewm
|
||||
> KDE
|
||||
> Other
|
||||
> Sawfish
|
||||
> WindowMakes
|
||||
> Xfce
|
||||
> Graphics
|
||||
> Monitoring
|
||||
> Networking
|
||||
> Chat
|
||||
> File transfer
|
||||
> Instant messaging
|
||||
> Instant Messaging
|
||||
> IRC
|
||||
> Mail
|
||||
> News
|
||||
> Other
|
||||
> Remote access
|
||||
> WWW
|
||||
> Office
|
||||
> Publishing
|
||||
> Sciences
|
||||
> Astronomy
|
||||
> Biology
|
||||
> Chemistry
|
||||
> Computer science
|
||||
> Geosciences
|
||||
> Mathematics
|
||||
> Other
|
||||
> Physics
|
||||
> System
|
||||
> Base
|
||||
> Configuration
|
||||
> Cryptography
|
||||
> Deployment
|
||||
> Fonts
|
||||
> Internationalization
|
||||
> Kernel and hardware
|
||||
> Libraries
|
||||
> Servers
|
||||
> X11
|
||||
> XFree86
|
||||
> Shells
|
||||
> Sounds
|
||||
> System
|
||||
> Terminals
|
||||
> Text tools
|
||||
> Toys
|
||||
> Video
|
||||
|
||||
--
|
||||
Eray Ozkural (exa), Uludag Gelistiricisi
|
||||
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
|
||||
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
|
||||
|
||||
--===============2058226043==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============2058226043==--
|
||||
|
||||
From eray@uludag.org.tr Wed Sep 7 20:05:53 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 8D2A084F4B;
|
||||
Wed, 7 Sep 2005 20:06:04 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 086A718789A;
|
||||
Wed, 7 Sep 2005 20:00:16 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id F039818789A
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Wed, 7 Sep 2005 20:00:02 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 401CE8F9B9; Wed, 7 Sep 2005 20:05:51 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from [192.168.1.5] (unknown [85.97.87.38])
|
||||
by uludag.org.tr (Postfix) with ESMTP id A4EC084F4B
|
||||
for <cekirdek@uludag.org.tr>; Wed, 7 Sep 2005 20:05:50 +0300 (EEST)
|
||||
From: Eray Ozkural <eray@uludag.org.tr>
|
||||
Organization: Pardus
|
||||
To: cekirdek@uludag.org.tr
|
||||
Subject: Re: [Cekirdek] [PISI design] mandrake component / group tree
|
||||
Date: Wed, 7 Sep 2005 20:05:53 +0300
|
||||
User-Agent: KMail/1.7.2
|
||||
References: <200507211554.55602.eray@uludag.org.tr>
|
||||
In-Reply-To: <200507211554.55602.eray@uludag.org.tr>
|
||||
MIME-Version: 1.0
|
||||
Message-Id: <200509072005.53482.eray@uludag.org.tr>
|
||||
Cc:
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============1083705676=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 3642
|
||||
X-Length: 5765
|
||||
|
||||
--===============1083705676==
|
||||
Content-Type: text/plain;
|
||||
charset="iso-8859-1"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Disposition: inline
|
||||
|
||||
bunu gormus muydunuz?
|
||||
|
||||
genelde iki seviye ana component'lari belirtmek icin yeterli
|
||||
mandrake'nin kafasinin karismasi icin kategorilerle componentlari
|
||||
karismasi yeterli olmus, yani asagidaki bircok sey aslinda component
|
||||
*degil* kategori.
|
||||
|
||||
On Thursday 21 July 2005 03:54 pm, Eray Ozkural wrote:
|
||||
> Mandrake'de "group" diye geciyor terim. Belki daha iyidir component'dan. Bu
|
||||
> component lafi akil mi karistiriyor diye killaniyorum hala.
|
||||
>
|
||||
> Bu arada, mandrake'nin bu olayini tam begenmedim. Tamam System'in alti
|
||||
> bizim dusundugumuz gibi, ama gerisi biraz daha category mantiginda,
|
||||
> debian'inki gibi yapilmis. Aslinda Archiving, Books, Cluster, Sciences,
|
||||
> Development, Networking fena degil ama bizim daha anlamli birseyler
|
||||
> yapmamiz gerekebilir, bu vizyonunuzu daraltmasin. Ornegin bizde OpenOffice
|
||||
> bir component olacak. Ya bizim componentlari en iyi python module'leri gibi
|
||||
> dusunun, "alt" notasyonu da bunu gostermek icin '.' olsun.
|
||||
>
|
||||
> apps.office partof apps
|
||||
> apps.office.openoffice partof apps.office
|
||||
>
|
||||
> openoffice-libs partof apps.office.openoffice
|
||||
> openoffice-bin partof apps.office.openoffice
|
||||
>
|
||||
> ornegin
|
||||
>
|
||||
> buradaki tabii ki onemli sey component hiyerarsisinin *birlikte install
|
||||
> edilen* adamlara ihtimam etmesi. Ornegin asagida System.Base aynen boyle,
|
||||
> bu da guzel birsey!
|
||||
>
|
||||
> Altlarindaki paketleri yazmiyorum.
|
||||
>
|
||||
> Mandrake Group Tree
|
||||
> --------------------------------
|
||||
>
|
||||
> Accessibility
|
||||
> Archiving
|
||||
> Backup
|
||||
> Cd burning
|
||||
> Compression
|
||||
> Other
|
||||
> Books
|
||||
> Computer books
|
||||
> Howtos
|
||||
> Literature
|
||||
> Other
|
||||
> Cluster
|
||||
> Message Passing
|
||||
> Queueing Services
|
||||
> Communications
|
||||
> Configuration
|
||||
> Databases
|
||||
> Development
|
||||
> C
|
||||
> C++
|
||||
> Databases
|
||||
> Debug
|
||||
> Gnome and GTK+
|
||||
> Java
|
||||
> KDE and Qt
|
||||
> Kernel
|
||||
> Libraries
|
||||
> Other
|
||||
> Perl
|
||||
> Python
|
||||
> Documentation
|
||||
> Editors
|
||||
> Education
|
||||
> Emulators
|
||||
> File Tools
|
||||
> Games
|
||||
> Adventure
|
||||
> Arcade
|
||||
> Boards
|
||||
> Cards
|
||||
> Other
|
||||
> Puzzles
|
||||
> Sports
|
||||
> Strategy
|
||||
> Graphical desktop
|
||||
> Enlightenment
|
||||
> FVWM based
|
||||
> GNOME
|
||||
> Icewm
|
||||
> KDE
|
||||
> Other
|
||||
> Sawfish
|
||||
> WindowMakes
|
||||
> Xfce
|
||||
> Graphics
|
||||
> Monitoring
|
||||
> Networking
|
||||
> Chat
|
||||
> File transfer
|
||||
> Instant messaging
|
||||
> Instant Messaging
|
||||
> IRC
|
||||
> Mail
|
||||
> News
|
||||
> Other
|
||||
> Remote access
|
||||
> WWW
|
||||
> Office
|
||||
> Publishing
|
||||
> Sciences
|
||||
> Astronomy
|
||||
> Biology
|
||||
> Chemistry
|
||||
> Computer science
|
||||
> Geosciences
|
||||
> Mathematics
|
||||
> Other
|
||||
> Physics
|
||||
> System
|
||||
> Base
|
||||
> Configuration
|
||||
> Cryptography
|
||||
> Deployment
|
||||
> Fonts
|
||||
> Internationalization
|
||||
> Kernel and hardware
|
||||
> Libraries
|
||||
> Servers
|
||||
> X11
|
||||
> XFree86
|
||||
> Shells
|
||||
> Sounds
|
||||
> System
|
||||
> Terminals
|
||||
> Text tools
|
||||
> Toys
|
||||
> Video
|
||||
|
||||
--
|
||||
Eray Ozkural (exa), Uludag Gelistiricisi
|
||||
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
|
||||
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
|
||||
|
||||
--===============1083705676==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============1083705676==--
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
From eray@uludag.org.tr Thu Jul 28 16:42:47 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from liste.uludag.org.tr (pisi.uludag.org.tr [193.140.100.210])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 0CE2C75758;
|
||||
Thu, 28 Jul 2005 16:43:24 +0300 (EEST)
|
||||
Received: from pisi.uludag.org.tr (localhost [127.0.0.1])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id 2FD2D109DF1;
|
||||
Thu, 28 Jul 2005 16:40:43 +0300 (EEST)
|
||||
X-Original-To: cekirdek@liste.uludag.org.tr
|
||||
Delivered-To: cekirdek@liste.uludag.org.tr
|
||||
Received: from uludag.org.tr (comar.uludag.org.tr [193.140.100.220])
|
||||
by liste.uludag.org.tr (Postfix) with ESMTP id B4AAA109DDB
|
||||
for <cekirdek@liste.uludag.org.tr>;
|
||||
Thu, 28 Jul 2005 16:40:40 +0300 (EEST)
|
||||
Received: by uludag.org.tr (Postfix)
|
||||
id 5D432858B5; Thu, 28 Jul 2005 16:43:21 +0300 (EEST)
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from [192.168.1.5] (unknown [85.99.19.56])
|
||||
by uludag.org.tr (Postfix) with ESMTP id CC4ED75758
|
||||
for <cekirdek@uludag.org.tr>; Thu, 28 Jul 2005 16:43:19 +0300 (EEST)
|
||||
From: Eray Ozkural <eray@uludag.org.tr>
|
||||
Organization: Pardus
|
||||
To: cekirdek@uludag.org.tr
|
||||
Date: Thu, 28 Jul 2005 16:42:47 +0300
|
||||
User-Agent: KMail/1.7.2
|
||||
MIME-Version: 1.0
|
||||
Message-Id: <200507281642.47792.eray@uludag.org.tr>
|
||||
Cc:
|
||||
Subject: [Cekirdek] [PISI design] release hikayesi
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============1945860385=="
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 1875
|
||||
X-Length: 4330
|
||||
|
||||
--===============1945860385==
|
||||
Content-Type: text/plain;
|
||||
charset="us-ascii"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Disposition: inline
|
||||
|
||||
selamlar,
|
||||
|
||||
dun konustuklarimiz uzerine biraz yatip dusundum. bir ara haci
|
||||
kuslugu yapmisim ben de ve bir durumu gozden kacirmisim.
|
||||
dun caglar ve murat'in getirdigi 2. oneriyle karisik bir sey yapmamiz
|
||||
gerekebilir. benim kafam mi dagilmis ne kusura bakmayin. ama
|
||||
o kadar onemli degil.
|
||||
|
||||
soyle bir sorun var.
|
||||
|
||||
simdi bir source var x diye, 5. release'de
|
||||
|
||||
icindeki sadece bir paketi ornegin x2'yi etkileyecek bir degisiklik yaptik.
|
||||
|
||||
bu durumda x1 ve x3'un build numaralari artmiyor. ama source release
|
||||
numaralari mi artiyor? tamam buraya kadar ok. ama build sistemi
|
||||
bu paketlerden yeni bir paket dosyasi yapip repository'e koymuyor bu
|
||||
durumda, yani source release'i arttigi halde build nosu degismedigi icin
|
||||
yeni paket koyulmayacak ve indekslenmeyecek bu mantikli mi? mantikli
|
||||
degilse nasil bir cozum bulunur?
|
||||
|
||||
bunun yani sira, simdi bu source release degistiginde, eger paketi build
|
||||
*edersek*, bir yerde metadata degismek zorunda degil mi? hangi source'un
|
||||
hangi release'inden build edildigini kaydetmek zorundayiz yanlis mi
|
||||
dusunuyorum. hemen kizmayin butun bu olaylari basimiza ben sarmadim, bir
|
||||
sekilde evrimlesti :)
|
||||
|
||||
not: galiba olay suna geliyor, tamam build no degismediyse ozunde bu paket
|
||||
bir onceki source release'inde kalmistir deriz, yani yeni bir dosya kesinlikle
|
||||
koymayiz repository'e cunku install dosyalari ve metadata, release numarasi
|
||||
disinda ayni kalacak. offffff.
|
||||
|
||||
su anda multiple repository olayinin ilkel versiyonunu kodluyorum bu arada.
|
||||
|
||||
gorusuruz,
|
||||
|
||||
--
|
||||
Eray Ozkural (exa), Uludag Gelistiricisi
|
||||
http://cekirdek.uludag.org.tr/~eray http://www.kde.org
|
||||
http://www.cs.bilkent.edu.tr/~erayo http://www.malfunct.com
|
||||
|
||||
--===============1945860385==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/mailman/listinfo/cekirdek
|
||||
|
||||
--===============1945860385==--
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
From eray@uludag.org.tr Tue Jun 28 18:49:13 2005
|
||||
Return-Path: <cekirdek-bounces@liste.uludag.org.tr>
|
||||
X-Original-To: eray@uludag.org.tr
|
||||
Delivered-To: eray@uludag.org.tr
|
||||
Received: from uludag.org.tr (localhost [127.0.0.1])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 80D2653C00C;
|
||||
Tue, 28 Jun 2005 16:07:58 +0300 (EEST)
|
||||
X-Original-To: cekirdek@uludag.org.tr
|
||||
Delivered-To: cekirdek@uludag.org.tr
|
||||
Received: from [192.168.3.131] (unknown [193.140.73.11])
|
||||
by uludag.org.tr (Postfix) with ESMTP id 0B6B753C00C
|
||||
for <cekirdek@uludag.org.tr>; Tue, 28 Jun 2005 16:07:57 +0300 (EEST)
|
||||
From: Eray Ozkural <eray@uludag.org.tr>
|
||||
To: cekirdek@uludag.org.tr
|
||||
Date: Tue, 28 Jun 2005 15:49:13 +0000
|
||||
User-Agent: KMail/1.8.1
|
||||
MIME-Version: 1.0
|
||||
Message-Id: <200506281549.13525.eray@uludag.org.tr>
|
||||
Subject: [Cekirdek] source release ve binary build numaralari
|
||||
X-BeenThere: cekirdek@liste.uludag.org.tr
|
||||
X-Mailman-Version: 2.1.5
|
||||
Precedence: list
|
||||
Reply-To: cekirdek@uludag.org.tr
|
||||
List-Id: cekirdek.liste.uludag.org.tr
|
||||
List-Unsubscribe: <http://liste.uludag.org.tr/cgi-bin/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=unsubscribe>
|
||||
List-Archive: <http://liste.uludag.org.tr/cgi-bin/mailman/private/cekirdek>
|
||||
List-Post: <mailto:cekirdek@liste.uludag.org.tr>
|
||||
List-Help: <mailto:cekirdek-request@liste.uludag.org.tr?subject=help>
|
||||
List-Subscribe: <http://liste.uludag.org.tr/cgi-bin/mailman/listinfo/cekirdek>,
|
||||
<mailto:cekirdek-request@liste.uludag.org.tr?subject=subscribe>
|
||||
Content-Type: multipart/mixed;
|
||||
boundary="===============2126904290=="
|
||||
Mime-version: 1.0
|
||||
Sender: cekirdek-bounces@liste.uludag.org.tr
|
||||
Errors-To: cekirdek-bounces@liste.uludag.org.tr
|
||||
X-UID: 865
|
||||
X-Length: 4347
|
||||
|
||||
--===============2126904290==
|
||||
Content-Type: text/plain;
|
||||
charset="us-ascii"
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Disposition: inline
|
||||
|
||||
yigitler,
|
||||
|
||||
bu post'da dun tartistigimiz bir konuyu ilgilendiren
|
||||
paket versiyonu numaralarina tam bir aciklik getirmeye
|
||||
calisacagim.
|
||||
|
||||
Bir PISI source'u uc tane sayiyla belirlenir:
|
||||
name, version, release
|
||||
|
||||
Burada version "convenience" icin bulunur. name + release
|
||||
ise her zaman *ayni* PISI source build islemine sebep olur.
|
||||
Bundan kasit su. version ve release tamamen bagimsiz.
|
||||
release 0 ile baslar. ornegin kdevelop
|
||||
|
||||
ilk basta
|
||||
name: kdevelop, version: 3.4.1, release: 0
|
||||
dir
|
||||
|
||||
Bu paketin yeni versiyonlariyla biz ugrastikca, yaptigimiz
|
||||
her update, en minikleri dahi, release'i arttirir, ve her zaman
|
||||
bir arttirir (daha aciklayacagim), ama hic bir zaman geriye
|
||||
gitmez (0'lanmaz ornegin).
|
||||
|
||||
ileride:
|
||||
name: kdevelop, version: 3.4.2, release: 3
|
||||
olabilir ornegin.
|
||||
|
||||
Eger paketin gelistirildigi *distribution* source'u dahilinde
|
||||
bir versiyon dallanmasi varsa, ancak bu durumda release'e
|
||||
branch'ler eklenir.
|
||||
|
||||
Diyelim ki yukaridaki paket pardus 1.0'in parcasiydi. simdi
|
||||
pardus 1.0.1 diye bir branch olusturulmak istendi, ornegin
|
||||
bu UEKAE overlay'ine karsilik gelsin. Bu durumda, versiyon
|
||||
iliskisini eksiksiz bicimde belirtmek icin:
|
||||
name: kdevelop, version: 3.4.2, release: 3.1
|
||||
|
||||
demek yeterli olacaktir. Ekledigimiz '.1' suffix'i bize butun
|
||||
dal bilgisini verir. Boylece eksik hicbirsey olmaz.
|
||||
|
||||
Binary package'lara gelince. Bir binary package dort
|
||||
stringle her zaman bulunabilir.
|
||||
name, version, build, architecture
|
||||
|
||||
burada name + build, *bir binary distribution* dahilinde,
|
||||
ornegin Pardus 1.0 x86 icin yapilmis tek ve tek bir paketi
|
||||
her zaman gosterir.
|
||||
|
||||
Build, o architecture icin, o paketin kacinci kere build
|
||||
edildigini gosterir. Her paket release'inde en fazla bir
|
||||
artar.
|
||||
|
||||
Bunu daha once tartistigimiz gibi tamamen incremental
|
||||
build script'inin buldugu md5'larla hesapliyoruz.
|
||||
|
||||
Binary package'larin build numarasi icin bir dallanma/budaklanma
|
||||
semasi henuz onermiyorum, ama bana su anda ayni
|
||||
source package'lar gibi birsey yapilabilir geliyor. Bana buradaki
|
||||
en kritik mesele source ve binary package'larini, ve upstream
|
||||
version string'lerle kendi, SVN-tarzi version string'lerimizi
|
||||
karistirmamamiz.
|
||||
|
||||
Kaplan gunler,
|
||||
|
||||
--
|
||||
Eray
|
||||
|
||||
--===============2126904290==
|
||||
Content-Type: text/plain; charset="iso-8859-9"
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Disposition: inline
|
||||
|
||||
_______________________________________________
|
||||
Cekirdek mailing list
|
||||
Cekirdek@uludag.org.tr
|
||||
http://liste.uludag.org.tr/cgi-bin/mailman/listinfo/cekirdek
|
||||
|
||||
--===============2126904290==--
|
||||
|
||||
Binary file not shown.
@@ -1,187 +0,0 @@
|
||||
\documentclass[a4paper,11pt]{article}
|
||||
|
||||
\title{P\.IS\.I Packages: Version Policy v0.2}
|
||||
\date{\today}
|
||||
\author{Eray \"Ozkural and T. Bar\i{}\c s Metin}
|
||||
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
|
||||
\section*{Revision History}
|
||||
\begin{itemize}
|
||||
\item v0.1: Bar\i\c s Metin wrote the first version preparing the outline,
|
||||
detailed Source Version Section, and started the Section on Release Number.
|
||||
\item v0.2: Eray \"Ozkural wrote a detailed introduction, added
|
||||
explanations of release and build numbers, reorganized a bit.
|
||||
\end{itemize}
|
||||
|
||||
\section{Introduction}
|
||||
|
||||
This document explains the \emph{version policy} that applies to
|
||||
P\.IS\.I packages. Classically, the issue of distinguishing source and
|
||||
binary distributions unambiguously has not received a rigorous
|
||||
treatment in the context of LINUX distributions. We have identified
|
||||
several shortcomings of the usual practices of extending the original
|
||||
version with suffixes and prefixes, colorfully illustrated in the
|
||||
following common problems.
|
||||
|
||||
\begin{description}
|
||||
|
||||
\item[The problem of future downgrades]
|
||||
The distribution chooses to use a previous version of the package in
|
||||
the next release. There is no way to indicate this, so ad-hoc
|
||||
solutions such as version prefixes are used. It is
|
||||
impossible to denote a future dependency that requires at least this
|
||||
distribution source release in this case, either.
|
||||
|
||||
\item[The problem of redundant distributions]
|
||||
A trivial patch has been applied to the source. While few binary
|
||||
packages have been affected by this change, all binary packages
|
||||
built from the source are redistributed.
|
||||
|
||||
\item[The problem of underdetermined rebuilds]
|
||||
There have been rapid changes in the system, and although no
|
||||
changes have been made to the package source, a new binary
|
||||
distribution must be prepared.
|
||||
|
||||
\end{description}
|
||||
|
||||
We have devised a slightly new approach in order to alleviate these
|
||||
problems. Our solution consists of encoding the history of source and
|
||||
binary package developments in separate version strings we call release and
|
||||
build numbers.
|
||||
|
||||
Since the source version is usually used by the users and developers
|
||||
to identify software, we retain the notion of a source version in
|
||||
P\.IS\.I as a convenience.
|
||||
|
||||
In the following sections, we explain the components of our
|
||||
versioning scheme.
|
||||
|
||||
\subsection{Source Version}
|
||||
|
||||
Source version is the version number provided by the
|
||||
upstream maintainer of the source archive used in package. It must
|
||||
always be the same as the upstream version used.
|
||||
|
||||
\textbf{Example}: If the upstream archive name is
|
||||
\emph{bash-3.0.tar.gz} the version number of the package is \emph{3.0}
|
||||
|
||||
\subsubsection{Version Suffixes}
|
||||
|
||||
There is a pre-defined list of suffixes a package version can
|
||||
take.
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{alpha} Source/Package is in alpha state
|
||||
\item \textbf{beta} Source/Package is in beta state
|
||||
\item \textbf{pre} Source/Pacgage passed the beta state but stable
|
||||
version is not relased yet.
|
||||
\item \textbf{rc} Source/Package is a release-candidate.
|
||||
\item \textbf{m} Source/Package is a milestone before stable version.
|
||||
\item \textbf{p} Source/Package is released and some patches are
|
||||
applied after the release. This is the patch level.
|
||||
\end{itemize}
|
||||
|
||||
The suffix should be written after the special separator
|
||||
character \textbf{\_}. And there must allways be a number after a
|
||||
suffix. \textbf{Example}: packagename-1.0\_beta1
|
||||
|
||||
The basic order of the priorities for suffixes is:\newline
|
||||
\emph{p $>$ (no suffix) $>$ m $>$ rc $>$ pre $>$ beta $>$ alpha}.
|
||||
|
||||
The scope of a source version string is global in the literal
|
||||
sense. It shall not vary from repository to repository.
|
||||
|
||||
The support for these special suffixes as well as usual alphanumeric
|
||||
version string ordering has been implemented in P\.IS\.I.
|
||||
|
||||
\section{Identifying Package Sources}
|
||||
|
||||
A P\.IS\.I source has three identity elements written under
|
||||
\texttt{SOURCE} tag: name, source version, and source release number.
|
||||
We usually say just version and release number/release instead of
|
||||
source version and source release number, respectively. Name is available in
|
||||
the \texttt{<Name>} tag. Version and release are available in the last
|
||||
\texttt{<Update>} element of \texttt{<History>} tag of a \texttt{PSPEC}.
|
||||
|
||||
The name of a source package is constant throughout its revision
|
||||
history. The version is the original version, given by its
|
||||
programmers. Release is a positive integer. Name and release
|
||||
is sufficient to uniquely identify a particular PISI source revision.
|
||||
That is, version and release are independent.
|
||||
|
||||
\subsection{Release Number}
|
||||
|
||||
Release number is the number of the changes that are made to the
|
||||
package source since the initial version in the distribution source. A
|
||||
change can be a patch applied to the source archive, modification in
|
||||
the actions.py, pspec.xml or any file in the source package
|
||||
directory. This change is indicated in \texttt{<Update>} tags manually
|
||||
by the package maintainer.
|
||||
|
||||
The initial release of a package is by default \texttt{1}. The release
|
||||
number always increments by $1$ in each revision in the
|
||||
\texttt{History}, even the slightest ones, but it never decrements.
|
||||
|
||||
The scope of the release number is a given distribution, regardless of
|
||||
its version, e.g. Pardus.
|
||||
|
||||
In the future, PISI will have strict checks for release numbers.
|
||||
|
||||
\subsection{Dependency Specifications}
|
||||
|
||||
We allow a package to use both source version and release to identify
|
||||
a particular version or a range of package versions.
|
||||
|
||||
\section{Identifying Binary packages}
|
||||
|
||||
A PISI binary package is produced from a PISI source package. It has a
|
||||
name that is constant throughout the history of the source package,
|
||||
and it inherits the source version and release number from the source
|
||||
package. However, a binary package has in addition a binary build
|
||||
number. Shortly, build number or just build. For each of the
|
||||
architecture targets, e.g. particular binaries, it also has an
|
||||
architecture tag.
|
||||
|
||||
A binary package is uniquely identified by its name, build number, and
|
||||
architecture regardless of the source version.
|
||||
|
||||
\section{Build Number}
|
||||
|
||||
Similarly to source release number, binary build number is the number
|
||||
of changes that are made to a binary package. By change, we mean any
|
||||
bit change. The existence of a change is tested by comparing the
|
||||
cryptographic checksums in files.xml with those of the previous build, and the
|
||||
build number is automatically determined by the P\.IS\.I build system.
|
||||
The build number starts from $1$ as in release number, and increments
|
||||
by one with each binary change.
|
||||
|
||||
The user never interferes with the build number himself. However, if
|
||||
the user fails to provide the previous build, then a package without
|
||||
a build number is built. A package without a build number is evaluated
|
||||
on the basis of release number, which is guaranteed to exist.
|
||||
|
||||
The scope of a build number is a given distribution build environment
|
||||
for a particular architecture, which may vary from repository to
|
||||
repository. Therefore, it is not used in dependency
|
||||
specifications. However, the system does assume that a build of a
|
||||
given package and architecture is unique in a given repository.
|
||||
|
||||
\section{Package File Names}
|
||||
|
||||
A P\.IS\.I binary package file name contains all the components relevant
|
||||
to its identification, separated by dashes:
|
||||
\begin{verbatim}
|
||||
<binary name>-<source version>-<source release>-<binary build>.pisi
|
||||
\end{verbatim}
|
||||
|
||||
\section{Future Work}
|
||||
|
||||
In the future, it may be necessary to extend the notion of release
|
||||
number and build number to support branches and forks of a
|
||||
distribution. A proposal was to have CVS-like branching, but it
|
||||
was dismissed as unnecessary.
|
||||
|
||||
\end{document}
|
||||
@@ -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,80 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import locale
|
||||
import traceback
|
||||
import exceptions
|
||||
import signal
|
||||
import bsddb3.db as db
|
||||
|
||||
import pisi.ui
|
||||
import pisi.context as ctx
|
||||
from pisi.cli.pisicli import PisiCLI
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
def exit():
|
||||
pisi.api.finalize()
|
||||
sys.exit(1)
|
||||
|
||||
def handle_exception(exception, value, tb):
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN) # disable further interrupts
|
||||
ui = pisi.cli.CLI() # make a temporary UI
|
||||
show_traceback = False
|
||||
if exception == exceptions.KeyboardInterrupt:
|
||||
ui.error(_("Keyboard Interrupt: Exiting..."))
|
||||
exit()
|
||||
elif isinstance(value, pisi.Error):
|
||||
ui.error(_("Program Terminated."))
|
||||
show_traceback = ctx.get_option('debug')
|
||||
elif isinstance(value, db.DBRunRecoveryError):
|
||||
ui.error(_("""A database operation has been aborted. You should run pisi
|
||||
again for normal DB recovery procedure. Make sure you have free disk space.
|
||||
You have to run rebuild-db only when there is file corruption and database upgrades."""))
|
||||
elif isinstance(value, pisi.Exception):
|
||||
show_traceback = True
|
||||
ui.error(_("""Unhandled internal exception.
|
||||
Please file a bug report. (http://bugs.uludag.org.tr)"""))
|
||||
else:
|
||||
# For any other exception (possibly Python exceptions) show
|
||||
# the traceback!
|
||||
show_traceback = ctx.get_option('debug')
|
||||
ui.error(_("System Error. Program Terminated."))
|
||||
|
||||
if ctx.get_option('debug'):
|
||||
ui.error(u"%s: %s" % (exception, value))
|
||||
else:
|
||||
ui.error(unicode(value))
|
||||
|
||||
ui.info(_("Please use 'pisi help' for general help."))
|
||||
|
||||
if show_traceback:
|
||||
ui.info(_("Traceback:"))
|
||||
traceback.print_tb(tb)
|
||||
else:
|
||||
ui.info(_("Use --debug to see a traceback."))
|
||||
|
||||
exit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
cli = PisiCLI()
|
||||
try:
|
||||
cli.run_command()
|
||||
except pisi.operations.PisiUpgradeException, e:
|
||||
print _('PISI has been upgraded.')
|
||||
@@ -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,619 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Project SYSTEM "Project-3.8.dtd">
|
||||
<!-- Project file for project pisi -->
|
||||
<!-- Saved: 2006-05-25, 16:13:51 -->
|
||||
<!-- Copyright (C) 2006 PiSi Development Team, -->
|
||||
<Project version="3.8">
|
||||
<ProgLanguage mixed="0">Python</ProgLanguage>
|
||||
<UIType>Console</UIType>
|
||||
<Description>The package management software of Pardus distribution.
|
||||
</Description>
|
||||
<Author>PiSi Development Team</Author>
|
||||
<EmaIl></EmaIl>
|
||||
<Sources>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>popt</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>zip</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>unzip</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>configfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>sourcedbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>utiltests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>installdbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>run.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>metadatatests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>constantstests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>fetchertests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>archivetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>versiontests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>actionsapitests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>specfiletests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>packagetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>packagedbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>graphtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>get.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>shelltools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>variables.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>autotools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pisitools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>kde.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>libtools.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>coreutils.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>pisicli.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>commands.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>lockeddbshelve.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>specfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>package.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>build.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>packagedb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>files.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>config.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>index.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>repodb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>ui.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>util.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>dependency.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>metadata.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>pgraph.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>operations.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>context.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>graph.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcearchive.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>configfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>sourcedb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>installdb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>constants.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>fetcher.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>archive.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>version.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>setup.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pisitoolsfunctions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>uri.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>api.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>TODO</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>pythonmodules.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>scons.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>cli</Dir>
|
||||
<Name>colors.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Name>pisi-cli</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>actionsapi</Dir>
|
||||
<Name>perlmodules.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>comariface.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>po</Dir>
|
||||
<Name>tr.po</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>oo.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>dependencytests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>atomicoperations.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>ootests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>testcase.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>xmlexttests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>filestests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>component.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>autoxmltests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlextpiks.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>autoxml.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfilemdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlextmdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfile.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlext.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfilecdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlextcdom.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>pxml</Dir>
|
||||
<Name>xmlfilepiks.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>svndist.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>filetests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>file.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>search</Dir>
|
||||
<Name>invertedindex.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>search</Dir>
|
||||
<Name>tokenize.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>search</Dir>
|
||||
<Name>__init__.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Dir>search</Dir>
|
||||
<Name>preprocess.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>searchtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>dbshelve.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>repodbtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>itembyrepodb.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>buildtests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tools</Dir>
|
||||
<Name>pygettext.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>conflicttests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Name>zipfileexttests.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>pisi</Dir>
|
||||
<Name>environment.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>cat-db.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>rmcomp-source-repo.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>missing-binary.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>update-environ.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>convert_history.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>calc-build-order.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>fix_old_metadata_files.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>repo-diff.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>find-lib-deps.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>check-source-repo.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>fetchAll.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>repostats.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>zip2</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>unzip2</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>lang</Dir>
|
||||
<Dir>de</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>lang</Dir>
|
||||
<Dir>ef</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>ab</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>ab</Dir>
|
||||
<Dir>comar</Dir>
|
||||
<Name>package.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>bc</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>stress</Dir>
|
||||
<Dir>comp</Dir>
|
||||
<Dir>cd</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>helloworld</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>tests</Dir>
|
||||
<Dir>pccts</Dir>
|
||||
<Name>actions.py</Name>
|
||||
</Source>
|
||||
<Source>
|
||||
<Dir>scripts</Dir>
|
||||
<Name>lspisi</Name>
|
||||
</Source>
|
||||
</Sources>
|
||||
<Forms>
|
||||
</Forms>
|
||||
<Translations>
|
||||
</Translations>
|
||||
<Interfaces>
|
||||
</Interfaces>
|
||||
<Others>
|
||||
<Other>
|
||||
<Name>pisi-cli</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta-light.sh</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta-upgrade.sh</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta.sh</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>sandbox</Name>
|
||||
</Other>
|
||||
<Other>
|
||||
<Dir>tests</Dir>
|
||||
<Name>beta-remoterepo.sh</Name>
|
||||
</Other>
|
||||
</Others>
|
||||
<MainScript>
|
||||
<Name>pisi-cli</Name>
|
||||
</MainScript>
|
||||
<Vcs>
|
||||
<VcsType>Subversion</VcsType>
|
||||
<VcsOptions>{'status': [], 'log': [], 'global': [], 'update': [], 'remove': [], 'add': [], 'tag': [], 'export': [], 'diff': [], 'commit': [], 'checkout': [], 'history': []}</VcsOptions>
|
||||
<VcsOtherData>{'standardLayout': 1}</VcsOtherData>
|
||||
</Vcs>
|
||||
<FiletypeAssociations>
|
||||
<FiletypeAssociation pattern="*.ui.h" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.ui" type="FORMS" />
|
||||
<FiletypeAssociation pattern="*.idl" type="INTERFACES" />
|
||||
<FiletypeAssociation pattern="*.ptl" type="SOURCES" />
|
||||
<FiletypeAssociation pattern="*.py" type="SOURCES" />
|
||||
</FiletypeAssociations>
|
||||
</Project>
|
||||
@@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PiSi version
|
||||
|
||||
__version__ = "1.1_beta8"
|
||||
|
||||
__dbversion__ = "1.1_beta8"
|
||||
__filesdbversion__ = "1.0.5" # yes, this is the real bottleneck
|
||||
|
||||
__all__ = [ 'api', 'config', 'packagedb', 'installdb', 'search' ]
|
||||
|
||||
class Exception(Exception):
|
||||
"""Class of exceptions that must be caught and handled within PISI"""
|
||||
def __str__(self):
|
||||
s = u''
|
||||
for x in self.args:
|
||||
if s != '':
|
||||
s += '\n'
|
||||
s += unicode(x)
|
||||
return s
|
||||
|
||||
class Error(Exception):
|
||||
"""Class of exceptions that lead to program termination"""
|
||||
pass
|
||||
|
||||
import pisi.api
|
||||
|
||||
# FIXME: can't do this due to name clashes in config and other singletons booo
|
||||
#pisi.api import *
|
||||
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import pisi
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
import pisi.context as ctx
|
||||
|
||||
def error(msg):
|
||||
if ctx.config.get_option('ignore_action_errors'):
|
||||
ctx.ui.error(msg)
|
||||
else:
|
||||
raise Error(msg)
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
from pisi.actionsapi.shelltools import can_access_file
|
||||
from pisi.actionsapi.libtools import gnuconfig_update
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
if can_access_file('config.log'):
|
||||
ctx.ui.error(_('Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def configure(parameters = ''):
|
||||
'''configure source with given parameters = "--with-nls --with-libusb --with-something-usefull"'''
|
||||
if can_access_file('configure'):
|
||||
gnuconfig_update()
|
||||
|
||||
args = './configure \
|
||||
--prefix=/%s \
|
||||
--host=%s \
|
||||
--mandir=/%s \
|
||||
--infodir=/%s \
|
||||
--datadir=/%s \
|
||||
--sysconfdir=/%s \
|
||||
--localstatedir=/%s \
|
||||
%s' % (get.defaultprefixDIR(), \
|
||||
get.HOST(), get.manDIR(), \
|
||||
get.infoDIR(), get.dataDIR(), \
|
||||
get.confDIR(), get.localstateDIR(), parameters)
|
||||
|
||||
if system(args):
|
||||
raise ConfigureError(_('Configure failed.'))
|
||||
else:
|
||||
raise ConfigureError(_('No configure script found.'))
|
||||
|
||||
def rawConfigure(parameters = ''):
|
||||
'''configure source with given parameters = "--prefix=/usr --libdir=/usr/lib --with-nls"'''
|
||||
if can_access_file('configure'):
|
||||
gnuconfig_update()
|
||||
|
||||
if system('./configure %s' % parameters):
|
||||
raise ConfigureError(_('Configure failed.'))
|
||||
else:
|
||||
raise ConfigureError(_('No configure script found.'))
|
||||
|
||||
def compile(parameters = ''):
|
||||
#FIXME: Only one package uses this until now, hmmm
|
||||
system('%s %s %s' % (get.CC(), get.CFLAGS(), parameters))
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters = "all" || "doc" etc.'''
|
||||
if system('make %s %s' % (get.makeJOBS(), parameters)):
|
||||
raise MakeError(_('Make failed.'))
|
||||
|
||||
def install(parameters = '', argument = 'install'):
|
||||
'''install source into install directory with given parameters'''
|
||||
if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'):
|
||||
args = 'make prefix=%(prefix)s/%(defaultprefix)s \
|
||||
datadir=%(prefix)s/%(data)s \
|
||||
infodir=%(prefix)s/%(info)s \
|
||||
localstatedir=%(prefix)s/%(localstate)s \
|
||||
mandir=%(prefix)s/%(man)s \
|
||||
sysconfdir=%(prefix)s/%(conf)s \
|
||||
%(parameters)s \
|
||||
%(argument)s' % {'prefix': get.installDIR(),
|
||||
'defaultprefix': get.defaultprefixDIR(),
|
||||
'man': get.manDIR(),
|
||||
'info': get.infoDIR(),
|
||||
'localstate': get.localstateDIR(),
|
||||
'conf': get.confDIR(),
|
||||
'data': get.dataDIR(),
|
||||
'parameters': parameters,
|
||||
'argument':argument}
|
||||
|
||||
if system(args):
|
||||
raise InstallError(_('Install failed.'))
|
||||
else:
|
||||
raise InstallError(_('No Makefile found.'))
|
||||
|
||||
def rawInstall(parameters = '', argument = 'install'):
|
||||
'''install source into install directory with given parameters = PREFIX=%s % get.installDIR()'''
|
||||
if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'):
|
||||
if system('make %s %s' % (parameters, argument)):
|
||||
raise InstallError(_('Install failed.'))
|
||||
else:
|
||||
raise InstallError(_('No Makefile found.'))
|
||||
|
||||
def aclocal(parameters = ''):
|
||||
'''generates an aclocal.m4 based on the contents of configure.in.'''
|
||||
if system('aclocal %s' % parameters):
|
||||
raise RunTimeError(_('Running aclocal failed.'))
|
||||
|
||||
def autoconf(parameters = ''):
|
||||
'''generates a configure script'''
|
||||
if system('autoconf %s' % parameters):
|
||||
raise RunTimeError(_('Running autoconf failed.'))
|
||||
|
||||
def autoreconf(parameters = ''):
|
||||
'''re-generates a configure script'''
|
||||
if system('autoreconf %s' % parameters):
|
||||
raise RunTimeError(_('Running autoconf failed.'))
|
||||
|
||||
def automake(parameters = ''):
|
||||
'''generates a makefile'''
|
||||
if system('automake %s' % parameters):
|
||||
raise RunTimeError(_('Running automake failed.'))
|
||||
|
||||
def autoheader(parameters = ''):
|
||||
'''generates templates for configure'''
|
||||
if system('autoheader %s' % parameters):
|
||||
raise RunTimeError(_('Running autoheader failed.'))
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import re
|
||||
import sys
|
||||
from itertools import izip
|
||||
from itertools import imap
|
||||
from itertools import count
|
||||
from itertools import ifilter
|
||||
from itertools import ifilterfalse
|
||||
|
||||
# ActionsAPI
|
||||
import pisi.actionsapi
|
||||
|
||||
def cat(filename):
|
||||
return file(filename).xreadlines()
|
||||
|
||||
class grep:
|
||||
'''keep only lines that match the regexp'''
|
||||
def __init__(self, pat, flags = 0):
|
||||
self.fun = re.compile(pat, flags).match
|
||||
def __ror__(self, input):
|
||||
return ifilter(self.fun, input)
|
||||
|
||||
class tr:
|
||||
'''apply arbitrary transform to each sequence element'''
|
||||
def __init__(self, transform):
|
||||
self.tr = transform
|
||||
def __ror__(self, input):
|
||||
return imap(self.tr, input)
|
||||
|
||||
class printto:
|
||||
'''print sequence elements one per line'''
|
||||
def __init__(self, out = sys.stdout):
|
||||
self.out = out
|
||||
def __ror__(self,input):
|
||||
for line in input:
|
||||
print >> self.out, line
|
||||
|
||||
printlines = printto(sys.stdout)
|
||||
|
||||
class terminator:
|
||||
def __init__(self,method):
|
||||
self.process = method
|
||||
def __ror__(self,input):
|
||||
return self.process(input)
|
||||
|
||||
aslist = terminator(list)
|
||||
asdict = terminator(dict)
|
||||
astuple = terminator(tuple)
|
||||
join = terminator(''.join)
|
||||
enum = terminator(enumerate)
|
||||
|
||||
class sort:
|
||||
def __ror__(self,input):
|
||||
ll = list(input)
|
||||
ll.sort()
|
||||
return ll
|
||||
sort = sort()
|
||||
|
||||
class uniq:
|
||||
def __ror__(self,input):
|
||||
for i in input:
|
||||
try:
|
||||
if i == prev:
|
||||
continue
|
||||
except NameError:
|
||||
pass
|
||||
prev = i
|
||||
yield i
|
||||
uniq = uniq()
|
||||
@@ -1,193 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PISI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi.variables
|
||||
|
||||
class BinutilsError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
# Globals
|
||||
env = pisi.actionsapi.variables.glb.env
|
||||
dirs = pisi.actionsapi.variables.glb.dirs
|
||||
|
||||
def curDIR():
|
||||
'''returns current work directory's path'''
|
||||
return os.getcwd()
|
||||
|
||||
def curKERNEL():
|
||||
'''returns currently running kernel's version'''
|
||||
return os.uname()[2]
|
||||
|
||||
def curPYTHON():
|
||||
''' returns currently used python's version'''
|
||||
(a, b, c, x, y) = sys.version_info
|
||||
return 'python%s.%s' % (a, b)
|
||||
|
||||
def ENV(environ):
|
||||
'''returns any given environ variable'''
|
||||
try:
|
||||
return os.environ[environ];
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
# PİSİ Related Functions
|
||||
|
||||
def pkgDIR():
|
||||
'''returns the path of binary packages'''
|
||||
'''Default: /var/cache/pisi/packages'''
|
||||
return env.pkg_dir
|
||||
|
||||
def workDIR():
|
||||
return env.work_dir
|
||||
|
||||
def installDIR():
|
||||
'''returns the path of binary packages'''
|
||||
return env.install_dir
|
||||
|
||||
# PSPEC Related Functions
|
||||
|
||||
def srcNAME():
|
||||
return env.src_name
|
||||
|
||||
def srcVERSION():
|
||||
return env.src_version
|
||||
|
||||
def srcRELEASE():
|
||||
return env.src_release
|
||||
|
||||
def srcTAG():
|
||||
return '%s-%s-%s' % (env.src_name, env.src_version, env.src_release)
|
||||
|
||||
def srcDIR():
|
||||
return '%s-%s' % (env.src_name, env.src_version)
|
||||
|
||||
# Build Related Functions
|
||||
|
||||
def HOST():
|
||||
return env.host
|
||||
|
||||
def CHOST():
|
||||
# FIXME: Currently it behave same as HOST,
|
||||
# but will be used for cross-compiling when PİSİ ready...
|
||||
return env.host
|
||||
|
||||
def CFLAGS():
|
||||
return env.cflags
|
||||
|
||||
def CXXFLAGS():
|
||||
return env.cxxflags
|
||||
|
||||
def LDFLAGS():
|
||||
return env.ldflags
|
||||
|
||||
def makeJOBS():
|
||||
return env.jobs
|
||||
|
||||
# Directory Related Functions
|
||||
|
||||
def docDIR():
|
||||
return dirs.doc
|
||||
|
||||
def sbinDIR():
|
||||
return dirs.sbin
|
||||
|
||||
def infoDIR():
|
||||
return dirs.info
|
||||
|
||||
def manDIR():
|
||||
return dirs.man
|
||||
|
||||
def dataDIR():
|
||||
return dirs.data
|
||||
|
||||
def confDIR():
|
||||
return dirs.conf
|
||||
|
||||
def localstateDIR():
|
||||
return dirs.localstate
|
||||
|
||||
def defaultprefixDIR():
|
||||
return dirs.defaultprefix
|
||||
|
||||
def kdeDIR():
|
||||
return dirs.kde
|
||||
|
||||
def qtDIR():
|
||||
return dirs.qt
|
||||
|
||||
def qtLIBDIR():
|
||||
return '%s/lib/' % qtDIR()
|
||||
|
||||
# Binutils Variables
|
||||
|
||||
def existBinary(bin):
|
||||
# determine if path has binary
|
||||
path = os.environ['PATH'].split(':')
|
||||
for directory in path:
|
||||
if os.path.exists(os.path.join(directory, bin)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def getBinutilsInfo(util):
|
||||
cross_build_name = '%s-%s' % (HOST(), util)
|
||||
if not existBinary(cross_build_name):
|
||||
if not existBinary(util):
|
||||
raise BinutilsError(_('Util %s cannot be found') % util)
|
||||
else:
|
||||
ctx.ui.debug(_('Warning: %s does not exist, using plain name %s') \
|
||||
% (cross_build_name, util))
|
||||
return util
|
||||
else:
|
||||
return cross_build_name
|
||||
|
||||
def AR():
|
||||
return getBinutilsInfo('ar')
|
||||
|
||||
def AS():
|
||||
return getBinutilsInfo('as')
|
||||
|
||||
def CC():
|
||||
return getBinutilsInfo('gcc')
|
||||
|
||||
def CXX():
|
||||
return getBinutilsInfo('g++')
|
||||
|
||||
def LD():
|
||||
return getBinutilsInfo('ld')
|
||||
|
||||
def NM():
|
||||
return getBinutilsInfo('nm')
|
||||
|
||||
def RANLIB():
|
||||
return getBinutilsInfo('ranlib')
|
||||
|
||||
def F77():
|
||||
return getBinutilsInfo('f77')
|
||||
|
||||
def GCJ():
|
||||
return getBinutilsInfo('gcj')
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
from pisi.actionsapi.shelltools import can_access_file
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
if can_access_file('config.log'):
|
||||
ctx.ui.error(_('\n!!! Please attach the config.log to your bug report:\n%s/config.log') % os.getcwd())
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def configure(parameters = ''):
|
||||
''' parameters = '--with-nls --with-libusb --with-something-usefull '''
|
||||
if can_access_file('configure'):
|
||||
args = './configure \
|
||||
--prefix=%s \
|
||||
--host=%s \
|
||||
--with-x \
|
||||
--enable-mitshm \
|
||||
--with-xinerama \
|
||||
--with-qt-dir=%s \
|
||||
--enable-mt \
|
||||
--with-qt-libraries=%s \
|
||||
--disable-dependency-tracking \
|
||||
--disable-debug \
|
||||
%s' % (get.kdeDIR(), get.HOST(), get.qtDIR(), get.qtLIBDIR(), parameters)
|
||||
|
||||
if system(args):
|
||||
raise ConfigureError(_('Configure failed.'))
|
||||
else:
|
||||
raise ConfigureError(_('No configure script found.'))
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters = "all" || "doc" etc.'''
|
||||
if system('make %s %s' % (get.makeJOBS(), parameters)):
|
||||
raise MakeError(_('Make failed.'))
|
||||
|
||||
def install(parameters = 'install'):
|
||||
if can_access_file('Makefile'):
|
||||
args = 'make DESTDIR=%s destdir=%s %s' % (get.installDIR(), get.installDIR(), parameters)
|
||||
|
||||
if system(args):
|
||||
raise InstallError(_('Install failed.'))
|
||||
else:
|
||||
raise InstallError(_('No Makefile found.'))
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi-Core Modules
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
from pisi.actionsapi.shelltools import *
|
||||
import pisi.actionsapi.get as get
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def preplib(sourceDirectory = '/usr/lib'):
|
||||
sourceDirectory = join_path(get.installDIR(), sourceDirectory)
|
||||
if can_access_directory(sourceDirectory):
|
||||
if system('/sbin/ldconfig -n -N %s' % sourceDirectory):
|
||||
raise RunTimeError(_('Running ldconfig failed.'))
|
||||
|
||||
def gnuconfig_update():
|
||||
''' copy newest config.* onto source\'s '''
|
||||
for root, dirs, files in os.walk(os.getcwd()):
|
||||
for fileName in files:
|
||||
if fileName in ['config.sub', 'config.guess']:
|
||||
targetFile = os.path.join(root, fileName)
|
||||
if os.path.islink(targetFile):
|
||||
unlink(targetFile)
|
||||
copy('/usr/share/gnuconfig/%s' % fileName, join_path(root, fileName))
|
||||
ctx.ui.info(_('GNU Config Update Finished.'))
|
||||
|
||||
def libtoolize(parameters = ''):
|
||||
if system('/usr/bin/libtoolize %s' % parameters):
|
||||
raise RunTimeError(_('Running libtoolize failed.'))
|
||||
|
||||
def gen_usr_ldscript(dynamicLib):
|
||||
|
||||
makedirs('%s/usr/lib' % get.installDIR())
|
||||
|
||||
destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w')
|
||||
content = '''
|
||||
/* GNU ld script
|
||||
Since Pardus has critical dynamic libraries
|
||||
in /lib, and the static versions in /usr/lib,
|
||||
we need to have a "fake" dynamic lib in /usr/lib,
|
||||
otherwise we run into linking problems.
|
||||
*/
|
||||
GROUP ( /lib/%s )
|
||||
''' % dynamicLib
|
||||
|
||||
destinationFile.write(content)
|
||||
destinationFile.close()
|
||||
chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib))
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
from pisi.actionsapi.shelltools import can_access_file
|
||||
from pisi.actionsapi.shelltools import export
|
||||
|
||||
class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def configure(parameters = ''):
|
||||
'''configure source with given parameters.'''
|
||||
export('PERL_MM_USE_DEFAULT', '1')
|
||||
if can_access_file('Build.PL'):
|
||||
if system('perl Build.PL installdirs=vendor destdir=%s' % get.installDIR()):
|
||||
raise CompileError, _('Configure failed.')
|
||||
else:
|
||||
if system('perl Makefile.PL %s PREFIX=/usr INSTALLDIRS=vendor DESTDIR=%s' % (parameters, get.installDIR())):
|
||||
raise CompileError, _('Configure failed.')
|
||||
|
||||
def make(parameters = ''):
|
||||
'''make source with given parameters.'''
|
||||
if can_access_file('Makefile'):
|
||||
if system('make %s' % parameters):
|
||||
raise MakeError, _('Make failed.')
|
||||
else:
|
||||
if system('perl Build build'):
|
||||
raise MakeError, _('perl build failed.')
|
||||
|
||||
def install(parameters = 'install'):
|
||||
'''install source with given parameters.'''
|
||||
if can_access_file('Makefile'):
|
||||
if system('make %s' % parameters):
|
||||
raise InstallError, _('Make failed.')
|
||||
else:
|
||||
if system('perl Build install'):
|
||||
raise MakeError, _('perl install failed.')
|
||||
|
||||
# FIXME: fix_local_pod % update_pod
|
||||
@@ -1,235 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
'''supports globs in sourceFile arguments'''
|
||||
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import sys
|
||||
import fileinput
|
||||
import re
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.pisitoolsfunctions import *
|
||||
from pisi.actionsapi.shelltools import *
|
||||
|
||||
from pisi.actionsapi import error
|
||||
|
||||
def dobin(sourceFile, destinationDirectory = '/usr/bin'):
|
||||
'''insert a executable file into /bin or /usr/bin'''
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/bin", "xload") '''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dodir(destinationDirectory):
|
||||
'''creates a directory tree'''
|
||||
makedirs(join_path(get.installDIR(), destinationDirectory))
|
||||
|
||||
def dodoc(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE'''
|
||||
readable_insinto(join_path(get.installDIR(), join_path('/usr/share/doc', get.srcTAG())), *sourceFiles)
|
||||
|
||||
def doexe(sourceFile, destinationDirectory):
|
||||
'''insert a executable file into destination directory'''
|
||||
|
||||
''' example call: pisitools.doexe("kde-3.4.sh", "/etc/X11/Sessions")'''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dohtml(*sourceFiles):
|
||||
'''inserts the files in the list of files into /usr/share/doc/PACKAGE/html'''
|
||||
|
||||
''' example call: pisitools.dohtml("doc/doxygen/html/*")'''
|
||||
destionationDirectory = join_path(get.installDIR(), 'usr/share/doc' ,get.srcTAG(), 'html')
|
||||
|
||||
if not can_access_directory(destionationDirectory):
|
||||
makedirs(destionationDirectory)
|
||||
|
||||
allowed_extensions = ['.png', '.gif', '.html', '.htm', '.jpg', '.css', '.js']
|
||||
disallowed_directories = ['CVS']
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
if os.path.isfile(source) and os.path.splitext(source)[1] in allowed_extensions:
|
||||
system('install -m0644 "%s" %s' % (source, destionationDirectory))
|
||||
if os.path.isdir(source) and os.path.basename(source) not in disallowed_directories:
|
||||
for root, dirs, files in os.walk(source):
|
||||
for source in files:
|
||||
if os.path.splitext(source)[1] in allowed_extensions:
|
||||
makedirs(destionationDirectory)
|
||||
system('install -m0644 %s %s' % (join_path(root, source), destionationDirectory))
|
||||
|
||||
def doinfo(*sourceFiles):
|
||||
'''inserts the into files in the list of files into /usr/share/info'''
|
||||
readable_insinto(join_path(get.installDIR(), get.infoDIR()), *sourceFiles)
|
||||
|
||||
def dolib(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the library into /usr/lib'''
|
||||
|
||||
'''example call: pisitools.dolib_a("libz.a")'''
|
||||
'''example call: pisitools.dolib_a("libz.so")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 755)
|
||||
|
||||
def dolib_a(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the static library into /usr/lib with permission 0644'''
|
||||
|
||||
'''example call: pisitools.dolib_a("staticlib/libvga.a")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 644)
|
||||
|
||||
def dolib_so(sourceFile, destinationDirectory = '/usr/lib'):
|
||||
'''insert the static library into /usr/lib with permission 0755'''
|
||||
|
||||
'''example call: pisitools.dolib_so("pppd/plugins/minconn.so")'''
|
||||
sourceFile = join_path(os.getcwd(), sourceFile)
|
||||
destinationDirectory = join_path(get.installDIR(), destinationDirectory)
|
||||
|
||||
lib_insinto(sourceFile, destinationDirectory, 755)
|
||||
|
||||
def doman(*sourceFiles):
|
||||
'''inserts the man pages in the list of files into /usr/share/man/'''
|
||||
|
||||
'''example call: pisitools.doman("man.1", "pardus.*")'''
|
||||
manDIR = join_path(get.installDIR(), get.manDIR())
|
||||
if not can_access_directory(manDIR):
|
||||
makedirs(manDIR)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
try:
|
||||
pageName, pageDirectory = source[:source.rindex('.')], \
|
||||
source[source.rindex('.')+1:]
|
||||
except ValueError:
|
||||
error(_('ActionsAPI [doman]: Wrong man page file: %s') % (source))
|
||||
|
||||
makedirs(join_path(manDIR, '/man%s' % pageDirectory))
|
||||
system('install -m0644 %s %s' % (source, join_path(manDIR, '/man%s' % pageDirectory)))
|
||||
|
||||
def domo(sourceFile, locale, destinationFile ):
|
||||
'''inserts the mo files in the list of files into /usr/share/locale/LOCALE/LC_MESSAGES'''
|
||||
|
||||
'''example call: pisitools.domo("po/tr.po", "tr", "pam_login.mo")'''
|
||||
|
||||
system('msgfmt %s' % sourceFile)
|
||||
makedirs('%s/usr/share/locale/%s/LC_MESSAGES/' % (get.installDIR(), locale))
|
||||
move('messages.mo', '%s/usr/share/locale/%s/LC_MESSAGES/%s' % (get.installDIR(), locale, destinationFile))
|
||||
|
||||
def domove(sourceFile, destination, destinationFile = ''):
|
||||
'''moves sourceFile/Directory into destinationFile/Directory'''
|
||||
|
||||
''' example call: pisitools.domove("/usr/bin/bash", "/bin/bash")'''
|
||||
''' example call: pisitools.domove("/usr/bin/", "/usr/sbin")'''
|
||||
makedirs(join_path(get.installDIR(), destination))
|
||||
|
||||
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
|
||||
if not destinationFile:
|
||||
move(filePath, join_path(get.installDIR(), join_path(destination, os.path.basename(filePath))))
|
||||
else:
|
||||
move(filePath, join_path(get.installDIR(), join_path(destination, destinationFile)))
|
||||
|
||||
def rename(sourceFile, destinationFile):
|
||||
''' renames sourceFile as destinationFile'''
|
||||
|
||||
''' example call: pisitools.rename("/usr/bin/bash", "bash.old") '''
|
||||
''' the result of the previous example would be "/usr/bin/bash.old" '''
|
||||
|
||||
baseDir = os.path.dirname(sourceFile)
|
||||
|
||||
try:
|
||||
os.rename(join_path(get.installDIR(), sourceFile), join_path(get.installDIR(), baseDir, destinationFile))
|
||||
except OSError:
|
||||
error(_('ActionsAPI [rename]: No such file or directory: %s') % (sourceFile))
|
||||
|
||||
def dosed(sourceFiles, findPattern, replacePattern = ''):
|
||||
'''replaces patterns in sourceFiles'''
|
||||
|
||||
''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")'''
|
||||
''' example call: pisitools.dosed("/etc/passwd", "caglar")'''
|
||||
''' example call: pisitools.dosed("/etc/pass*", "caglar")'''
|
||||
''' example call: pisitools.dosed("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")'''
|
||||
|
||||
for sourceFile in glob.glob(sourceFiles):
|
||||
if can_access_file(sourceFile):
|
||||
for line in fileinput.input(sourceFile, inplace = 1):
|
||||
#FIXME: In-place filtering is disabled when standard input is read
|
||||
line = re.sub(findPattern, replacePattern, line)
|
||||
sys.stdout.write(line)
|
||||
else:
|
||||
raise FileError(_('File does not exist or permission denied: %s') % sourceFile)
|
||||
|
||||
def dosbin(sourceFile, destinationDirectory = '/usr/sbin'):
|
||||
'''insert a executable file into /sbin or /usr/sbin'''
|
||||
|
||||
''' example call: pisitools.dobin("bin/xloadimage", "/sbin") '''
|
||||
executable_insinto(join_path(get.installDIR(), destinationDirectory), sourceFile)
|
||||
|
||||
def dosym(sourceFile, destinationFile):
|
||||
'''creates soft link between sourceFile and destinationFile'''
|
||||
|
||||
''' example call: pisitools.dosym("/usr/bin/bash", "/bin/bash")'''
|
||||
makedirs(join_path(get.installDIR(), os.path.dirname(destinationFile)))
|
||||
|
||||
try:
|
||||
os.symlink(sourceFile, join_path(get.installDIR() ,destinationFile))
|
||||
except OSError:
|
||||
error(_('ActionsAPI [dosym]: File exists: %s') % (sourceFile))
|
||||
|
||||
def insinto(destinationDirectory, sourceFile, destinationFile = '', sym = True):
|
||||
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
|
||||
makedirs(join_path(get.installDIR(), destinationDirectory))
|
||||
|
||||
if not destinationFile:
|
||||
for filePath in glob.glob(sourceFile):
|
||||
if can_access_file(filePath):
|
||||
copy(filePath, join_path(get.installDIR(), join_path(destinationDirectory, os.path.basename(filePath))), sym)
|
||||
else:
|
||||
copy(sourceFile, join_path(get.installDIR(), join_path(destinationDirectory, destinationFile)), sym)
|
||||
|
||||
def newdoc(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile'''
|
||||
destinationDirectory = '' #490
|
||||
destinationDirectory = os.path.dirname(destinationFile)
|
||||
destinationFile = os.path.basename(destinationFile)
|
||||
# Use copy instead of move or let build-install scream like file not found!
|
||||
copy(sourceFile, destinationFile)
|
||||
readable_insinto(join_path(get.installDIR(), 'usr/share/doc', get.srcTAG(), destinationDirectory), destinationFile)
|
||||
|
||||
def newman(sourceFile, destinationFile):
|
||||
'''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile'''
|
||||
# Use copy instead of move or let build-install scream like file not found!
|
||||
copy(sourceFile, destinationFile)
|
||||
doman(destinationFile)
|
||||
|
||||
def remove(sourceFile):
|
||||
'''removes sourceFile'''
|
||||
for filePath in glob.glob(join_path(get.installDIR(), sourceFile)):
|
||||
unlink(filePath)
|
||||
|
||||
def removeDir(destinationDirectory):
|
||||
'''removes destinationDirectory and its subtrees'''
|
||||
for directory in glob.glob(join_path(get.installDIR(), destinationDirectory)):
|
||||
unlinkDir(directory)
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Generic functions for common usage of pisitools #
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
from pisi.actionsapi.shelltools import *
|
||||
|
||||
class FileError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class ArgumentError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def executable_insinto(destinationDirectory, *sourceFiles):
|
||||
'''insert a executable file into destinationDirectory'''
|
||||
|
||||
if not sourceFiles or not destinationDirectory:
|
||||
raise ArgumentError(_('Insufficient arguments.'))
|
||||
|
||||
if not can_access_directory(destinationDirectory):
|
||||
makedirs(destinationDirectory)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
# FIXME: use an internal install routine for these
|
||||
system('install -m0755 -o root -g root %s %s' % (source, destinationDirectory))
|
||||
|
||||
def readable_insinto(destinationDirectory, *sourceFiles):
|
||||
'''inserts file list into destinationDirectory'''
|
||||
|
||||
if not sourceFiles or not destinationDirectory:
|
||||
raise ArgumentError(_('Insufficient arguments.'))
|
||||
|
||||
if not can_access_directory(destinationDirectory):
|
||||
makedirs(destinationDirectory)
|
||||
|
||||
for sourceFile in sourceFiles:
|
||||
for source in glob.glob(sourceFile):
|
||||
system('install -m0644 "%s" %s' % (source, destinationDirectory))
|
||||
|
||||
def lib_insinto(sourceFile, destinationDirectory, permission = 0644):
|
||||
'''inserts a library fileinto destinationDirectory with given permission'''
|
||||
|
||||
if not sourceFile or not destinationDirectory:
|
||||
raise ArgumentError(_('Insufficient arguments.'))
|
||||
|
||||
if not can_access_directory(destinationDirectory):
|
||||
makedirs(destinationDirectory)
|
||||
|
||||
if os.path.islink(sourceFile):
|
||||
os.symlink(os.path.realpath(sourceFile), os.path.join(destinationDirectory, sourceFile))
|
||||
else:
|
||||
system('install -m%s %s %s' % (permission, sourceFile, destinationDirectory))
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system, can_access_file
|
||||
from pisi.actionsapi.pisitools import dodoc
|
||||
|
||||
class CompileError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class RunTimeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def compile(parameters = ''):
|
||||
'''compile source with given parameters.'''
|
||||
if system('python setup.py build %s' % (parameters)):
|
||||
raise CompileError, _('Make failed.')
|
||||
|
||||
def install(parameters = ''):
|
||||
'''does python setup.py install'''
|
||||
if system('python setup.py install --root=%s --no-compile %s' % (get.installDIR(), parameters)):
|
||||
raise InstallError, _('Install failed.')
|
||||
|
||||
DDOCS = 'CHANGELOG COPYRIGHT KNOWN_BUGS MAINTAINERS PKG-INFO \
|
||||
CONTRIBUTORS LICENSE COPYING* Change* MANIFEST* README*'
|
||||
|
||||
for doc in DDOCS:
|
||||
if can_access_file(doc):
|
||||
pisitools.dodoc(doc)
|
||||
|
||||
def run(parameters = ''):
|
||||
'''executes parameters with python'''
|
||||
if system('python %s' % (parameters)):
|
||||
raise RunTimeError, _('Running %s failed.') % parameters
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get as get
|
||||
from pisi.actionsapi.shelltools import system
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
class InstallError(pisi.actionsapi.Error):
|
||||
def __init__(self, value=''):
|
||||
pisi.actionsapi.Error.__init__(self, value)
|
||||
self.value = value
|
||||
ctx.ui.error(value)
|
||||
|
||||
def make(parameters = ''):
|
||||
if system('scons %s' % parameters):
|
||||
raise MakeError(_('Make failed.'))
|
||||
|
||||
def install(parameters = 'install', prefix = get.installDIR()):
|
||||
if system('scons prefix=%s %s' % (prefix, parameters)):
|
||||
raise InstallError(_('Install failed.'))
|
||||
@@ -1,225 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#-*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standart Python Modules
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
import string
|
||||
import pwd
|
||||
import grp
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# Pisi Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# ActionsAPI Modules
|
||||
import pisi.actionsapi
|
||||
import pisi.actionsapi.get
|
||||
|
||||
from pisi.actionsapi import error
|
||||
from pisi.util import run_batch
|
||||
from pisi.util import join_path
|
||||
|
||||
def can_access_file(filePath):
|
||||
'''test the existence of file'''
|
||||
return os.access(filePath, os.F_OK)
|
||||
|
||||
def can_access_directory(destinationDirectory):
|
||||
'''test readability, writability and executablility of directory'''
|
||||
return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK)
|
||||
|
||||
def makedirs(destinationDirectory):
|
||||
'''recursive directory creation function'''
|
||||
try:
|
||||
if not os.access(destinationDirectory, os.F_OK):
|
||||
os.makedirs(destinationDirectory)
|
||||
except OSError:
|
||||
error(_('Cannot create directory %s') % destinationDirectory)
|
||||
|
||||
def echo(destionationFile, content):
|
||||
try:
|
||||
f = open(destionationFile, 'a')
|
||||
f.write('%s\n' % content)
|
||||
f.close()
|
||||
except IOError:
|
||||
error(_('ActionsAPI [echo]: Can\'t append to file %s.') % (destionationFile))
|
||||
|
||||
def chmod(filePath, mode = 0755):
|
||||
'''change the mode of filePath to the mode'''
|
||||
for fileName in glob.glob(filePath):
|
||||
if can_access_file(fileName):
|
||||
try:
|
||||
os.chmod(fileName, mode)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [chmod]: Operation not permitted: %s (mode: %s)') \
|
||||
% (fileName, mode))
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [chmod]: File %s doesn\'t exists.') % (fileName))
|
||||
|
||||
def chown(filePath, uid = "root", gid = "root"):
|
||||
'''change the owner and group id of filePath to uid and gid'''
|
||||
if can_access_file(filePath):
|
||||
try:
|
||||
os.chown(filePath, pwd.getpwnam(uid)[2], grp.getgrnam(gid)[2])
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [chown]: Operation not permitted: %s (uid: %s, gid: %s)') \
|
||||
% (filePath, uid, gid))
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [chown]: File %s doesn\'t exists.') % filePath)
|
||||
|
||||
def sym(source, destination):
|
||||
'''creates symbolic link'''
|
||||
try:
|
||||
os.symlink(source, destination)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [sym]: Permission denied: %s to %s') % (source, destination))
|
||||
|
||||
def unlink(filePath):
|
||||
'''remove the file path'''
|
||||
if isFile(filePath) or isLink(filePath):
|
||||
try:
|
||||
os.unlink(filePath)
|
||||
except OSError:
|
||||
ctx.ui.error(_('ActionsAPI [unlink]: Permission denied: %s.') % (filePath))
|
||||
elif isDirectory(filePath):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error(_('ActionsAPI [unlink]: File %s doesn\'t exists.') % (filePath))
|
||||
|
||||
def unlinkDir(sourceDirectory):
|
||||
'''delete an entire directory tree'''
|
||||
if isDirectory(sourceDirectory) or isLink(sourceDirectory):
|
||||
try:
|
||||
shutil.rmtree(sourceDirectory)
|
||||
except OSError:
|
||||
error(_('ActionsAPI [unlinkDir]: Operation not permitted: %s') % (sourceDirectory))
|
||||
elif isFile(sourceDirectory):
|
||||
pass
|
||||
else:
|
||||
error(_('ActionsAPI [unlinkDir]: Directory %s doesn\'t exists.') % (sourceDirectory))
|
||||
|
||||
def move(source, destination):
|
||||
'''recursively move a "source" file or directory to "destination"'''
|
||||
for filePath in glob.glob(source):
|
||||
if isFile(filePath) or isLink(filePath) or isDirectory(filePath):
|
||||
try:
|
||||
shutil.move(filePath, destination)
|
||||
except OSError:
|
||||
error(_('ActionsAPI [move]: Permission denied: %s to %s') % (filePath, destination))
|
||||
else:
|
||||
error(_('ActionsAPI [move]: File %s doesn\'t exists.') % (filePath))
|
||||
|
||||
# FIXME: instead of passing a sym parameter, split copy and copytree into 4 different function
|
||||
def copy(source, destination, sym = True):
|
||||
'''recursively copy a "source" file or directory to "destination"'''
|
||||
for filePath in glob.glob(source):
|
||||
if isFile(filePath) and not isLink(filePath):
|
||||
try:
|
||||
shutil.copy(filePath, destination)
|
||||
except IOError:
|
||||
error(_('ActionsAPI [copy]: Permission denied: %s to %s') % (filePath, destination))
|
||||
elif isLink(filePath) and sym:
|
||||
if isDirectory(destination):
|
||||
os.symlink(os.readlink(filePath), join_path(destination, os.path.basename(filePath)))
|
||||
else:
|
||||
if isFile(destination):
|
||||
os.remove(destination)
|
||||
os.symlink(os.readlink(filePath), destination)
|
||||
elif isLink(filePath) and not sym:
|
||||
if isDirectory(filePath):
|
||||
copytree(filePath, destination)
|
||||
else:
|
||||
shutil.copy(filePath, destination)
|
||||
elif isDirectory(filePath):
|
||||
copytree(filePath, destination, sym)
|
||||
else:
|
||||
error(_('ActionsAPI [copy]: File %s does not exist.') % filePath)
|
||||
|
||||
def copytree(source, destination, sym = True):
|
||||
'''recursively copy an entire directory tree rooted at source'''
|
||||
if isDirectory(source):
|
||||
if os.path.exists(destination):
|
||||
if isDirectory(destination):
|
||||
copytree(source, join_path(destination, os.path.basename(source.strip('/'))))
|
||||
return
|
||||
else:
|
||||
copytree(source, join_path(destination, os.path.basename(source)))
|
||||
return
|
||||
try:
|
||||
shutil.copytree(source, destination, sym)
|
||||
except OSError, e:
|
||||
error(_('ActionsAPI [copytree] %s to %s: %s') % (source, destination, e))
|
||||
else:
|
||||
error(_('ActionsAPI [copytree]: Directory %s doesn\'t exists.') % (source))
|
||||
|
||||
def touch(filePath):
|
||||
'''changes the access time of the 'filePath', or creates it if it is not exist'''
|
||||
if glob.glob(filePath):
|
||||
for f in glob.glob(filePath):
|
||||
os.utime(f, None)
|
||||
else:
|
||||
try:
|
||||
f = open(filePath, 'w')
|
||||
f.close()
|
||||
except IOError:
|
||||
error(_('ActionsAPI [touch]: Permission denied: %s') % (filePath))
|
||||
|
||||
def cd(directoryName = ''):
|
||||
'''change directory'''
|
||||
current = os.getcwd()
|
||||
if directoryName:
|
||||
os.chdir(directoryName)
|
||||
else:
|
||||
os.chdir(os.path.dirname(current))
|
||||
|
||||
def ls(source):
|
||||
'''listdir'''
|
||||
if os.path.isdir(source):
|
||||
return os.listdir(source)
|
||||
else:
|
||||
return glob.glob(source)
|
||||
|
||||
def export(key, value):
|
||||
'''export environ variable'''
|
||||
os.environ[key] = value
|
||||
|
||||
def isLink(filePath):
|
||||
'''return True if filePath refers to a symbolic link'''
|
||||
return os.path.islink(filePath)
|
||||
|
||||
def isFile(filePath):
|
||||
'''return True if filePath is an existing regular file'''
|
||||
return os.path.isfile(filePath)
|
||||
|
||||
def isDirectory(filePath):
|
||||
'''Return True if filePath is an existing directory'''
|
||||
return os.path.isdir(filePath)
|
||||
|
||||
def realPath(filePath):
|
||||
'''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path'''
|
||||
return os.path.realpath(filePath)
|
||||
|
||||
def baseName(filePath):
|
||||
'''return the base name of pathname filePath'''
|
||||
return os.path.basename(filePath)
|
||||
|
||||
def dirName(filePath):
|
||||
'''return the directory name of pathname path'''
|
||||
return os.path.dirname(filePath)
|
||||
|
||||
def system(command):
|
||||
command = string.join(string.split(command))
|
||||
return run_batch(command, True)[0]
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# Standard Python Modules
|
||||
from os import getenv
|
||||
from os import environ
|
||||
|
||||
# Pisi-Core Modules
|
||||
import pisi.context as ctx
|
||||
|
||||
# Set individual information, that are generally needed for ActionsAPI
|
||||
|
||||
def exportFlags():
|
||||
'''General flags used in actions API.'''
|
||||
|
||||
# Build systems depend on these environment variables. That is why
|
||||
# we export them instead of using as (instance) variables.
|
||||
values = ctx.config.values
|
||||
environ['HOST'] = values.build.host
|
||||
environ['CFLAGS'] = values.build.cflags
|
||||
environ['CXXFLAGS'] = values.build.cxxflags
|
||||
environ['LDFLAGS'] = values.build.ldflags
|
||||
environ['USER_LDFLAGS'] = values.build.ldflags
|
||||
environ['JOBS'] = values.build.jobs
|
||||
|
||||
class Env(object):
|
||||
'''General environment variables used in actions API'''
|
||||
def __init__(self):
|
||||
|
||||
exportFlags()
|
||||
|
||||
self.__vars = {
|
||||
'pkg_dir': 'PKG_DIR',
|
||||
'work_dir': 'WORK_DIR',
|
||||
'install_dir': 'INSTALL_DIR',
|
||||
'src_name': 'SRC_NAME',
|
||||
'src_version': 'SRC_VERSION',
|
||||
'src_release': 'SRC_RELEASE',
|
||||
'host': 'HOST',
|
||||
'cflags': 'CFLAGS',
|
||||
'cxxflags': 'CXXFLAGS',
|
||||
'ldflags': 'LDFLAGS',
|
||||
'jobs': 'JOBS'
|
||||
}
|
||||
|
||||
def __getattr__(self, attr):
|
||||
|
||||
# Using environment variables is somewhat tricky. Each time
|
||||
# you need them you need to check for their value.
|
||||
if self.__vars.has_key(attr):
|
||||
return getenv(self.__vars[attr])
|
||||
else:
|
||||
return None
|
||||
|
||||
class Dirs:
|
||||
'''General directories used in actions API.'''
|
||||
# TODO: Eventually we should consider getting these from a/the
|
||||
# configuration file
|
||||
doc = 'usr/share/doc'
|
||||
sbin = 'usr/sbin'
|
||||
man = 'usr/share/man'
|
||||
info = 'usr/share/info'
|
||||
data = 'usr/share'
|
||||
conf = 'etc'
|
||||
localstate = 'var/lib'
|
||||
defaultprefix = 'usr'
|
||||
|
||||
# These should be owned by object not the class. Or else Python
|
||||
# will bug us with NoneType errors because of uninitialized
|
||||
# context (ctx) because of the import in build.py.
|
||||
def __init__(self):
|
||||
self.values = ctx.config.values
|
||||
self.kde = self.values.dirs.kde_dir
|
||||
self.qt = self.values.dirs.qt_dir
|
||||
|
||||
|
||||
# As we import this module from build.py, we can't init glb as a
|
||||
# singleton here. Or else Python will bug us with NoneType errors
|
||||
# because of uninitialized context (ctx) because of exportFlags().
|
||||
#
|
||||
# We import this modue from build.py becase we need to reset/init glb
|
||||
# for each build. # See bug #2575
|
||||
glb = None
|
||||
|
||||
def initVariables():
|
||||
global glb
|
||||
ctx.env = Env()
|
||||
ctx.dirs = Dirs()
|
||||
glb = ctx
|
||||
@@ -1,496 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray at pardus.org.tr>
|
||||
# Baris Metin <baris at pardus.org.tr>
|
||||
|
||||
"""Top level PISI interfaces. a facade to the entire PISI system"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import logging.handlers
|
||||
from os.path import exists
|
||||
import bsddb3.db as db
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
import pisi.util as util
|
||||
import pisi.dependency as dependency
|
||||
import pisi.pgraph as pgraph
|
||||
import pisi.operations as operations
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
import pisi.sourcedb
|
||||
import pisi.component as component
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
from pisi.operations import install, remove, upgrade, emerge
|
||||
from pisi.build import build_until
|
||||
from pisi.atomicoperations import resurrect_package, build
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
from pisi.file import File
|
||||
import pisi.search
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
def init(database = True, write = True,
|
||||
options = None, ui = None, comar = True,
|
||||
stdout = None, stderr = None,
|
||||
comar_sockname = None):
|
||||
"""Initialize PiSi subsystem"""
|
||||
|
||||
# UI comes first
|
||||
|
||||
if ui is None:
|
||||
from pisi.cli import CLI
|
||||
if options:
|
||||
ctx.ui = CLI(options.debug)
|
||||
else:
|
||||
ctx.ui = CLI()
|
||||
else:
|
||||
ctx.ui = ui
|
||||
|
||||
if os.access('/var/log', os.W_OK):
|
||||
handler = logging.handlers.RotatingFileHandler('/var/log/pisi.log')
|
||||
#handler.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)-12s: %(levelname)-8s %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
ctx.log = logging.getLogger('pisi')
|
||||
ctx.log.addHandler(handler)
|
||||
ctx.loghandler = handler
|
||||
ctx.log.setLevel(logging.DEBUG)
|
||||
else:
|
||||
ctx.log = None
|
||||
|
||||
# If given define stdout and stderr. Needed by buildfarm currently
|
||||
# but others can benefit from this too.
|
||||
if stdout:
|
||||
ctx.stdout = stdout
|
||||
if stderr:
|
||||
ctx.stderr = stderr
|
||||
|
||||
import pisi.config
|
||||
ctx.config = pisi.config.Config(options)
|
||||
|
||||
# TODO: this is definitely not dynamic beyond this point!
|
||||
ctx.comar = comar and not ctx.config.get_option('ignore_comar')
|
||||
# This is for YALI, used in comariface.py:make_com()
|
||||
ctx.comar_sockname = comar_sockname
|
||||
|
||||
# initialize repository databases
|
||||
ctx.database = database
|
||||
if database:
|
||||
shelve.init_dbenv(write=write)
|
||||
ctx.repodb = pisi.repodb.init()
|
||||
ctx.installdb = pisi.installdb.init()
|
||||
ctx.filesdb = pisi.files.FilesDB()
|
||||
ctx.componentdb = pisi.component.ComponentDB()
|
||||
ctx.packagedb = packagedb.init_db()
|
||||
ctx.sourcedb = pisi.sourcedb.init()
|
||||
pisi.search.init(['summary', 'description'], ['en', 'tr'])
|
||||
else:
|
||||
ctx.repodb = None
|
||||
ctx.installdb = None
|
||||
ctx.filesdb = None
|
||||
ctx.componentdb = None
|
||||
ctx.packagedb = None
|
||||
ctx.sourcedb = None
|
||||
ctx.ui.debug('PISI API initialized')
|
||||
ctx.initialized = True
|
||||
|
||||
def finalize():
|
||||
if ctx.initialized:
|
||||
|
||||
if ctx.log:
|
||||
ctx.loghandler.flush()
|
||||
ctx.log.removeHandler(ctx.loghandler)
|
||||
|
||||
pisi.repodb.finalize()
|
||||
pisi.installdb.finalize()
|
||||
if ctx.filesdb != None:
|
||||
ctx.filesdb.close()
|
||||
ctx.filesdb = None
|
||||
if ctx.componentdb != None:
|
||||
ctx.componentdb.close()
|
||||
ctx.componentdb = None
|
||||
if ctx.packagedb:
|
||||
packagedb.finalize_db()
|
||||
ctx.packagedb = None
|
||||
if ctx.sourcedb:
|
||||
pisi.sourcedb.finalize()
|
||||
ctx.sourcedb = None
|
||||
pisi.search.finalize()
|
||||
if ctx.dbenv:
|
||||
ctx.dbenv.close()
|
||||
ctx.dbenv_lock.close()
|
||||
|
||||
ctx.ui.debug('PISI API finalized')
|
||||
ctx.ui.close()
|
||||
ctx.initialized = False
|
||||
|
||||
def list_available(repo = None):
|
||||
'''returns a set of available package names'''
|
||||
return set(ctx.packagedb.list_packages(repo = repo))
|
||||
|
||||
def list_upgradable():
|
||||
ignore_build = ctx.get_option('ignore_build_no')
|
||||
|
||||
return filter(pisi.operations.is_upgradable, ctx.installdb.list_installed())
|
||||
|
||||
def package_graph(A, repo = pisi.itembyrepodb.installed, ignore_installed = False):
|
||||
"""Construct a package relations graph, containing
|
||||
all dependencies of packages A, if ignore_installed
|
||||
option is True, then only uninstalled deps will
|
||||
be added."""
|
||||
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(ctx.packagedb, repo) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
#state = {}
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = ctx.packagedb.get_package(x, repo)
|
||||
#print pkg
|
||||
for dep in pkg.runtimeDependencies():
|
||||
if ignore_installed:
|
||||
if dependency.installed_satisfies_dep(dep):
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
return G_f
|
||||
|
||||
def configure_pending():
|
||||
# start with pending packages
|
||||
# configure them in reverse topological order of dependency
|
||||
A = ctx.installdb.list_pending()
|
||||
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
|
||||
for x in A.keys():
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B.keys():
|
||||
pkg = ctx.packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies():
|
||||
if dep.package in G_f.vertices():
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
if ctx.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
try:
|
||||
import pisi.comariface as comariface
|
||||
for x in order:
|
||||
if ctx.installdb.is_installed(x):
|
||||
pkginfo = A[x]
|
||||
pkgname = util.package_name(x, pkginfo.version,
|
||||
pkginfo.release,
|
||||
False,
|
||||
False)
|
||||
pkg_path = util.join_path(ctx.config.lib_dir(),
|
||||
'package', pkgname)
|
||||
m = MetaData()
|
||||
metadata_path = util.join_path(pkg_path, ctx.const.metadata_xml)
|
||||
m.read(metadata_path)
|
||||
for pcomar in m.package.providesComar:
|
||||
scriptPath = util.join_path(pkg_path,
|
||||
ctx.const.comar_dir,
|
||||
pcomar.script)
|
||||
comariface.register(pcomar, x, scriptPath)
|
||||
# FIXME: we need a full package info here!
|
||||
# Eray, please fix this
|
||||
# your wish is a command, darling -- eray
|
||||
pkginfo.name = x
|
||||
ctx.ui.notify(pisi.ui.configuring, package = pkginfo, files = None)
|
||||
comariface.run_postinstall(x)
|
||||
ctx.ui.notify(pisi.ui.configured, package = pkginfo, files = None)
|
||||
ctx.installdb.clear_pending(x)
|
||||
except ImportError:
|
||||
raise Error(_("COMAR: comard not fully installed"))
|
||||
|
||||
def info(package, installed = False):
|
||||
if package.endswith(ctx.const.package_suffix):
|
||||
return info_file(package)
|
||||
else:
|
||||
return info_name(package, installed)
|
||||
|
||||
def info_file(package_fn):
|
||||
from package import Package
|
||||
|
||||
if not os.path.exists(package_fn):
|
||||
raise Error (_('File %s not found') % package_fn)
|
||||
|
||||
package = Package(package_fn)
|
||||
package.read()
|
||||
return package.metadata, package.files
|
||||
|
||||
def info_name(package_name, installed=False):
|
||||
"""fetch package information for a package"""
|
||||
if 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('sumdesc', 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, False, True)
|
||||
if file.hash != util.sha1_file('/' + file.path):
|
||||
corrupt.append(file)
|
||||
ctx.ui.info("Corrupt file: %s" % file)
|
||||
else:
|
||||
ctx.ui.info("OK", False)
|
||||
return corrupt
|
||||
|
||||
def index(dirs=None, output='pisi-index.xml', skip_sources=False, skip_signing=False,
|
||||
non_recursive=False):
|
||||
"""accumulate PISI XML files in a directory"""
|
||||
index = Index()
|
||||
if not dirs:
|
||||
dirs = ['.']
|
||||
for repo_dir in dirs:
|
||||
repo_dir = str(repo_dir)
|
||||
ctx.ui.info(_('* Building index of PISI files under %s') % repo_dir)
|
||||
index.index(repo_dir, skip_sources, non_recursive)
|
||||
|
||||
if skip_signing:
|
||||
index.write(output, sha1sum=True, compress=File.bz2, sign=None)
|
||||
else:
|
||||
index.write(output, sha1sum=True, compress=File.bz2, sign=File.detached)
|
||||
ctx.ui.info(_('* Index file written'))
|
||||
|
||||
def add_repo(name, indexuri):
|
||||
if ctx.repodb.has_repo(name):
|
||||
raise Error(_('Repo %s already present.') % name)
|
||||
else:
|
||||
repo = pisi.repodb.Repo(URI(indexuri))
|
||||
ctx.repodb.add_repo(name, repo)
|
||||
ctx.ui.info(_('Repo %s added to system.') % name)
|
||||
|
||||
def remove_repo(name):
|
||||
if ctx.repodb.has_repo(name):
|
||||
ctx.repodb.remove_repo(name)
|
||||
pisi.util.clean_dir(os.path.join(ctx.config.index_dir(), name))
|
||||
ctx.ui.info(_('Repo %s removed from system.') % name)
|
||||
else:
|
||||
ctx.ui.error(_('Repository %s does not exist. Cannot remove.')
|
||||
% name)
|
||||
|
||||
def list_repos():
|
||||
return ctx.repodb.list()
|
||||
|
||||
def update_repo(repo, force=False):
|
||||
ctx.ui.info(_('* Updating repository: %s') % repo)
|
||||
index = Index()
|
||||
if ctx.repodb.has_repo(repo):
|
||||
repouri = ctx.repodb.get_repo(repo).indexuri.get_uri()
|
||||
try:
|
||||
index.read_uri(repouri, repo)
|
||||
except pisi.file.AlreadyHaveException, e:
|
||||
ctx.ui.info(_('No updates available for repository %s.') % repo)
|
||||
if force:
|
||||
ctx.ui.info(_('Updating database at any rate as requested'))
|
||||
index.read_uri(repouri, repo, force = force)
|
||||
else:
|
||||
return
|
||||
try:
|
||||
index.check_signature(repouri, repo)
|
||||
except pisi.file.NoSignatureFound, e:
|
||||
ctx.ui.warning(e)
|
||||
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
|
||||
ctx.ui.info(_('* Package database updated.'))
|
||||
else:
|
||||
raise Error(_('No repository named %s found.') % repo)
|
||||
|
||||
def rebuild_repo(repo):
|
||||
ctx.ui.info(_('* Rebuilding \'%s\' named repo... ') % repo, noln=True)
|
||||
|
||||
index = Index()
|
||||
if ctx.repodb.has_repo(repo):
|
||||
repouri = URI(ctx.repodb.get_repo(repo).indexuri.get_uri())
|
||||
|
||||
indexname = repouri.filename()
|
||||
indexpath = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, indexname)
|
||||
|
||||
uri_str = repouri.get_uri()
|
||||
if os.path.exists(indexpath):
|
||||
uri_str = indexpath
|
||||
|
||||
try:
|
||||
index.read_uri(uri_str, repo, force = True)
|
||||
except IOError:
|
||||
ctx.ui.warning(_("Repo index file \'%s\' not found.") % uri_str)
|
||||
return
|
||||
else:
|
||||
raise Error(_('No repository named %s found.') % repo)
|
||||
|
||||
ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn))
|
||||
ctx.ui.info(_('OK.'))
|
||||
|
||||
def delete_cache():
|
||||
util.clean_dir(ctx.config.packages_dir())
|
||||
util.clean_dir(ctx.config.archives_dir())
|
||||
util.clean_dir(ctx.config.tmp_dir())
|
||||
|
||||
def rebuild_db(files=False):
|
||||
|
||||
assert ctx.database == False
|
||||
|
||||
# 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):
|
||||
#from pisi.lockeddbshelve import LockedDBShelf
|
||||
pisi.lockeddbshelve.init_dbenv(write=True, writeversion=True)
|
||||
#TODO: either don't delete version files here, or remove force flag...
|
||||
import bsddb3.db
|
||||
for db in os.listdir(ctx.config.db_dir()):
|
||||
if db.endswith('.bdb'):# or db.startswith('log'): # delete only db files
|
||||
if db.startswith('files') or db.startswith('filesdbversion'):
|
||||
clean = files
|
||||
else:
|
||||
clean = True
|
||||
if clean:
|
||||
fn = pisi.util.join_path(ctx.config.db_dir(), db)
|
||||
#FIXME: there is a bug with bsddb3
|
||||
#ctx.dbenv.dbremove(fn, "", None, bsddb3.db.DB_AUTO_COMMIT)
|
||||
ctx.dbenv.dbremove(file=fn, flags=bsddb3.db.DB_AUTO_COMMIT)
|
||||
#os.unlink(fn)
|
||||
ctx.dbenv.close()
|
||||
|
||||
def reload_packages(files, txn):
|
||||
for package_fn in os.listdir( pisi.util.join_path( ctx.config.lib_dir(),
|
||||
'package' ) ):
|
||||
if not package_fn == "scripts":
|
||||
ctx.ui.debug('Resurrecting %s' % package_fn)
|
||||
pisi.api.resurrect_package(package_fn, files, txn)
|
||||
|
||||
def reload_indexes(txn):
|
||||
index_dir = ctx.config.index_dir()
|
||||
if os.path.exists(index_dir): # it may have been erased, or we may be upgrading from a previous version -- exa
|
||||
for repo in os.listdir(index_dir):
|
||||
indexuri = pisi.util.join_path(ctx.config.lib_dir(), 'index', repo, 'uri')
|
||||
indexuri = open(indexuri, 'r').readline()
|
||||
pisi.api.add_repo(repo, indexuri)
|
||||
pisi.api.rebuild_repo(repo)
|
||||
|
||||
# check db schema versions
|
||||
try:
|
||||
pisi.lockeddbshelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=False)
|
||||
except:
|
||||
files = True # exception means the files db version was wrong
|
||||
destroy(files) # bye bye
|
||||
pisi.lockeddbshelve.check_dbversion('dbversion', pisi.__dbversion__, write=True, update=True)
|
||||
pisi.lockeddbshelve.check_dbversion('filesdbversion', pisi.__filesdbversion__, write=True, update=True)
|
||||
|
||||
# save parameters and shutdown pisi
|
||||
options = ctx.config.options
|
||||
ui = ctx.ui
|
||||
comar = ctx.comar
|
||||
finalize()
|
||||
# construct new database version
|
||||
init(database=True, options=options, ui=ui, comar=comar)
|
||||
#ctx.txn_proc(reload)
|
||||
clean_duplicates()
|
||||
reload_packages(files, None)
|
||||
reload_indexes(None)
|
||||
@@ -1,295 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Archive module provides access to regular archive file types.
|
||||
# maintainer baris and meren
|
||||
|
||||
# standard library modules
|
||||
import os
|
||||
import stat
|
||||
import shutil
|
||||
import tarfile
|
||||
import zipfile
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
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"):
|
||||
super(ArchiveTar, self).__init__(file_path, arch_type)
|
||||
self.tar = None
|
||||
|
||||
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('.lzma')
|
||||
ret, out, err = util.run_batch("lzma d %s %s" % (self.file_path + '.lzma', 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)
|
||||
for tarinfo in self.tar:
|
||||
self.tar.extract(tarinfo)
|
||||
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('.lzma')
|
||||
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':
|
||||
ret, out, err = util.run_batch("lzma e -a2 -d26 -fb64 %s %s" % (self.file_path, self.file_path + '.lzma'))
|
||||
if ret != 0:
|
||||
raise LZMAError(err)
|
||||
|
||||
class ArchiveZip(ArchiveBase):
|
||||
"""ArchiveZip handles zip archives.
|
||||
|
||||
Being a zip archive PISI packages also use this class
|
||||
extensively. This class provides unpacking and packing magic for
|
||||
zip archives."""
|
||||
|
||||
symmagic = 2716663808 #long of hex val '0xA1ED0000L'
|
||||
|
||||
def __init__(self, file_path, arch_type = "zip", mode = 'r'):
|
||||
super(ArchiveZip, self).__init__(file_path, arch_type)
|
||||
|
||||
self.zip_obj = zipfile.ZipFile(self.file_path, mode)
|
||||
|
||||
def close(self):
|
||||
"""Close the zip archive."""
|
||||
self.zip_obj.close()
|
||||
|
||||
def add_to_archive(self, file_name, arc_name=None):
|
||||
"""Add file or directory path to the zip file"""
|
||||
# It's a pity that zipfile can't handle unicode strings. Grrr!
|
||||
file_name = str(file_name)
|
||||
if os.path.isdir(file_name) and not os.path.islink(file_name):
|
||||
self.zip_obj.writestr(file_name + '/', '')
|
||||
attr_obj = self.zip_obj.getinfo(file_name + '/')
|
||||
attr_obj.external_attr = stat.S_IMODE(os.stat(file_name)[0]) << 16L
|
||||
for f in os.listdir(file_name):
|
||||
self.add_to_archive(os.path.join(file_name, f))
|
||||
else:
|
||||
if os.path.islink(file_name):
|
||||
dest = os.readlink(file_name)
|
||||
attr = zipfile.ZipInfo()
|
||||
attr.filename = file_name
|
||||
attr.create_system = 3
|
||||
attr.external_attr = self.symmagic
|
||||
self.zip_obj.writestr(attr, dest)
|
||||
else:
|
||||
self.zip_obj.write(file_name, arc_name, zipfile.ZIP_DEFLATED)
|
||||
|
||||
if not arc_name:
|
||||
zinfo = self.zip_obj.getinfo(file_name)
|
||||
else:
|
||||
zinfo = self.zip_obj.getinfo(arc_name)
|
||||
zinfo.create_system = 3
|
||||
|
||||
def add_basename_to_archive(self, file_name):
|
||||
"""Add only the basepath to the zip file. For example; if the given
|
||||
file_name parameter is /usr/local/bin/somedir, this function
|
||||
will create only the base directory/file somedir in the
|
||||
archive."""
|
||||
cwd = os.getcwd()
|
||||
path_name = os.path.dirname(file_name)
|
||||
file_name = os.path.basename(file_name)
|
||||
if path_name:
|
||||
os.chdir(path_name)
|
||||
self.add_to_archive(file_name)
|
||||
os.chdir(cwd)
|
||||
|
||||
def has_file(self, file_path):
|
||||
""" Returns true if file_path is member of the zip archive"""
|
||||
return file_path in self.zip_obj.namelist()
|
||||
|
||||
def unpack_file_cond(self, pred, target_dir, archive_root = ''):
|
||||
"""Unpack/Extract files according to predicate function
|
||||
pred: filename -> bool
|
||||
unpacks stuff into target_dir and only extracts files
|
||||
from archive_root, treating it as the archive root"""
|
||||
zip_obj = self.zip_obj
|
||||
for info in zip_obj.infolist():
|
||||
if pred(info.filename): # check if condition holds
|
||||
|
||||
# below code removes that, so we find it here
|
||||
is_dir = info.filename.endswith('/')
|
||||
|
||||
# calculate output file name
|
||||
if archive_root == '':
|
||||
outpath = info.filename
|
||||
else:
|
||||
# change archive_root
|
||||
if util.subpath(archive_root, info.filename):
|
||||
outpath = util.removepathprefix(archive_root,
|
||||
info.filename)
|
||||
else:
|
||||
continue # don't extract if not under
|
||||
|
||||
ofile = os.path.join(target_dir, outpath)
|
||||
|
||||
if is_dir: # this is a directory
|
||||
d = os.path.join(target_dir, outpath)
|
||||
if not os.path.isdir(d):
|
||||
os.makedirs(d)
|
||||
perm = info.external_attr
|
||||
perm &= 0xFFFF0000
|
||||
perm >>= 16
|
||||
perm |= 0x00000100
|
||||
os.chmod(d, perm)
|
||||
continue
|
||||
|
||||
# check that output dir is present
|
||||
util.check_dir(os.path.dirname(ofile))
|
||||
|
||||
# remove output file we might be overwriting.
|
||||
# (also check for islink? for broken symlinks...)
|
||||
if os.path.isfile(ofile) or os.path.islink(ofile):
|
||||
os.remove(ofile)
|
||||
|
||||
if info.external_attr == self.symmagic:
|
||||
if os.path.isdir(ofile):
|
||||
shutil.rmtree(ofile) # a rare case, the file used to be a dir, now it is a symlink!
|
||||
target = zip_obj.read(info.filename)
|
||||
os.symlink(target, ofile)
|
||||
else:
|
||||
perm = info.external_attr
|
||||
perm &= 0x08FF0000
|
||||
perm >>= 16
|
||||
perm |= 0x00000100
|
||||
buff = open (ofile, 'wb')
|
||||
file_content = zip_obj.read(info.filename)
|
||||
buff.write(file_content)
|
||||
buff.close()
|
||||
os.chmod(ofile, perm)
|
||||
|
||||
def unpack_files(self, paths, target_dir):
|
||||
self.unpack_file_cond(lambda f:f in paths, target_dir)
|
||||
|
||||
def unpack_dir(self, path, target_dir):
|
||||
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir)
|
||||
|
||||
def unpack_dir_flat(self, path, target_dir):
|
||||
self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir, path)
|
||||
|
||||
def unpack(self, target_dir, clean_dir=False):
|
||||
super(ArchiveZip, self).unpack(target_dir, clean_dir)
|
||||
|
||||
self.unpack_file_cond(lambda f: True, target_dir)
|
||||
self.close()
|
||||
return
|
||||
|
||||
|
||||
class Archive:
|
||||
"""Archive is the main factory for ArchiveClasses, regarding the
|
||||
Abstract Factory Pattern :)."""
|
||||
|
||||
def __init__(self, file_path, arch_type):
|
||||
"""accepted archive types:
|
||||
targz, tarbz2, zip, tar"""
|
||||
|
||||
handlers = {
|
||||
'targz': ArchiveTar,
|
||||
'tarbz2': ArchiveTar,
|
||||
'tar': ArchiveTar,
|
||||
'zip': ArchiveZip,
|
||||
'binary': ArchiveBinary
|
||||
}
|
||||
|
||||
self.archive = handlers.get(arch_type)(file_path, arch_type)
|
||||
|
||||
def unpack(self, target_dir, clean_dir = False):
|
||||
self.archive.unpack(target_dir, clean_dir)
|
||||
|
||||
def unpack_files(self, files, target_dir):
|
||||
self.archive.unpack_files(files, target_dir)
|
||||
@@ -1,523 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
"Atomic package operations such as install/remove/upgrade"
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import sys
|
||||
import os
|
||||
import bsddb3.db as db
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.dependency as dependency
|
||||
import pisi.util as util
|
||||
from pisi.specfile import *
|
||||
#from pisi.package import Package
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
from pisi.uri import URI
|
||||
import pisi.ui
|
||||
from pisi.version import Version
|
||||
#import conflicts
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
pass
|
||||
|
||||
# single package operations
|
||||
|
||||
class AtomicOperation(object):
|
||||
|
||||
def __init__(self, ignore_dep = None):
|
||||
#self.package = package
|
||||
if ignore_dep==None:
|
||||
self.ignore_dep = ctx.config.get_option('ignore_dependency')
|
||||
else:
|
||||
self.ignore_dep = ignore_dep
|
||||
|
||||
def run(self, package):
|
||||
"perform an atomic package operation"
|
||||
pass
|
||||
|
||||
|
||||
class Install(AtomicOperation):
|
||||
"Install class, provides install routines for pisi packages"
|
||||
|
||||
@staticmethod
|
||||
def from_name(name, ignore_dep = None):
|
||||
# download package and return an installer object
|
||||
# find package in repository
|
||||
repo = ctx.packagedb.which_repo(name)
|
||||
if repo:
|
||||
repo = ctx.repodb.get_repo(repo)
|
||||
pkg = ctx.packagedb.get_package(name)
|
||||
|
||||
# FIXME: let pkg.packageURI be stored as URI type rather than string
|
||||
pkg_uri = URI(pkg.packageURI)
|
||||
if pkg_uri.is_absolute_path():
|
||||
pkg_path = str(pkg.packageURI)
|
||||
else:
|
||||
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(pkg_uri.path()))
|
||||
|
||||
ctx.ui.debug(_("Package URI: %s") % pkg_path)
|
||||
|
||||
return Install(pkg_path, ignore_dep)
|
||||
else:
|
||||
raise Error(_("Package %s not found in any active repository.") % name)
|
||||
|
||||
def __init__(self, package_fname, ignore_dep = None):
|
||||
"initialize from a file name"
|
||||
super(Install, self).__init__(ignore_dep)
|
||||
self.package_fname = package_fname
|
||||
self.package = pisi.package.Package(package_fname)
|
||||
self.package.read()
|
||||
self.metadata = self.package.metadata
|
||||
self.files = self.package.files
|
||||
self.pkginfo = self.metadata.package
|
||||
|
||||
def install(self, ask_reinstall = True):
|
||||
"entry point"
|
||||
ctx.ui.status(_('Installing %s, version %s, release %s, build %s') %
|
||||
(self.pkginfo.name, self.pkginfo.version,
|
||||
self.pkginfo.release, self.pkginfo.build))
|
||||
ctx.ui.notify(pisi.ui.installing, package = self.pkginfo, files = self.files)
|
||||
|
||||
self.ask_reinstall = ask_reinstall
|
||||
self.check_requirements()
|
||||
self.check_relations()
|
||||
self.check_reinstall()
|
||||
self.extract_install()
|
||||
self.store_pisi_files()
|
||||
|
||||
self.register_comar()
|
||||
self.postinstall()
|
||||
|
||||
txn = ctx.dbenv.txn_begin()
|
||||
try:
|
||||
self.update_databases(txn)
|
||||
txn.commit()
|
||||
except db.DBError, e:
|
||||
txn.abort()
|
||||
raise e
|
||||
|
||||
self.update_environment()
|
||||
ctx.ui.status()
|
||||
if self.upgrade:
|
||||
event = pisi.ui.upgraded
|
||||
else:
|
||||
event = pisi.ui.installed
|
||||
ctx.ui.notify(event, package = self.pkginfo, files = self.files)
|
||||
|
||||
def check_requirements(self):
|
||||
"""check system requirements"""
|
||||
#TODO: IS THERE ENOUGH SPACE?
|
||||
# what to do if / is split into /usr, /var, etc.
|
||||
# check comar
|
||||
if self.metadata.package.providesComar and ctx.comar:
|
||||
import pisi.comariface as comariface
|
||||
com = comariface.make_com()
|
||||
|
||||
def check_relations(self):
|
||||
# check conflicts
|
||||
for pkg in self.metadata.package.conflicts:
|
||||
if ctx.installdb.is_installed(self.pkginfo):
|
||||
raise Error(_("Package conflicts %s") % pkg)
|
||||
|
||||
# check dependencies
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
if not self.pkginfo.installable():
|
||||
ctx.ui.error(_('Dependencies for %s not satisfied') %
|
||||
self.pkginfo.name)
|
||||
raise Error(_("Package not installable"))
|
||||
|
||||
# check if package is in database
|
||||
# If it is not, put it into 3rd party packagedb
|
||||
if not ctx.packagedb.has_package(self.pkginfo.name):
|
||||
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.thirdparty)
|
||||
|
||||
# check file conflicts
|
||||
for file in self.files.list:
|
||||
if ctx.filesdb.has_file(file.path):
|
||||
pkg, existing_file = ctx.filesdb.get_file(file.path)
|
||||
if pkg != self.pkginfo.name:
|
||||
#raise Error(_('Trying to overwrite an existing file: %s') % file.path)
|
||||
ctx.ui.warning(_('Trying to overwrite an existing file: %s') % file.path)
|
||||
|
||||
def check_reinstall(self):
|
||||
"check reinstall, confirm action, and schedule reinstall"
|
||||
|
||||
pkg = self.pkginfo
|
||||
|
||||
self.reinstall = False
|
||||
self.upgrade = False
|
||||
if ctx.installdb.is_installed(pkg.name): # is this a reinstallation?
|
||||
|
||||
#FIXME: consider REPOSITORY instead of DISTRIBUTION -- exa
|
||||
#ipackage = ctx.packagedb.get_package(pkg.name, pisi.itembyrepodb.installed)
|
||||
ipkg = ctx.installdb.get_info(pkg.name)
|
||||
repomismatch = ipkg.distribution != pkg.distribution
|
||||
|
||||
(iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name)
|
||||
|
||||
# determine if same version
|
||||
self.same_ver = False
|
||||
ignore_build = ctx.config.options and ctx.config.options.ignore_build_no
|
||||
if repomismatch or (not ibuild) or (not pkg.build) or ignore_build:
|
||||
# we don't look at builds to compare two package versions
|
||||
if Version(pkg.release) == Version(irelease):
|
||||
self.same_ver = True
|
||||
else:
|
||||
if pkg.build == ibuild:
|
||||
self.same_ver = True
|
||||
|
||||
if self.same_ver:
|
||||
if self.ask_reinstall:
|
||||
if not ctx.ui.confirm(_('Re-install same version package?')):
|
||||
raise Error(_('Package re-install declined'))
|
||||
else:
|
||||
upgrade = False
|
||||
# is this an upgrade?
|
||||
# determine and report the kind of upgrade: version, release, build
|
||||
if Version(pkg.version) > Version(iversion):
|
||||
ctx.ui.info(_('Upgrading to new upstream version'))
|
||||
upgrade = True
|
||||
elif Version(pkg.release) > Version(irelease):
|
||||
ctx.ui.info(_('Upgrading to new distribution release'))
|
||||
upgrade = True
|
||||
elif ((not ignore_build) and ibuild and pkg.build
|
||||
and pkg.build > ibuild):
|
||||
ctx.ui.info(_('Upgrading to new distribution build'))
|
||||
upgrade = True
|
||||
self.upgrade = upgrade
|
||||
|
||||
# is this a downgrade? confirm this action.
|
||||
if self.ask_reinstall and (not upgrade):
|
||||
if Version(pkg.version) < Version(iversion):
|
||||
x = _('Downgrade to old upstream version?')
|
||||
elif Version(pkg.release) < Version(irelease):
|
||||
x = _('Downgrade to old distribution release?')
|
||||
else:
|
||||
x = _('Downgrade to old distribution build?')
|
||||
if not ctx.ui.confirm(x):
|
||||
raise Error(_('Package downgrade declined'))
|
||||
|
||||
# schedule for reinstall
|
||||
self.old_files = ctx.installdb.files(pkg.name)
|
||||
self.old_path = ctx.installdb.pkg_dir(pkg.name, iversion, irelease)
|
||||
self.reinstall = True
|
||||
self.remove_old = Remove(pkg.name)
|
||||
self.remove_old.run_preremove()
|
||||
|
||||
def register_comar(self):
|
||||
self.config_later = False
|
||||
|
||||
if self.metadata.package.providesComar:
|
||||
if ctx.comar:
|
||||
import pisi.comariface as comariface
|
||||
self.register_comar_scripts()
|
||||
else:
|
||||
self.config_later = True # configure-pending will register scripts later
|
||||
|
||||
def postinstall(self):
|
||||
if 'System.Package' in [x.om for x in self.metadata.package.providesComar]:
|
||||
if ctx.comar:
|
||||
import pisi.comariface as comariface
|
||||
ctx.ui.notify(pisi.ui.configuring, package = self.pkginfo, files = self.files)
|
||||
try:
|
||||
comariface.run_postinstall(self.pkginfo.name)
|
||||
except Exception, e:
|
||||
if ctx.get_option('ignore_script_errors'):
|
||||
ctx.ui.warning(_('Ignoring script error: ') + unicode(e))
|
||||
else:
|
||||
raise e
|
||||
ctx.ui.notify(pisi.ui.configured, package = self.pkginfo, files = self.files)
|
||||
else:
|
||||
self.config_later = True
|
||||
|
||||
def extract_install(self):
|
||||
"unzip package in place"
|
||||
|
||||
ctx.ui.notify(pisi.ui.extracting, package = self.pkginfo, files = self.files)
|
||||
|
||||
if self.reinstall:
|
||||
# remove left over files
|
||||
new = set(map(lambda x: str(x.path), self.files.list))
|
||||
old = set(map(lambda x: str(x.path), self.old_files.list))
|
||||
leftover = old - new
|
||||
old_fileinfo = {}
|
||||
for fileinfo in self.old_files.list:
|
||||
old_fileinfo[str(fileinfo.path)] = fileinfo
|
||||
for path in old:
|
||||
if path in leftover or old_fileinfo[path].type == ctx.const.conf:
|
||||
Remove.remove_file( old_fileinfo[path] )
|
||||
|
||||
self.package.extract_install(ctx.config.dest_dir())
|
||||
|
||||
def store_pisi_files(self):
|
||||
"""put files.xml, metadata.xml, actions.py and COMAR scripts
|
||||
somewhere in the file system. We'll need these in future..."""
|
||||
|
||||
if self.reinstall:
|
||||
util.clean_dir(self.old_path)
|
||||
|
||||
ctx.ui.info(_('Storing %s, ') % ctx.const.files_xml)
|
||||
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
|
||||
|
||||
ctx.ui.info(_('Storing %s.') % ctx.const.metadata_xml)
|
||||
self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
|
||||
# comar prefix is added to the pkg_dir while extracting comar
|
||||
# script file. so we'll use pkg_dir as destination.
|
||||
ctx.ui.info(_('Storing %s') % fpath)
|
||||
self.package.extract_file(fpath, self.package.pkg_dir())
|
||||
|
||||
def register_comar_scripts(self):
|
||||
"register COMAR scripts"
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
scriptPath = os.path.join(self.package.comar_dir(),pcomar.script)
|
||||
import pisi.comariface
|
||||
pisi.comariface.register(pcomar, self.metadata.package.name,
|
||||
scriptPath)
|
||||
|
||||
def update_databases(self, txn):
|
||||
"update databases"
|
||||
if self.reinstall:
|
||||
self.remove_old.remove_db(txn)
|
||||
|
||||
# installdb
|
||||
ctx.installdb.install(self.metadata.package.name,
|
||||
self.metadata.package.version,
|
||||
self.metadata.package.release,
|
||||
self.metadata.package.build,
|
||||
self.metadata.package.distribution,
|
||||
config_later = self.config_later,
|
||||
txn = txn)
|
||||
|
||||
# filesdb
|
||||
ctx.filesdb.add_files(self.metadata.package.name, self.files, txn=txn)
|
||||
|
||||
# installed packages
|
||||
ctx.packagedb.add_package(self.pkginfo, pisi.itembyrepodb.installed, txn=txn)
|
||||
|
||||
def update_environment(self):
|
||||
# check if we have any shared objects or anything under
|
||||
# /etc/env.d
|
||||
shared = False
|
||||
for x in self.files.list:
|
||||
if x.path.endswith('.so') or x.path.startswith('/etc/env.d'):
|
||||
shared = True
|
||||
break
|
||||
if not ctx.get_option('bypass_ldconfig'):
|
||||
if shared:
|
||||
ctx.ui.info(_("Regenerating /etc/ld.so.cache..."))
|
||||
util.env_update()
|
||||
else:
|
||||
ctx.ui.warning(_("Bypassing ldconfig"))
|
||||
|
||||
def install_single(pkg, upgrade = False):
|
||||
"""install a single package from URI or ID"""
|
||||
url = URI(pkg)
|
||||
# Check if we are dealing with a remote file or a real path of
|
||||
# package filename. Otherwise we'll try installing a package from
|
||||
# the package repository.
|
||||
if url.is_remote_file() or os.path.exists(url.uri):
|
||||
install_single_file(pkg, upgrade)
|
||||
else:
|
||||
install_single_name(pkg, upgrade)
|
||||
|
||||
# FIXME: Here and elsewhere pkg_location must be a URI
|
||||
def install_single_file(pkg_location, upgrade = False):
|
||||
"""install a package file"""
|
||||
Install(pkg_location).install(not upgrade)
|
||||
|
||||
def install_single_name(name, upgrade = False):
|
||||
"""install a single package from ID"""
|
||||
install = Install.from_name(name)
|
||||
install.install(not upgrade)
|
||||
|
||||
class Remove(AtomicOperation):
|
||||
|
||||
def __init__(self, package_name, ignore_dep = None):
|
||||
super(Remove, self).__init__(ignore_dep)
|
||||
self.package_name = package_name
|
||||
self.package = ctx.packagedb.get_package(self.package_name, pisi.itembyrepodb.installed)
|
||||
try:
|
||||
self.files = ctx.installdb.files(self.package_name)
|
||||
except pisi.Error, e:
|
||||
# for some reason file was deleted, we still allow removes!
|
||||
ctx.ui.error(unicode(e))
|
||||
ctx.ui.warning(_('File list could not be read for package %s, continuing removal.') % package_name)
|
||||
self.files = Files()
|
||||
|
||||
def run(self):
|
||||
"""Remove a single package"""
|
||||
|
||||
ctx.ui.status(_('Removing package %s') % self.package_name)
|
||||
ctx.ui.notify(pisi.ui.removing, package = self.package, files = self.files)
|
||||
if not ctx.installdb.is_installed(self.package_name):
|
||||
raise Exception(_('Trying to remove nonexistent package ')
|
||||
+ self.package_name)
|
||||
|
||||
self.check_dependencies()
|
||||
|
||||
self.run_preremove()
|
||||
|
||||
for fileinfo in self.files.list:
|
||||
self.remove_file(fileinfo)
|
||||
|
||||
txn = ctx.dbenv.txn_begin()
|
||||
try:
|
||||
self.remove_db(txn)
|
||||
txn.commit()
|
||||
except db.DBError, e:
|
||||
txn.abort()
|
||||
raise e
|
||||
|
||||
self.remove_pisi_files()
|
||||
ctx.ui.status()
|
||||
ctx.ui.notify(pisi.ui.removed, package = self.package, files = self.files)
|
||||
|
||||
def check_dependencies(self):
|
||||
#we only have to check the dependencies to ensure the
|
||||
#system will be consistent after this removal
|
||||
pass
|
||||
# is there any package who depends on this package?
|
||||
|
||||
def remove_file(fileinfo):
|
||||
fpath = pisi.util.join_path(ctx.config.dest_dir(), fileinfo.path)
|
||||
# TODO: We have to store configuration files for futher
|
||||
# usage. Currently we'are doing it like rpm does, saving
|
||||
# with a prefix and leaving the user to edit it. In the future
|
||||
# we'll have a plan for these configuration files.
|
||||
if fileinfo.permanent:
|
||||
# do not remove precious files :)
|
||||
# just write anything permanent="true" for instance
|
||||
return
|
||||
if fileinfo.type == ctx.const.conf:
|
||||
if os.path.isfile(fpath):
|
||||
os.rename(fpath, fpath + ".oldconfig")
|
||||
else:
|
||||
if os.path.isfile(fpath) or os.path.islink(fpath):
|
||||
os.unlink(fpath)
|
||||
elif os.path.isdir(fpath) and not os.listdir(fpath):
|
||||
os.rmdir(fpath)
|
||||
else:
|
||||
ctx.ui.warning(_('Not removing non-file, non-link %s') % fpath, True)
|
||||
return
|
||||
|
||||
# remove emptied directories
|
||||
dpath = os.path.dirname(fpath)
|
||||
while dpath != '/' and not os.listdir(dpath):
|
||||
os.rmdir(dpath)
|
||||
dpath = os.path.dirname(dpath)
|
||||
|
||||
remove_file = staticmethod(remove_file)
|
||||
|
||||
def run_preremove(self):
|
||||
if ctx.comar and self.package.providesComar:
|
||||
import pisi.comariface as comariface
|
||||
try:
|
||||
comariface.run_preremove(self.package_name)
|
||||
except Exception, e:
|
||||
if ctx.get_option('ignore_script_errors'):
|
||||
ctx.ui.warning(_('Ignoring script error: ') + unicode(e))
|
||||
else:
|
||||
raise e
|
||||
else:
|
||||
# TODO: store this somewhere
|
||||
pass
|
||||
|
||||
def remove_pisi_files(self):
|
||||
util.clean_dir(self.package.pkg_dir())
|
||||
|
||||
def remove_db(self, txn):
|
||||
ctx.installdb.remove(self.package_name, txn)
|
||||
ctx.filesdb.remove_files(self.files, txn)
|
||||
pisi.packagedb.remove_tracking_package(self.package_name, txn)
|
||||
|
||||
def remove_single(package_name):
|
||||
Remove(package_name).run()
|
||||
|
||||
def build(package, authinfo=None):
|
||||
# wrapper for build op
|
||||
import pisi.build
|
||||
return pisi.build.build(package, authinfo)
|
||||
|
||||
def virtual_install(metadata, files, txn):
|
||||
"""Recreate the package info for rebuilddb command"""
|
||||
# installdb
|
||||
ctx.installdb.install(metadata.package.name,
|
||||
metadata.package.version,
|
||||
metadata.package.release,
|
||||
metadata.package.build,
|
||||
metadata.package.distribution,
|
||||
rebuild=True,
|
||||
txn=txn)
|
||||
|
||||
# filesdb
|
||||
if files:
|
||||
ctx.filesdb.add_files(metadata.package.name, files, txn=txn)
|
||||
|
||||
# installed packages
|
||||
ctx.packagedb.add_package(metadata.package, pisi.itembyrepodb.installed, txn=txn)
|
||||
|
||||
def resurrect_package(package_fn, write_files, txn = None):
|
||||
"""Resurrect the package from xml files"""
|
||||
|
||||
from os.path import exists
|
||||
|
||||
metadata_xml = util.join_path(ctx.config.lib_dir(), 'package',
|
||||
package_fn, ctx.const.metadata_xml)
|
||||
if not exists(metadata_xml):
|
||||
raise Error, _("Metadata XML '%s' cannot be found") % metadata_xml
|
||||
|
||||
metadata = MetaData()
|
||||
metadata.read(metadata_xml)
|
||||
|
||||
errs = metadata.errors()
|
||||
if errs:
|
||||
util.Checks.print_errors(errs)
|
||||
raise Error, _("MetaData format wrong (%s)") % package_fn
|
||||
|
||||
ctx.ui.info(_('* Adding \'%s\' to db... ') % (metadata.package.name), noln=True)
|
||||
|
||||
if write_files:
|
||||
files_xml = util.join_path(ctx.config.lib_dir(), 'package',
|
||||
package_fn, ctx.const.files_xml)
|
||||
if not exists(files_xml):
|
||||
raise Error, _("Files XML '%s' cannot be found") % files_xml
|
||||
|
||||
files = Files()
|
||||
files.read(files_xml)
|
||||
if files.errors():
|
||||
raise Error, _("Invalid %s") % ctx.const.files_xml
|
||||
else:
|
||||
files = None
|
||||
|
||||
#import pisi.atomicoperations
|
||||
def f(t):
|
||||
pisi.atomicoperations.virtual_install(metadata, files, t)
|
||||
ctx.txn_proc(f, txn)
|
||||
|
||||
ctx.ui.info(_('OK.'))
|
||||
@@ -1,927 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris at pardus.org.tr>
|
||||
# A. Murat Eren <meren at pardus.org.tr>
|
||||
# Eray Ozkural <eray at pardus.org.tr>
|
||||
|
||||
"""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 not ctx.get_option('no_debug')):
|
||||
# don't throw collision error for these files.
|
||||
# we'll handle this in gen_files_xml..
|
||||
continue
|
||||
if util.subpath(pinfo.path, path.path):
|
||||
collisions.append(path.path)
|
||||
ctx.ui.error(_('Path %s belongs in multiple packages') %
|
||||
path.path)
|
||||
return collisions
|
||||
|
||||
|
||||
class Builder:
|
||||
"""Provides the package build and creation routines"""
|
||||
#FIXME: this class and every other class must use URLs as paths!
|
||||
|
||||
@staticmethod
|
||||
def from_name(name, authinfo = None):
|
||||
# download package and return an installer object
|
||||
# find package in repository
|
||||
sf, reponame = ctx.sourcedb.get_spec_repo(name)
|
||||
src = sf.source
|
||||
if src:
|
||||
|
||||
src_uri = URI(src.sourceURI)
|
||||
if src_uri.is_absolute_path():
|
||||
pkg_path = str(src_uri)
|
||||
else:
|
||||
repo = ctx.repodb.get_repo(reponame)
|
||||
src_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(src_uri.path()))
|
||||
|
||||
ctx.ui.debug(_("Source URI: %s") % src_path)
|
||||
|
||||
return Builder(src_path, authinfo)
|
||||
else:
|
||||
raise Error(_("Source %s not found in any active repository.") % name)
|
||||
|
||||
def __init__(self, specuri, authinfo = None):
|
||||
|
||||
# process args
|
||||
if not isinstance(specuri, URI):
|
||||
specuri = URI(specuri)
|
||||
if authinfo:
|
||||
specuri.set_auth_info(authinfo)
|
||||
|
||||
self.authinfo = authinfo
|
||||
|
||||
# read spec file, we'll need it :)
|
||||
self.set_spec_file(specuri)
|
||||
|
||||
if specuri.is_remote_file():
|
||||
#make local here and fuck up
|
||||
self.specdir = self.fetch_files()
|
||||
else:
|
||||
self.specdir = dirname(self.specuri.get_uri())
|
||||
|
||||
self.sourceArchive = SourceArchive(self.spec, self.pkg_work_dir())
|
||||
|
||||
self.set_environment_vars()
|
||||
|
||||
self.actionLocals = None
|
||||
self.actionGlobals = None
|
||||
self.srcDir = None
|
||||
|
||||
def set_spec_file(self, specuri):
|
||||
if not specuri.is_remote_file():
|
||||
specuri = URI(os.path.realpath(specuri.get_uri())) # FIXME: doesn't work for file://
|
||||
self.specuri = specuri
|
||||
spec = SpecFile()
|
||||
spec.read(specuri, ctx.config.tmp_dir())
|
||||
self.spec = spec
|
||||
|
||||
# directory accessor functions
|
||||
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def pkg_dir(self):
|
||||
"package build directory"
|
||||
packageDir = self.spec.source.name + '-' + \
|
||||
self.spec.source.version + '-' + self.spec.source.release
|
||||
return util.join_path(ctx.config.dest_dir(), ctx.config.values.dirs.tmp_dir,
|
||||
packageDir)
|
||||
|
||||
def pkg_work_dir(self):
|
||||
return self.pkg_dir() + ctx.const.work_dir_suffix
|
||||
|
||||
def pkg_debug_dir(self):
|
||||
return self.pkg_dir() + ctx.const.debug_dir_suffix
|
||||
|
||||
def pkg_install_dir(self):
|
||||
return self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
def set_state(self, state):
|
||||
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
|
||||
open(stateFile, "w").write(state)
|
||||
|
||||
def get_state(self):
|
||||
stateFile = util.join_path(self.pkg_work_dir(), "pisiBuildState")
|
||||
if not os.path.exists(stateFile): # no state
|
||||
return None
|
||||
return open(stateFile, "r").read()
|
||||
|
||||
def build(self):
|
||||
"""Build the package in one shot."""
|
||||
|
||||
ctx.ui.status(_("Building PISI source package: %s") % self.spec.source.name)
|
||||
|
||||
self.compile_action_script()
|
||||
|
||||
# check if all patch files exists, if there are missing no need to unpack!
|
||||
self.patch_exists()
|
||||
|
||||
self.check_build_dependencies()
|
||||
self.fetch_component()
|
||||
self.fetch_source_archive()
|
||||
self.unpack_source_archive()
|
||||
|
||||
# apply the patches and prepare a source directory for build.
|
||||
self.apply_patches()
|
||||
|
||||
self.run_setup_action()
|
||||
self.run_build_action()
|
||||
self.run_install_action()
|
||||
|
||||
# after all, we are ready to build/prepare the packages
|
||||
return self.build_packages()
|
||||
|
||||
def set_environment_vars(self):
|
||||
"""Sets the environment variables for actions API to use"""
|
||||
|
||||
# Each time a builder is created we must reset
|
||||
# environment. See bug #2575
|
||||
pisi.actionsapi.variables.initVariables()
|
||||
|
||||
env = {
|
||||
"PKG_DIR": self.pkg_dir(),
|
||||
"WORK_DIR": self.pkg_work_dir(),
|
||||
"INSTALL_DIR": self.pkg_install_dir(),
|
||||
"SRC_NAME": self.spec.source.name,
|
||||
"SRC_VERSION": self.spec.source.version,
|
||||
"SRC_RELEASE": self.spec.source.release
|
||||
}
|
||||
os.environ.update(env)
|
||||
|
||||
# First check icecream, if not found use ccache, no need to use both
|
||||
# together (according to kde-wiki it cause performance loss)
|
||||
if ctx.config.values.general.buildhelper == "icecream":
|
||||
if os.path.exists("/opt/icecream/bin/gcc"):
|
||||
# Add icecream directory for support distributed compiling :)
|
||||
os.environ["PATH"] = "/opt/icecream/bin/:%s" % os.environ["PATH"]
|
||||
ctx.ui.info(_("IceCream detected. Make sure your daemon is up and running..."))
|
||||
elif ctx.config.values.general.buildhelper == "ccache":
|
||||
if os.path.exists("/usr/lib/ccache/bin/gcc"):
|
||||
# Add ccache directory for support Compiler Cache :)
|
||||
os.environ["PATH"] = "/usr/lib/ccache/bin/:%s" % os.environ["PATH"]
|
||||
ctx.ui.info(_("CCache detected..."))
|
||||
|
||||
def fetch_files(self):
|
||||
self.specdiruri = dirname(self.specuri.get_uri())
|
||||
pkgname = basename(self.specdiruri)
|
||||
self.destdir = join(ctx.config.tmp_dir(), pkgname)
|
||||
#self.location = dirname(self.url.uri)
|
||||
|
||||
self.fetch_actionsfile()
|
||||
self.fetch_patches()
|
||||
self.fetch_comarfiles()
|
||||
self.fetch_additionalFiles()
|
||||
|
||||
return self.destdir
|
||||
|
||||
def fetch_actionsfile(self):
|
||||
actionsuri = join(self.specdiruri, ctx.const.actions_file)
|
||||
self.download(actionsuri, self.destdir)
|
||||
|
||||
def fetch_patches(self):
|
||||
spec = self.spec
|
||||
for patch in spec.source.patches:
|
||||
file_name = basename(patch.filename)
|
||||
dir_name = dirname(patch.filename)
|
||||
patchuri = join(self.specdiruri,
|
||||
ctx.const.files_dir, dir_name, file_name)
|
||||
self.download(patchuri, join(self.destdir, ctx.const.files_dir, dir_name))
|
||||
|
||||
def fetch_comarfiles(self):
|
||||
spec = self.spec
|
||||
for package in spec.packages:
|
||||
for pcomar in package.providesComar:
|
||||
comaruri = join(self.specdiruri,
|
||||
ctx.const.comar_dir, pcomar.script)
|
||||
self.download(comaruri, join(self.destdir, ctx.const.comar_dir))
|
||||
|
||||
def fetch_additionalFiles(self):
|
||||
spec = self.spec
|
||||
for pkg in spec.packages:
|
||||
for afile in pkg.additionalFiles:
|
||||
file_name = basename(afile.filename)
|
||||
dir_name = dirname(afile.filename)
|
||||
afileuri = join(self.specdiruri,
|
||||
ctx.const.files_dir, dir_name, file_name)
|
||||
self.download(afileuri, join(self.destdir, ctx.const.files_dir, dir_name))
|
||||
|
||||
def download(self, uri, transferdir):
|
||||
# fix auth info and download
|
||||
uri = File.make_uri(uri)
|
||||
if self.authinfo:
|
||||
uri.set_auth_info(self.authinfo)
|
||||
File.download(uri, transferdir)
|
||||
|
||||
def fetch_component(self):
|
||||
if not self.spec.source.partOf:
|
||||
ctx.ui.warning(_('PartOf tag not defined, looking for component'))
|
||||
diruri = parenturi(self.specuri.get_uri())
|
||||
parentdir = parenturi(diruri)
|
||||
url = util.join_path(parentdir, 'component.xml')
|
||||
progress = ctx.ui.Progress
|
||||
if URI(url).is_remote_file():
|
||||
fetch_url(url, self.pkg_work_dir(), progress)
|
||||
path = util.join_path(self.pkg_work_dir(), 'component.xml')
|
||||
else:
|
||||
if not os.path.exists(url):
|
||||
raise Exception(_('Cannot find component.xml in upper directory'))
|
||||
path = url
|
||||
comp = component.Component()
|
||||
comp.read(path)
|
||||
ctx.ui.info(_('Source is part of %s component') % comp.name)
|
||||
self.spec.source.partOf = comp.name
|
||||
self.spec.override_tags()
|
||||
|
||||
def fetch_source_archive(self):
|
||||
ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archive.uri)
|
||||
self.sourceArchive.fetch()
|
||||
ctx.ui.info(_("Source archive is stored: %s/%s")
|
||||
%(ctx.config.archives_dir(), self.spec.source.archive.name))
|
||||
|
||||
def unpack_source_archive(self):
|
||||
ctx.ui.info(_("Unpacking archive..."), noln = True)
|
||||
self.sourceArchive.unpack()
|
||||
ctx.ui.info(_(" unpacked (%s)") % self.pkg_work_dir())
|
||||
self.set_state("unpack")
|
||||
|
||||
def run_setup_action(self):
|
||||
# Run configure, build and install phase
|
||||
ctx.ui.action(_("Setting up source..."))
|
||||
self.run_action_function(ctx.const.setup_func)
|
||||
self.set_state("setupaction")
|
||||
|
||||
def run_build_action(self):
|
||||
ctx.ui.action(_("Building source..."))
|
||||
self.run_action_function(ctx.const.build_func)
|
||||
self.set_state("buildaction")
|
||||
|
||||
def run_install_action(self):
|
||||
ctx.ui.action(_("Installing..."))
|
||||
|
||||
# Before install make sure install_dir is clean
|
||||
if os.path.exists(self.pkg_install_dir()):
|
||||
util.clean_dir(self.pkg_install_dir())
|
||||
|
||||
# install function is mandatory!
|
||||
self.run_action_function(ctx.const.install_func, True)
|
||||
self.set_state("installaction")
|
||||
|
||||
def get_abandoned_files(self):
|
||||
# return the files those are not collected from the install dir
|
||||
|
||||
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
abandoned_files = []
|
||||
all_paths_in_packages = []
|
||||
|
||||
|
||||
for package in self.spec.packages:
|
||||
for path in package.files:
|
||||
map(lambda p: all_paths_in_packages.append(p), [p for p in glob.glob(install_dir + path.path)])
|
||||
|
||||
for root, dirs, files in os.walk(install_dir):
|
||||
for file_ in files:
|
||||
already_in_package = False
|
||||
fpath = util.join_path(root, file_)
|
||||
for path in all_paths_in_packages:
|
||||
if not fpath.find(path):
|
||||
already_in_package = True
|
||||
if not already_in_package:
|
||||
abandoned_files.append(fpath)
|
||||
|
||||
return abandoned_files
|
||||
|
||||
|
||||
def compile_action_script(self):
|
||||
"""Compiles actions.py and sets the actionLocals and actionGlobals"""
|
||||
scriptfile = util.join_path(self.specdir, ctx.const.actions_file)
|
||||
try:
|
||||
localSymbols = globalSymbols = {}
|
||||
buf = open(scriptfile).read()
|
||||
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
|
||||
except IOError, e:
|
||||
raise Error(_("Unable to read Action Script (%s): %s") %(scriptfile,e))
|
||||
except SyntaxError, e:
|
||||
raise Error(_("SyntaxError in Action Script (%s): %s") %(scriptfile,e))
|
||||
|
||||
self.actionLocals = localSymbols
|
||||
self.actionGlobals = globalSymbols
|
||||
self.srcDir = self.pkg_src_dir()
|
||||
|
||||
def pkg_src_dir(self):
|
||||
"""Returns the real path of WorkDir for an unpacked archive."""
|
||||
try:
|
||||
workdir = self.actionGlobals['WorkDir']
|
||||
except KeyError:
|
||||
workdir = self.spec.source.name + "-" + self.spec.source.version
|
||||
|
||||
return util.join_path(self.pkg_work_dir(), workdir)
|
||||
|
||||
def run_action_function(self, func, mandatory=False):
|
||||
"""Calls the corresponding function in actions.py.
|
||||
|
||||
If mandatory parameter is True, and function is not present in
|
||||
actionLocals pisi.build.Error will be raised."""
|
||||
# we'll need our working directory after actionscript
|
||||
# finished its work in the archive source directory.
|
||||
curDir = os.getcwd()
|
||||
os.chdir(self.srcDir)
|
||||
|
||||
|
||||
if func in self.actionLocals:
|
||||
self.actionLocals[func]()
|
||||
else:
|
||||
if mandatory:
|
||||
Error, _("unable to call function from actions: %s") %func
|
||||
|
||||
os.chdir(curDir)
|
||||
|
||||
def check_build_dependencies(self):
|
||||
"""check and try to install build dependencies, otherwise fail."""
|
||||
|
||||
build_deps = self.spec.source.buildDependencies
|
||||
|
||||
if not ctx.get_option('bypass_safety'):
|
||||
if ctx.componentdb.has_component('system.devel'):
|
||||
build_deps_names = set([x.package for x in build_deps])
|
||||
devel_deps_names = set(ctx.componentdb.get_component('system.devel').packages)
|
||||
extra_names = devel_deps_names - build_deps_names
|
||||
extra_names = filter(lambda x: not ctx.installdb.is_installed(x), extra_names)
|
||||
if extra_names:
|
||||
ctx.ui.warning(_('Safety switch: following extra packages in system.devel will be installed: ') +
|
||||
util.strlist(extra_names))
|
||||
extra_deps = [dependency.Dependency(package = x) for x in extra_names]
|
||||
build_deps.extend(extra_deps)
|
||||
else:
|
||||
ctx.ui.warning(_('Safety switch: system.devel is already installed'))
|
||||
else:
|
||||
ctx.ui.warning(_('Safety switch: the component system.devel cannot be found'))
|
||||
|
||||
# find out the build dependencies that are not satisfied...
|
||||
dep_unsatis = []
|
||||
for dep in build_deps:
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
dep_unsatis.append(dep)
|
||||
|
||||
if dep_unsatis:
|
||||
ctx.ui.info(_("Unsatisfied Build Dependencies:") + ' '
|
||||
+ util.strlist([str(x) for x in dep_unsatis]) )
|
||||
|
||||
def fail():
|
||||
raise Error(_('Cannot build package due to unsatisfied build dependencies'))
|
||||
|
||||
if ctx.config.get_option('no_install'):
|
||||
fail()
|
||||
|
||||
if not ctx.config.get_option('ignore_dependency'):
|
||||
for dep in dep_unsatis:
|
||||
if not dependency.repo_satisfies_dep(dep):
|
||||
raise Error(_('Build dependency %s cannot be satisfied') % str(dep))
|
||||
if ctx.ui.confirm(
|
||||
_('Do you want to install the unsatisfied build dependencies')):
|
||||
ctx.ui.info(_('Installing build dependencies.'))
|
||||
operations.install([dep.package for dep in dep_unsatis])
|
||||
else:
|
||||
fail()
|
||||
else:
|
||||
ctx.ui.warning(_('Ignoring build dependencies.'))
|
||||
|
||||
def patch_exists(self):
|
||||
"""check existence of patch files declared in PSPEC"""
|
||||
|
||||
files_dir = os.path.abspath(util.join_path(self.specdir,
|
||||
ctx.const.files_dir))
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = util.join_path(files_dir, patch.filename)
|
||||
if not os.access(patchFile, os.F_OK):
|
||||
raise Error(_("Patch file is missing: %s\n") % patch.filename)
|
||||
|
||||
def apply_patches(self):
|
||||
files_dir = os.path.abspath(util.join_path(self.specdir,
|
||||
ctx.const.files_dir))
|
||||
|
||||
for patch in self.spec.source.patches:
|
||||
patchFile = util.join_path(files_dir, patch.filename)
|
||||
if patch.compressionType:
|
||||
patchFile = util.uncompress(patchFile,
|
||||
compressType=patch.compressionType,
|
||||
targetDir=ctx.config.tmp_dir())
|
||||
|
||||
ctx.ui.action(_("* Applying patch: %s") % patch.filename)
|
||||
util.do_patch(self.srcDir, patchFile, level=patch.level, target=patch.target)
|
||||
|
||||
def generate_static_package_object(self):
|
||||
ar_files = []
|
||||
for root, dirs, files in os.walk(self.pkg_install_dir()):
|
||||
for f in files:
|
||||
if f.endswith(ctx.const.ar_file_suffix) and util.is_ar_file(util.join_path(root, f)):
|
||||
ar_files.append(util.join_path(root, f))
|
||||
|
||||
if not len(ar_files):
|
||||
return None
|
||||
|
||||
static_package_obj = pisi.specfile.Package()
|
||||
static_package_obj.name = self.spec.source.name + ctx.const.static_name_suffix
|
||||
# FIXME: find a better way to deal with the summary and description constants.
|
||||
static_package_obj.summary['en'] = u'Ar files for %s' % (self.spec.source.name)
|
||||
static_package_obj.description['en'] = u'Ar files for %s' % (self.spec.source.name)
|
||||
static_package_obj.partOf = self.spec.source.partOf
|
||||
for f in ar_files:
|
||||
static_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_install_dir()):], fileType = "library"))
|
||||
|
||||
# append all generated packages to dependencies
|
||||
for p in self.spec.packages:
|
||||
static_package_obj.packageDependencies.append(
|
||||
pisi.dependency.Dependency(package = p.name))
|
||||
|
||||
return static_package_obj
|
||||
|
||||
def generate_debug_package_object(self):
|
||||
debug_files = []
|
||||
for root, dirs, files in os.walk(self.pkg_debug_dir()):
|
||||
for f in files:
|
||||
if f.endswith(ctx.const.debug_file_suffix):
|
||||
debug_files.append(util.join_path(root, f))
|
||||
|
||||
if not len(debug_files):
|
||||
return None
|
||||
|
||||
static_package_obj = pisi.specfile.Package()
|
||||
static_package_obj.debug_package = True
|
||||
static_package_obj.name = self.spec.source.name + ctx.const.debug_name_suffix
|
||||
# FIXME: find a better way to deal with the summary and description constants.
|
||||
static_package_obj.summary['en'] = u'Debug files for %s' % (self.spec.source.name)
|
||||
static_package_obj.description['en'] = u'Debug files for %s' % (self.spec.source.name)
|
||||
static_package_obj.partOf = self.spec.source.partOf
|
||||
for f in debug_files:
|
||||
static_package_obj.files.append(pisi.specfile.Path(path = f[len(self.pkg_debug_dir()):], fileType = "debug"))
|
||||
|
||||
# append all generated packages to dependencies
|
||||
for p in self.spec.packages:
|
||||
static_package_obj.packageDependencies.append(
|
||||
pisi.dependency.Dependency(package = p.name))
|
||||
|
||||
return static_package_obj
|
||||
|
||||
def strip_install_dir(self):
|
||||
"""strip install directory"""
|
||||
ctx.ui.action(_("Stripping files.."))
|
||||
install_dir = self.pkg_install_dir()
|
||||
try:
|
||||
nostrip = self.actionGlobals['NoStrip']
|
||||
util.strip_directory(install_dir, nostrip)
|
||||
except KeyError:
|
||||
util.strip_directory(install_dir)
|
||||
|
||||
def gen_metadata_xml(self, package):
|
||||
"""Generate the metadata.xml file for build source.
|
||||
|
||||
metadata.xml is composed of the information from specfile plus
|
||||
some additional information."""
|
||||
metadata = MetaData()
|
||||
metadata.from_spec(self.spec.source, package)
|
||||
|
||||
metadata.package.distribution = ctx.config.values.general.distribution
|
||||
metadata.package.distributionRelease = ctx.config.values.general.distribution_release
|
||||
metadata.package.architecture = "Any"
|
||||
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
|
||||
|
||||
old_build = None
|
||||
|
||||
# build no
|
||||
if ctx.config.options.ignore_build_no:
|
||||
metadata.package.build = None
|
||||
ctx.ui.warning(_('Build number is not available due to --ignore-build'))
|
||||
elif (not ctx.config.values.build.buildno):
|
||||
metadata.package.build = None
|
||||
ctx.ui.warning(_('Build number is not available. For repo builds you must enable buildno in pisi.conf.'))
|
||||
else:
|
||||
metadata.package.build, old_build = self.calc_build_no(metadata.package.name)
|
||||
|
||||
metadata_xml_path = util.join_path(self.pkg_dir(), ctx.const.metadata_xml)
|
||||
metadata.write(metadata_xml_path)
|
||||
self.metadata = metadata
|
||||
return metadata.package.build, old_build
|
||||
|
||||
def gen_files_xml(self, package):
|
||||
"""Generates files.xml using the path definitions in specfile and
|
||||
the files produced by the build system."""
|
||||
files = Files()
|
||||
|
||||
if package.debug_package:
|
||||
install_dir = self.pkg_debug_dir()
|
||||
else:
|
||||
install_dir = self.pkg_install_dir()
|
||||
|
||||
# FIXME: We need to expand globs before trying to calculate hashes
|
||||
# Not on the fly like now.
|
||||
|
||||
# we'll exclude collisions in get_file_hashes. Having a
|
||||
# collisions list is not wrong, we must just handle it :).
|
||||
collisions = check_path_collision(package, self.spec.packages)
|
||||
# FIXME: material collisions after expanding globs could be
|
||||
# reported as errors
|
||||
|
||||
d = {}
|
||||
def add_path(path):
|
||||
# add the files under material path
|
||||
for fpath, fhash in util.get_file_hashes(path, collisions, install_dir):
|
||||
if ctx.get_option('create_static') \
|
||||
and fpath.endswith(ctx.const.ar_file_suffix) \
|
||||
and not package.name.endswith(ctx.const.static_name_suffix) \
|
||||
and util.is_ar_file(fpath):
|
||||
# if this is an ar file, and this package is not a static package,
|
||||
# don't include this file into the package.
|
||||
continue
|
||||
frpath = util.removepathprefix(install_dir, fpath) # relative path
|
||||
ftype, permanent = get_file_type(frpath, package.files, install_dir)
|
||||
fsize = util.dir_size(fpath)
|
||||
d[frpath] = FileInfo(path=frpath, type=ftype, permanent=permanent,
|
||||
size=fsize, hash=fhash)
|
||||
|
||||
for pinfo in package.files:
|
||||
wildcard_path = util.join_path(install_dir, pinfo.path)
|
||||
for path in glob.glob(wildcard_path):
|
||||
add_path(path)
|
||||
|
||||
for (p, fileinfo) in d.iteritems():
|
||||
files.append(fileinfo)
|
||||
|
||||
files_xml_path = util.join_path(self.pkg_dir(), ctx.const.files_xml)
|
||||
files.write(files_xml_path)
|
||||
self.files = files
|
||||
|
||||
def calc_build_no(self, package_name):
|
||||
"""Calculate build number"""
|
||||
# find previous build in packages dir
|
||||
found = []
|
||||
def locate_old_package(old_package_fn):
|
||||
if util.is_package_name(os.path.basename(old_package_fn), package_name):
|
||||
try:
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
ctx.ui.info(_('(found old version %s)') % old_package_fn)
|
||||
if str(old_pkg.metadata.package.name) != package_name:
|
||||
ctx.ui.warning(_('Skipping %s with wrong pkg name ') %
|
||||
old_package_fn)
|
||||
return
|
||||
old_build = old_pkg.metadata.package.build
|
||||
found.append( (old_package_fn, old_build) )
|
||||
except:
|
||||
ctx.ui.warning('Package file %s may be corrupt. Skipping.' % old_package_fn)
|
||||
|
||||
for root, dirs, files in os.walk(ctx.config.packages_dir()):
|
||||
for file in files:
|
||||
locate_old_package(join(root,file))
|
||||
|
||||
outdir=ctx.get_option('output_dir')
|
||||
if not outdir:
|
||||
outdir = '.'
|
||||
for file in [join(outdir,entry) for entry in os.listdir(outdir)]:
|
||||
if os.path.isfile(file):
|
||||
locate_old_package(file)
|
||||
|
||||
if not found:
|
||||
return (1, None)
|
||||
ctx.ui.warning(_('(no previous build found, setting build no to 1.)'))
|
||||
else:
|
||||
a = filter(lambda (x,y): y != None, found)
|
||||
ctx.ui.debug(str(a))
|
||||
if a:
|
||||
# sort in order of increasing build number
|
||||
a.sort(lambda x,y : cmp(x[1],y[1]))
|
||||
old_package_fn = a[-1][0] # get the last one
|
||||
old_build = a[-1][1]
|
||||
|
||||
# compare old files.xml with the new one..
|
||||
old_pkg = Package(old_package_fn, 'r')
|
||||
old_pkg.read(util.join_path(ctx.config.tmp_dir(), 'oldpkg'))
|
||||
|
||||
# FIXME: TAKE INTO ACCOUNT MINOR CHANGES IN METADATA
|
||||
changed = False
|
||||
fnew = self.files.list
|
||||
fold = old_pkg.files.list
|
||||
fold.sort(lambda x,y : cmp(x.path,y.path))
|
||||
fnew.sort(lambda x,y : cmp(x.path,y.path))
|
||||
|
||||
if len(fnew) != len(fold):
|
||||
changed = True
|
||||
else:
|
||||
for i in range(len(fold)):
|
||||
fo = fold.pop(0)
|
||||
fn = fnew.pop(0)
|
||||
if fo.path != fn.path:
|
||||
changed = True
|
||||
break
|
||||
else:
|
||||
if fo.hash != fn.hash:
|
||||
changed = True
|
||||
break
|
||||
else: # no old build had a build number
|
||||
old_build = None
|
||||
|
||||
ctx.ui.debug('old build number: %s' % old_build)
|
||||
|
||||
# set build number
|
||||
if old_build is None:
|
||||
ctx.ui.warning(_('(old package lacks a build no, setting build no to 1.)'))
|
||||
return (1, None)
|
||||
elif changed:
|
||||
ctx.ui.info(_('There are changes, incrementing build no to %d') % (old_build + 1))
|
||||
return (old_build + 1, old_build)
|
||||
else:
|
||||
ctx.ui.info(_('There is no change from previous build %d') % old_build)
|
||||
return (old_build, old_build)
|
||||
|
||||
def build_packages(self):
|
||||
"""Build each package defined in PSPEC file. After this process there
|
||||
will be .pisi files hanging around, AS INTENDED ;)"""
|
||||
|
||||
self.fetch_component() # bug 856
|
||||
|
||||
# Strip install directory before building .pisi packages.
|
||||
self.strip_install_dir()
|
||||
|
||||
if ctx.get_option('create_static'):
|
||||
obj = self.generate_static_package_object()
|
||||
if obj:
|
||||
self.spec.packages.append(obj)
|
||||
|
||||
if not ctx.get_option('no_debug'):
|
||||
obj = self.generate_debug_package_object()
|
||||
if obj:
|
||||
self.spec.packages.append(obj)
|
||||
|
||||
package_names = []
|
||||
old_package_names = []
|
||||
|
||||
for package in self.spec.packages:
|
||||
old_package_name = None
|
||||
# store additional files
|
||||
c = os.getcwd()
|
||||
os.chdir(self.specdir)
|
||||
install_dir = self.pkg_dir() + ctx.const.install_dir_suffix
|
||||
for afile in package.additionalFiles:
|
||||
src = os.path.join(ctx.const.files_dir, afile.filename)
|
||||
dest = os.path.join(install_dir + os.path.dirname(afile.target), os.path.basename(afile.target))
|
||||
util.copy_file(src, dest)
|
||||
if afile.permission:
|
||||
# mode is octal!
|
||||
os.chmod(dest, int(afile.permission, 8))
|
||||
|
||||
os.chdir(c)
|
||||
|
||||
ctx.ui.action(_("** Building package %s") % package.name);
|
||||
|
||||
ctx.ui.info(_("Generating %s,") % ctx.const.files_xml)
|
||||
self.gen_files_xml(package)
|
||||
|
||||
ctx.ui.info(_("Generating %s,") % ctx.const.metadata_xml)
|
||||
build_number, old_build_number = self.gen_metadata_xml(package)
|
||||
|
||||
name = util.package_name(package.name,
|
||||
self.spec.source.version,
|
||||
self.spec.source.release,
|
||||
self.metadata.package.build)
|
||||
|
||||
outdir = ctx.get_option('output_dir')
|
||||
if outdir:
|
||||
name = pisi.util.join_path(outdir, name)
|
||||
|
||||
ctx.ui.info(_("Creating PISI package %s.") % name)
|
||||
|
||||
# somebody explain to me why this is done here -- exa
|
||||
if old_build_number:
|
||||
old_package_name = util.package_name(package.name,
|
||||
self.spec.source.version,
|
||||
self.spec.source.release,
|
||||
old_build_number)
|
||||
pkg = Package(name, 'w')
|
||||
package_names.append(name)
|
||||
old_package_names.append(old_package_name)
|
||||
|
||||
# add comar files to package
|
||||
os.chdir(self.specdir)
|
||||
for pcomar in package.providesComar:
|
||||
fname = util.join_path(ctx.const.comar_dir,
|
||||
pcomar.script)
|
||||
pkg.add_to_package(fname)
|
||||
|
||||
# add xmls and files
|
||||
os.chdir(self.pkg_dir())
|
||||
|
||||
pkg.add_to_package(ctx.const.metadata_xml)
|
||||
pkg.add_to_package(ctx.const.files_xml)
|
||||
|
||||
# Now it is time to add files to the packages using newly
|
||||
# created files.xml
|
||||
files = Files()
|
||||
files.read(ctx.const.files_xml)
|
||||
|
||||
if ctx.get_option('package_format') == "1.1":
|
||||
tar = archive.ArchiveTar("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("install.tar.lzma")
|
||||
pkg.close()
|
||||
os.unlink("install.tar")
|
||||
os.unlink("install.tar.lzma")
|
||||
else:
|
||||
for finfo in files.list:
|
||||
orgname = arcname = join("install", finfo.path)
|
||||
if package.debug_package:
|
||||
orgname = join("debug", finfo.path)
|
||||
pkg.add_to_package(orgname, arcname)
|
||||
pkg.close()
|
||||
|
||||
os.chdir(c)
|
||||
self.set_state("buildpackages")
|
||||
ctx.ui.info(_("Done."))
|
||||
|
||||
#show the files those are not collected from the install dir
|
||||
if ctx.get_option('show_abandoned_files') or ctx.get_option('debug'):
|
||||
abandoned_files = self.get_abandoned_files()
|
||||
if abandoned_files:
|
||||
ctx.ui.warning(_('Abandoned files under the install dir (%s):') % (install_dir))
|
||||
for f in abandoned_files:
|
||||
ctx.ui.info(' - %s' % (f))
|
||||
else:
|
||||
ctx.ui.warning(_('All of the files under the install dir (%s) has been collected by package(s)')
|
||||
% (install_dir))
|
||||
|
||||
if ctx.config.values.general.autoclean is True:
|
||||
ctx.ui.info(_("Cleaning Build Directory..."))
|
||||
util.clean_dir(self.pkg_dir())
|
||||
else:
|
||||
ctx.ui.info(_("Keeping Build Directory"))
|
||||
|
||||
return package_names, old_package_names
|
||||
|
||||
|
||||
# build functions...
|
||||
|
||||
def build(pspec, authinfo=None):
|
||||
if pspec.endswith('.xml'):
|
||||
pb = Builder(pspec, authinfo)
|
||||
else:
|
||||
pb = Builder.from_name(pspec, authinfo)
|
||||
return pb.build()
|
||||
|
||||
order = {"none": 0,
|
||||
"unpack": 1,
|
||||
"setupaction": 2,
|
||||
"buildaction": 3,
|
||||
"installaction": 4,
|
||||
"buildpackages": 5}
|
||||
|
||||
def __buildState_unpack(pb):
|
||||
# unpack is the first state to run.
|
||||
pb.fetch_source_archive()
|
||||
pb.unpack_source_archive()
|
||||
pb.apply_patches()
|
||||
|
||||
def __buildState_setupaction(pb, last):
|
||||
|
||||
if order[last] < order["unpack"]:
|
||||
__buildState_unpack(pb)
|
||||
pb.run_setup_action()
|
||||
|
||||
def __buildState_buildaction(pb, last):
|
||||
|
||||
if order[last] < order["setupaction"]:
|
||||
__buildState_setupaction(pb, last)
|
||||
pb.run_build_action()
|
||||
|
||||
def __buildState_installaction(pb, last):
|
||||
|
||||
if order[last] < order["buildaction"]:
|
||||
__buildState_buildaction(pb, last)
|
||||
pb.run_install_action()
|
||||
|
||||
def __buildState_buildpackages(pb, last):
|
||||
|
||||
if order[last] < order["installaction"]:
|
||||
__buildState_installaction(pb, last)
|
||||
pb.build_packages()
|
||||
|
||||
def build_until(pspec, state, authinfo=None):
|
||||
if pspec.endswith('.xml'):
|
||||
pb = Builder(pspec, authinfo)
|
||||
else:
|
||||
pb = Builder.from_name(pspec, authinfo)
|
||||
|
||||
pb.compile_action_script()
|
||||
|
||||
last = pb.get_state()
|
||||
ctx.ui.info("Last state was %s"%last)
|
||||
|
||||
if not last: last = "none"
|
||||
|
||||
if state == "unpack":
|
||||
__buildState_unpack(pb)
|
||||
return
|
||||
|
||||
if state == "setup":
|
||||
__buildState_setupaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "build":
|
||||
__buildState_buildaction(pb, last)
|
||||
return
|
||||
|
||||
if state == "install":
|
||||
__buildState_installaction(pb, last)
|
||||
return
|
||||
|
||||
__buildState_buildpackages(pb, last)
|
||||
@@ -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,160 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import locale
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.ui import UI
|
||||
import pisi.ui as ui
|
||||
import pisi.util as util
|
||||
from pisi.cli.colors import colorize
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
|
||||
def printu(obj, err = False):
|
||||
if not isinstance(obj, unicode):
|
||||
obj = unicode(obj)
|
||||
if err:
|
||||
out = sys.stderr
|
||||
else:
|
||||
out = sys.stdout
|
||||
out.write(obj.encode('utf-8'))
|
||||
out.flush()
|
||||
|
||||
class CLI(UI):
|
||||
"Command Line Interface"
|
||||
|
||||
def __init__(self, show_debug = False, show_verbose = False):
|
||||
super(CLI, self).__init__(show_debug, show_verbose)
|
||||
|
||||
def close(self):
|
||||
util.xterm_title_reset()
|
||||
|
||||
def output(self, msg, err = False, verbose = False):
|
||||
if (verbose and self.show_verbose) or (not verbose):
|
||||
if type(msg)==type(unicode()):
|
||||
msg = msg.encode('utf-8')
|
||||
if err:
|
||||
out = sys.stderr
|
||||
else:
|
||||
out = sys.stdout
|
||||
out.write(msg)
|
||||
out.flush()
|
||||
|
||||
def info(self, msg, verbose = False, noln = False):
|
||||
# TODO: need to look at more kinds of info messages
|
||||
# let's cheat from KDE :)
|
||||
if noln:
|
||||
msgend = ''
|
||||
else:
|
||||
msgend = '\n'
|
||||
self.output( unicode(msg) + msgend, verbose=verbose)
|
||||
|
||||
def warning(self, msg, verbose = False):
|
||||
msg = unicode(msg)
|
||||
if ctx.log:
|
||||
ctx.log.warning(msg)
|
||||
if ctx.get_option('no_color'):
|
||||
self.output(_('Warning: ') + msg + '\n', err=True, verbose=verbose)
|
||||
else:
|
||||
self.output(colorize(msg + '\n', 'purple'), err=True, verbose=verbose)
|
||||
|
||||
def error(self, msg):
|
||||
msg = unicode(msg)
|
||||
if ctx.log:
|
||||
ctx.log.error(msg)
|
||||
if ctx.get_option('no_color'):
|
||||
self.output(_('Error: ') + msg + '\n', err=True)
|
||||
else:
|
||||
self.output(colorize(msg + '\n', 'red'), err=True)
|
||||
|
||||
def action(self, msg, verbose = False):
|
||||
#TODO: this seems quite redundant?
|
||||
msg = unicode(msg)
|
||||
if ctx.log:
|
||||
ctx.log.info(msg)
|
||||
self.output(colorize(msg + '\n', 'green'))
|
||||
|
||||
def choose(self, msg, opts):
|
||||
print msg
|
||||
for i in range(0,len(opts)):
|
||||
print i + 1, opts(i)
|
||||
while True:
|
||||
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
|
||||
try:
|
||||
opt = int(s)
|
||||
if 1 <= opt and opt <= len(opts):
|
||||
return opts(opt-1)
|
||||
except (Exception,e):
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
msg = unicode(msg)
|
||||
if ctx.config.options and ctx.config.options.yes_all:
|
||||
return True
|
||||
while True:
|
||||
import re
|
||||
yesexpr = re.compile(locale.nl_langinfo(locale.YESEXPR))
|
||||
|
||||
prompt = msg + colorize(_(' (yes/no)'), 'red')
|
||||
s = raw_input(prompt.encode('utf-8'))
|
||||
if yesexpr.search(s):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def display_progress(self, **ka):
|
||||
totalsize = '%.1f %s' % pisi.util.human_readable_size(ka['total_size'])
|
||||
out = '\r%-30.30s (%s)%3d%% %9.2f %s [%s]' % \
|
||||
(ka['filename'], totalsize, ka['percent'],
|
||||
ka['rate'], ka['symbol'], ka['eta'])
|
||||
self.output(out)
|
||||
if ka['percent'] == 100:
|
||||
self.output(colorize(_(' [complete]\n'), 'gray'))
|
||||
|
||||
def status(self, msg = None):
|
||||
if msg:
|
||||
msg = unicode(msg)
|
||||
self.output(colorize(msg + '\n', 'purple'))
|
||||
util.xterm_title(msg)
|
||||
|
||||
def notify(self, event, **keywords):
|
||||
if event == ui.installed:
|
||||
msg = _('Installed %s') % keywords['package'].name
|
||||
elif event == ui.removed:
|
||||
msg = _('Removed %s') % keywords['package'].name
|
||||
elif event == ui.upgraded:
|
||||
msg = _('Upgraded %s') % keywords['package'].name
|
||||
elif event == ui.configured:
|
||||
msg = _('Configured %s') % keywords['package'].name
|
||||
elif event == ui.extracting:
|
||||
msg = _('Extracting the files of %s') % keywords['package'].name
|
||||
else:
|
||||
msg = None
|
||||
if msg:
|
||||
self.output(colorize(msg + '\n', 'cyan'))
|
||||
if ctx.log:
|
||||
ctx.log.info(msg)
|
||||
@@ -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,112 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.cli
|
||||
from pisi.cli import printu
|
||||
from pisi.uri import URI
|
||||
from pisi.cli.commands import *
|
||||
|
||||
class ParserError(pisi.Exception):
|
||||
pass
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class PreParser(OptionParser):
|
||||
"""consumes any options, and finds arguments from command line"""
|
||||
|
||||
def __init__(self, version):
|
||||
OptionParser.__init__(self, usage=usage_text, version=version)
|
||||
|
||||
def error(self, msg):
|
||||
raise ParserError, msg
|
||||
|
||||
def parse_args(self, args=None):
|
||||
self.opts = []
|
||||
self.rargs = self._get_args(args)
|
||||
self._process_args()
|
||||
return (self.opts, self.args)
|
||||
|
||||
def _process_args(self):
|
||||
args = []
|
||||
rargs = self.rargs
|
||||
if not self.allow_interspersed_args:
|
||||
first_arg = False
|
||||
while rargs:
|
||||
arg = rargs[0]
|
||||
def option():
|
||||
if not self.allow_interspersed_args and first_arg:
|
||||
self.error(_('Options must precede non-option arguments'))
|
||||
arg = rargs[0]
|
||||
if arg.startswith('--'):
|
||||
self.opts.append(arg[2:])
|
||||
else:
|
||||
self.opts.append(arg[1:])
|
||||
del rargs[0]
|
||||
return
|
||||
# We handle bare "--" explicitly, and bare "-" is handled by the
|
||||
# standard arg handler since the short arg case ensures that the
|
||||
# len of the opt string is greater than 1.
|
||||
if arg == "--":
|
||||
del rargs[0]
|
||||
break
|
||||
elif arg[0:2] == "--":
|
||||
# process a single long option (possibly with value(s))
|
||||
option()
|
||||
elif arg[:1] == "-" and len(arg) > 1:
|
||||
# process a cluster of short options (possibly with
|
||||
# value(s) for the last one only)
|
||||
option()
|
||||
else: # then it must be an argument
|
||||
args.append(arg)
|
||||
del rargs[0]
|
||||
self.args = args
|
||||
|
||||
|
||||
class PisiCLI(object):
|
||||
|
||||
def __init__(self):
|
||||
# first construct a parser for common options
|
||||
# this is really dummy
|
||||
self.parser = PreParser(version="%prog " + pisi.__version__)
|
||||
|
||||
try:
|
||||
opts, args = self.parser.parse_args()
|
||||
if len(args)==0: # more explicit than using IndexError
|
||||
if 'version' in opts:
|
||||
self.parser.print_version()
|
||||
sys.exit(0)
|
||||
elif 'help' in opts or 'h' in opts:
|
||||
self.die()
|
||||
raise Error(_('No command given'))
|
||||
cmd_name = args[0]
|
||||
except ParserError:
|
||||
raise Error(_('Command line parsing error'))
|
||||
|
||||
self.command = Command.get_command(cmd_name)
|
||||
if not self.command:
|
||||
raise Error(_("Unrecognized command: %s") % cmd_name)
|
||||
|
||||
def die(self):
|
||||
printu('\n' + self.parser.format_help())
|
||||
sys.exit(1)
|
||||
|
||||
def run_command(self):
|
||||
self.command.run()
|
||||
@@ -1,129 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@pardus.org.tr>
|
||||
# Gurer Ozen <gurer@pardus.org.tr>
|
||||
|
||||
import comar
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
def make_com():
|
||||
try:
|
||||
import comar
|
||||
|
||||
if ctx.comar_sockname:
|
||||
comard = comar.Link(sockname=ctx.comar_sockname)
|
||||
else:
|
||||
comard = comar.Link()
|
||||
return comard
|
||||
except ImportError:
|
||||
raise Error(_("COMAR: comard not fully installed"))
|
||||
except comar.Error:
|
||||
raise Error(_("COMAR: comard not running or defunct"))
|
||||
|
||||
def register(pcomar, name, path):
|
||||
ctx.ui.info(_("Registering COMAR script %s") % pcomar.script)
|
||||
com = make_com()
|
||||
assert(com)
|
||||
com.register(pcomar.om, name, path)
|
||||
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
else:
|
||||
raise Error, _("COMAR.register ERROR!")
|
||||
|
||||
def wait_comar():
|
||||
import socket, time
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
timeout = 5
|
||||
while timeout > 0:
|
||||
try:
|
||||
if ctx.comar_sockname:
|
||||
sock.connect(ctx.comar_sockname)
|
||||
else:
|
||||
sock.connect("/var/run/comar.socket")
|
||||
return True
|
||||
except:
|
||||
timeout -= 0.2
|
||||
time.sleep(0.2)
|
||||
return False
|
||||
|
||||
def run_postinstall(package_name):
|
||||
"run postinstall scripts trough COMAR"
|
||||
|
||||
com = make_com()
|
||||
assert(com)
|
||||
ctx.ui.info(_("Running post-install script for %s") % package_name)
|
||||
com.call_package("System.Package.postInstall", package_name)
|
||||
while 1:
|
||||
try:
|
||||
reply = com.read_cmd()
|
||||
except:
|
||||
# Comar postInstall does a "service comar restart" which cuts
|
||||
# our precious communication link, so we waitsss
|
||||
if package_name == "comar":
|
||||
if not wait_comar():
|
||||
raise Error, _("Could not restart Comar")
|
||||
return
|
||||
else:
|
||||
raise Error, _("Comar daemon closed the connection")
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
elif reply[0] == com.NONE: # package has no postInstall script
|
||||
break
|
||||
elif reply[0] == com.FAIL:
|
||||
e = _("COMAR.call_package(System.Package.postInstall, %s) failed!: %s") % (
|
||||
package_name, reply[2])
|
||||
raise Error, e
|
||||
else:
|
||||
raise Error, _("COMAR.call_package ERROR: %d") % reply[0]
|
||||
|
||||
def run_preremove(package_name):
|
||||
|
||||
com = make_com()
|
||||
assert(com)
|
||||
|
||||
# First, call preRemove script!
|
||||
ctx.ui.info(_("Running pre-remove script for %s") % package_name)
|
||||
com.call_package("System.Package.preRemove", package_name)
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
elif reply[0] == com.NONE: # package has no preRemove script
|
||||
break
|
||||
elif reply[0] == com.FAIL:
|
||||
e = _("COMAR.call_package(System.Package.preRemove, %s) failed!: %s") % (
|
||||
package_name, reply[2])
|
||||
raise Error, e
|
||||
else:
|
||||
raise Error, _("COMAR.call_package ERROR: %d") % reply[0]
|
||||
|
||||
# and then, remove package's Comar Scripts...
|
||||
ctx.ui.info(_("Unregistering COMAR scripts for %s") % package_name)
|
||||
com.remove(package_name)
|
||||
while 1:
|
||||
reply = com.read_cmd()
|
||||
if reply[0] == com.RESULT:
|
||||
break
|
||||
elif reply[1] == com.ERROR:
|
||||
raise Error, "COMAR.remove failed!"
|
||||
@@ -1,182 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.pxml.xmlfile as xmlfile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.itembyrepodb import ItemByRepoDB
|
||||
import pisi.itembyrepodb as itembyrepodb
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
|
||||
class Distribution(xmlfile.XmlFile):
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_SourceName = [autoxml.Text, autoxml.mandatory] # name of distribution (source)
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Version = [autoxml.Text, autoxml.optional]
|
||||
t_Type = [autoxml.Text, autoxml.mandatory]
|
||||
t_Dependencies = [ [autoxml.Text], autoxml.optional, "Dependencies/Distribution"]
|
||||
|
||||
t_BinaryName = [autoxml.Text, autoxml.optional] # name of repository (binary distro)
|
||||
t_Architecture = [autoxml.Text, autoxml.optional] # architecture identifier
|
||||
|
||||
|
||||
class Component(xmlfile.XmlFile):
|
||||
"representation for component declarations"
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory] # fully qualified name
|
||||
|
||||
# component name in other languages, for instance in Turkish
|
||||
# LocalName for system.base could be sistem.taban or "Taban Sistem",
|
||||
# this could be useful for GUIs
|
||||
|
||||
t_LocalName = [autoxml.LocalText, autoxml.mandatory]
|
||||
|
||||
# Information about the component
|
||||
t_Summary = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
#t_Icon = [autoxml.Binary, autoxml.mandatory]
|
||||
|
||||
# Dependencies to other components
|
||||
t_Dependencies = [ [autoxml.String], autoxml.optional, "Dependencies/Component"]
|
||||
|
||||
# the parts of this component.
|
||||
# to be filled by the component database, thus it is optional.
|
||||
t_Packages = [ [autoxml.String], autoxml.optional, "Parts/Package"]
|
||||
|
||||
t_Sources = [ [autoxml.String], autoxml.optional, "Parts/Source"]
|
||||
|
||||
# TODO: this is probably not necessary since we use fully qualified
|
||||
# module names (like in Java)
|
||||
#t_PartOf = [autoxml.Text, autoxml.mandatory]
|
||||
|
||||
#FIXME: recursive declarations do not work!
|
||||
#class ComponentTree(xmlfile.XmlFile):
|
||||
# "index representation for the component structure"
|
||||
#
|
||||
# __metaclass__ = autoxml.autoxml
|
||||
#
|
||||
# tag = "Component"
|
||||
#
|
||||
# t_Name = [autoxml.Text, autoxml.mandatory] # fully qualified name
|
||||
# #t_Icon = [autoxml.Binary, autoxml.mandatory]
|
||||
# t_Dependencies = [ [autoxml.Text], autoxml.optional, "Component"]
|
||||
# #t_Parts = [ [pisi.component.ComponentTree], autoxml.optional, "Component"]
|
||||
|
||||
class ComponentDB(object):
|
||||
"""a database of components"""
|
||||
|
||||
#FIXME: we might need a database per repo in the future
|
||||
def __init__(self):
|
||||
self.d = ItemByRepoDB('component')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def destroy(self):
|
||||
self.d.destroy()
|
||||
|
||||
def has_component(self, name, repo = pisi.itembyrepodb.repos, txn = None):
|
||||
#name = shelve.LockedDBShelf.encodekey(name)
|
||||
name = str(name)
|
||||
return self.d.has_key(name, repo, txn)
|
||||
|
||||
def get_component(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item(name, repo, txn=txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise Error(_('Component %s not found') % name)
|
||||
|
||||
def get_component_repo(self, name, repo=None, txn = None):
|
||||
#name = shelve.LockedDBShelf.encodekey(name)
|
||||
try:
|
||||
return self.d.get_item_repo(name, repo, txn=txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise Error(_('Component %s not found') % name)
|
||||
|
||||
def get_union_comp(self, name, txn = None, repo = pisi.itembyrepodb.repos ):
|
||||
"""get a union of all repository components packages, not just the first repo in order.
|
||||
get only basic repo info from the first repo"""
|
||||
def proc(txn):
|
||||
s = self.d.d.get(name, txn=txn)
|
||||
pkgs = set()
|
||||
for repostr in self.d.order(repo = repo):
|
||||
if s.has_key(repostr):
|
||||
pkgs |= set(s[repostr].packages)
|
||||
comp = self.get_component(name)
|
||||
comp.packages = list(pkgs)
|
||||
return comp
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def list_components(self, repo=None):
|
||||
return self.d.list(repo)
|
||||
|
||||
def update_component(self, component, repo, txn = None):
|
||||
def proc(txn):
|
||||
if self.has_component(component.name, repo, txn):
|
||||
# preserve the list of packages
|
||||
component.packages = self.d.get_item(component.name, repo, txn).packages
|
||||
self.d.add_item(component.name, component, repo, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def add_package(self, component_name, package, repo, txn = None):
|
||||
def proc(txn):
|
||||
if self.has_component(component_name, repo, txn):
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
else:
|
||||
component = Component( name = component_name )
|
||||
if not package in component.packages:
|
||||
component.packages.append(package)
|
||||
self.d.add_item(component_name, component, repo, txn) # update
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_package(self, component_name, package, repo = None, txn = None):
|
||||
def proc(txn, repo):
|
||||
if not self.has_component(component_name, repo, txn):
|
||||
raise Error(_('Information for component %s not available') % component_name)
|
||||
if not repo:
|
||||
repo = self.d.which_repo(component_name, txn=txn) # get default repo then
|
||||
component = self.get_component(component_name, repo, txn)
|
||||
if package in component.packages:
|
||||
component.packages.remove(package)
|
||||
self.d.add_item(component_name, component, repo, txn) # update
|
||||
|
||||
ctx.txn_proc(lambda x: proc(txn, repo), txn)
|
||||
|
||||
def clear(self, txn = None):
|
||||
self.d.clear(txn)
|
||||
|
||||
def remove_component(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
self.d.remove_item(name, repo, txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
self.d.remove_repo(repo, txn=txn)
|
||||
@@ -1,113 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@pardus.org.tr>
|
||||
# Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
"""
|
||||
PISI Configuration module is used for gathering and providing
|
||||
regular PISI configurations.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.configfile import ConfigurationFile
|
||||
import pisi.util
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Options(object):
|
||||
def __getattr__(self, name):
|
||||
if not self.__dict__.has_key(name):
|
||||
return None
|
||||
else:
|
||||
return self.__dict__[name]
|
||||
|
||||
class Config(object):
|
||||
"""Config Singleton"""
|
||||
|
||||
def __init__(self, options = Options()):
|
||||
self.options = options
|
||||
self.values = ConfigurationFile("/etc/pisi/pisi.conf")
|
||||
|
||||
destdir = self.get_option('destdir')
|
||||
if destdir:
|
||||
if destdir.strip().startswith('/'):
|
||||
self.destdir = destdir
|
||||
else:
|
||||
self.destdir = join(os.getcwd(), destdir)
|
||||
else:
|
||||
self.destdir = self.values.general.destinationdirectory
|
||||
|
||||
if not os.path.exists(self.destdir):
|
||||
ctx.ui.warning( _('Destination directory %s does not exist. Creating it.') % self.destdir)
|
||||
os.makedirs(self.destdir)
|
||||
|
||||
|
||||
def get_option(self, opt):
|
||||
if self.options:
|
||||
if hasattr(self.options, opt):
|
||||
return getattr(self.options, opt)
|
||||
return None
|
||||
|
||||
# directory accessor functions
|
||||
# here is how it goes
|
||||
# x_dir: system wide directory for storing info type x
|
||||
# pkg_x_dir: per package directory for storing info type x
|
||||
|
||||
def dest_dir(self):
|
||||
return self.destdir
|
||||
|
||||
def subdir(self, path):
|
||||
dir = join(self.dest_dir(), path)
|
||||
pisi.util.check_dir(dir)
|
||||
return dir
|
||||
|
||||
def lib_dir(self):
|
||||
return self.subdir(self.values.dirs.lib_dir)
|
||||
|
||||
def db_dir(self):
|
||||
return self.subdir(self.values.dirs.db_dir)
|
||||
|
||||
def archives_dir(self):
|
||||
return self.subdir(self.values.dirs.archives_dir)
|
||||
|
||||
def packages_dir(self):
|
||||
return self.subdir(self.values.dirs.packages_dir)
|
||||
|
||||
def index_dir(self):
|
||||
return self.subdir(self.values.dirs.index_dir)
|
||||
|
||||
def tmp_dir(self):
|
||||
sysdir = self.subdir(self.values.dirs.tmp_dir)
|
||||
userdir = '/tmp/pisi-' + os.environ['USER'] # FIXME: too unix specific?
|
||||
# check write access
|
||||
if os.access(sysdir, os.W_OK):
|
||||
return sysdir
|
||||
else:
|
||||
return userdir
|
||||
|
||||
# bu dizini neden kullanıyoruz? Yalnızca index.py içerisinde
|
||||
# kullanılıyor ama /var/tmp/pisi/install gibi bir dizine niye
|
||||
# ihtiyacımız var? (baris)
|
||||
def install_dir(self):
|
||||
return self.tmp_dir() + ctx.const.install_dir_suffix
|
||||
|
||||
#TODO: remove this
|
||||
config = Config()
|
||||
@@ -1,153 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI Configuration File module, obviously, is used to read from the
|
||||
# configuration file. Module also defines default values for
|
||||
# configuration parameters.
|
||||
#
|
||||
# Configuration file is located in /etc/pisi/pisi.conf by default,
|
||||
# having an INI like format like below.
|
||||
#
|
||||
#[general]
|
||||
#destinationdirectory = /
|
||||
#buildhelper = None / ccache / icecream
|
||||
#autoclean = False
|
||||
#
|
||||
#[build]
|
||||
#host = i686-pc-linux-gnu
|
||||
#debug = False
|
||||
#jobs = "-j1"
|
||||
#CFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#CXXFLAGS= -mtune=i686 -O2 -pipe -fomit-frame-pointer
|
||||
#LDFLAGS=
|
||||
#buildno=True # necessary for generating build nos
|
||||
#
|
||||
#[directories]
|
||||
#lib_dir = /var/lib/pisi
|
||||
#db_dir = /var/db/pisi
|
||||
#archives_dir = /var/cache/pisi/archives
|
||||
#packages_dir = /var/cache/pisi/packages
|
||||
#index_dir = /var/cache/pisi/index
|
||||
#tmp_dir = /var/tmp/pisi
|
||||
#icon_theme_dir = /usr/share/icons/Tulliana-1.0
|
||||
#kde_dir = /usr/kde/3.5
|
||||
#qt_dir = /usr/qt/3
|
||||
|
||||
import os
|
||||
from ConfigParser import ConfigParser, NoSectionError
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class GeneralDefaults:
|
||||
"""Default values for [general] section"""
|
||||
destinationdirectory = "/"
|
||||
buildhelper = None
|
||||
autoclean = False
|
||||
distribution = "Pardus"
|
||||
distribution_release = "1.1"
|
||||
http_proxy = os.getenv("HTTP_PROXY") or None
|
||||
https_proxy = os.getenv("HTTPS_PROXY") or None
|
||||
ftp_proxy = os.getenv("FTP_PROXY") or None
|
||||
|
||||
class BuildDefaults:
|
||||
"""Default values for [build] section"""
|
||||
host = "i686-pc-linux-gnu"
|
||||
jobs = "-j1"
|
||||
debug = False
|
||||
cflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
cxxflags = "-mtune=i686 -O2 -pipe -fomit-frame-pointer"
|
||||
ldflags = ""
|
||||
buildno = False
|
||||
|
||||
class DirsDefaults:
|
||||
"Default values for [directories] section"
|
||||
lib_dir = "/var/lib/pisi"
|
||||
db_dir = "/var/db/pisi"
|
||||
archives_dir = "/var/cache/pisi/archives"
|
||||
packages_dir = "/var/cache/pisi/packages"
|
||||
index_dir = "/var/lib/pisi/index"
|
||||
tmp_dir = "/var/tmp/pisi"
|
||||
icon_theme_dir = "/usr/share/icons/Tulliana-1.0"
|
||||
kde_dir = "/usr/kde/3.5"
|
||||
qt_dir = "/usr/qt/3"
|
||||
|
||||
class ConfigurationSection(object):
|
||||
"""ConfigurationSection class defines a section in the configuration
|
||||
file, using defaults (above) as a fallback."""
|
||||
def __init__(self, section, items=[]):
|
||||
self.items = items
|
||||
|
||||
if section == "general":
|
||||
self.defaults = GeneralDefaults
|
||||
elif section == "build":
|
||||
self.defaults = BuildDefaults
|
||||
elif section == "directories":
|
||||
self.defaults = DirsDefaults
|
||||
else:
|
||||
e = _("No section by name '%s'") % section
|
||||
raise Error, e
|
||||
|
||||
self.section = section
|
||||
|
||||
def __getattr__(self, attr):
|
||||
|
||||
# first search for attribute in the items provided in the
|
||||
# configuration file.
|
||||
if self.items:
|
||||
for item in self.items:
|
||||
if item[0] == attr:
|
||||
return item[1]
|
||||
|
||||
# then fall back to defaults
|
||||
if hasattr(self.defaults, attr):
|
||||
return getattr(self.defaults, attr)
|
||||
|
||||
return ""
|
||||
|
||||
# We'll need to access configuration keys by their names as a
|
||||
# string. Like; ["default"]...
|
||||
def __getitem__(self, key):
|
||||
return self.__getattr__(key)
|
||||
|
||||
|
||||
class ConfigurationFile(object):
|
||||
"""Parse and get configuration values from the configuration file"""
|
||||
def __init__(self, filePath):
|
||||
parser = ConfigParser()
|
||||
self.filePath = filePath
|
||||
|
||||
parser.read(self.filePath)
|
||||
|
||||
try:
|
||||
generalitems = parser.items("general")
|
||||
except NoSectionError:
|
||||
generalitems = []
|
||||
self.general = ConfigurationSection("general", generalitems)
|
||||
|
||||
try:
|
||||
builditems = parser.items("build")
|
||||
except NoSectionError:
|
||||
builditems = []
|
||||
self.build = ConfigurationSection("build", builditems)
|
||||
|
||||
try:
|
||||
dirsitems = parser.items("directories")
|
||||
except NoSectionError:
|
||||
dirsitems = []
|
||||
self.dirs = ConfigurationSection("directories", dirsitems)
|
||||
@@ -1,102 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""PISI constants.
|
||||
If you have a "magic" constant value this is where it should be
|
||||
defined."""
|
||||
|
||||
# Author: Baris Metin <baris@pardus.org.tr
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
|
||||
class _constant:
|
||||
"Constant members implementation"
|
||||
class ConstError(TypeError):
|
||||
pass
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, _("Can't rebind constant: %s") % name
|
||||
# Binding an attribute once to a const is available
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __delattr__(self, name):
|
||||
if self.__dict__.has_key(name):
|
||||
raise self.ConstError, _("Can't unbind constant: %s") % name
|
||||
# we don't have an attribute by this name
|
||||
raise NameError, name
|
||||
|
||||
class Constants:
|
||||
"Pisi Constants Singleton"
|
||||
|
||||
__c = _constant()
|
||||
|
||||
def __init__(self):
|
||||
# suffix for package names
|
||||
self.__c.package_suffix = ".pisi"
|
||||
self.__c.xdelta_suffix = ".xdelta"
|
||||
|
||||
# suffix for auto generated debug packages
|
||||
self.__c.debug_name_suffix = "-debug"
|
||||
self.__c.debug_file_suffix = ".debug"
|
||||
|
||||
# suffix for auto generated ar packages
|
||||
self.__c.static_name_suffix = "-static" # an admissible use of constant
|
||||
self.__c.ar_file_suffix = ".a"
|
||||
|
||||
# directory suffixes for build
|
||||
self.__c.work_dir_suffix = "/work" # these, too, because we might wanna change 'em
|
||||
self.__c.install_dir_suffix = "/install"
|
||||
self.__c.debug_dir_suffix = "/debug"
|
||||
|
||||
# file/directory names
|
||||
#note: these don't seem very well, constants are used
|
||||
#when it is easier/more meaningful to write the constant name, or
|
||||
#when the constant is bound to change later on.
|
||||
#in some places literals are just as good, for instance
|
||||
#when constant is the same as string. readability is important...
|
||||
self.__c.actions_file = "actions.py"
|
||||
self.__c.files_dir = "files"
|
||||
self.__c.metadata_dir = "metadata"
|
||||
self.__c.comar_dir = "comar"
|
||||
self.__c.files_xml = "files.xml"
|
||||
self.__c.metadata_xml = "metadata.xml"
|
||||
|
||||
# functions in actions_file
|
||||
self.__c.setup_func = "setup"
|
||||
self.__c.build_func = "build"
|
||||
self.__c.install_func = "install"
|
||||
|
||||
# file types
|
||||
# FIXME: these seem redundant
|
||||
self.__c.doc = "doc"
|
||||
self.__c.man = "man"
|
||||
self.__c.info = "info"
|
||||
self.__c.conf = "config"
|
||||
self.__c.header = "header"
|
||||
self.__c.library = "library"
|
||||
self.__c.executable = "executable"
|
||||
self.__c.data = "data"
|
||||
self.__c.localedata = "localedata"
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.__c, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
setattr(self.__c, attr, value)
|
||||
|
||||
def __delattr__(self, attr):
|
||||
delattr(self.__c, attr)
|
||||
@@ -1,77 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Context module.
|
||||
|
||||
# Authors: Baris Metin <baris@pardus.org.tr
|
||||
# Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
# global variables here
|
||||
|
||||
import pisi.constants
|
||||
|
||||
const = pisi.constants.Constants()
|
||||
|
||||
config = None
|
||||
|
||||
log = None
|
||||
|
||||
use_mdom = False
|
||||
|
||||
def get_option(opt):
|
||||
return config and config.get_option(opt)
|
||||
|
||||
# default UI is CLI
|
||||
ui = None # not now
|
||||
|
||||
# stdout, stderr for PiSi API
|
||||
stdout = None
|
||||
stderr = None
|
||||
|
||||
dbenv = None
|
||||
installdb = None
|
||||
packagedb = None
|
||||
repodb = None
|
||||
invidx = None
|
||||
|
||||
comar = None
|
||||
comar_sockname = None
|
||||
|
||||
initialized = False
|
||||
|
||||
#def register(_impl):
|
||||
# """ Register a UI implementation"""
|
||||
# ui = _impl
|
||||
|
||||
import bsddb3.db as db
|
||||
|
||||
# copy of DBShelve.txn_proc, the only difference is it doesn't need a shelf object
|
||||
#FIXME: remove this redundancy, and move all this stuff to database.py
|
||||
def txn_proc(proc, txn = None):
|
||||
# can be used to txn protect a method automatically
|
||||
if not txn:
|
||||
if dbenv:
|
||||
autotxn = dbenv.txn_begin()
|
||||
try:
|
||||
retval = proc(autotxn)
|
||||
except db.DBError, e:
|
||||
autotxn.abort()
|
||||
raise e
|
||||
except Exception, e:
|
||||
autotxn.abort()
|
||||
raise e
|
||||
autotxn.commit()
|
||||
else:
|
||||
retval = proc(None)
|
||||
return retval
|
||||
else:
|
||||
return proc(txn)
|
||||
@@ -1,322 +0,0 @@
|
||||
#!/bin/env python
|
||||
#------------------------------------------------------------------------
|
||||
# Copyright (c) 1997-2001 by Total Control Software
|
||||
# All Rights Reserved
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Module Name: dbShelve.py
|
||||
#
|
||||
# Description: A reimplementation of the standard shelve.py that
|
||||
# forces the use of cPickle, and DB.
|
||||
#
|
||||
# Creation Date: 11/3/97 3:39:04PM
|
||||
#
|
||||
# License: This is free software. You may use this software for any
|
||||
# purpose including modification/redistribution, so long as
|
||||
# this header remains intact and that you do not claim any
|
||||
# rights of ownership or authorship of this software. This
|
||||
# software has been tested, but no warranty is expressed or
|
||||
# implied.
|
||||
#
|
||||
# 13-Dec-2000: Updated to be used with the new bsddb3 package.
|
||||
# Added DBShelfCursor class.
|
||||
#
|
||||
# 13-Dec-2005: Minor hacking by exa to make it work better with PISI
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
"""Manage shelves of pickled objects using bsddb database files for the
|
||||
storage.
|
||||
|
||||
Add transaction processing by default to dictionary ops also
|
||||
Also other minor improvements -- exa
|
||||
|
||||
Now added support for overriding the marshalling method
|
||||
"""
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
import cPickle
|
||||
import bsddb3.db as db
|
||||
import bsddb3.dbobj as dbobj
|
||||
import string
|
||||
import sys
|
||||
import traceback
|
||||
import pisi
|
||||
|
||||
class CodingError(pisi.Error):
|
||||
pass
|
||||
|
||||
class DBShelf:
|
||||
"""A shelf to hold pickled objects, built upon a bsddb DB object. It
|
||||
automatically pickles/unpickles data objects going to/from the DB.
|
||||
"""
|
||||
def __init__(self, dbenv = None):
|
||||
self.dbenv = dbenv
|
||||
# how lame is bsddb3?
|
||||
if self.dbenv:
|
||||
self.db = dbobj.DB(dbenv)
|
||||
else:
|
||||
self.db = db.DB(None)
|
||||
|
||||
# it is better to explicitly close a shelf
|
||||
#def __del__(self):
|
||||
# self.close()
|
||||
|
||||
allowed_chars = string.letters + string.digits + '-'
|
||||
def check_key(key):
|
||||
return pisi.util.all(lambda x: x in allowed_chars, key)
|
||||
|
||||
def has_key(self, key, txn = None):
|
||||
if txn:
|
||||
return self.db.has_key(key, txn)
|
||||
else:
|
||||
return self.db.has_key(key)
|
||||
|
||||
def txn_proc(self, proc, txn):
|
||||
# can be used to txn protect a method automatically
|
||||
if not txn:
|
||||
if self.dbenv:
|
||||
autotxn = self.dbenv.txn_begin()
|
||||
try:
|
||||
retval = proc(autotxn)
|
||||
except db.DBError, e:
|
||||
autotxn.abort()
|
||||
raise e
|
||||
except Exception, e:
|
||||
autotxn.abort()
|
||||
#e.args += tuple(traceback.format_tb(sys.exc_traceback))
|
||||
raise e
|
||||
autotxn.commit()
|
||||
else: # execute without transactions
|
||||
retval = proc(None)
|
||||
return retval
|
||||
else:
|
||||
return proc(txn)
|
||||
|
||||
def decode(self, data):
|
||||
try:
|
||||
return cPickle.loads(data)
|
||||
except:
|
||||
raise CodingError()
|
||||
|
||||
def encode(self, obj):
|
||||
return cPickle.dumps(obj, 1)
|
||||
|
||||
def clear(self, txn = None):
|
||||
def proc(txn):
|
||||
for x in self.keys(txn):
|
||||
self.db.delete(x, txn)
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
def delete(self, x, txn):
|
||||
def proc(txn):
|
||||
self.db.delete(x, txn)
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
# another lame pythonic implementation method:
|
||||
#def __getattr__(self, name):
|
||||
# """Many methods we can just pass through to the DB object.
|
||||
# (See below)
|
||||
# """
|
||||
# print 'aptal bsddb3', name
|
||||
# return getattr(self.db, name)
|
||||
|
||||
#-----------------------------------
|
||||
# Dictionary access methods
|
||||
|
||||
def __len__(self):
|
||||
return len(self.db)
|
||||
|
||||
def __getitem__(self, key):
|
||||
def proc(txn):
|
||||
data = self.db.get(key)
|
||||
return self.decode(data)
|
||||
return self.txn_proc(proc, None)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# hyperdandik transactions
|
||||
def proc(txn):
|
||||
data = self.encode(value)
|
||||
self.db.put(key,data,txn)
|
||||
return self.txn_proc(proc, None)
|
||||
|
||||
def __delitem__(self, key):
|
||||
txn = self.dbenv.txn_begin()
|
||||
try:
|
||||
self.db.delete(key, txn)
|
||||
except db.DBError, e:
|
||||
txn.abort()
|
||||
raise e
|
||||
txn.commit()
|
||||
|
||||
def keys(self, txn=None):
|
||||
if txn != None:
|
||||
return self.db.keys(txn)
|
||||
else:
|
||||
return self.db.keys()
|
||||
|
||||
def items(self, txn=None):
|
||||
if txn != None:
|
||||
items = self.db.items(txn)
|
||||
else:
|
||||
items = self.db.items()
|
||||
newitems = []
|
||||
|
||||
for k, v in items:
|
||||
newitems.append( (k, self.decode(v) ) )
|
||||
return newitems
|
||||
|
||||
def values(self, txn=None):
|
||||
if txn != None:
|
||||
values = self.db.values(txn)
|
||||
else:
|
||||
values = self.db.values()
|
||||
|
||||
return map(lambda x : self.decode(x), values)
|
||||
|
||||
#-----------------------------------
|
||||
# Other methods
|
||||
|
||||
def __append(self, value, txn=None):
|
||||
data = self.encode(value)
|
||||
return self.db.append(data, txn)
|
||||
|
||||
def append(self, value, txn=None):
|
||||
if self.get_type() != db.DB_RECNO:
|
||||
self.append = self.__append
|
||||
return self.append(value, txn=txn)
|
||||
raise db.DBError, "append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO"
|
||||
|
||||
|
||||
def associate(self, secondaryDB, callback, flags=0):
|
||||
def _shelf_callback(priKey, priData, realCallback=callback):
|
||||
data = self.decode(priData)
|
||||
return realCallback(priKey, data)
|
||||
return self.db.associate(secondaryDB, _shelf_callback, flags)
|
||||
|
||||
|
||||
#def get(self, key, default=None, txn=None, flags=0):
|
||||
def get(self, *args, **kw):
|
||||
# We do it with *args and **kw so if the default value wasn't
|
||||
# given nothing is passed to the extension module. That way
|
||||
# an exception can be raised if set_get_returns_none is turned
|
||||
# off.
|
||||
data = apply(self.db.get, args, kw)
|
||||
try:
|
||||
return self.decode(data)
|
||||
except (TypeError, CodingError):
|
||||
return data # we may be getting the default value, or None,
|
||||
# so it doesn't need unpickled.
|
||||
|
||||
def get_both(self, key, value, txn=None, flags=0):
|
||||
data = self.encode(value)
|
||||
data = self.db.get(key, data, txn, flags)
|
||||
return self.decode(data)
|
||||
|
||||
def cursor(self, txn=None, flags=0):
|
||||
c = DBShelfCursor(self.db.cursor(txn, flags))
|
||||
c.binary = self.binary
|
||||
return c
|
||||
|
||||
def put(self, key, value, txn=None, flags=0):
|
||||
data = self.encode(value)
|
||||
return self.db.put(key, data, txn, flags)
|
||||
|
||||
def join(self, cursorList, flags=0):
|
||||
raise NotImplementedError
|
||||
|
||||
#----------------------------------------------
|
||||
# Methods allowed to pass-through to self.db
|
||||
#
|
||||
# close, delete, fd, get_byteswapped, get_type, has_key,
|
||||
# key_range, open, remove, rename, stat, sync,
|
||||
# upgrade, verify, and all set_* methods.
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class DBShelfCursor:
|
||||
"""
|
||||
"""
|
||||
def __init__(self, cursor):
|
||||
self.dbc = cursor
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Some methods we can just pass through to the cursor object. (See below)"""
|
||||
return getattr(self.dbc, name)
|
||||
|
||||
#----------------------------------------------
|
||||
|
||||
def dup(self, flags=0):
|
||||
return DBShelfCursor(self.dbc.dup(flags))
|
||||
|
||||
def put(self, key, value, flags=0):
|
||||
data = self.encode(value)
|
||||
return self.dbc.put(key, data, flags)
|
||||
|
||||
|
||||
def get(self, *args):
|
||||
count = len(args) # a method overloading hack
|
||||
method = getattr(self, 'get_%d' % count)
|
||||
apply(method, args)
|
||||
|
||||
def get_1(self, flags):
|
||||
rec = self.dbc.get(flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def get_2(self, key, flags):
|
||||
rec = self.dbc.get(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def get_3(self, key, value, flags):
|
||||
data = self.encode(value)
|
||||
rec = self.dbc.get(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
|
||||
def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT)
|
||||
def first(self, flags=0): return self.get_1(flags|db.DB_FIRST)
|
||||
def last(self, flags=0): return self.get_1(flags|db.DB_LAST)
|
||||
def next(self, flags=0): return self.get_1(flags|db.DB_NEXT)
|
||||
def prev(self, flags=0): return self.get_1(flags|db.DB_PREV)
|
||||
def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME)
|
||||
def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP)
|
||||
def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP)
|
||||
def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP)
|
||||
|
||||
def get_both(self, key, value, flags=0):
|
||||
data = self.encode(value)
|
||||
rec = self.dbc.get_both(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def set(self, key, flags=0):
|
||||
rec = self.dbc.set(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def set_range(self, key, flags=0):
|
||||
rec = self.dbc.set_range(key, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
def set_recno(self, recno, flags=0):
|
||||
rec = self.dbc.set_recno(recno, flags)
|
||||
return self._extract(rec)
|
||||
|
||||
set_both = get_both
|
||||
|
||||
def _extract(self, rec):
|
||||
if rec is None:
|
||||
return None
|
||||
else:
|
||||
key, data = rec
|
||||
return key, self.decode(data)
|
||||
|
||||
#----------------------------------------------
|
||||
# Methods allowed to pass-through to self.dbc
|
||||
#
|
||||
# close, count, delete, get_recno, join_item
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1,123 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
"""dependency analyzer"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.context as ctx
|
||||
#import pisi.packagedb as packagedb
|
||||
from pisi.version import Version
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.util import Checks
|
||||
import pisi.itembyrepodb
|
||||
|
||||
class Dependency:
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
s_Package = [autoxml.String, autoxml.mandatory]
|
||||
a_version = [autoxml.String, autoxml.optional]
|
||||
a_versionFrom = [autoxml.String, autoxml.optional]
|
||||
a_versionTo = [autoxml.String, autoxml.optional]
|
||||
a_release = [autoxml.String, autoxml.optional]
|
||||
a_releaseFrom = [autoxml.String, autoxml.optional]
|
||||
a_releaseTo = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.package
|
||||
if self.versionFrom:
|
||||
s += _(" version >= ") + self.versionFrom
|
||||
if self.versionTo:
|
||||
s += _(" version <= ") + self.versionTo
|
||||
if self.releaseFrom:
|
||||
s += _(" release >= ") + self.releaseFrom
|
||||
if self.releaseTo:
|
||||
s += _(" release <= ") + self.releaseTo
|
||||
return s
|
||||
|
||||
def satisfies(self, pkg_name, version, release):
|
||||
"""determine if a package ver. satisfies given dependency spec"""
|
||||
ret = True
|
||||
v = Version(version)
|
||||
if self.version:
|
||||
ret &= v == Version(self.version)
|
||||
if self.versionFrom:
|
||||
ret &= v >= Version(self.versionFrom)
|
||||
if self.versionTo:
|
||||
ret &= v <= Version(self.versionTo)
|
||||
r = Version(release)
|
||||
if self.release:
|
||||
ret &= r == Version(self.release)
|
||||
if self.releaseFrom:
|
||||
ret &= r >= Version(self.releaseFrom)
|
||||
if self.releaseTo:
|
||||
ret &= r <= Version(self.releaseTo)
|
||||
return ret
|
||||
|
||||
def dict_satisfies_dep(dict, depinfo):
|
||||
"""determine if a package in a dictionary satisfies given dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not dict.has_key(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = dict[pkg_name]
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def installed_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not ctx.installdb.is_installed(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = ctx.packagedb.get_package(pkg_name, pisi.itembyrepodb.installed)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def repo_satisfies_dep(depinfo):
|
||||
"""determine if a package in *repository* satisfies given
|
||||
dependency spec"""
|
||||
pkg_name = depinfo.package
|
||||
if not ctx.packagedb.has_package(pkg_name):
|
||||
return False
|
||||
else:
|
||||
pkg = ctx.packagedb.get_package(pkg_name)
|
||||
(version, release) = (pkg.version, pkg.release)
|
||||
return depinfo.satisfies(pkg_name, version, release)
|
||||
|
||||
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
|
||||
for dep in deps:
|
||||
if not sat(dep):
|
||||
ctx.ui.error(_('Package %s does not satisfy dependency %s') %
|
||||
(pkg,dep))
|
||||
return False
|
||||
return True
|
||||
|
||||
def satisfies_runtime_deps(pkg):
|
||||
deps = ctx.packagedb.get_package(pkg).runtimeDependencies()
|
||||
return satisfies_dependencies(pkg, deps)
|
||||
|
||||
def installable(pkg):
|
||||
"""calculate if pkg name is installable currently
|
||||
which means it has to satisfy both install and runtime dependencies"""
|
||||
if not ctx.packagedb.has_package(pkg):
|
||||
ctx.ui.info(_("Package %s is not present in the package database") % pkg);
|
||||
return False
|
||||
elif satisfies_runtime_deps(pkg):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -1,148 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2006, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation; either version 2 of the License, or (at your
|
||||
# option) any later version. Please read the COPYING file.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import getopt
|
||||
|
||||
header = "### This file is automatically generated by update-environment"
|
||||
|
||||
header_note = """
|
||||
#
|
||||
# Please do not edit this file directly. If you want to change
|
||||
# anything, please take a look at the files in /etc/env.d
|
||||
# and read the Pardus initialization system documentation.
|
||||
#
|
||||
# Bu otomatik olarak oluşturulmuş bir dosyadır.
|
||||
# Lütfen bu dosyayı elle değiştirmeyin. Değiştirmek istediğiniz
|
||||
# şeyler varsa, /etc/env.d dizinindeki dosyalara ve Pardus
|
||||
# açılış sistemi belgesine bakın.
|
||||
#
|
||||
"""
|
||||
|
||||
specials = (
|
||||
"KDEDIRS",
|
||||
"PATH",
|
||||
"CLASSPATH",
|
||||
"LDPATH",
|
||||
"MANPATH",
|
||||
"INFOPATH",
|
||||
"ROOTPATH",
|
||||
"CONFIG_PROTECT",
|
||||
"CONFIG_PROTECT_MASK",
|
||||
"PRELINK_PATH",
|
||||
"PRELINK_PATH_MASK",
|
||||
"PYTHONPATH",
|
||||
"ADA_INCLUDE_PATH",
|
||||
"ADA_OBJECTS_PATH",
|
||||
)
|
||||
|
||||
def read_env_d(envdir):
|
||||
dict = {}
|
||||
|
||||
paths = []
|
||||
for name in os.listdir(envdir):
|
||||
path = os.path.join(envdir, name)
|
||||
# skip dirs (.svn, .cvs, etc)
|
||||
if os.path.isdir(path):
|
||||
continue
|
||||
# skip backup and version control files
|
||||
if name.endswith("~") or name.endswith(".bak") or name.endswith(",v"):
|
||||
continue
|
||||
# skip pisi's .oldconfig files
|
||||
if name.endswith(".oldconfig"):
|
||||
continue
|
||||
paths.append(path)
|
||||
paths.sort()
|
||||
|
||||
for path in paths:
|
||||
for line in file(path):
|
||||
if line == "" or line.startswith("#"):
|
||||
continue
|
||||
|
||||
line = line.rstrip("\n")
|
||||
if "=" in line:
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if value.startswith('"') or value.startswith("'"):
|
||||
value = value[1:-1]
|
||||
|
||||
# Merge for special variables, override for others
|
||||
if key in specials:
|
||||
if dict.has_key(key):
|
||||
dict[key].extend(value.split(":"))
|
||||
else:
|
||||
dict[key] = value.split(":")
|
||||
else:
|
||||
dict[key] = value
|
||||
|
||||
return dict
|
||||
|
||||
def generate_profile_env(envdict, format='export %s="%s"\n'):
|
||||
profile = ""
|
||||
keys = envdict.keys()
|
||||
keys.sort()
|
||||
for key in keys:
|
||||
if key != "LDPATH":
|
||||
tmp = envdict[key]
|
||||
if isinstance(tmp, list):
|
||||
tmp = ":".join(tmp)
|
||||
profile += format % (key, tmp)
|
||||
return header + header_note + profile
|
||||
|
||||
def generate_ld_so_conf(envdict):
|
||||
ldpaths = envdict["LDPATH"]
|
||||
tmp = "\n".join(ldpaths)
|
||||
return tmp + "\n"
|
||||
|
||||
def update_file(path, content):
|
||||
f = file(path, "w")
|
||||
f.write(content)
|
||||
f.close()
|
||||
|
||||
def update_environment(prefix):
|
||||
join = os.path.join
|
||||
|
||||
env = read_env_d(join(prefix, "etc/env.d"))
|
||||
update_file(join(prefix, "etc/profile.env"), generate_profile_env(env))
|
||||
update_file(join(prefix, "etc/csh.env"), generate_profile_env(env, 'setenv %s %s\n'))
|
||||
if env.has_key("LDPATH"):
|
||||
update_file(join(prefix, "etc/ld.so.conf"), generate_ld_so_conf(env))
|
||||
subprocess.call(["/sbin/ldconfig", "-r", prefix])
|
||||
|
||||
#
|
||||
# Command line driver
|
||||
#
|
||||
|
||||
def usage():
|
||||
print "update-environment [--destdir <prefix>]"
|
||||
|
||||
def main(argv):
|
||||
prefix = "/"
|
||||
|
||||
try:
|
||||
opts, args = getopt.gnu_getopt(argv, "h", [ "help", "destdir=" ])
|
||||
except getopt.GetoptError:
|
||||
usage()
|
||||
|
||||
for o, a in opts:
|
||||
if o in ("-h", "--help"):
|
||||
usage()
|
||||
sys.exit(0)
|
||||
if o in ("--destdir"):
|
||||
prefix = a
|
||||
|
||||
update_environment(prefix)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
@@ -1,369 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""Yet another Pisi module for fetching files from various sources. Of
|
||||
course, this is not limited to just fetching source files. We fetch
|
||||
all kinds of things: source tarballs, index files, packages, and God
|
||||
knows what."""
|
||||
|
||||
# python standard library modules
|
||||
import urllib2
|
||||
import urllib
|
||||
import ftplib
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from mimetypes import guess_type
|
||||
from mimetools import Message
|
||||
from base64 import encodestring
|
||||
from shutil import move
|
||||
from time import time
|
||||
from time import gmtime
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
|
||||
class FetchError(pisi.Error):
|
||||
pass
|
||||
|
||||
class RangeError(pisi.Error):
|
||||
pass
|
||||
|
||||
# helper functions
|
||||
def fetch_url(url, destdir, progress=None, resume=True):
|
||||
fetch = Fetcher(url, destdir)
|
||||
if not resume:
|
||||
fetch.resume = False
|
||||
fetch.progress = progress
|
||||
fetch.fetch()
|
||||
if progress:
|
||||
pass
|
||||
|
||||
|
||||
class Fetcher:
|
||||
"""Fetcher can fetch a file from various sources using various
|
||||
protocols."""
|
||||
def __init__(self, url, destdir, resume = True):
|
||||
if not isinstance(url, URI):
|
||||
url = URI(url)
|
||||
|
||||
self.resume = resume
|
||||
self.scheme = url.scheme()
|
||||
self.url = url
|
||||
self.destdir = destdir
|
||||
util.check_dir(self.destdir)
|
||||
self.eta = '??:??:??'
|
||||
self.percent = 0
|
||||
self.rate = 0.0
|
||||
self.progress = None
|
||||
self.exist_size = 0
|
||||
|
||||
def fetch (self):
|
||||
"""Return value: Fetched file's full path.."""
|
||||
|
||||
if not self.url.filename():
|
||||
self.err(_('Filename error'))
|
||||
|
||||
if not os.access(self.destdir, os.W_OK):
|
||||
self.err(_('Access denied to write to destination directory: "%s"') % (self.destdir))
|
||||
|
||||
archive_file = os.path.join(self.destdir, self.url.filename())
|
||||
|
||||
if os.path.exists(archive_file) and not os.access(archive_file, os.W_OK):
|
||||
self.err(_('Access denied to destination file: "%s"') % (archive_file))
|
||||
|
||||
partial_file = archive_file + '.part'
|
||||
|
||||
if self.url.is_local_file():
|
||||
self.fetchLocalFile(partial_file)
|
||||
else:
|
||||
self.fetchRemoteFile(partial_file)
|
||||
|
||||
if os.stat(partial_file).st_size == 0:
|
||||
os.remove(partial_file)
|
||||
self.err(_('A problem occured. Please check the archive address and/or permissions again.'))
|
||||
|
||||
move(partial_file, archive_file)
|
||||
|
||||
return archive_file
|
||||
|
||||
def _do_grab(self, fileURI, dest, total_size):
|
||||
ctx.ui.notify(pisi.ui.downloading, url = fileURI)
|
||||
bs, tt, = 1024, int(time())
|
||||
s_time = time()
|
||||
Tdiff = lambda: time() - s_time
|
||||
downloaded_size = exist_size = self.exist_size
|
||||
symbol = 'B/s'
|
||||
st = time()
|
||||
chunk = fileURI.read(bs)
|
||||
downloaded_size += len(chunk)
|
||||
|
||||
if self.progress:
|
||||
p = self.progress(total_size, exist_size)
|
||||
self.percent = p.update(downloaded_size)
|
||||
self.complete = False
|
||||
|
||||
while chunk:
|
||||
dest.write(chunk)
|
||||
chunk = fileURI.read(bs)
|
||||
downloaded_size += len(chunk)
|
||||
ct = time()
|
||||
if int(tt) != int(ct):
|
||||
self.rate = (downloaded_size - exist_size) / (ct - st)
|
||||
|
||||
if self.percent:
|
||||
self.eta = '%02d:%02d:%02d' %\
|
||||
tuple([i for i in gmtime((Tdiff() * (100 - self.percent)) / self.percent)[3:6]])
|
||||
|
||||
self.rate, symbol = util.human_readable_rate(self.rate)
|
||||
|
||||
tt = time()
|
||||
|
||||
if self.progress:
|
||||
if p.update(downloaded_size):
|
||||
self.percent = p.percent
|
||||
if not self.complete:
|
||||
ctx.ui.display_progress(filename = self.url.filename(),
|
||||
percent = self.percent,
|
||||
total_size = total_size,
|
||||
downloaded_size = downloaded_size,
|
||||
rate = self.rate,
|
||||
eta = self.eta,
|
||||
symbol = symbol)
|
||||
if self.percent == 100: #FIXME: will be superseded by a
|
||||
self.complete = True # working progress interface
|
||||
|
||||
dest.close()
|
||||
|
||||
def fetchLocalFile (self, archive_file):
|
||||
url = self.url
|
||||
|
||||
if not os.access(url.path(), os.F_OK):
|
||||
self.err(_('No such file or no permission to read'))
|
||||
|
||||
dest = open(archive_file, 'w')
|
||||
total_size = os.path.getsize(url.path())
|
||||
fileObj = open(url.path())
|
||||
self._do_grab(fileObj, dest, total_size)
|
||||
|
||||
def fetchRemoteFile (self, archive_file):
|
||||
from httplib import HTTPException
|
||||
|
||||
if os.path.exists(archive_file) and self.resume:
|
||||
if self.scheme == 'http' or self.scheme == 'https' or self.scheme == 'ftp':
|
||||
self.exist_size = os.path.getsize(archive_file)
|
||||
dest = open(archive_file, 'ab')
|
||||
else:
|
||||
dest = open(archive_file, 'wb')
|
||||
|
||||
uri = self.url.get_uri()
|
||||
|
||||
flag = 1
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
|
||||
except RangeError:
|
||||
ctx.ui.info(_('Requested range not satisfiable, starting again.'))
|
||||
dest = open(archive_file, 'wb')
|
||||
self.exist_size = 0
|
||||
fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri)))
|
||||
headers = fileObj.info()
|
||||
flag = 0
|
||||
except ValueError, e:
|
||||
self.err(_('Cannot fetch %s; value error: %s') % (uri, e))
|
||||
except urllib2.HTTPError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e))
|
||||
except urllib2.URLError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e[-1][-1]))
|
||||
except OSError, e:
|
||||
self.err(_('Cannot fetch %s; %s') % (uri, e))
|
||||
except HTTPException, e:
|
||||
self.err(_('Cannot fetch %s; (%s): %s') % (uri, e.__class__.__name__, e))
|
||||
finally:
|
||||
if flag:
|
||||
if os.stat(archive_file).st_size == 0:
|
||||
os.remove(archive_file)
|
||||
|
||||
try:
|
||||
total_size = int(headers['Content-Length']) + self.exist_size
|
||||
except:
|
||||
total_size = 0
|
||||
|
||||
self._do_grab(fileObj, dest, total_size)
|
||||
|
||||
def formatRequest(self, request):
|
||||
if self.url.auth_info():
|
||||
enc = encodestring('%s:%s' % self.url.auth_info())
|
||||
request.add_header('Authorization', 'Basic %s' % enc)
|
||||
|
||||
range_handlers = {
|
||||
'http' : HTTPRangeHandler,
|
||||
'https': HTTPRangeHandler,
|
||||
'ftp' : FTPRangeHandler
|
||||
}
|
||||
|
||||
if self.exist_size and range_handlers.has_key(self.scheme):
|
||||
opener = urllib2.build_opener(range_handlers.get(self.scheme)())
|
||||
urllib2.install_opener(opener)
|
||||
request.add_header('Range', 'bytes=%d-' % self.exist_size)
|
||||
|
||||
proxy_handler = None
|
||||
|
||||
if ctx.config.values.general.http_proxy and self.url.scheme() == "http":
|
||||
http_proxy = ctx.config.values.general.http_proxy
|
||||
proxy_handler = urllib2.ProxyHandler({URI(http_proxy).scheme(): http_proxy})
|
||||
|
||||
elif ctx.config.values.general.https_proxy and self.url.scheme() == "https":
|
||||
https_proxy = ctx.config.values.general.https_proxy
|
||||
proxy_handler = urllib2.ProxyHandler({URI(https_proxy): https_proxy})
|
||||
|
||||
elif ctx.config.values.general.ftp_proxy and self.url.scheme() == "ftp":
|
||||
ftp_proxy = ctx.config.values.general.ftp_proxy
|
||||
proxy_handler = urllib2.ProxyHandler({URI(http_proxy): ftp_proxy})
|
||||
|
||||
if proxy_handler:
|
||||
ctx.ui.info(_("Proxy configuration has been found for '%s' protocol") % self.url.scheme())
|
||||
opener = urllib2.build_opener(proxy_handler)
|
||||
urllib2.install_opener(opener)
|
||||
|
||||
return request
|
||||
|
||||
def err (self, error):
|
||||
raise FetchError(error)
|
||||
|
||||
class HTTPRangeHandler(urllib2.BaseHandler):
|
||||
"""
|
||||
to override the urllib2 error: 'Error 206: Partial Content'
|
||||
this reponse from the HTTP server is already what we expected to get.
|
||||
Don't give up, resume downloading..
|
||||
"""
|
||||
|
||||
def http_error_206(self, request, fp, errcode, msg, headers):
|
||||
return urllib.addinfourl(fp, headers, request.get_full_url())
|
||||
|
||||
def http_error_416(self, request, fp, errcode, msg, headers):
|
||||
# HTTP 1.1's 'Range Not Satisfiable' error..
|
||||
raise RangeError
|
||||
|
||||
|
||||
class FTPRangeHandler(urllib2.FTPHandler):
|
||||
"""
|
||||
FTP Range support..
|
||||
"""
|
||||
def ftp_open(self, req):
|
||||
host = req.get_host()
|
||||
host, port = urllib.splitport(host)
|
||||
if port is None:
|
||||
port = ftplib.FTP_PORT
|
||||
|
||||
try:
|
||||
host = socket.gethostbyname(host)
|
||||
except socket.error, msg:
|
||||
raise FetchError(msg)
|
||||
|
||||
path, attrs = urllib.splitattr(req.get_selector())
|
||||
dirs = path.split('/')
|
||||
dirs = map(urllib.unquote, dirs)
|
||||
dirs, file = dirs[:-1], dirs[-1]
|
||||
if dirs and not dirs[0]:
|
||||
dirs = dirs[1:]
|
||||
try:
|
||||
fw = self.connect_ftp('', '', host, port, dirs)
|
||||
type = file and 'I' or 'D'
|
||||
for attr in attrs:
|
||||
attr, value = urllib.splitattr(attr)
|
||||
if attr.lower() == 'type' and \
|
||||
value in ('a', 'A', 'i', 'I', 'd', 'D'):
|
||||
type = value.upper()
|
||||
|
||||
rawr = req.headers.get('Range', None)
|
||||
if rawr:
|
||||
rest = int(rawr.split("=")[1].rstrip("-"))
|
||||
else:
|
||||
rest = 0
|
||||
|
||||
fp, retrlen = fw.retrfile(file, type, rest)
|
||||
|
||||
fb, lb = rest, retrlen
|
||||
if retrlen is None or retrlen == 0:
|
||||
raise RangeError
|
||||
retrlen = lb - fb
|
||||
if retrlen < 0:
|
||||
# beginning of range is larger than file
|
||||
raise RangeError
|
||||
|
||||
headers = ''
|
||||
mtype = guess_type(req.get_full_url())[0]
|
||||
if mtype:
|
||||
headers += 'Content-Type: %s\n' % mtype
|
||||
if retrlen is not None and retrlen >= 0:
|
||||
headers += 'Content-Length: %d\n' % retrlen
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError, msg:
|
||||
from StringIO import StringIO
|
||||
|
||||
return urllib.addinfourl(fp, Message(StringIO(headers)), req.get_full_url())
|
||||
except ftplib.all_errors, msg:
|
||||
raise IOError, (_('ftp error'), msg), sys.exc_info()[2]
|
||||
|
||||
def connect_ftp(self, user, passwd, host, port, dirs):
|
||||
fw = ftpwrapper('', '', host, port, dirs)
|
||||
return fw
|
||||
|
||||
class ftpwrapper(urllib.ftpwrapper):
|
||||
def retrfile(self, file, type, rest=None):
|
||||
self.endtransfer()
|
||||
if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1
|
||||
else: cmd = 'TYPE ' + type; isdir = 0
|
||||
try:
|
||||
self.ftp.voidcmd(cmd)
|
||||
except ftplib.all_errors:
|
||||
self.init()
|
||||
self.ftp.voidcmd(cmd)
|
||||
conn = None
|
||||
if file and not isdir:
|
||||
try:
|
||||
self.ftp.nlst(file)
|
||||
except ftplib.error_perm, reason:
|
||||
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||
# Restore the transfer mode!
|
||||
self.ftp.voidcmd(cmd)
|
||||
try:
|
||||
cmd = 'RETR ' + file
|
||||
conn = self.ftp.ntransfercmd(cmd, rest)
|
||||
except ftplib.error_perm, reason:
|
||||
if str(reason)[:3] == '501':
|
||||
# workaround for REST not suported error
|
||||
fp, retrlen = self.retrfile(file, type)
|
||||
fp = RangeableFileObject(fp, (rest,''))
|
||||
return (fp, retrlen)
|
||||
elif str(reason)[:3] != '550':
|
||||
raise IOError, (_('ftp error'), reason), sys.exc_info()[2]
|
||||
if not conn:
|
||||
self.ftp.voidcmd('TYPE A')
|
||||
if file: cmd = 'LIST ' + file
|
||||
else: cmd = 'LIST'
|
||||
conn = self.ftp.ntransfercmd(cmd)
|
||||
self.busy = 1
|
||||
return (urllib.addclosehook(conn[0].makefile('rb'),
|
||||
self.endtransfer), conn[1])
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
"""
|
||||
generic file abstraction that allows us to use URIs for everything
|
||||
we support only the simple read case ATM
|
||||
we are just encapsulating a common pattern in our program, nothing big.
|
||||
like all pisi classes, it has been programmed in a non-restricting way
|
||||
"""
|
||||
|
||||
import os
|
||||
import types
|
||||
import bz2
|
||||
import 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, zip) = range(2) # compress enums
|
||||
|
||||
(detached, whatelse) = range(2)
|
||||
|
||||
@staticmethod
|
||||
def make_uri(uri):
|
||||
"handle URI arg"
|
||||
if type(uri) == types.StringType or type(uri) == types.UnicodeType:
|
||||
uri = URI(uri)
|
||||
elif not isinstance(uri, URI):
|
||||
raise Error(_("uri must have type either URI or string"))
|
||||
return uri
|
||||
|
||||
@staticmethod
|
||||
def decompress(localfile, compress):
|
||||
if compress == File.bz2:
|
||||
open(localfile[:-4], "w").write(bz2.BZ2File(localfile).read())
|
||||
localfile = localfile[:-4]
|
||||
elif compress == File.zip:
|
||||
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())
|
||||
fetch_url(uri, transfer_dir)
|
||||
else:
|
||||
ctx.ui.info(_("Copying %s to transfer dir") % uri.get_uri())
|
||||
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.zip:
|
||||
raise Error(_("zip 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,95 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
'''Files module provides access to files.xml. files.xml is generated
|
||||
during the build process of a package and used in installation.'''
|
||||
|
||||
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.util import Checks
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
|
||||
class FileInfo:
|
||||
"""File holds the information for a File node/tag in files.xml"""
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
t_Path = [ autoxml.String, autoxml.mandatory ]
|
||||
t_Type = [ autoxml.String, autoxml.mandatory ]
|
||||
t_Size = [ autoxml.Long, autoxml.optional ]
|
||||
t_Hash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
|
||||
t_Permanent = [ autoxml.String, autoxml.optional ]
|
||||
|
||||
def __str__(self):
|
||||
s = "%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type,
|
||||
self.size, self.hash)
|
||||
return s
|
||||
|
||||
|
||||
class Files(autoxml.XmlFile):
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "Files"
|
||||
|
||||
t_List = [ [FileInfo], autoxml.optional, "File"]
|
||||
|
||||
def append(self, fileinfo):
|
||||
self.list.append(fileinfo)
|
||||
|
||||
|
||||
class FilesDB(shelve.LockedDBShelf):
|
||||
|
||||
def __init__(self):
|
||||
shelve.LockedDBShelf.__init__(self, 'files')
|
||||
|
||||
def add_files(self, pkg_name, files, txn = None):
|
||||
def proc(txn):
|
||||
for x in files.list:
|
||||
path = x.path
|
||||
del x.path # don't store redundant attribute in db
|
||||
self.put(path, (pkg_name, x), txn)
|
||||
x.path = path # store it back in
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
def remove_files(self, files, txn = None):
|
||||
def proc(txn):
|
||||
for x in files.list:
|
||||
if self.has_key(x.path):
|
||||
self.delete(x.path, txn)
|
||||
self.txn_proc(proc, txn)
|
||||
|
||||
def has_file(self, path, txn = None):
|
||||
return self.has_key(str(path), txn)
|
||||
|
||||
def get_file(self, path, txn = None):
|
||||
path = str(path)
|
||||
def proc(txn):
|
||||
if not self.has_key(path, txn):
|
||||
return None
|
||||
else:
|
||||
(name, fileinfo) = self.get(path, txn)
|
||||
fileinfo.path = path
|
||||
return (name, fileinfo)
|
||||
return self.txn_proc(proc, txn)
|
||||
|
||||
def match_files(self, glob):
|
||||
# NB: avoid using, this reads the entire db
|
||||
import fnmatch
|
||||
glob = str(glob)
|
||||
infos = []
|
||||
for key in self.keys():
|
||||
if fnmatch.fnmatch(key, glob):
|
||||
infos.append(self[key])
|
||||
return infos
|
||||
@@ -1,169 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
|
||||
# the most simple minded digraph class ever
|
||||
|
||||
|
||||
import sys
|
||||
import pisi
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# not an error!
|
||||
|
||||
class CycleException(pisi.Exception):
|
||||
def __init__(self, cycle):
|
||||
self.cycle = cycle
|
||||
|
||||
def __str__(self):
|
||||
return _('Encountered cycle %s') % self.cycle
|
||||
|
||||
class Digraph(object):
|
||||
|
||||
def __init__(self):
|
||||
self.__v = set()
|
||||
self.__adj = {}
|
||||
self.__vdata = {}
|
||||
self.__edata = {}
|
||||
|
||||
def vertices(self):
|
||||
"return set of vertex descriptors"
|
||||
return self.__v
|
||||
|
||||
def edges(self):
|
||||
"return a list of edge descriptors"
|
||||
list = []
|
||||
for u in self.__v:
|
||||
for v in self.__u:
|
||||
list.append( (u,v) )
|
||||
return list
|
||||
|
||||
def from_list(self, el):
|
||||
"convert a list of edges (u,v) to graph"
|
||||
for (u,v) in el:
|
||||
self.add_edge(u,v)
|
||||
|
||||
def add_vertex(self, u, data = None):
|
||||
"add vertex u, optionally with data"
|
||||
assert not u in self.__v
|
||||
self.__v.add(u)
|
||||
self.__adj[u] = set()
|
||||
if data:
|
||||
self.__vdata[u] = data
|
||||
self.__edata[u] = {}
|
||||
|
||||
def add_edge(self, u, v, edata = None, udata = None, vdata = None):
|
||||
"add edge u -> v"
|
||||
if not u in self.__v:
|
||||
self.add_vertex(u, udata)
|
||||
if not v in self.__v:
|
||||
self.add_vertex(v, vdata)
|
||||
self.__adj[u].add(v)
|
||||
if edata != None:
|
||||
self.__edata[u][v] = edata
|
||||
|
||||
def add_biedge(self, u, v, edata = None):
|
||||
self.add_edge(u, v, edata)
|
||||
self.add_edge(v, u, edata)
|
||||
|
||||
def set_vertex_data(self, u, data):
|
||||
self.__vdata[u] = data
|
||||
|
||||
def vertex_data(self, u):
|
||||
return self.__vdata[u]
|
||||
|
||||
def edge_data(self, u, v):
|
||||
return self.__edata[u][v]
|
||||
|
||||
def has_vertex(self, u):
|
||||
return u in self.__v
|
||||
|
||||
def has_edge(self, u,v):
|
||||
if u in self.__v:
|
||||
return v in self.__adj[u]
|
||||
else:
|
||||
return False
|
||||
|
||||
def adj(self, u):
|
||||
return self.__adj[u]
|
||||
|
||||
def dfs(self, finish_hook = None):
|
||||
self.color = {}
|
||||
self.p = {}
|
||||
self.d = {}
|
||||
self.f = {}
|
||||
for u in self.__v:
|
||||
self.color[u] = 'w' # mark white (unexplored)
|
||||
self.p[u] = None
|
||||
self.time = 0
|
||||
for u in self.__v:
|
||||
if self.color[u] == 'w':
|
||||
self.dfs_visit(u, finish_hook)
|
||||
|
||||
def dfs_visit(self, u, finish_hook):
|
||||
self.color[u] = 'g' # mark green (discovered)
|
||||
self.d[u] = self.time = self.time + 1
|
||||
for v in self.adj(u):
|
||||
if self.color[v] == 'w': # explore unexplored vertices
|
||||
self.p[v] = u
|
||||
self.dfs_visit(v, finish_hook)
|
||||
elif self.color[v] == 'g': # cycle detected
|
||||
cycle = [u]
|
||||
while self.p[u]:
|
||||
u = self.p[u]
|
||||
cycle.append(u)
|
||||
cycle.reverse()
|
||||
raise CycleException(cycle)
|
||||
self.color[u] = 'b' # mark black (completed)
|
||||
if finish_hook:
|
||||
finish_hook(u)
|
||||
self.f[u] = self.time = self.time + 1
|
||||
|
||||
def cycle_free(self):
|
||||
try:
|
||||
self.dfs()
|
||||
return True
|
||||
except CycleException:
|
||||
return False
|
||||
|
||||
def topological_sort(self):
|
||||
list = []
|
||||
self.dfs(lambda u: list.append(u))
|
||||
list.reverse()
|
||||
return list
|
||||
|
||||
def id_str(self, u):
|
||||
s = str(u)
|
||||
return s.replace('-', '_')
|
||||
|
||||
def write_graphviz(self, f):
|
||||
f.write('digraph G {\n')
|
||||
for u in self.vertices():
|
||||
f.write(self.id_str(u))
|
||||
self.write_graphviz_vlabel(f, u)
|
||||
f.write(';\n')
|
||||
f.write('\n')
|
||||
for u in self.vertices():
|
||||
for v in self.adj(u):
|
||||
f.write( self.id_str(u) + ' -> ' + self.id_str(v))
|
||||
self.write_graphviz_elabel(f, u, v)
|
||||
f.write(';\n')
|
||||
f.write('\n')
|
||||
f.write('}\n')
|
||||
|
||||
def write_graphviz_vlabel(self, f, u):
|
||||
pass
|
||||
|
||||
def write_graphviz_elabel(self, f, u, v):
|
||||
pass
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
|
||||
"""PISI source/package index"""
|
||||
|
||||
import os
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile as specfile
|
||||
import pisi.metadata as metadata
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.sourcedb as sourcedb
|
||||
import pisi.util as util
|
||||
from pisi.package import Package
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
from pisi.file import File
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
from pisi.uri import URI
|
||||
import pisi.component as component
|
||||
import pisi.specfile as specfile
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Index(XmlFile):
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Distribution = [ component.Distribution, autoxml.optional ]
|
||||
t_Specs = [ [specfile.SpecFile], autoxml.optional, "SpecFile"]
|
||||
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
|
||||
#t_Metadatas = [ [metadata.MetaData], autoxml.optional, "MetaData"]
|
||||
t_Components = [ [component.Component], autoxml.optional, "Component"]
|
||||
|
||||
def name():
|
||||
return self.distribution.name + self.distribution.repositoryname
|
||||
|
||||
# read index for a given repo, force means download even if remote not updated
|
||||
def read_uri(self, filename, repo = None, force = False):
|
||||
"""Read PSPEC file"""
|
||||
if repo:
|
||||
tmpdir = os.path.join(ctx.config.index_dir(), repo)
|
||||
else:
|
||||
tmpdir = os.path.join(ctx.config.tmp_dir(), 'index')
|
||||
pisi.util.clean_dir(tmpdir)
|
||||
pisi.util.check_dir(tmpdir)
|
||||
|
||||
# write uri
|
||||
urlfile = file(pisi.util.join_path(tmpdir, 'uri'), 'w')
|
||||
urlfile.write(filename) # uri
|
||||
|
||||
if filename.endswith(".bz2"):
|
||||
self.read(filename, tmpDir=tmpdir, sha1sum=not force,
|
||||
compress=File.bz2, sign=File.detached, copylocal = True)
|
||||
else:
|
||||
self.read(filename, tmpDir=tmpdir, sha1sum=not force,
|
||||
compress=None, sign=File.detached, copylocal = True)
|
||||
|
||||
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, non_recursive=False):
|
||||
self.repo_dir = repo_uri
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn.endswith(ctx.const.package_suffix):
|
||||
ctx.ui.info(_('Adding %s to package index') % fn)
|
||||
self.add_package(os.path.join(root, fn), repo_uri)
|
||||
if fn == 'component.xml':
|
||||
ctx.ui.info(_('Adding %s to component index') % fn)
|
||||
self.add_component(os.path.join(root, fn))
|
||||
if fn == 'pspec.xml' and not skip_sources:
|
||||
self.add_spec(os.path.join(root, fn), repo_uri)
|
||||
if fn == 'distribution.xml':
|
||||
self.add_distro(os.path.join(root, fn))
|
||||
if non_recursive:
|
||||
del dirs[0:]
|
||||
|
||||
def update_db(self, repo, txn = None):
|
||||
for comp in self.components:
|
||||
ctx.componentdb.update_component(comp, repo, txn)
|
||||
ctx.packagedb.remove_repo(repo, txn=txn)
|
||||
for pkg in self.packages:
|
||||
ctx.packagedb.add_package(pkg, repo, txn=txn)
|
||||
ctx.sourcedb.remove_repo(repo, txn=txn)
|
||||
for sf in self.specs:
|
||||
ctx.sourcedb.add_spec(sf, repo, txn=txn)
|
||||
|
||||
def add_package(self, path, repo_uri):
|
||||
package = Package(path, 'r')
|
||||
# extract control files
|
||||
util.clean_dir(ctx.config.install_dir())
|
||||
package.extract_PISI_files(ctx.config.install_dir())
|
||||
|
||||
md = metadata.MetaData()
|
||||
md.read(os.path.join(ctx.config.install_dir(), ctx.const.metadata_xml))
|
||||
md.package.packageSize = os.path.getsize(path)
|
||||
if ctx.config.options and ctx.config.options.absolute_uris:
|
||||
# FIXME: the name "absolute_uris" does not seem to fit below :/
|
||||
md.package.packageURI = os.path.realpath(path)
|
||||
else: # create relative path by default
|
||||
# TODO: in the future well do all of this with purl/pfile/&helpers
|
||||
# really? heheh -- future exa
|
||||
md.package.packageURI = util.removepathprefix(repo_uri, path)
|
||||
# check package semantics
|
||||
errs = md.errors()
|
||||
if md.errors():
|
||||
ctx.ui.error(_('Package %s: metadata corrupt, skipping...') % md.package.name)
|
||||
ctx.ui.error(unicode(Error(*errs)))
|
||||
else:
|
||||
self.packages.append(md.package)
|
||||
|
||||
def add_component(self, path):
|
||||
comp = component.Component()
|
||||
#try:
|
||||
comp.read(path)
|
||||
self.components.append(comp)
|
||||
#except:
|
||||
# raise Error(_('Component in %s is corrupt') % path)
|
||||
#ctx.ui.error(str(Error(*errs)))
|
||||
|
||||
def add_distro(self, path):
|
||||
distro = component.Distribution()
|
||||
#try:
|
||||
distro.read(path)
|
||||
self.distribution = distro
|
||||
#except:
|
||||
# raise Error(_('Distribution in %s is corrupt') % path)
|
||||
#ctx.ui.error(str(Error(*errs)))
|
||||
|
||||
def add_spec(self, path, repo_uri):
|
||||
ctx.ui.info(_('Adding %s to source index') % path)
|
||||
sf = specfile.SpecFile()
|
||||
sf.read(path)
|
||||
errs = sf.errors()
|
||||
if sf.errors():
|
||||
ctx.ui.error(_('SpecFile in %s is corrupt, skipping...') % path)
|
||||
ctx.ui.error(str(Error(*errs)))
|
||||
else:
|
||||
if ctx.config.options and ctx.config.options.absolute_uris:
|
||||
sf.source.sourceURI = os.path.realpath(path)
|
||||
else: # create relative path by default
|
||||
sf.source.sourceURI = util.removepathprefix(repo_uri, path)
|
||||
self.specs.append(sf)
|
||||
@@ -1,216 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# installation database
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
# System
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
from pisi.util import join_path as join
|
||||
|
||||
|
||||
class InstallDBError(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class InstallInfo:
|
||||
# some data is replicated from packagedb
|
||||
# we store as an object, hey, we can waste O(1) space.
|
||||
# this is also easier to modify in the future, without
|
||||
# requiring database upgrades! wow!
|
||||
def __init__(self, state, version, release, build, distribution, time):
|
||||
self.state = state
|
||||
self.version = version
|
||||
self.release = release
|
||||
self.build = build
|
||||
self.distribution = distribution
|
||||
self.time = time
|
||||
|
||||
def one_liner(self):
|
||||
import time
|
||||
time_str = time.strftime("%d %b %Y %H:%M", self.time)
|
||||
s = '%2s|%10s|%6s|%6s|%8s|%12s' % (self.state, self.version, self.release,
|
||||
self.build, self.distribution,
|
||||
time_str)
|
||||
return s
|
||||
|
||||
state_map = { 'i': _('installed'), 'ip':_('installed-pending'),
|
||||
'r':_('removed'), 'p': _('purged') }
|
||||
|
||||
def __str__(self):
|
||||
s = _("State: %s\nVersion: %s, Release: %s, Build: %s\n") % \
|
||||
(InstallInfo.state_map[self.state], self.version,
|
||||
self.release, self.build)
|
||||
import time
|
||||
time_str = time.strftime("%d %b %Y %H:%M", self.time)
|
||||
s += _('Distribution: %s, Install Time: %s\n') % (self.distribution,
|
||||
time_str)
|
||||
return s
|
||||
|
||||
|
||||
class InstallDB:
|
||||
|
||||
def __init__(self):
|
||||
self.d = shelve.LockedDBShelf('install')
|
||||
self.dp = shelve.LockedDBShelf('configpending')
|
||||
self.files_dir = join(ctx.config.db_dir(), 'files')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dp.close()
|
||||
|
||||
def files_name(self, pkg, version, release):
|
||||
pkg_dir = self.pkg_dir(pkg, version, release)
|
||||
return join(pkg_dir, ctx.const.files_xml)
|
||||
|
||||
def files(self, pkg):
|
||||
pkg = str(pkg)
|
||||
pkginfo = self.d[pkg]
|
||||
files = Files()
|
||||
files.read(self.files_name(pkg,pkginfo.version,pkginfo.release))
|
||||
return files
|
||||
|
||||
def pkg_dir(self, pkg, version, release):
|
||||
return join(ctx.config.lib_dir(), 'package',
|
||||
pkg + '-' + version + '-' + release)
|
||||
|
||||
def is_recorded(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
return self.d.has_key(pkg)
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def is_installed(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
if self.is_recorded(pkg, txn):
|
||||
info = self.d.get(pkg, txn)
|
||||
return info.state=='i' or info.state=='ip'
|
||||
else:
|
||||
return False
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def list_installed(self, txn = None):
|
||||
def proc(txn):
|
||||
list = []
|
||||
for (pkg, info) in self.d.items(txn):
|
||||
if info.state=='i' or info.state=='ip':
|
||||
list.append(pkg)
|
||||
return list
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def list_pending(self):
|
||||
# warning: reads the entire db
|
||||
dict = {}
|
||||
for (pkg, x) in self.dp.items():
|
||||
pkginfo = self.d[pkg]
|
||||
dict[pkg] = pkginfo
|
||||
return dict
|
||||
|
||||
def get_info(self, pkg):
|
||||
pkg = str(pkg)
|
||||
return self.d[pkg]
|
||||
|
||||
def get_version(self, pkg):
|
||||
pkg = str(pkg)
|
||||
info = self.d[pkg]
|
||||
return (info.version, info.release, info.build)
|
||||
|
||||
def is_removed(self, pkg):
|
||||
pkg = str(pkg)
|
||||
if self.is_recorded(pkg):
|
||||
info = self.d[pkg]
|
||||
return info.state=='r'
|
||||
else:
|
||||
return False
|
||||
|
||||
def install(self, pkg, version, release, build, distro = "",
|
||||
config_later = False, rebuild=False, txn = None):
|
||||
"""install package with specific version, release, build"""
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
if self.is_installed(pkg, txn):
|
||||
raise InstallDBError(_("Already installed"))
|
||||
if config_later:
|
||||
state = 'ip'
|
||||
self.dp.put(pkg, True, txn)
|
||||
else:
|
||||
state = 'i'
|
||||
|
||||
# FIXME: it might be more appropriate to pass date
|
||||
# as an argument, or installation data afterwards
|
||||
# to do this -- exa
|
||||
if not rebuild:
|
||||
import time
|
||||
ctime = time.localtime()
|
||||
else:
|
||||
files_xml = self.files_name(pkg, version, release)
|
||||
ctime = util.creation_time(files_xml)
|
||||
|
||||
self.d.put(pkg, InstallInfo(state, version, release, build, distro, ctime), txn)
|
||||
|
||||
self.d.txn_proc(proc,txn)
|
||||
|
||||
def clear_pending(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
info = self.d.get(pkg, txn)
|
||||
if self.is_installed(pkg, txn):
|
||||
assert info.state == 'ip'
|
||||
info.state = 'i'
|
||||
self.d.put(pkg, info, txn)
|
||||
self.dp.delete(pkg, txn)
|
||||
self.d.txn_proc(proc,txn)
|
||||
|
||||
def remove(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
info = self.d.get(pkg, txn)
|
||||
info.state = 'r'
|
||||
self.d.put(pkg, info, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def purge(self, pkg, txn = None):
|
||||
pkg = str(pkg)
|
||||
def proc(txn):
|
||||
if self.d.has_key(pkg, txn):
|
||||
self.d.delete(pkg, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
db = None
|
||||
|
||||
def init():
|
||||
global db
|
||||
if db:
|
||||
return db
|
||||
|
||||
db = InstallDB()
|
||||
return db
|
||||
|
||||
def finalize():
|
||||
global db
|
||||
if db:
|
||||
db.close()
|
||||
db = None
|
||||
@@ -1,221 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
import os
|
||||
import fcntl
|
||||
import types
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
|
||||
installed, thirdparty, repos, all = range(1, 5)
|
||||
|
||||
"""installed and thirdparty are special databases to keep track
|
||||
of already installed stuff and third party stuff not in any real repository.
|
||||
repos means search in repositories only, and all means search in
|
||||
repositories and special databases (called tracking databases)
|
||||
"""
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
pass
|
||||
|
||||
class ItemByRepoDB(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.d = shelve.LockedDBShelf(name)
|
||||
#self.dbyrepo = shelve.LockedDBShelf(name + '-byrepo')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def clear(self, txn = None):
|
||||
self.d.clear(txn=txn)
|
||||
|
||||
def txn_proc(self, proc, txn):
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def items(self):
|
||||
return self.d.items()
|
||||
|
||||
@staticmethod
|
||||
def not_just_tracking(data):
|
||||
keys = data.keys()
|
||||
if len(keys)==1:
|
||||
if 'trdparty' in keys or 'inst' in keys:
|
||||
return False
|
||||
elif len(keys)==2:
|
||||
if 'trdparty' in keys and 'inst' in keys:
|
||||
return False
|
||||
return True
|
||||
#below is a slower way
|
||||
#for x in data.keys():
|
||||
# if x.startsWith('repo-'):
|
||||
# return True
|
||||
#return False
|
||||
|
||||
def list_if(self, pred):
|
||||
return [ k for k,data in self.d.items() if pred(k, data)]
|
||||
|
||||
def list(self, repo = None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
if repo not in [repos, all]:
|
||||
return [ k for k,data in self.d.items() if data.has_key(self.repo_str(repo))]
|
||||
else:
|
||||
if repo == all:
|
||||
return [ pkg for pkg in self.d.keys() ]
|
||||
else:
|
||||
return self.list_if( lambda k,data: ItemByRepoDB.not_just_tracking(data) )
|
||||
|
||||
# TODO: carry this to repodb, really :/
|
||||
def order(self, repo = None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
assert repo in [all, repos]
|
||||
import pisi.repodb
|
||||
order = [ 'repo-' + x for x in ctx.repodb.list() ]
|
||||
if repo == all:
|
||||
order += ['trdparty', 'inst']
|
||||
return order
|
||||
|
||||
# def list_repo(self, repo):
|
||||
# return self.dbyrepo[repo]
|
||||
|
||||
def repo_str(self, repo):
|
||||
if repo==thirdparty:
|
||||
repo='trdparty'
|
||||
elif repo==installed:
|
||||
repo='inst'
|
||||
else:
|
||||
assert type(repo) == type("")
|
||||
repo='repo-'+repo
|
||||
return repo
|
||||
|
||||
def str_repo(self, str):
|
||||
if str.startswith('repo-'):
|
||||
return str[5:]
|
||||
elif str=='trdparty':
|
||||
return thirdparty
|
||||
elif str=='inst':
|
||||
return installed
|
||||
else:
|
||||
raise Error(_('Invalid repository string'))
|
||||
|
||||
def has_key(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
if repo == None:
|
||||
repo = repos
|
||||
haskey = self.d.has_key(name, txn)
|
||||
if repo == all:
|
||||
return haskey
|
||||
elif repo == repos:
|
||||
data = self.d.get(name, txn)
|
||||
return haskey and ItemByRepoDB.not_just_tracking(data)
|
||||
else:
|
||||
repostr = self.repo_str(repo)
|
||||
return haskey and self.d.get(name, txn).has_key(repostr)
|
||||
|
||||
def get_item_repo(self, name, repo = None, txn = None):
|
||||
name = str(name)
|
||||
if repo == None:
|
||||
repo = repos
|
||||
def proc(txn):
|
||||
if not self.d.has_key(name, txn=txn):
|
||||
raise NotfoundError(_('Key %s not found') % name)
|
||||
s = self.d.get(name, txn=txn)
|
||||
if repo in [repos, all]:
|
||||
for repostr in self.order(repo):
|
||||
if s.has_key(repostr):
|
||||
return (s[repostr], self.str_repo(repostr))
|
||||
else:
|
||||
repostr = self.repo_str(repo)
|
||||
if s.has_key(repostr):
|
||||
return (s[repostr], repo)
|
||||
raise NotfoundError(_('Key %s in repo %s not found') % (name, repo))
|
||||
#return None
|
||||
|
||||
return self.d.txn_proc(proc, txn)
|
||||
|
||||
def get_item(self, name, repo = None, txn = None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
x = self.get_item_repo(name, repo, txn)
|
||||
if x:
|
||||
item, repo = x
|
||||
# discard repo, not always needed
|
||||
return item
|
||||
else:
|
||||
return None
|
||||
|
||||
def which_repo(self, name, txn = None):
|
||||
x = self.get_item_repo(name, txn=txn)
|
||||
if x:
|
||||
item, repo = x
|
||||
return repo
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_item(self, name, obj, repo, txn = None):
|
||||
assert not repo in [all, repos]
|
||||
repostr = self.repo_str(repo)
|
||||
def proc(txn):
|
||||
if not self.d.has_key(name):
|
||||
s = dict()
|
||||
else:
|
||||
s = self.d.get(name, txn)
|
||||
s[ repostr ] = obj
|
||||
self.d.put(name, s, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_item_repo(self, name, repo, txn = None):
|
||||
assert not repo in [all, repos]
|
||||
name = str(name)
|
||||
def p(txn):
|
||||
s = self.d.get(name, txn)
|
||||
repostr = self.repo_str(repo)
|
||||
if s.has_key(repostr):
|
||||
del s[repostr]
|
||||
if (len(s)==0):
|
||||
self.d.delete(name, txn)
|
||||
else:
|
||||
self.d.put(name, s, txn)
|
||||
self.d.txn_proc(p, txn)
|
||||
|
||||
def remove_item_only(self, name, txn = None):
|
||||
def p(txn):
|
||||
repo = self.which_repo(name, txn=txn)
|
||||
self.remove_item_repo(name, repo, txn=txn)
|
||||
self.d.txn_proc(p, txn)
|
||||
|
||||
def remove_item(self, name, repo=None, txn=None):
|
||||
if repo == None:
|
||||
repo = repos
|
||||
if not repo in [all, repos]:
|
||||
self.remove_item_repo(name, repo,txn=txn)
|
||||
else:
|
||||
self.remove_item_only(name,txn=txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
def proc(txn):
|
||||
for key in self.d.keys():
|
||||
self.remove_item_repo(key, repo, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
@@ -1,186 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
import os
|
||||
import fcntl
|
||||
import types
|
||||
import cPickle
|
||||
|
||||
import bsddb3.db as db
|
||||
import bsddb3.dbobj as dbobj
|
||||
#import bsddb3.dbshelve as shelve
|
||||
import pisi.dbshelve as shelve
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.util import join_path
|
||||
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# check database version
|
||||
# if write is given it knows it has write access
|
||||
# if force is given it updates the specified db version
|
||||
def check_dbversion(versionfile, ver, write=False, update=False):
|
||||
verfn = join_path(pisi.context.config.db_dir(), versionfile)
|
||||
firsttime = False
|
||||
if os.path.exists(verfn):
|
||||
verfile = file(verfn, 'r')
|
||||
ls = verfile.readlines()
|
||||
currver = Version(ls[0])
|
||||
dbver = Version(ver)
|
||||
if currver < dbver:
|
||||
if not update:
|
||||
raise Error(_('Database version for %s insufficient. Please run rebuild-db command.') % versionfile)
|
||||
else:
|
||||
pass # continue to update, then
|
||||
elif currver > dbver:
|
||||
raise Error(_('Database version for %s greater than PiSi version. You need a newer PiSi.') % versionfile)
|
||||
elif not update:
|
||||
return True # db version is OK
|
||||
else:
|
||||
firsttime = True
|
||||
if write and (update or firsttime):
|
||||
if os.access(pisi.context.config.db_dir(), os.W_OK):
|
||||
ctx.ui.warning(_('Writing current database version for %s') % versionfile)
|
||||
verfile = file(verfn, 'w')
|
||||
verfile.write(ver)
|
||||
verfile.close()
|
||||
else:
|
||||
raise Error(_('Cannot attain write access to database environment'))
|
||||
else:
|
||||
raise Error(_('Database version %s not present.') % versionfile)
|
||||
|
||||
def lock_dbenv():
|
||||
ctx.dbenv_lock = file(join_path(pisi.context.config.db_dir(), 'dbenv.lock'), 'w')
|
||||
try:
|
||||
fcntl.flock(ctx.dbenv_lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
raise Error(_("Another instance of PISI is running. Only one instance is allowed to modify the PISI database at a time."))
|
||||
|
||||
# write: write access to database environment
|
||||
# writeversion: would you like to be able
|
||||
def init_dbenv(write=False, writeversion=False):
|
||||
if os.access(pisi.context.config.db_dir(), os.R_OK):
|
||||
# try to read version
|
||||
check_dbversion('dbversion', pisi.__dbversion__, write=write, update=writeversion)
|
||||
check_dbversion('filesdbversion', pisi.__filesdbversion__, write=write, update=writeversion)
|
||||
else:
|
||||
raise Error(_('Cannot attain read access to database environment'))
|
||||
if write and os.access(pisi.context.config.db_dir(), os.W_OK):
|
||||
lock_dbenv()
|
||||
ctx.dbenv = dbobj.DBEnv()
|
||||
flags = (db.DB_INIT_MPOOL | # cache
|
||||
db.DB_INIT_TXN | # transaction subsystem
|
||||
db.DB_INIT_LOG | # logging subsystem
|
||||
db.DB_RECOVER | # run normal recovery
|
||||
db.DB_CREATE) # allow db to create files
|
||||
ctx.dbenv.set_cachesize(0, 4*1024*1024)
|
||||
ctx.dbenv.open(pisi.context.config.db_dir(), flags)
|
||||
ctx.dbenv.set_flags(db.DB_LOG_AUTOREMOVE, 1) # clear inactive logs automatically
|
||||
else:
|
||||
# Warn if not on purpose
|
||||
if write:
|
||||
ctx.ui.warning(_("Opening PISI database in read-only mode. Operations that require write access will fail."))
|
||||
ctx.dbenv = None
|
||||
|
||||
#def open(filename, flags='r', mode = 0644, filetype = db.DB_BTREE):
|
||||
# db = LockedDBShelf(None, mode, filetype, None, True)
|
||||
# db.open(filename, filename, filetype, flags, mode)
|
||||
# return db
|
||||
|
||||
class LockedDBShelf(shelve.DBShelf):
|
||||
"""A simple wrapper to implement locking for bsddb's dbshelf"""
|
||||
|
||||
def __init__(self, dbname, mode=0644,
|
||||
filetype=db.DB_BTREE, dbenv = None):
|
||||
if dbenv == None:
|
||||
dbenv = ctx.dbenv
|
||||
shelve.DBShelf.__init__(self, dbenv)
|
||||
filename = join_path(pisi.context.config.db_dir(), dbname + '.bdb')
|
||||
if dbenv and os.access(os.path.dirname(filename), os.W_OK):
|
||||
flags = 'w'
|
||||
elif os.access(filename, os.R_OK):
|
||||
flags = 'r'
|
||||
else:
|
||||
raise Error(_('Cannot attain read or write access to database %s') % dbname)
|
||||
self.open(filename, dbname, filetype, flags, mode)
|
||||
|
||||
def destroy(self):
|
||||
os.unlink(self.filename)
|
||||
#self.close()
|
||||
#self.db.remove(self.filename)
|
||||
|
||||
def __del__(self):
|
||||
# superclass does something funky, we don't need that
|
||||
pass
|
||||
|
||||
def open(self, filename, dbname, filetype, flags=db.DB_CREATE, mode=0644):
|
||||
self.filename = filename
|
||||
self.closed = False
|
||||
if type(flags) == type(''):
|
||||
sflag = flags
|
||||
if sflag == 'r':
|
||||
flags = db.DB_RDONLY
|
||||
elif sflag == 'rw':
|
||||
flags = 0
|
||||
elif sflag == 'w':
|
||||
flags = db.DB_CREATE
|
||||
elif sflag == 'c':
|
||||
flags = db.DB_CREATE
|
||||
elif sflag == 'n':
|
||||
flags = db.DB_TRUNCATE | db.DB_CREATE
|
||||
else:
|
||||
raise Error, _("Flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags")
|
||||
self.flags = flags
|
||||
if self.flags & db.DB_RDONLY == 0:
|
||||
flags |= db.DB_AUTO_COMMIT # use txn subsystem in write mode
|
||||
self.lock()
|
||||
filename = os.path.realpath(filename) # we give absolute path due to dbenv
|
||||
#print 'opening', filename, filetype, flags, mode
|
||||
return self.db.open(filename, None, filetype, flags, mode)
|
||||
|
||||
def lock(self):
|
||||
self.lockfile = file(self.filename + '.lock', 'w')
|
||||
try:
|
||||
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
raise Error(_("Another instance of PISI is running. Only one instance is allowed to modify the PISI database at a time."))
|
||||
|
||||
def close(self):
|
||||
if self.closed:
|
||||
return
|
||||
self.db.close()
|
||||
if self.flags & db.DB_RDONLY == 0:
|
||||
self.unlock()
|
||||
self.closed = True
|
||||
|
||||
def unlock(self):
|
||||
self.lockfile.close()
|
||||
os.unlink(self.filename + '.lock')
|
||||
|
||||
@staticmethod
|
||||
def encodekey(key):
|
||||
'''utility method for dbs that must store unicodes in keys'''
|
||||
if type(key)==types.UnicodeType:
|
||||
return key.encode('utf-8')
|
||||
elif type(key)==types.StringType:
|
||||
return key
|
||||
else:
|
||||
raise Error('Key must be either string or unicode')
|
||||
@@ -1,107 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr
|
||||
|
||||
"""
|
||||
Metadata module provides access to metadata.xml. metadata.xml is
|
||||
generated during the build process of a package and used in the
|
||||
installation. Package repository also uses metadata.xml for building
|
||||
a package index.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile as specfile
|
||||
import pisi.pxml.xmlfile as xmlfile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
import pisi.util as util
|
||||
|
||||
class Source:
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory]
|
||||
t_Homepage = [autoxml.String, autoxml.optional]
|
||||
t_Packager = [specfile.Packager, autoxml.mandatory]
|
||||
|
||||
class Package(specfile.Package):
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
# if this is working autoxml r0Xx0rz
|
||||
t_PartOf = [autoxml.String, autoxml.mandatory]
|
||||
|
||||
t_Build = [ autoxml.Integer, autoxml.optional]
|
||||
t_Distribution = [ autoxml.String, autoxml.mandatory]
|
||||
t_DistributionRelease = [ autoxml.String, autoxml.mandatory]
|
||||
t_Architecture = [ autoxml.String, autoxml.mandatory]
|
||||
t_InstalledSize = [ autoxml.Long, autoxml.mandatory]
|
||||
t_PackageSize = [ autoxml.Long, autoxml.optional]
|
||||
t_PackageURI = [ autoxml.String, autoxml.optional]
|
||||
t_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 += _('Build: % s, Distribution: %s, Dist. Release: %s\n') % \
|
||||
(self.build, self.distribution, self.distributionRelease)
|
||||
s += _('Architecture: %s, Installed Size: %s\n') % \
|
||||
(self.architecture, self.installedSize)
|
||||
return s
|
||||
|
||||
class MetaData(xmlfile.XmlFile):
|
||||
"""Package metadata. Metadata is composed of Specfile and various
|
||||
other information. A metadata has two parts, Source and Package."""
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Source = [ Source, autoxml.mandatory]
|
||||
t_Package = [ Package, autoxml.mandatory]
|
||||
#t_History = [ [Update], autoxml.mandatory]
|
||||
|
||||
def from_spec(self, src, pkg):
|
||||
# this just copies fields, it doesn't fix every necessary field
|
||||
self.source.name = src.name
|
||||
self.source.homepage = src.homepage
|
||||
self.source.packager = src.packager
|
||||
self.package.source = self.source # FIXME: I know that replication sucks here, but this is the easiest for now-- exa
|
||||
self.package.name = pkg.name
|
||||
self.package.summary = pkg.summary
|
||||
self.package.description = pkg.description
|
||||
self.package.icon = pkg.icon
|
||||
self.package.isA = pkg.isA
|
||||
self.package.partOf = pkg.partOf
|
||||
self.package.license = pkg.license
|
||||
self.package.packageDependencies = pkg.packageDependencies
|
||||
self.package.componentDependencies = pkg.componentDependencies
|
||||
self.package.files = pkg.files
|
||||
# FIXME: no need to copy full history with comments
|
||||
self.package.history = src.history
|
||||
self.package.conflicts = pkg.conflicts
|
||||
self.package.providesComar = pkg.providesComar
|
||||
#self.package.requiresComar = pkg.requiresComar
|
||||
self.package.additionalFiles = pkg.additionalFiles
|
||||
|
||||
# FIXME: right way to do it?
|
||||
self.source.version = src.version
|
||||
self.source.release = src.release
|
||||
self.package.version = src.version
|
||||
self.package.release = src.release
|
||||
@@ -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,800 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray at pardus.org.tr>
|
||||
|
||||
"Package Operations: install/remove/upgrade"
|
||||
|
||||
import os
|
||||
import sys
|
||||
ver = sys.version_info
|
||||
if ver[0] <= 2 and ver[1] < 4:
|
||||
from sets import Set as set
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
from pisi.uri import URI
|
||||
import pisi.util as util
|
||||
import pisi.dependency as dependency
|
||||
import pisi.pgraph as pgraph
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
import pisi.atomicoperations as atomicoperations
|
||||
import pisi.ui as ui
|
||||
from pisi.version import Version
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class PisiUpgradeException(pisi.Exception):
|
||||
"""application must reload all pisi modules it imported after receiving
|
||||
this exception"""
|
||||
def __init__(self):
|
||||
pisi.Exception.__init__(self, _("Upgrading PISI requires database rebuild and restart"))
|
||||
|
||||
def upgrade_pisi():
|
||||
ctx.ui.warning(_("PISI package has been upgraded. Rebuilding PISI database."))
|
||||
pisi.api.finalize()
|
||||
os.system('pisi rebuild-db -y')
|
||||
#reload(pisi)
|
||||
#pisi.api.init()
|
||||
#pisi.api.rebuild_db()
|
||||
raise PisiUpgradeException()
|
||||
|
||||
# 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()
|
||||
check_conflicts(order, packagedb)
|
||||
order.reverse()
|
||||
ctx.ui.info(_('Installation order: ') + util.strlist(order) )
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
for x in order:
|
||||
atomicoperations.install_single_file(dfn[x])
|
||||
|
||||
def check_conflict(pkg):
|
||||
conflicts = []
|
||||
|
||||
for c in pkg.conflicts:
|
||||
if ctx.installdb.is_installed(c):
|
||||
conflicts.append(c)
|
||||
|
||||
return conflicts
|
||||
|
||||
def calculate_conflicts(order, packagedb):
|
||||
B_0 = set(order)
|
||||
C = D = set()
|
||||
pkg_conflicts = {}
|
||||
|
||||
for x in order:
|
||||
pkg = packagedb.get_package(x)
|
||||
B_p = set(check_conflict(pkg))
|
||||
if B_p:
|
||||
pkg_conflicts[x] = B_p
|
||||
C = C.union(B_p)
|
||||
|
||||
B_i = B_0.intersection(set(pkg.conflicts))
|
||||
# check if there are any conflicts within the packages that are
|
||||
# going to be installed
|
||||
if B_i:
|
||||
D = D.union(B_i)
|
||||
D.add(pkg.name)
|
||||
|
||||
return (C, D, pkg_conflicts)
|
||||
|
||||
def check_conflicts(order, packagedb):
|
||||
"""check if upgrading to the latest versions will cause havoc
|
||||
done in a simple minded way without regard for dependencies of
|
||||
conflicts, etc."""
|
||||
|
||||
(C, D, pkg_conflicts) = calculate_conflicts(order, packagedb)
|
||||
|
||||
if D:
|
||||
raise Error(_("Selected packages [%s] are in conflict with each other.") %
|
||||
util.strlist(list(D)))
|
||||
|
||||
if pkg_conflicts:
|
||||
conflicts = ""
|
||||
for pkg in pkg_conflicts.keys():
|
||||
conflicts += _("[%s conflicts with: %s]") % (pkg, util.strlist(pkg_conflicts[pkg]))
|
||||
|
||||
ctx.ui.info(_("The following packages have conflicts: %s") %
|
||||
conflicts)
|
||||
|
||||
if not ctx.ui.confirm(_('Remove the following conflicting packages?')):
|
||||
raise Error(_("Conflicts remain"))
|
||||
|
||||
if remove(list(C)) == False:
|
||||
raise Error(_("Conflicts remain"))
|
||||
|
||||
def expand_components(A):
|
||||
Ap = set()
|
||||
for x in A:
|
||||
if ctx.componentdb.has_component(x):
|
||||
Ap = Ap.union(ctx.componentdb.get_union_comp(x).packages)
|
||||
else:
|
||||
Ap.add(x)
|
||||
return Ap
|
||||
|
||||
def is_upgradable(name, ignore_build = False):
|
||||
if not ctx.installdb.is_installed(name):
|
||||
return False
|
||||
(version, release, build) = ctx.installdb.get_version(name)
|
||||
try:
|
||||
pkg = ctx.packagedb.get_package(name)
|
||||
except:
|
||||
return False
|
||||
if ignore_build or (not build) or (not pkg.build):
|
||||
return Version(release) < Version(pkg.release)
|
||||
else:
|
||||
return build < pkg.build
|
||||
|
||||
def upgrade_base(A = set()):
|
||||
ignore_build = ctx.get_option('ignore_build_no')
|
||||
if not ctx.get_option('bypass_safety'):
|
||||
if ctx.componentdb.has_component('system.base'):
|
||||
systembase = set(ctx.componentdb.get_union_comp('system.base').packages)
|
||||
extra_installs = filter(lambda x: not ctx.installdb.is_installed(x), systembase - A)
|
||||
if extra_installs:
|
||||
ctx.ui.warning(_('Safety switch: Following packages in system.base will be installed: ') +
|
||||
util.strlist(extra_installs))
|
||||
G_f, install_order = plan_install_pkg_names(extra_installs)
|
||||
extra_upgrades = filter(lambda x: is_upgradable(x, ignore_build), systembase - set(install_order))
|
||||
if extra_upgrades:
|
||||
ctx.ui.warning(_('Safety switch: Following packages in system.base will be upgraded: ') +
|
||||
util.strlist(extra_upgrades))
|
||||
upgrade_pkg_names(extra_upgrades)
|
||||
# return packages that must be added to any installation
|
||||
return set(install_order + extra_upgrades)
|
||||
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(_('Not re-installing the following packages: ') +
|
||||
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 = A
|
||||
|
||||
ctx.ui.info(_("""Following packages will be installed
|
||||
in the respective order to satisfy dependencies:
|
||||
""") + util.strlist(order))
|
||||
|
||||
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
|
||||
total_size, symbol = util.human_readable_size(total_size)
|
||||
ctx.ui.info(_('Total size of packages: %.2f %s') % (total_size, symbol))
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
if len(order) > len(A_0):
|
||||
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
|
||||
return False
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
pisi_installed = ctx.installdb.is_installed('pisi')
|
||||
|
||||
for x in order:
|
||||
atomicoperations.install_single_name(x, True) # allow reinstalls here
|
||||
|
||||
if 'pisi' in order and pisi_installed:
|
||||
upgrade_pisi()
|
||||
|
||||
def plan_install_pkg_names(A):
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pgraph.PGraph(ctx.packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = ctx.packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies():
|
||||
ctx.ui.debug('checking %s' % str(dep))
|
||||
# we don't deal with already *satisfied* dependencies
|
||||
if not dependency.installed_satisfies_dep(dep):
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
B = Bp
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
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')
|
||||
|
||||
if not A:
|
||||
# if A is empty, then upgrade all packages
|
||||
A = ctx.installdb.list_installed()
|
||||
|
||||
# filter packages that are not upgradable
|
||||
A_0 = A = expand_components(set(A))
|
||||
|
||||
Ap = []
|
||||
for x in A:
|
||||
if x.endswith(ctx.const.package_suffix):
|
||||
ctx.ui.debug(_("Warning: package *name* ends with '.pisi'"))
|
||||
if not ctx.installdb.is_installed(x):
|
||||
ctx.ui.info(_('Package %s is not installed.') % x, True)
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
if ctx.packagedb.has_package(x):
|
||||
pkg = ctx.packagedb.get_package(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is not available in repositories.') % x, True)
|
||||
continue
|
||||
|
||||
if ignore_build or (not build) or (not pkg.build):
|
||||
if Version(release) < Version(pkg.release):
|
||||
Ap.append(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is already at the latest release %s.')
|
||||
% (pkg.name, pkg.release), True)
|
||||
else:
|
||||
if build < pkg.build:
|
||||
Ap.append(x)
|
||||
else:
|
||||
ctx.ui.info(_('Package %s is already at the latest build %s.')
|
||||
% (pkg.name, pkg.build), True)
|
||||
A = set(Ap)
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info(_('No packages to upgrade.'))
|
||||
return True
|
||||
|
||||
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 = A
|
||||
|
||||
ctx.ui.info(_('The following packages will be upgraded: ') +
|
||||
util.strlist(order))
|
||||
|
||||
total_size = sum([ctx.packagedb.get_package(p).packageSize for p in order])
|
||||
total_size, symbol = util.human_readable_size(total_size)
|
||||
ctx.ui.info(_('Total size of packages: %.2f %s') % (total_size, symbol))
|
||||
|
||||
if ctx.get_option('dry_run'):
|
||||
return
|
||||
|
||||
if len(order) > len(A_0):
|
||||
if not ctx.ui.confirm(_('There are extra packages due to dependencies. Do you want to continue?')):
|
||||
return False
|
||||
|
||||
ctx.ui.notify(ui.packagestogo, order = order)
|
||||
|
||||
paths = []
|
||||
for x in order:
|
||||
install_op = atomicoperations.Install.from_name(x)
|
||||
paths.append(install_op.package_fname)
|
||||
|
||||
for path in paths:
|
||||
install_op = atomicoperations.Install(path)
|
||||
install_op.install(True)
|
||||
|
||||
if 'pisi' in order:
|
||||
upgrade_pisi()
|
||||
|
||||
def plan_upgrade(A, ignore_build = False):
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
packagedb = ctx.packagedb
|
||||
|
||||
G_f = pgraph.PGraph(packagedb) # construct G_f
|
||||
|
||||
# find the "install closure" graph of G_f by package
|
||||
# set A using packagedb
|
||||
for x in A:
|
||||
G_f.add_package(x)
|
||||
B = A
|
||||
|
||||
# TODO: conflicts
|
||||
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
for dep in pkg.runtimeDependencies():
|
||||
# add packages that can be upgraded
|
||||
if dependency.repo_satisfies_dep(dep):
|
||||
if ctx.installdb.is_installed(dep.package):
|
||||
if ctx.get_option('eager'):
|
||||
if not is_upgradable(dep.package):
|
||||
continue
|
||||
else:
|
||||
if dependency.installed_satisfies_dep(dep):
|
||||
continue
|
||||
if not dep.package in G_f.vertices():
|
||||
Bp.add(str(dep.package))
|
||||
G_f.add_dep(x, dep)
|
||||
else:
|
||||
raise Error(_('Dependency %s of %s cannot be satisfied') % (dep, x))
|
||||
B = Bp
|
||||
# now, search reverse dependencies to see if anything
|
||||
# should be upgraded
|
||||
B = A
|
||||
while len(B) > 0:
|
||||
Bp = set()
|
||||
for x in B:
|
||||
pkg = packagedb.get_package(x)
|
||||
rev_deps = packagedb.get_rev_deps(x)
|
||||
for (rev_dep, depinfo) in rev_deps:
|
||||
if ctx.get_option('eager'):
|
||||
# add all upgradable reverse deps
|
||||
if is_upgradable(rev_dep):
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
else:
|
||||
# add only installed but unsatisfied reverse dependencies
|
||||
if ctx.installdb.is_installed(rev_dep) and \
|
||||
(not dependency.installed_satisfies_dep(depinfo)):
|
||||
if not dependency.repo_satisfies_dep(depinfo):
|
||||
raise Error(_('Reverse dependency %s of %s cannot be satisfied') % (rev_dep, x))
|
||||
if not rev_dep in G_f.vertices():
|
||||
Bp.add(rev_dep)
|
||||
G_f.add_plain_dep(rev_dep, x)
|
||||
B = Bp
|
||||
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
check_conflicts(order, ctx.packagedb)
|
||||
return G_f, order
|
||||
|
||||
def remove(A):
|
||||
"""remove set A of packages from system (A is a list of package names)"""
|
||||
|
||||
A = [str(x) for x in A]
|
||||
|
||||
# filter packages that are not installed
|
||||
A_0 = A = expand_components(set(A))
|
||||
|
||||
if not ctx.get_option('bypass_safety'):
|
||||
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'):
|
||||
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, bypass_safety = 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
|
||||
|
||||
#if not bypass_safety:
|
||||
# A |= upgrade_base(A)
|
||||
|
||||
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, blah = atomicoperations.build(x)
|
||||
install_pkg_files(package_names) # handle inter-package deps here
|
||||
|
||||
if 'pisi' in order_build or ( ('pisi' in order_inst + order_build) 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,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.
|
||||
#
|
||||
|
||||
# package abstraction
|
||||
# provides methods to add/remove files, extract control files
|
||||
|
||||
# maintainer: baris and meren
|
||||
|
||||
from os.path import join, exists, basename, dirname
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
import pisi.archive as archive
|
||||
from pisi.uri import URI
|
||||
from pisi.metadata import MetaData
|
||||
from pisi.files import Files
|
||||
import pisi.util as util
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Package:
|
||||
"""PISI Package Class provides access to a pisi package (.pisi
|
||||
file)."""
|
||||
def __init__(self, packagefn, mode='r'):
|
||||
self.filepath = packagefn
|
||||
url = URI(packagefn)
|
||||
|
||||
if url.is_remote_file():
|
||||
from fetcher import fetch_url, FetchError
|
||||
dest = ctx.config.packages_dir()
|
||||
self.filepath = join(dest, url.filename())
|
||||
|
||||
# get current package name without version info (like tasma)
|
||||
current_pkg = util.pure_package_name(url.filename())
|
||||
|
||||
# if package is installed (which means we are upgrading it),
|
||||
# calculate needed info
|
||||
if ctx.installdb.is_installed(current_pkg):
|
||||
installed = True
|
||||
|
||||
# calculate currently installed package's name and pisi file's location
|
||||
(version, release, build) = ctx.installdb.get_version(current_pkg)
|
||||
current = util.package_name(current_pkg, version, release, build, prependSuffix=False)
|
||||
current_fn = join(dest, current) + ctx.const.package_suffix
|
||||
|
||||
# calculate xdelta file location (format: oldpackage_newpackage.xdelta)
|
||||
# ex: tasma-1.0.2-4-1_tasma-1.0.3-5-2.xdelta
|
||||
delta = basename(url.filename()).rstrip(ctx.const.package_suffix)
|
||||
delta = "%s_%s" % (current, delta) + ctx.const.xdelta_suffix
|
||||
|
||||
# create xdelta_url for fetching
|
||||
xdelta_url = URI(join(dirname(str(url)), delta))
|
||||
xdelta_fn = join(dest, xdelta_url.filename())
|
||||
else:
|
||||
installed = False
|
||||
|
||||
if installed and exists(current_fn):
|
||||
# if package is installed and old pisi file exists, fetch xdelta
|
||||
if not exists(xdelta_fn):
|
||||
ctx.ui.info(_("Trying to fetch xdelta: %s") % xdelta_url)
|
||||
try:
|
||||
fetch_url(xdelta_url, dest, ctx.ui.Progress)
|
||||
except FetchError,e:
|
||||
ctx.ui.error(_('XDelta %s: not exists') % xdelta_url)
|
||||
else:
|
||||
# generate new one using old pisi file and xdelta
|
||||
util.generate_pisi_file(xdelta_fn, current_fn, self.filepath)
|
||||
else:
|
||||
util.generate_pisi_file(xdelta_fn, current_fn, self.filepath)
|
||||
|
||||
# FIXME: exists is not enough, also sha1sum check needed
|
||||
# when implemented in pisi-index.xml
|
||||
if not exists(self.filepath):
|
||||
fetch_url(url, dest, ctx.ui.Progress)
|
||||
else:
|
||||
ctx.ui.info(_('%s [cached]') % url.filename())
|
||||
|
||||
self.impl = archive.ArchiveZip(self.filepath, 'zip', mode)
|
||||
|
||||
def add_to_package(self, fn, an=None):
|
||||
"""Add a file or directory to package"""
|
||||
self.impl.add_to_archive(fn, an)
|
||||
|
||||
def close(self):
|
||||
"""Close the package archive"""
|
||||
self.impl.close()
|
||||
|
||||
def extract(self, outdir):
|
||||
"""Extract entire package contents to directory"""
|
||||
self.extract_dir('', outdir) # means package root
|
||||
|
||||
def extract_files(self, paths, outdir):
|
||||
"""Extract paths to outdir"""
|
||||
self.impl.unpack_files(paths, outdir)
|
||||
|
||||
def extract_file(self, path, outdir):
|
||||
"""Extract file with path to outdir"""
|
||||
self.extract_files([path], outdir)
|
||||
|
||||
def extract_dir(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
copies the directory archiveroot/dir to outdir"""
|
||||
self.impl.unpack_dir(dir, outdir)
|
||||
|
||||
def extract_install(self, outdir):
|
||||
if self.impl.has_file('install.tar.lzma'):
|
||||
self.extract_file('install.tar.lzma', ctx.config.tmp_dir())
|
||||
tar = archive.ArchiveTar(join(ctx.config.tmp_dir(), 'install.tar.lzma'), 'tarlzma')
|
||||
tar.unpack_dir(outdir)
|
||||
else:
|
||||
self.extract_dir_flat('install', outdir)
|
||||
|
||||
def extract_dir_flat(self, dir, outdir):
|
||||
"""Extract directory recursively, this function
|
||||
unpacks the *contents* of directory archiveroot/dir inside outdir
|
||||
this is the function used by the installer"""
|
||||
self.impl.unpack_dir_flat(dir, outdir)
|
||||
|
||||
def extract_PISI_files(self, outdir):
|
||||
"""Extract PISI control files: metadata.xml, files.xml,
|
||||
action scripts, etc."""
|
||||
self.extract_files([ctx.const.metadata_xml, ctx.const.files_xml], outdir)
|
||||
self.extract_dir('config', outdir)
|
||||
|
||||
def read(self, outdir = None):
|
||||
if not outdir:
|
||||
outdir = ctx.config.tmp_dir()
|
||||
|
||||
# extract control files
|
||||
self.extract_PISI_files(outdir)
|
||||
|
||||
self.metadata = MetaData()
|
||||
self.metadata.read( join(outdir, ctx.const.metadata_xml) )
|
||||
errs = self.metadata.errors()
|
||||
if errs:
|
||||
util.Checks.print_errors(errs)
|
||||
raise Error, _("MetaData format wrong")
|
||||
|
||||
self.files = Files()
|
||||
self.files.read( join(outdir, ctx.const.files_xml) )
|
||||
if self.files.errors():
|
||||
raise Error, _("Invalid %s") % ctx.const.files_xml
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.metadata.package.name + '-' \
|
||||
+ self.metadata.package.version + '-' \
|
||||
+ self.metadata.package.release
|
||||
|
||||
return join( ctx.config.lib_dir(), 'package', packageDir)
|
||||
|
||||
def comar_dir(self):
|
||||
return join(self.pkg_dir(), ctx.const.comar_dir)
|
||||
@@ -1,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.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray at pardus.org.tr>
|
||||
# Baris Metin <baris at pardus.org.tr>
|
||||
|
||||
"""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)
|
||||
self.dr.add_item(dep_name, revdep, repo, txn)
|
||||
if self.dr.has_key(name, repo, txn=txn):
|
||||
self.dr.remove_item(name, repo, txn=txn)
|
||||
ctx.componentdb.remove_package(package_info.partOf, package_info.name, repo, txn)
|
||||
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,82 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# PISI package relation graph that represents the state of packagedb
|
||||
|
||||
from graph import *
|
||||
|
||||
# Cache the results from packagedb queries in a graph
|
||||
|
||||
class PGraph(Digraph):
|
||||
|
||||
def __init__(self, packagedb, 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,842 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Gurer Ozen <gurer@pardus.org.tr>
|
||||
# Bahadir Kandemir <bahadir@haftalik.net>
|
||||
# Baris Metin <baris@pardus.org.tr>
|
||||
|
||||
|
||||
"""
|
||||
autoxml is a metaclass for automatic XML translation, using
|
||||
a miniature type system. (w00t!) This is based on an excellent
|
||||
high-level XML processing prototype that Gurer prepared.
|
||||
|
||||
Method names are mixedCase for compatibility with minidom,
|
||||
an old library.
|
||||
"""
|
||||
|
||||
# System
|
||||
import locale
|
||||
import codecs
|
||||
import types
|
||||
import formatter
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# PiSi
|
||||
import pisi
|
||||
from pisi.pxml.xmlext import *
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
import pisi.context as ctx
|
||||
import pisi.util as util
|
||||
import pisi.oo as oo
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
# requirement specs
|
||||
|
||||
mandatory, optional = range(2) # poor man's enum
|
||||
|
||||
# basic types
|
||||
|
||||
String = types.StringType
|
||||
Text = types.UnicodeType
|
||||
Integer = types.IntType
|
||||
Long = types.LongType
|
||||
Float = types.FloatType
|
||||
|
||||
#class datatype(type):
|
||||
# def __init__(cls, name, bases, dict):
|
||||
# """entry point for metaclass code"""
|
||||
# # standard initialization
|
||||
# super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
class LocalText(dict):
|
||||
"""Handles XML tags with localized text"""
|
||||
|
||||
def __init__(self, tag = "", req = optional):
|
||||
self.tag = tag
|
||||
self.req = req
|
||||
dict.__init__(self)
|
||||
|
||||
def decode(self, node, errs, where = ""):
|
||||
# flags, tag name, instance attribute
|
||||
assert self.tag != ''
|
||||
nodes = getAllNodes(node, self.tag)
|
||||
if not nodes:
|
||||
if self.req == mandatory:
|
||||
errs.append(where + ': ' + _("At least one '%s' tag should have local text") %
|
||||
self.tag )
|
||||
else:
|
||||
for node in nodes:
|
||||
lang = getNodeAttribute(node, 'xml:lang')
|
||||
c = getNodeText(node)
|
||||
if not c:
|
||||
errs.append(where + ': ' + _("'%s' language of tag '%s' is empty") %
|
||||
(lang, self.tag))
|
||||
# FIXME: check for dups and 'en'
|
||||
if not lang:
|
||||
lang = 'en'
|
||||
self[lang] = c
|
||||
|
||||
def encode(self, node, errs):
|
||||
assert self.tag != ''
|
||||
for key in self.iterkeys():
|
||||
newnode = addNode(node, self.tag)
|
||||
setNodeAttribute(newnode, 'xml:lang', key)
|
||||
addText(newnode, '', self[key])
|
||||
|
||||
#FIXME: maybe more appropriate for pisi.util
|
||||
@staticmethod
|
||||
def get_lang():
|
||||
try:
|
||||
(lang, encoding) = locale.getlocale()
|
||||
if not lang:
|
||||
(lang, encoding) = locale.getdefaultlocale()
|
||||
if lang==None: # stupid python means it is C locale
|
||||
return 'en'
|
||||
else:
|
||||
return lang[0:2]
|
||||
except:
|
||||
raise Error(_('LocalText: unable to get either current or default locale'))
|
||||
|
||||
def errors(self, where = unicode()):
|
||||
errs = []
|
||||
langs = [ LocalText.get_lang(), 'en', 'tr', ]
|
||||
if not util.any(lambda x : self.has_key(x), langs):
|
||||
errs.append( where + ': ' + _("Tag should have at least the current locale, or failing that an English or Turkish version"))
|
||||
#FIXME: check if all entries are unicode
|
||||
return errs
|
||||
|
||||
def format(self, f, errs):
|
||||
L = LocalText.get_lang()
|
||||
if self.has_key(L):
|
||||
f.add_flowing_data(self[L])
|
||||
elif self.has_key('en'):
|
||||
# fallback to English, blah
|
||||
f.add_flowing_data(self['en'])
|
||||
elif self.has_key('tr'):
|
||||
# fallback to Turkish
|
||||
f.add_flowing_data(self['tr'])
|
||||
else:
|
||||
errs.append(_("Tag should have at least the current locale, or failing that an English or Turkish version"))
|
||||
|
||||
#FIXME: factor out these common routines
|
||||
def print_text(self, file = sys.stdout):
|
||||
w = Writer(file) # plain text
|
||||
f = formatter.AbstractFormatter(w)
|
||||
errs = []
|
||||
self.format(f, errs)
|
||||
if errs:
|
||||
for x in errs:
|
||||
ctx.ui.warning(x)
|
||||
|
||||
def __str__(self):
|
||||
L = LocalText.get_lang()
|
||||
if self.has_key(L):
|
||||
return unicode(self[L])
|
||||
elif self.has_key('en'):
|
||||
# fallback to English, blah
|
||||
return unicode(self['en'])
|
||||
elif self.has_key('tr'):
|
||||
# fallback to Turkish
|
||||
return unicode(self['tr'])
|
||||
else:
|
||||
return unicode()
|
||||
|
||||
class Writer(formatter.DumbWriter):
|
||||
"""adds unicode support"""
|
||||
|
||||
def __init__(self, file=None, maxcol=78):
|
||||
formatter.DumbWriter.__init__(self, file, maxcol)
|
||||
|
||||
def send_literal_data(self, data):
|
||||
self.file.write(data.encode("utf-8"))
|
||||
i = data.rfind('\n')
|
||||
if i >= 0:
|
||||
self.col = 0
|
||||
data = data[i+1:]
|
||||
data = data.expandtabs()
|
||||
self.col = self.col + len(data)
|
||||
self.atbreak = 0
|
||||
|
||||
|
||||
class autoxml(oo.autosuper, oo.autoprop):
|
||||
"""High-level automatic XML transformation interface for xmlfile.
|
||||
The idea is to declare a class for each XML tag. Inside the
|
||||
class the tags and attributes nested in the tag are further
|
||||
elaborated. A simple example follows:
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
|
||||
This class defines a tag and an attribute nested in Employee
|
||||
class. Name is a string and type is an integer, called basic
|
||||
types.
|
||||
While the tag is mandatory, the attribute may be left out.
|
||||
|
||||
Other basic types supported are: xmlfile.Float, xmlfile.Double
|
||||
and (not implemented yet): xmlfile.Binary
|
||||
|
||||
By default, the class name is taken as the corresponding tag,
|
||||
which may be overridden by defining a tag attribute. Thus,
|
||||
the same tag may also be written as:
|
||||
|
||||
class EmployeeXML:
|
||||
...
|
||||
tag = 'Employee'
|
||||
...
|
||||
|
||||
In addition to basic types, we allow for two kinds of complex
|
||||
types: class types and list types.
|
||||
|
||||
A declared class can be nested in another class as follows
|
||||
|
||||
class Position:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Description = [xmlfile.Text, xmlfile.optional]
|
||||
|
||||
which we can add to our Employee class.
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
t_Position = [Position, xmlfile.mandatory]
|
||||
|
||||
Note some unfortunate redundancy here with Position; this is
|
||||
justified by the implementation (kidding). Still, you might
|
||||
want to assign a different name than the class name that
|
||||
goes in there, which may be fully qualified.
|
||||
|
||||
There is more! Suppose we want to define a company, with
|
||||
of course many employees.
|
||||
|
||||
class Company:
|
||||
__metaclass__ = autoxml
|
||||
t_Employees = [ [Employee], xmlfile.mandatory, 'Employees/Employee']
|
||||
|
||||
Logically, inside the Company/Employees tag, we will have several
|
||||
Employee tags, which are inserted to the Employees instance variable of
|
||||
Company in order of appearance. We can define lists of any other valid
|
||||
type. Here we used a list of an autoxml class defined above.
|
||||
|
||||
The mandatory flag here asserts that at least one such record
|
||||
is to be found.
|
||||
|
||||
You see, it works like magic, when it works of course. All of it
|
||||
done without a single brain exploding.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def __init__(cls, name, bases, dict):
|
||||
"""entry point for metaclass code"""
|
||||
#print 'generating class', name
|
||||
|
||||
# standard initialization
|
||||
super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
xmlfile_support = XmlFile in bases
|
||||
|
||||
cls.autoxml_bases = filter(lambda base: isinstance(base, autoxml), bases)
|
||||
|
||||
#TODO: initialize class attribute __xml_tags
|
||||
#setattr(cls, 'xml_variables', [])
|
||||
|
||||
# default class tag is class name
|
||||
if not dict.has_key('tag'):
|
||||
cls.tag = name
|
||||
|
||||
# generate helper routines, for each XML component
|
||||
names = []
|
||||
inits = []
|
||||
decoders = []
|
||||
encoders = []
|
||||
errorss = []
|
||||
formatters = []
|
||||
#order = dict.keys()
|
||||
#order.sort()
|
||||
|
||||
# read declaration order from source
|
||||
# code contributed by bahadir kandemir
|
||||
from inspect import getsourcelines
|
||||
from itertools import ifilter
|
||||
import re
|
||||
|
||||
fn = re.compile('\s*([tas]_[a-zA-Z]+).*').findall
|
||||
|
||||
lines = filter(fn, getsourcelines(cls)[0])
|
||||
decl_order = map(lambda x:x.split()[0], lines)
|
||||
|
||||
# there should be at most one str member, and it should be
|
||||
# the first to process
|
||||
|
||||
order = filter(lambda x: not x.startswith('s_'), decl_order)
|
||||
|
||||
# find string member
|
||||
str_members = filter(lambda x:x.startswith('s_'), decl_order)
|
||||
if len(str_members)>1:
|
||||
raise Error('Only one str member can be defined')
|
||||
elif len(str_members)==1:
|
||||
order.insert(0, str_members[0])
|
||||
|
||||
for var in order:
|
||||
if var.startswith('t_') or var.startswith('a_') or var.startswith('s_'):
|
||||
name = var[2:]
|
||||
if var.startswith('a_'):
|
||||
x = autoxml.gen_attr_member(cls, name)
|
||||
elif var.startswith('t_'):
|
||||
x = autoxml.gen_tag_member(cls, name)
|
||||
elif var.startswith('s_'):
|
||||
x = autoxml.gen_str_member(cls, name)
|
||||
(name, init, decoder, encoder, errors, format_x) = x
|
||||
names.append(name)
|
||||
inits.append(init)
|
||||
decoders.append(decoder)
|
||||
encoders.append(encoder)
|
||||
errorss.append(errors)
|
||||
formatters.append(format_x)
|
||||
|
||||
# generate top-level helper functions
|
||||
cls.initializers = inits
|
||||
def initialize(self, uri = None, keepDoc = False, tmpDir = '/tmp',
|
||||
**args):
|
||||
if xmlfile_support:
|
||||
# totally dailywtf
|
||||
#if args.has_key('tag'):
|
||||
# tag = args['tag']
|
||||
#else:
|
||||
# tag = self.__class__.tag # ought to be autoxml
|
||||
#XmlFile.__init__(self, tag = tag)
|
||||
if not args.has_key('tag'):
|
||||
XmlFile.__init__(self, tag = cls.tag)
|
||||
for base in cls.autoxml_bases:
|
||||
base.__init__(self)
|
||||
#super(cls, self).__init__(tag = tag) cooperative shit disabled for now
|
||||
for init in inits:#self.__class__.initializers:
|
||||
init(self)
|
||||
for x in args.iterkeys():
|
||||
setattr(self, x, args[x])
|
||||
# init hook
|
||||
if hasattr(self, 'init'):
|
||||
self.init(tag)
|
||||
if xmlfile_support and uri:
|
||||
self.read(uri, keepDoc, tmpDir)
|
||||
|
||||
cls.__init__ = initialize
|
||||
|
||||
cls.decoders = decoders
|
||||
def decode(self, node, errs, where = unicode(cls.tag)):
|
||||
for base in cls.autoxml_bases:
|
||||
base.decode(self, node, errs, where)
|
||||
for decode_member in decoders:#self.__class__.decoders:
|
||||
decode_member(self, node, errs, where)
|
||||
if hasattr(self, 'decode_hook'):
|
||||
self.decode_hook(node, errs, where)
|
||||
cls.decode = decode
|
||||
|
||||
cls.encoders = encoders
|
||||
def encode(self, node, errs):
|
||||
for base in cls.autoxml_bases:
|
||||
base.encode(self, node, errs)
|
||||
for encode_member in encoders:#self.__class__.encoders:
|
||||
encode_member(self, node, errs)
|
||||
if hasattr(self, 'encode_hook'):
|
||||
self.encode_hook(node, errs)
|
||||
cls.encode = encode
|
||||
|
||||
cls.errorss = errorss
|
||||
def errors(self, where = unicode(name)):
|
||||
errs = []
|
||||
for base in cls.autoxml_bases:
|
||||
errs.extend(base.errors(self, where))
|
||||
for errors in errorss:#self.__class__.errorss:
|
||||
errs.extend(errors(self, where))
|
||||
if hasattr(self, 'errors_hook'):
|
||||
errs.extend(self.errors_hook(where))
|
||||
return errs
|
||||
cls.errors = errors
|
||||
def check(self):
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.check: '%s' errors") % len(errs))
|
||||
raise Error(*errs)
|
||||
cls.check = check
|
||||
|
||||
cls.formatters = formatters
|
||||
def format(self, f, errs):
|
||||
for base in cls.autoxml_bases:
|
||||
base.format(self, f, errs)
|
||||
for formatter in formatters:#self.__class__.formatters:
|
||||
formatter(self, f, errs)
|
||||
cls.format = format
|
||||
def print_text(self, file = sys.stdout):
|
||||
w = Writer(file) # plain text
|
||||
f = formatter.AbstractFormatter(w)
|
||||
errs = []
|
||||
self.format(f, errs)
|
||||
if errs:
|
||||
for x in errs:
|
||||
ctx.ui.warning(x)
|
||||
cls.print_text = print_text
|
||||
if not dict.has_key('__str__'):
|
||||
def str(self):
|
||||
strfile = StringIO()
|
||||
self.print_text(strfile)
|
||||
str = strfile.getvalue()
|
||||
strfile.close()
|
||||
return str
|
||||
cls.__str__ = str
|
||||
|
||||
if not dict.has_key('__eq__'):
|
||||
def equal(self, other):
|
||||
for name in names:
|
||||
try:
|
||||
if getattr(self, name) != getattr(other, name):
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
return True
|
||||
def notequal(self, other):
|
||||
return not self.__eq__(other)
|
||||
cls.__eq__ = equal
|
||||
cls.__ne__ = notequal
|
||||
|
||||
if xmlfile_support:
|
||||
def read(self, uri, keepDoc = False, tmpDir = '/tmp',
|
||||
sha1sum = False, compress = None, sign = None, copylocal = False):
|
||||
"read XML file and decode it into a python object"
|
||||
self.readxml(uri, tmpDir, sha1sum=sha1sum,
|
||||
compress=compress, sign=sign, copylocal=copylocal)
|
||||
errs = []
|
||||
self.decode(self.rootNode(), errs)
|
||||
if errs:
|
||||
errs.append(_("autoxml.read: File '%s' has errors") % uri)
|
||||
raise Error(*errs)
|
||||
if hasattr(self, 'read_hook'):
|
||||
self.read_hook(errs)
|
||||
|
||||
if not keepDoc:
|
||||
self.unlink() # get rid of the tree
|
||||
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.read: File '%s' has errors") % uri)
|
||||
raise Error(*errs)
|
||||
|
||||
def write(self, uri, keepDoc = False, tmpDir = '/tmp',
|
||||
sha1sum = False, compress = None, sign = None):
|
||||
"encode the contents of the python object into an XML file"
|
||||
errs = self.errors()
|
||||
if errs:
|
||||
errs.append(_("autoxml.write: object validation has failed"))
|
||||
raise Error(*errs)
|
||||
errs = []
|
||||
self.newDocument()
|
||||
self.encode(self.rootNode(), errs)
|
||||
if hasattr(self, 'write_hook'):
|
||||
self.write_hook(errs)
|
||||
if errs:
|
||||
errs.append(_("autoxml.write: File encoding '%s' has errors") % uri)
|
||||
raise Error(*errs)
|
||||
self.writexml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign)
|
||||
if not keepDoc:
|
||||
self.unlink() # get rid of the tree
|
||||
|
||||
cls.read = read
|
||||
cls.write = write
|
||||
|
||||
|
||||
def gen_attr_member(cls, attr):
|
||||
"""generate readers and writers for an attribute member"""
|
||||
#print 'attr:', attr
|
||||
spec = getattr(cls, 'a_' + attr)
|
||||
tag_type = spec[0]
|
||||
assert type(tag_type) == type(type)
|
||||
def readtext(node, attr):
|
||||
return getNodeAttribute(node, attr)
|
||||
def writetext(node, attr, text):
|
||||
#print 'write attr', attr, text
|
||||
setNodeAttribute(node, attr, text)
|
||||
anonfuns = cls.gen_anon_basic(attr, spec, readtext, writetext)
|
||||
return cls.gen_named_comp(attr, spec, anonfuns)
|
||||
|
||||
def gen_tag_member(cls, tag):
|
||||
"""generate helper funs for tag member of class"""
|
||||
#print 'tag:', tag
|
||||
spec = getattr(cls, 't_' + tag)
|
||||
anonfuns = cls.gen_tag(tag, spec)
|
||||
return cls.gen_named_comp(tag, spec, anonfuns)
|
||||
|
||||
def gen_tag(cls, tag, spec):
|
||||
"""generate readers and writers for the tag"""
|
||||
tag_type = spec[0]
|
||||
if type(tag_type) is types.TypeType and \
|
||||
autoxml.basic_cons_map.has_key(tag_type):
|
||||
def readtext(node, tagpath):
|
||||
#print 'read tag', node, tagpath
|
||||
return getNodeText(node, tagpath)
|
||||
def writetext(node, tagpath, text):
|
||||
#print 'write tag', node, tagpath, text
|
||||
addText(node, tagpath, text)
|
||||
return cls.gen_anon_basic(tag, spec, readtext, writetext)
|
||||
elif type(tag_type) is types.ListType:
|
||||
return cls.gen_list_tag(tag, spec)
|
||||
elif tag_type is LocalText:
|
||||
return cls.gen_insetclass_tag(tag, spec)
|
||||
elif type(tag_type) is autoxml or type(tag_type) is types.TypeType:
|
||||
return cls.gen_class_tag(tag, spec)
|
||||
else:
|
||||
raise Error(_('gen_tag: unrecognized tag type %s in spec') %
|
||||
str(tag_type))
|
||||
|
||||
def gen_str_member(cls, token):
|
||||
"""generate readers and writers for a string member"""
|
||||
spec = getattr(cls, 's_' + token)
|
||||
tag_type = spec[0]
|
||||
assert type(tag_type) == type(type)
|
||||
def readtext(node, blah):
|
||||
#node.normalize() # piksemel doesn't have this
|
||||
return getNodeText(node)
|
||||
def writetext(node, blah, text):
|
||||
addText(node, "", text)
|
||||
anonfuns = cls.gen_anon_basic(token, spec, readtext, writetext)
|
||||
return cls.gen_named_comp(token, spec, anonfuns)
|
||||
|
||||
def gen_named_comp(cls, token, spec, anonfuns):
|
||||
"""generate a named component tag/attr. a decoration of
|
||||
anonymous functions that do not bind to variable names"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = spec[0]
|
||||
req = spec[1]
|
||||
(init_a, decode_a, encode_a, errors_a, format_a) = anonfuns
|
||||
|
||||
def init(self):
|
||||
"""initialize component"""
|
||||
setattr(self, name, init_a())
|
||||
|
||||
def decode(self, node, errs, where):
|
||||
"""decode component from DOM node"""
|
||||
setattr(self, name, decode_a(node, errs, where + '.' + unicode(name)))
|
||||
|
||||
def encode(self, node, errs):
|
||||
"""encode self inside, possibly new, DOM node using xml"""
|
||||
if hasattr(self, name):
|
||||
value = getattr(self, name)
|
||||
else:
|
||||
value = None
|
||||
encode_a(node, value, errs)
|
||||
|
||||
def errors(self, where):
|
||||
"""return errors in the object"""
|
||||
errs = []
|
||||
if hasattr(self, name) and getattr(self, name) != None:
|
||||
value = getattr(self,name)
|
||||
errs.extend(errors_a(value, where + '.' + name))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory variable %s not available') % name)
|
||||
return errs
|
||||
|
||||
def format(self, f, errs):
|
||||
if hasattr(self, name):
|
||||
value = getattr(self,name)
|
||||
f.add_literal_data(token + ': ')
|
||||
format_a(value, f, errs)
|
||||
f.add_line_break()
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory variable %s not available') % name)
|
||||
|
||||
return (name, init, decode, encode, errors, format)
|
||||
|
||||
def mixed_case(cls, identifier):
|
||||
"""helper function to turn token name into mixed case"""
|
||||
if identifier is "":
|
||||
return ""
|
||||
else:
|
||||
if identifier[0]=='I':
|
||||
lowly = 'i' # because of pythonic idiots we can't choose locale in lower
|
||||
else:
|
||||
lowly = identifier[0].lower()
|
||||
return lowly + identifier[1:]
|
||||
|
||||
def tagpath_head_last(cls, tagpath):
|
||||
"returns split of the tag path into last tag and the rest"
|
||||
try:
|
||||
lastsep = tagpath.rindex('/')
|
||||
except ValueError, e:
|
||||
return ('', tagpath)
|
||||
return (tagpath[:lastsep], tagpath[lastsep+1:])
|
||||
|
||||
def parse_spec(cls, token, spec):
|
||||
"""decompose member specification"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = spec[0]
|
||||
req = spec[1]
|
||||
|
||||
if len(spec)>=3:
|
||||
path = spec[2] # an alternative path specified
|
||||
elif type(token_type) is type([]):
|
||||
if type(token_type[0]) is autoxml:
|
||||
# if list of class, by default nested like in most PSPEC
|
||||
path = token + '/' + token_type[0].tag
|
||||
else:
|
||||
# if list of ordinary type, just take the name for
|
||||
path = token
|
||||
elif type(token_type) is autoxml:
|
||||
# if a class, by default its tag
|
||||
path = token_type.tag
|
||||
else:
|
||||
path = token # otherwise it's the same name as
|
||||
# the token
|
||||
return name, token_type, req, path
|
||||
|
||||
def gen_anon_basic(cls, token, spec, readtext, writetext):
|
||||
"""Generate a tag or attribute with one of the basic
|
||||
types like integer. This has got to be pretty generic
|
||||
so that we can invoke it from the complex types such as Class
|
||||
and List. The readtext and writetext arguments achieve
|
||||
the DOM text access for this datatype."""
|
||||
|
||||
name, token_type, req, tagpath = cls.parse_spec(token, spec)
|
||||
|
||||
def initialize():
|
||||
"""default value for all basic types is None"""
|
||||
return None
|
||||
|
||||
def decode(node, errs, where):
|
||||
"""decode from DOM node, the value, watching the spec"""
|
||||
text = readtext(node, token)
|
||||
#print 'read text ', text
|
||||
if text:
|
||||
try:
|
||||
value = autoxml.basic_cons_map[token_type](text)
|
||||
except:
|
||||
value = None
|
||||
errs.append(where + ': ' + _('Type mismatch: read text cannot be decoded'))
|
||||
return value
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory token %s not available') % token)
|
||||
return None
|
||||
|
||||
def encode(node, value, errs):
|
||||
"""encode given value inside DOM node"""
|
||||
if value:
|
||||
writetext(node, token, unicode(value))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory token %s not available') % token)
|
||||
|
||||
def errors(value, where):
|
||||
errs = []
|
||||
if value and not isinstance(value, token_type):
|
||||
errs.append(where + ': ' + _('Type mismatch. Expected %s, got %s') %
|
||||
(token_type, type(value)) )
|
||||
return errs
|
||||
|
||||
def format(value, f, errs):
|
||||
"""format value for pretty printing"""
|
||||
f.add_literal_data(unicode(value))
|
||||
|
||||
return initialize, decode, encode, errors, format
|
||||
|
||||
def gen_class_tag(cls, tag, spec):
|
||||
"""generate a class datatype"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
def make_object():
|
||||
obj = tag_type.__new__(tag_type)
|
||||
obj.__init__(tag=tag, req=req)
|
||||
return obj
|
||||
|
||||
def init():
|
||||
return make_object()
|
||||
|
||||
def decode(node, errs, where):
|
||||
node = getNode(node, tag)
|
||||
if node:
|
||||
try:
|
||||
obj = make_object()
|
||||
obj.decode(node, errs, where)
|
||||
return obj
|
||||
except Error:
|
||||
errs.append(where + ': '+ _('Type mismatch: DOM cannot be decoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory argument not available'))
|
||||
return None
|
||||
|
||||
def encode(node, obj, errs):
|
||||
if node and obj:
|
||||
try:
|
||||
#FIXME: this doesn't look pretty
|
||||
classnode = newNode(node, tag)
|
||||
obj.encode(classnode, errs)
|
||||
addNode(node, '', classnode)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
# note: we can receive an error if obj has no content
|
||||
errs.append(_('Object cannot be encoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
def errors(obj, where):
|
||||
return obj.errors(where)
|
||||
|
||||
def format(obj, f, errs):
|
||||
try:
|
||||
obj.format(f, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
def gen_list_tag(cls, tag, spec):
|
||||
"""generate a list datatype. stores comps in tag/comp_tag"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
pathcomps = path.split('/')
|
||||
comp_tag = pathcomps.pop()
|
||||
list_tagpath = util.makepath(pathcomps, sep='/', relative=True)
|
||||
|
||||
if len(tag_type) != 1:
|
||||
raise Error(_('List type must contain only one element'))
|
||||
|
||||
x = cls.gen_tag(comp_tag, [tag_type[0], mandatory])
|
||||
(init_item, decode_item, encode_item, errors_item, format_item) = x
|
||||
|
||||
def init():
|
||||
return []
|
||||
|
||||
def decode(node, errs, where):
|
||||
l = []
|
||||
nodes = getAllNodes(node, path)
|
||||
#print node, tag + '/' + comp_tag, nodes
|
||||
if len(nodes)==0 and req==mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory list empty'))
|
||||
ix = 1
|
||||
for node in nodes:
|
||||
dummy = newNode(node, "Dummy")
|
||||
addNode(dummy, '', node)
|
||||
l.append(decode_item(dummy, errs, where + unicode("[%s]" % ix)))
|
||||
#l.append(decode_item(node, errs, where + unicode("[%s]" % ix)))
|
||||
ix += 1
|
||||
return l
|
||||
|
||||
def encode(node, l, errs):
|
||||
if l and len(l) > 0:
|
||||
for item in l:
|
||||
if list_tagpath:
|
||||
listnode = addNode(node, list_tagpath, branch = False)
|
||||
else:
|
||||
listnode = node
|
||||
encode_item(listnode, item, errs)
|
||||
#encode_item(node, item, errs)
|
||||
else:
|
||||
if req is mandatory:
|
||||
errs.append(_('Mandatory list empty'))
|
||||
|
||||
def errors(l, where):
|
||||
errs = []
|
||||
ix = 1
|
||||
for node in l:
|
||||
errs.extend(errors_item(node, where + '[%s]' % ix))
|
||||
ix += 1
|
||||
return errs
|
||||
|
||||
def format(l, f, errs):
|
||||
# TODO: indent here
|
||||
ix = 1
|
||||
length = len(l)
|
||||
for node in l:
|
||||
f.add_flowing_data(str(ix) + ': ')
|
||||
format_item(node, f, errs)
|
||||
if ix != length:
|
||||
f.add_flowing_data(', ')
|
||||
ix += 1
|
||||
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
def gen_insetclass_tag(cls, tag, spec):
|
||||
"""generate a class datatype that is highly integrated
|
||||
don't worry if that means nothing to you. this is a silly
|
||||
hack to implement local text quickly. it's not the most
|
||||
elegant thing in the world. it's basically a copy of
|
||||
class tag"""
|
||||
name, tag_type, req, path = cls.parse_spec(tag, spec)
|
||||
|
||||
def make_object():
|
||||
obj = tag_type.__new__(tag_type)
|
||||
obj.__init__(tag=tag, req=req)
|
||||
return obj
|
||||
|
||||
def init():
|
||||
return make_object()
|
||||
|
||||
def decode(node, errs, where):
|
||||
if node:
|
||||
try:
|
||||
obj = make_object()
|
||||
obj.decode(node, errs, where)
|
||||
return obj
|
||||
except Error:
|
||||
errs.append(where + ': ' + _('Type mismatch: DOM cannot be decoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(where + ': ' + _('Mandatory argument not available'))
|
||||
return None
|
||||
|
||||
def encode(node, obj, errs):
|
||||
if node and obj:
|
||||
try:
|
||||
#FIXME: this doesn't look pretty
|
||||
obj.encode(node, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
# note: we can receive an error if obj has no content
|
||||
errs.append(_('Object cannot be encoded'))
|
||||
else:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
def errors(obj, where):
|
||||
return obj.errors(where)
|
||||
|
||||
def format(obj, f, errs):
|
||||
try:
|
||||
obj.format(f, errs)
|
||||
except Error:
|
||||
if req == mandatory:
|
||||
errs.append(_('Mandatory argument not available'))
|
||||
|
||||
return (init, decode, encode, errors, format)
|
||||
|
||||
basic_cons_map = {
|
||||
types.StringType : str,
|
||||
types.UnicodeType : unicode,
|
||||
types.IntType : int,
|
||||
types.FloatType : float,
|
||||
types.LongType : long
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
try:
|
||||
from xmlextpiks import *
|
||||
except:
|
||||
try:
|
||||
#if pisi.context.use_mdom:
|
||||
# gibidi
|
||||
from xmlextcdom import *
|
||||
except:
|
||||
#raise Error('cannot find 4suite implementation')
|
||||
print 'xmlext: piksemel or cDomlette implementation cannot be loaded, falling back to minidom'
|
||||
from xmlextmdom import *
|
||||
@@ -1,218 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr
|
||||
# Gurer Ozen <gurer@pardus.org.tr>
|
||||
# Faik Uygur <faik@pardus.org.tr>
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses cDomlette from 4suite.org
|
||||
adapted from the minidom implementation by Faik Uygur and fixed by Eray
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import Ft
|
||||
from Ft.Xml.Domlette import implementation
|
||||
from Ft.Xml.Domlette import NoExtDtdReader
|
||||
import Ft.Lib
|
||||
import pisi
|
||||
|
||||
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
|
||||
XHTML_NS = unicode(XHTML_NAMESPACE)
|
||||
XML_NS = unicode(XML_NAMESPACE)
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
# Document wrappers
|
||||
|
||||
def newDocument(tag):
|
||||
# create a document and return document root
|
||||
doc = implementation.createDocument(None, tag, None)
|
||||
return doc.documentElement
|
||||
|
||||
def parse(filename):
|
||||
# parse file and return document root
|
||||
try:
|
||||
doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(filename))
|
||||
return doc.documentElement
|
||||
except Ft.FtException, e:
|
||||
raise Error(_("File '%s' has invalid XML: %s") % (filename, str(e)))
|
||||
|
||||
def newNode(node, tag):
|
||||
return node.ownerDocument.createElementNS(None, tag)
|
||||
|
||||
def newTextNode(node, text):
|
||||
return node.ownerDocument.createTextNode(text)
|
||||
|
||||
# Node related wrappers
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if attrname.startswith('xml:'):
|
||||
if node.hasAttributeNS(XML_NS, attrname[4:]):
|
||||
return node.getAttributeNS(XML_NS, attrname[4:])
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
if not node.hasAttributeNS(None, attrname):
|
||||
return None
|
||||
else:
|
||||
return node.getAttributeNS(None, attrname)
|
||||
|
||||
def setNodeAttribute(node, attrname, value):
|
||||
"""get named attribute from DOM node"""
|
||||
if attrname.startswith('xml:'):
|
||||
node.setAttributeNS(XML_NS, attrname, value)
|
||||
else:
|
||||
node.setAttributeNS(None, attrname, value)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
dom = node.ownerDocument
|
||||
for tag in tags:
|
||||
node = node.appendChild(dom.createElementNS(None, tag))
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
newnode = newTextNode(node, text)
|
||||
return addNode(node, tagPath, newnode, branch = branch)
|
||||
@@ -1,197 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr
|
||||
# Gurer Ozen <gurer@pardus.org.tr>
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
note: this is a particularly inefficient implementation of xmlext.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
import pisi
|
||||
|
||||
class XmlError(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
# Document wrappers
|
||||
|
||||
def newDocument(tag):
|
||||
impl = mdom.getDOMImplementation()
|
||||
dom = impl.createDocument(None, tag, None)
|
||||
return dom.documentElement
|
||||
|
||||
def parse(filename):
|
||||
try:
|
||||
dom = mdom.parse(filename)
|
||||
return dom.documentElement
|
||||
except ExpatError, inst:
|
||||
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def newNode(node, tag):
|
||||
return node.ownerDocument.createElement(tag)
|
||||
|
||||
def newTextNode(node, text):
|
||||
return node.ownerDocument.createTextNode(text)
|
||||
|
||||
# Node related wrappers
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
if not node.hasAttribute(attrname):
|
||||
return None
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def getChildElts(node):
|
||||
"""get only child elements"""
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.childNodes
|
||||
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
try:
|
||||
child = node.childNodes[0]
|
||||
except IndexError:
|
||||
return None
|
||||
except AttributeError: # no node by that name
|
||||
return None
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data.strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
|
||||
tags = tagPath.split('/')
|
||||
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
|
||||
nodeList = [node] # basis case
|
||||
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
|
||||
return nodeList
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
dom = node.ownerDocument
|
||||
for tag in tags:
|
||||
node = node.appendChild(dom.createElement(tag))
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.appendChild(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagPath, text, branch = True):
|
||||
newnode = newTextNode(node, text)
|
||||
return addNode(node, tagPath, newnode, branch = branch)
|
||||
@@ -1,171 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray at pardus.org.tr>
|
||||
# Gurer Ozen <gurer at pardus.org.tr>
|
||||
#
|
||||
# piksemel written by Gurer Ozen <gurer at pardus.org.tr>
|
||||
|
||||
"""
|
||||
xmlext is a helper module for accessing XML files using
|
||||
xml.dom.minidom . It is a convenient wrapper for some
|
||||
DOM functions, and provides path based get/add functions
|
||||
as in KDE API.
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses piksemel
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import piksemel as iks
|
||||
|
||||
parse = iks.parse
|
||||
newDocument = iks.newDocument
|
||||
|
||||
def getAllNodes(node, tagPath):
|
||||
"""retrieve all nodes that match a given tag path."""
|
||||
tags = tagPath.split('/')
|
||||
if len(tags) == 0:
|
||||
return []
|
||||
nodeList = [node] # basis case
|
||||
for tag in tags:
|
||||
results = map(lambda x: getTagByName(x, tag), nodeList)
|
||||
nodeList = []
|
||||
for x in results:
|
||||
nodeList.extend(x)
|
||||
pass # emacs indentation error, keep it here
|
||||
if len(nodeList) == 0:
|
||||
return []
|
||||
return nodeList
|
||||
|
||||
def getNodeAttribute(node, attrname):
|
||||
"""get named attribute from DOM node"""
|
||||
return node.getAttribute(attrname)
|
||||
|
||||
def setNodeAttribute(node, attrname, value):
|
||||
"""get named attribute from DOM node"""
|
||||
return node.setAttribute(attrname, value)
|
||||
|
||||
def getChildElts(parent):
|
||||
"""get only child elements"""
|
||||
return [x for x in parent.tags()]
|
||||
|
||||
def getTagByName(parent, childName):
|
||||
return [x for x in parent.tags(childName)]
|
||||
|
||||
def getNodeText(node, tagpath = ""):
|
||||
"""get the first child and expect it to be text!"""
|
||||
if tagpath!="":
|
||||
node = getNode(node, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
child = node.firstChild()
|
||||
if not child:
|
||||
return None
|
||||
if child.type() == iks.DATA:
|
||||
# in any case, strip whitespaces...
|
||||
return child.data().strip()
|
||||
else:
|
||||
raise XmlError(_("getNodeText: Expected text node, got something else!"))
|
||||
|
||||
def getChildText(node_s, tagpath):
|
||||
"""get the text of a child at the end of a tag path"""
|
||||
node = getNode(node_s, tagpath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getNode(node, tagpath):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
|
||||
if tagpath == "":
|
||||
return node
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = tagpath.split('/')
|
||||
assert len(tags)>0
|
||||
|
||||
# iterative code to search for the path
|
||||
for tag in tags:
|
||||
currentNode = None
|
||||
for child in node.tags():
|
||||
if child.name() == tag:
|
||||
currentNode = child
|
||||
break
|
||||
if not currentNode:
|
||||
return None
|
||||
else:
|
||||
node = currentNode
|
||||
return currentNode
|
||||
|
||||
def createTagPath(node, tags):
|
||||
"""create new child at the end of a tag chain starting from node
|
||||
no matter what"""
|
||||
if len(tags)==0:
|
||||
return node
|
||||
for tag in tags:
|
||||
node = node.insertTag(tag)
|
||||
return node
|
||||
|
||||
def addTagPath(node, tags, newnode=None):
|
||||
"""add newnode at the end of a tag chain, smart one"""
|
||||
node = createTagPath(node, tags)
|
||||
if newnode: # node to add specified
|
||||
node.insertNode(newnode)
|
||||
return node
|
||||
|
||||
def addNode(node, tagpath, newnode = None, branch=True):
|
||||
"""add a new node at the end of the tree and returns it
|
||||
if newnode is given adds that node, too."""
|
||||
|
||||
assert type(tagpath)==str
|
||||
tags = []
|
||||
if tagpath != "":
|
||||
tags = tagpath.split('/') # tag chain
|
||||
else:
|
||||
addTagPath(node, [], newnode)
|
||||
return node #FIXME: is this correct!?!?
|
||||
|
||||
assert len(tags)>0 # we want a chain
|
||||
|
||||
# iterative code to search for the path
|
||||
|
||||
if branch:
|
||||
rem = 1
|
||||
else:
|
||||
rem = 0
|
||||
|
||||
while len(tags) > rem:
|
||||
tag = tags.pop(0)
|
||||
nodeList = getTagByName(node, tag)
|
||||
if len(nodeList) == 0: # couldn't find
|
||||
tags.insert(0, tag) # put it back in
|
||||
return addTagPath(node, tags, newnode)
|
||||
else:
|
||||
node = nodeList[len(nodeList)-1] # discard other matches
|
||||
else:
|
||||
# had only one tag..
|
||||
return addTagPath(node, tags, newnode)
|
||||
|
||||
return node
|
||||
|
||||
def addText(node, tagpath, text):
|
||||
node = addNode(node, tagpath)
|
||||
node.insertData(text)
|
||||
|
||||
def newNode(node, tag):
|
||||
return iks.newDocument(tag)
|
||||
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
import pisi.context
|
||||
|
||||
try:
|
||||
from xmlfilepiks import *
|
||||
except:
|
||||
try:
|
||||
#if pisi.context.use_mdom:
|
||||
# zibidi
|
||||
#else:
|
||||
from xmlfilecdom import *
|
||||
except:
|
||||
#raise Error('cannot find 4suite implementation')
|
||||
print 'xmlext: cDomlette/piksemel implementation cannot be loaded, falling back to minidom'
|
||||
from xmlfilemdom import *
|
||||
@@ -1,171 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr
|
||||
# Faik Uygur <faik@pardus.org.tr>
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses cDomlette from 4suite.org
|
||||
adapted from the minidom implementation by Faik Uygur and fixed by Eray
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import codecs
|
||||
import exceptions
|
||||
|
||||
import Ft
|
||||
from Ft.Xml.Domlette import implementation
|
||||
from Ft.Xml.Domlette import NoExtDtdReader
|
||||
from Ft.Xml.Domlette import Print, PrettyPrint
|
||||
from xml.dom import XHTML_NAMESPACE, XML_NAMESPACE
|
||||
|
||||
XHTML_NS = unicode(XHTML_NAMESPACE)
|
||||
XML_NS = unicode(XML_NAMESPACE)
|
||||
|
||||
import pisi
|
||||
from pisi.pxml.xmlextcdom import *
|
||||
from pisi.file import File
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
self.doc = implementation.createDocument(None, self.rootTag, None)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
del self.doc
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc.documentElement
|
||||
|
||||
def readxml(self, uri, tmpDir='/tmp', sha1sum=False, compress=None, sign=None):
|
||||
uri = File.make_uri(uri)
|
||||
localpath = File.download(uri, tmpDir,sha1sum=sha1sum,compress=compress,sign=sign)
|
||||
try:
|
||||
self.doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(localpath))
|
||||
return self.doc.documentElement
|
||||
except Ft.FtException, e:
|
||||
raise Error(_("File '%s' has invalid XML: %s") % (localpath, str(e)) )
|
||||
except exceptions.ValueError, e:
|
||||
raise Error(_("File '%s' not found") % localpath )
|
||||
|
||||
def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None):
|
||||
f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign)
|
||||
PrettyPrint(self.rootNode(), stream = f)
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.rootNode().tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error(_("Root tagname %s not identical to %s as expected") %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
def newNode(self, tag):
|
||||
return self.doc.createElementNS(None, tag)
|
||||
|
||||
def newTextNode(self, text):
|
||||
return self.doc.createTextNode(text)
|
||||
|
||||
def newAttribute(self, attr):
|
||||
return self.doc.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.doc.documentElement, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.doc.documentElement, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return node.childNodes
|
||||
|
||||
# get only elements of a given type
|
||||
#FIXME: this doesn't work
|
||||
def getChildrenWithType(self, tagpath, type):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
return filter(lambda x:x.nodeType == type, node.childNodes)
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
try:
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
|
||||
node.childNodes)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.doc.documentElement, tagPath, newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.doc.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
node = self.addNode(tagPath, self.newTextNode(text))
|
||||
return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -1,162 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr
|
||||
# Gurer Ozen <gurer@pardus.org.tr>
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
note: this is a particularly inefficient implementation of xmlfile
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import xml.dom.minidom as mdom
|
||||
from xml.parsers.expat import ExpatError
|
||||
import codecs
|
||||
|
||||
import pisi
|
||||
from pisi.file import File
|
||||
|
||||
class Error(pisi.Error):
|
||||
"named this way because the class if mostly used with an import *"
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
impl = mdom.getDOMImplementation()
|
||||
self.doc = impl.createDocument(None, self.rootTag, None)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
self.doc.unlink()
|
||||
del self.doc
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc.documentElement
|
||||
|
||||
def readxml(self, uri, tmpDir = '/tmp'):
|
||||
uri = File.make_uri(uri)
|
||||
localpath = File.download(uri, tmpDir)
|
||||
|
||||
try:
|
||||
self.doc = mdom.parse(localpath)
|
||||
except ExpatError, inst:
|
||||
raise Error(_("File '%s' has invalid XML: %s\n") % (fileName,
|
||||
str(inst)))
|
||||
|
||||
def writexml(self, fileName):
|
||||
f = codecs.open(fileName,'w', "utf-8")
|
||||
f.write(self.doc.toprettyxml())
|
||||
f.close()
|
||||
|
||||
def verifyRootTag(self):
|
||||
actual_roottag = self.rootNode().tagName
|
||||
if actual_roottag != self.rootTag:
|
||||
raise Error(_("Root tagname %s not identical to %s as expected") %
|
||||
(actual_roottag, self.rootTag) )
|
||||
|
||||
# construction helpers
|
||||
|
||||
def newNode(self, tag):
|
||||
return self.doc.createElement(tag)
|
||||
|
||||
def newTextNode(self, text):
|
||||
return self.doc.createTextNode(text)
|
||||
|
||||
def newAttribute(self, attr):
|
||||
return self.doc.createAttribute(attr)
|
||||
|
||||
# read helpers
|
||||
|
||||
def getNode(self, tagPath = ""):
|
||||
"""returns the *first* matching node for given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getNode(self.doc.documentElement, tagPath)
|
||||
|
||||
def getNodeText(self, tagPath):
|
||||
"""returns the text of *first* matching node for given tag path."""
|
||||
node = self.getNode(tagPath)
|
||||
if not node:
|
||||
return None
|
||||
return getNodeText(node)
|
||||
|
||||
def getAllNodes(self, tagPath):
|
||||
"""returns all nodes matching a given tag path."""
|
||||
self.verifyRootTag()
|
||||
return getAllNodes(self.doc.documentElement, tagPath)
|
||||
|
||||
def getChildren(self, tagpath):
|
||||
""" returns the children of the given path"""
|
||||
node = self.getNode(tagpath)
|
||||
return node.childNodes
|
||||
|
||||
# get only elements of a given type
|
||||
#FIXME: this doesn't work
|
||||
def getChildrenWithType(self, tagpath, type):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
return filter(lambda x:x.nodeType == type, node.childNodes)
|
||||
|
||||
# get only child elements
|
||||
def getChildElts(self, tagpath):
|
||||
""" returns the children of the given path, only with given type """
|
||||
node = self.getNode(tagpath)
|
||||
try:
|
||||
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
|
||||
node.childNodes)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# write helpers
|
||||
|
||||
def addNode(self, tagPath, newnode = None):
|
||||
"this adds the newnode under given tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(self.doc.documentElement, tagPath, newnode)
|
||||
|
||||
def addNodeUnder(self, node, tagPath, newnode = None):
|
||||
"this adds the new stuff under node and then following tag path"
|
||||
self.verifyRootTag()
|
||||
return addNode(node, tagPath, newnode)
|
||||
|
||||
def addChild(self, newnode):
|
||||
"add a new child node right under root element document"
|
||||
self.doc.documentElement.appendChild(newnode)
|
||||
|
||||
def addText(self, node, text):
|
||||
"add text to node"
|
||||
node.appendChild(self.newTextNode(text))
|
||||
|
||||
def addTextNode(self, tagPath, text):
|
||||
"add a text node with given tag path"
|
||||
node = self.addNode(tagPath, self.newTextNode(text))
|
||||
return node
|
||||
|
||||
def addTextNodeUnder(self, node, tagPath, text):
|
||||
"add a text node under given node with tag path (phew)"
|
||||
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
|
||||
@@ -1,75 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
"""
|
||||
XmlFile class further abstracts a dom object using the
|
||||
high-level dom functions provided in xmlext module (and sorely lacking
|
||||
in xml.dom :( )
|
||||
|
||||
function names are mixedCase for compatibility with minidom,
|
||||
an 'old library'
|
||||
|
||||
this implementation uses piksemel
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import codecs
|
||||
import exceptions
|
||||
|
||||
import piksemel as iks
|
||||
|
||||
import pisi
|
||||
from pisi.file import File
|
||||
from pisi.util import join_path as join
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class XmlFile(object):
|
||||
"""A class to help reading and writing an XML file"""
|
||||
|
||||
def __init__(self, tag):
|
||||
self.rootTag = tag
|
||||
|
||||
def newDocument(self):
|
||||
"""clear DOM"""
|
||||
self.doc = iks.newDocument(self.rootTag)
|
||||
|
||||
def unlink(self):
|
||||
"""deallocate DOM structure"""
|
||||
del self.doc
|
||||
|
||||
def rootNode(self):
|
||||
"""returns root document element"""
|
||||
return self.doc
|
||||
|
||||
def readxml(self, uri, tmpDir='/tmp', sha1sum=False,
|
||||
compress=None, sign=None, copylocal = False):
|
||||
uri = File.make_uri(uri)
|
||||
try:
|
||||
localpath = File.download(uri, tmpDir, sha1sum=sha1sum,
|
||||
compress=compress,sign=sign, copylocal=copylocal)
|
||||
except IOError:
|
||||
raise Error(_("Cannot access URI %s") % (uri) )
|
||||
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()
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
# Author: Eray Ozkural
|
||||
|
||||
import os, fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.lockeddbshelve as shelve
|
||||
import pisi.context as ctx
|
||||
import pisi.packagedb as packagedb
|
||||
import pisi.util as util
|
||||
from pisi.uri import URI
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Repo:
|
||||
def __init__(self, indexuri):
|
||||
self.indexuri = indexuri
|
||||
|
||||
#class HttpRepo
|
||||
|
||||
#class FtpRepo
|
||||
|
||||
#class RemovableRepo
|
||||
|
||||
|
||||
class RepoDB(object):
|
||||
"""RepoDB maps repo ids to repository information"""
|
||||
|
||||
def __init__(self, txn = None):
|
||||
self.d = shelve.LockedDBShelf("repo")
|
||||
def proc(txn):
|
||||
if not self.d.has_key("order", txn):
|
||||
self.d.put("order", [], txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
|
||||
def repo_name(self, ix):
|
||||
l = self.list()
|
||||
return l[ix]
|
||||
|
||||
def swap(self, x, y):
|
||||
def proc(txn):
|
||||
l = self.d.get("order", txn)
|
||||
t = l[x]
|
||||
l[x] = l[y]
|
||||
l[y] = t
|
||||
self.d.put("order", l, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def has_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d.has_key("repo-" + name)
|
||||
|
||||
def get_repo(self, name):
|
||||
name = str(name)
|
||||
return self.d["repo-" + name]
|
||||
|
||||
def add_repo(self, name, repo_info, txn = None):
|
||||
name = str(name)
|
||||
assert (isinstance(repo_info,Repo))
|
||||
def proc(txn):
|
||||
if self.d.has_key("repo-" + name, txn):
|
||||
raise Error(_('Repository %s already exists') % name)
|
||||
self.d.put("repo-" + name, repo_info, txn)
|
||||
order = self.d.get("order", txn)
|
||||
order.append(name)
|
||||
self.d.put("order", order, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def list(self):
|
||||
return self.d["order"]
|
||||
|
||||
def clear(self):
|
||||
self.d.clear()
|
||||
|
||||
def remove_repo(self, name, txn = None):
|
||||
name = str(name)
|
||||
def proc(txn):
|
||||
self.d.delete("repo-" + name, txn)
|
||||
list = self.d.get("order", txn)
|
||||
list.remove(name)
|
||||
self.d.put("order", list, txn)
|
||||
ctx.packagedb.remove_repo(name, txn=txn)
|
||||
ctx.sourcedb.remove_repo(name, txn=txn)
|
||||
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,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.
|
||||
#
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
# API
|
||||
|
||||
from invertedindex import InvertedIndex
|
||||
import preprocess as p
|
||||
|
||||
def init(ids, langs):
|
||||
"initialize databases"
|
||||
|
||||
assert type(ids)==type([])
|
||||
assert type(langs)==type([])
|
||||
|
||||
ctx.invidx = {}
|
||||
for id in ids:
|
||||
ctx.invidx[id] = {}
|
||||
for lang in langs:
|
||||
ctx.invidx[id][lang] = InvertedIndex(id, lang)
|
||||
|
||||
def finalize():
|
||||
import pisi.context as ctx
|
||||
|
||||
if ctx.invidx:
|
||||
for id in ctx.invidx.iterkeys():
|
||||
for lang in ctx.invidx[id].iterkeys():
|
||||
ctx.invidx[id][lang].close()
|
||||
ctx.invidx = {}
|
||||
|
||||
def add_doc(id, lang, docid, str, 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,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.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
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,34 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
#
|
||||
# rev 1: very simple tokenization, for testing
|
||||
|
||||
import string
|
||||
|
||||
def tokenize(lang, str):
|
||||
if type(str) != type(unicode()):
|
||||
str = unicode(str)
|
||||
tokens = []
|
||||
token = unicode()
|
||||
for x in str:
|
||||
if x in string.whitespace or x in string.punctuation:
|
||||
if len(token) > 0:
|
||||
tokens.append(token)
|
||||
token = unicode()
|
||||
else:
|
||||
token += x
|
||||
|
||||
if token:
|
||||
tokens.append(token)
|
||||
|
||||
return tokens
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@pardus.org.tr>
|
||||
# Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
# python standard library
|
||||
|
||||
from os.path import join
|
||||
from os import access, R_OK
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
from pisi.archive import Archive
|
||||
from pisi.uri import URI
|
||||
from pisi.fetcher import fetch_url
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class SourceArchive:
|
||||
"""source archive. this is a class responsible for fetching
|
||||
and unpacking a source archive"""
|
||||
def __init__(self, spec, pkg_work_dir):
|
||||
self.url = URI(spec.source.archive.uri)
|
||||
self.pkg_work_dir = pkg_work_dir
|
||||
self.archiveFile = join(ctx.config.archives_dir(), self.url.filename())
|
||||
self.archive = spec.source.archive
|
||||
|
||||
def fetch(self, interactive=True):
|
||||
if not self.is_cached(interactive):
|
||||
if interactive:
|
||||
progress = ctx.ui.Progress
|
||||
else:
|
||||
progress = None
|
||||
fetch_url(self.url, ctx.config.archives_dir(), progress)
|
||||
|
||||
def is_cached(self, interactive=True):
|
||||
if not access(self.archiveFile, R_OK):
|
||||
return False
|
||||
|
||||
# check hash
|
||||
if util.check_file_hash(self.archiveFile, self.archive.sha1sum):
|
||||
if interactive:
|
||||
ctx.ui.info(_('%s [cached]') % self.archive.name)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def unpack(self, clean_dir=True):
|
||||
|
||||
# check archive file's integrity
|
||||
if not util.check_file_hash(self.archiveFile, self.archive.sha1sum):
|
||||
raise Error, _("unpack: check_file_hash failed")
|
||||
|
||||
archive = Archive(self.archiveFile, self.archive.type)
|
||||
archive.unpack(self.pkg_work_dir, clean_dir)
|
||||
@@ -1,111 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@pardus.org.tr>
|
||||
|
||||
"""
|
||||
package source database
|
||||
interface for update/query to local package repository
|
||||
we basically store everything in sourceinfo class
|
||||
yes, we are cheap
|
||||
to handle multiple repositories, for sources, we
|
||||
store a set of repositories in which the source appears.
|
||||
the actual guy to take is determined from the repo order.
|
||||
"""
|
||||
|
||||
import os
|
||||
import fcntl
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.util as util
|
||||
import pisi.context as ctx
|
||||
import pisi.lockeddbshelve as shelve
|
||||
import pisi.repodb
|
||||
from pisi.itembyrepodb import ItemByRepoDB
|
||||
|
||||
class NotfoundError(pisi.Error):
|
||||
pass
|
||||
|
||||
class SourceDB(object):
|
||||
|
||||
def __init__(self):
|
||||
self.d = ItemByRepoDB('source')
|
||||
self.dpkgtosrc = ItemByRepoDB('pkgtosrc')
|
||||
|
||||
def close(self):
|
||||
self.d.close()
|
||||
self.dpkgtosrc.close()
|
||||
|
||||
def list(self):
|
||||
return self.d.list()
|
||||
|
||||
def has_spec(self, name, repo=None, txn=None):
|
||||
return self.d.has_key(name, repo, txn)
|
||||
|
||||
def get_spec(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item(name, repo, txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise NotfoundError(_("Source package %s not found") % name)
|
||||
|
||||
def get_spec_repo(self, name, repo=None, txn = None):
|
||||
try:
|
||||
return self.d.get_item_repo(name, repo, txn)
|
||||
except pisi.itembyrepodb.NotfoundError, e:
|
||||
raise NotfoundError(_("Source package %s not found") % name)
|
||||
|
||||
def pkgtosrc(self, name, txn = None):
|
||||
return self.dpkgtosrc.get_item(name, txn=txn)
|
||||
|
||||
def add_spec(self, spec, repo, txn = None):
|
||||
assert not spec.errors()
|
||||
name = str(spec.source.name)
|
||||
def proc(txn):
|
||||
self.d.add_item(name, spec, repo, txn)
|
||||
for pkg in spec.packages:
|
||||
self.dpkgtosrc.add_item(pkg.name, name, repo, txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_spec(self, name, repo, txn = None):
|
||||
name = str(name)
|
||||
def proc(txn):
|
||||
assert self.has_spec(name, txn=txn)
|
||||
spec = self.d.get_item(name, repo, txn)
|
||||
self.d.remove_item(name, txn=txn)
|
||||
for pkg in spec.packages:
|
||||
self.dpkgtosrc.remove_item_repo(pkg.name, repo, txn)
|
||||
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
def remove_repo(self, repo, txn = None):
|
||||
def proc(txn):
|
||||
self.d.remove_repo(repo, txn=txn)
|
||||
self.dpkgtosrc.remove_repo(repo, txn=txn)
|
||||
self.d.txn_proc(proc, txn)
|
||||
|
||||
sourcedb = None
|
||||
|
||||
def init():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
return sourcedb
|
||||
|
||||
sourcedb = SourceDB()
|
||||
return sourcedb
|
||||
|
||||
def finalize():
|
||||
global sourcedb
|
||||
if sourcedb:
|
||||
sourcedb.close()
|
||||
sourcedb = None
|
||||
@@ -1,274 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr>
|
||||
|
||||
"""
|
||||
Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
|
||||
files are specification files for PISI source packages. This module
|
||||
provides read and write routines for PSPEC files.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# standard python modules
|
||||
from os.path import basename
|
||||
|
||||
# pisi modules
|
||||
from pisi.pxml.xmlfile import XmlFile
|
||||
import pisi.pxml.autoxml as autoxml
|
||||
import pisi.context as ctx
|
||||
from pisi.dependency import Dependency
|
||||
import pisi.dependency
|
||||
import pisi.component as component
|
||||
from pisi.util import Checks
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
__metaclass__ = autoxml.autoxml
|
||||
|
||||
class Packager:
|
||||
|
||||
t_Name = [autoxml.Text, autoxml.mandatory]
|
||||
t_Email = [autoxml.String, autoxml.mandatory]
|
||||
|
||||
def __str__(self):
|
||||
s = "%s <%s>" % (self.name, self.email)
|
||||
return s
|
||||
|
||||
|
||||
class AdditionalFile:
|
||||
|
||||
s_Filename = [autoxml.String, autoxml.mandatory]
|
||||
a_target = [autoxml.String, autoxml.mandatory]
|
||||
a_permission = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = "%s -> %s " % (self.filename, self.target)
|
||||
if self.permission:
|
||||
s += '(%s)' % self.permission
|
||||
return s
|
||||
|
||||
|
||||
class Patch:
|
||||
|
||||
s_Filename = [autoxml.String, autoxml.mandatory]
|
||||
a_compressionType = [autoxml.String, autoxml.optional]
|
||||
a_level = [autoxml.Integer, autoxml.optional]
|
||||
a_target = [autoxml.String, autoxml.optional]
|
||||
|
||||
#FIXME: what's the cleanest way to give a default value for reading level?
|
||||
#def decode_hook(self, node, errs, where):
|
||||
# if self.level == None:
|
||||
# self.level = 0
|
||||
|
||||
def __str__(self):
|
||||
s = self.filename
|
||||
if self.compressionType:
|
||||
s += ' (' + self.compressionType + ')'
|
||||
if self.level:
|
||||
s += ' level:' + self.level
|
||||
if self.target:
|
||||
s += ' target:' + self.target
|
||||
return s
|
||||
|
||||
|
||||
class Update:
|
||||
|
||||
a_release = [autoxml.String, autoxml.mandatory]
|
||||
t_Date = [autoxml.String, autoxml.mandatory]
|
||||
t_Version = [autoxml.String, autoxml.mandatory]
|
||||
t_Type = [autoxml.String, autoxml.optional]
|
||||
t_Name = [autoxml.Text, autoxml.optional]
|
||||
t_Email = [autoxml.String, autoxml.optional]
|
||||
t_Comment = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.date
|
||||
s += ", ver=" + self.version
|
||||
s += ", rel=" + self.release
|
||||
if self.type:
|
||||
s += ", type=" + self.type
|
||||
return s
|
||||
|
||||
|
||||
class Path:
|
||||
|
||||
s_Path = [autoxml.String, autoxml.mandatory]
|
||||
a_fileType = [autoxml.String, autoxml.optional]
|
||||
a_permanent = [autoxml.String, autoxml.optional]
|
||||
|
||||
def __str__(self):
|
||||
s = self.path
|
||||
s += ", type=" + self.fileType
|
||||
return s
|
||||
|
||||
|
||||
class ComarProvide:
|
||||
|
||||
s_om = [autoxml.String, autoxml.mandatory]
|
||||
a_script = [autoxml.String, autoxml.mandatory]
|
||||
|
||||
def __str__(self):
|
||||
# FIXME: descriptive enough?
|
||||
s = self.script
|
||||
s += ' (' + self.om + ')'
|
||||
return s
|
||||
|
||||
|
||||
class Archive:
|
||||
|
||||
s_uri = [ autoxml.String, autoxml.mandatory ]
|
||||
a_type =[ autoxml.String, autoxml.mandatory ]
|
||||
a_sha1sum =[ autoxml.String, autoxml.mandatory ]
|
||||
|
||||
def decode_hook(self, node, errs, where):
|
||||
self.name = basename(self.uri)
|
||||
|
||||
def __str__(self):
|
||||
s = _('URI: %s, type: %s, sha1sum: %s') % (self.uri, self.type, self.sha1sum)
|
||||
return s
|
||||
|
||||
|
||||
class Source:
|
||||
|
||||
t_Name = [autoxml.String, autoxml.mandatory]
|
||||
t_Homepage = [autoxml.String, autoxml.optional]
|
||||
t_Packager = [Packager, autoxml.mandatory]
|
||||
t_Summary = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_Description = [autoxml.LocalText, autoxml.mandatory]
|
||||
t_IsA = [ [autoxml.String], autoxml.optional]
|
||||
t_PartOf = [autoxml.String, autoxml.optional]
|
||||
t_Icon = [ autoxml.String, autoxml.optional]
|
||||
t_License = [ [autoxml.String], autoxml.mandatory]
|
||||
t_Archive = [Archive, autoxml.mandatory ]
|
||||
t_Patches = [ [Patch], autoxml.optional]
|
||||
t_BuildDependencies = [ [Dependency], autoxml.optional]
|
||||
t_Version = [ autoxml.String, autoxml.optional]
|
||||
t_Release = [ autoxml.String, autoxml.optional]
|
||||
t_SourceURI = [ autoxml.String, autoxml.optional ] # used in index
|
||||
|
||||
|
||||
class Package:
|
||||
|
||||
t_Name = [ autoxml.String, autoxml.mandatory ]
|
||||
t_Summary = [ autoxml.LocalText, autoxml.optional ]
|
||||
t_Description = [ autoxml.LocalText, autoxml.optional ]
|
||||
t_IsA = [ [autoxml.String], autoxml.optional]
|
||||
t_PartOf = [autoxml.String, autoxml.optional]
|
||||
t_License = [ [autoxml.String], autoxml.optional]
|
||||
t_Icon = [ autoxml.String, autoxml.optional]
|
||||
t_PackageDependencies = [ [Dependency], autoxml.optional, "RuntimeDependencies/Dependency"]
|
||||
t_ComponentDependencies = [ [autoxml.String], autoxml.optional, "RuntimeDependencies/Component"]
|
||||
t_Files = [ [Path], autoxml.optional]
|
||||
t_Conflicts = [ [autoxml.String], autoxml.optional, "Conflicts/Package"]
|
||||
t_ProvidesComar = [ [ComarProvide], autoxml.optional, "Provides/COMAR"]
|
||||
#t_RequiresComar = [ [autoxml.String], autoxml.mandatory, "Requires/COMAR"]
|
||||
t_AdditionalFiles = [ [AdditionalFile], autoxml.optional]
|
||||
t_History = [ [Update], autoxml.optional]
|
||||
|
||||
# FIXME: needed in build process, to distinguish dynamically generated debug packages.
|
||||
# find a better way to do this.
|
||||
debug_package = False
|
||||
|
||||
def runtimeDependencies(self):
|
||||
deps = self.packageDependencies
|
||||
deps += [ ctx.componentdb.get_component[x].packages for x in self.componentDependencies ]
|
||||
return deps
|
||||
|
||||
def pkg_dir(self):
|
||||
packageDir = self.name + '-' \
|
||||
+ self.version + '-' \
|
||||
+ self.release
|
||||
|
||||
return util.join_path( ctx.config.lib_dir(), 'package', packageDir)
|
||||
|
||||
def installable(self):
|
||||
"""calculate if pkg is installable currently"""
|
||||
deps = self.runtimeDependencies()
|
||||
return pisi.dependency.satisfies_dependencies(self.name, deps)
|
||||
|
||||
def __str__(self):
|
||||
s = _('Name: %s, version: %s, release: %s, build %s\n') % (
|
||||
self.name, self.version, self.release, self.build)
|
||||
s += _('Summary: %s\n') % unicode(self.summary)
|
||||
s += _('Description: %s\n') % unicode(self.description)
|
||||
s += _('Component: %s\n') % unicode(self.partOf)
|
||||
s += _('Provides: ')
|
||||
for x in self.providesComar:
|
||||
s += x.om
|
||||
return s + '\n'
|
||||
|
||||
|
||||
class SpecFile(XmlFile):
|
||||
__metaclass__ = autoxml.autoxml #needed when we specify a superclass
|
||||
|
||||
tag = "PISI"
|
||||
|
||||
t_Source = [ Source, autoxml.mandatory]
|
||||
t_Packages = [ [Package], autoxml.mandatory, "Package"]
|
||||
t_History = [ [Update], autoxml.mandatory]
|
||||
t_Components = [ [component.Component], autoxml.optional, "Component"]
|
||||
|
||||
def read_hook(self, errs):
|
||||
"""Read PSPEC file"""
|
||||
self.merge_tags()
|
||||
self.override_tags()
|
||||
|
||||
def merge_tags(self):
|
||||
"""Merge tags from Source in Packages. Some tags in Packages merged
|
||||
with the tags from Source. There is a more detailed
|
||||
description in documents."""
|
||||
|
||||
# FIXME: copy only needed information
|
||||
# no need to keep full history with comments in metadata.xml
|
||||
self.source.history = self.history
|
||||
|
||||
# To avoid tag duplication in PSPEC we need to get
|
||||
# the last version and release information
|
||||
# from the most recent History/Update.
|
||||
if not self.source.version:
|
||||
self.source.version = self.history[0].version
|
||||
if not self.source.release:
|
||||
self.source.release = self.history[0].release
|
||||
|
||||
tmp = []
|
||||
for pkg in self.packages:
|
||||
pkg.isA.extend(self.source.isA)
|
||||
pkg.history = self.history
|
||||
tmp.append(pkg)
|
||||
self.packages = tmp
|
||||
|
||||
def override_tags(self):
|
||||
"""Override tags from Source in Packages. Some tags in Packages
|
||||
overrides the tags from Source. There is a more detailed
|
||||
description in documents."""
|
||||
|
||||
tmp = []
|
||||
for pkg in self.packages:
|
||||
if not pkg.summary:
|
||||
pkg.summary = self.source.summary
|
||||
if not pkg.description:
|
||||
pkg.description = self.source.description
|
||||
if not pkg.partOf:
|
||||
pkg.partOf = self.source.partOf
|
||||
if not pkg.license:
|
||||
pkg.license = self.source.license
|
||||
if not pkg.icon:
|
||||
pkg.icon = self.source.icon
|
||||
tmp.append(pkg)
|
||||
self.packages = tmp
|
||||
@@ -1,103 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# generic user interface
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# A. Murat Eren <meren@pardus.org.tr>
|
||||
|
||||
import sys
|
||||
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
(installed, upgraded, removed, installing, removing,
|
||||
configuring, configured, extracting, downloading, packagestogo) = range(10)
|
||||
|
||||
class UI(object):
|
||||
"Abstract class for UI operations, derive from this."
|
||||
|
||||
class Progress:
|
||||
def __init__(self, totalsize, existsize = 0):
|
||||
self.totalsize = totalsize
|
||||
try:
|
||||
self.percent = (existsize * 100) / totalsize
|
||||
except:
|
||||
self.percent = 0
|
||||
|
||||
def update(self, size):
|
||||
if not self.totalsize:
|
||||
return 100
|
||||
try:
|
||||
self.percent = (size * 100) / self.totalsize
|
||||
except:
|
||||
self.percent = 0
|
||||
return self.percent
|
||||
|
||||
def __init__(self, debuggy = False, verbose = False):
|
||||
self.show_debug = debuggy
|
||||
self.show_verbose = verbose
|
||||
|
||||
def close(self):
|
||||
"cleanup stuff here"
|
||||
pass
|
||||
|
||||
def set_verbose(self, flag):
|
||||
self.show_verbose = flag
|
||||
|
||||
def set_debug(self, flag):
|
||||
self.show_debug = flag
|
||||
|
||||
def info(self, msg, verbose = False, noln = False):
|
||||
"give an informative message"
|
||||
pass
|
||||
|
||||
def ack(self, msg):
|
||||
"inform the user of an important event and wait for acknowledgement"
|
||||
pass
|
||||
|
||||
def debug(self, msg):
|
||||
"show debugging info"
|
||||
if self.show_debug:
|
||||
self.info('DEBUG: ' + msg)
|
||||
|
||||
def warning(self,msg):
|
||||
"warn the user"
|
||||
pass
|
||||
|
||||
def error(self,msg):
|
||||
"inform a (possibly fatal) error"
|
||||
pass
|
||||
|
||||
#FIXME: merge this with info, this just means "important message"
|
||||
def action(self,msg):
|
||||
"uh?"
|
||||
pass
|
||||
|
||||
def choose(self, msg, list):
|
||||
"ask the user to choose from a list of alternatives"
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
"ask a yes/no question"
|
||||
pass
|
||||
|
||||
def display_progress(self, pd):
|
||||
"display progress"
|
||||
pass
|
||||
|
||||
def status(self, msg = None):
|
||||
"set status, if not given clear it"
|
||||
pass
|
||||
|
||||
def notify(self, event, **keywords):
|
||||
"notify UI of a significant event"
|
||||
pass
|
||||
@@ -1,109 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Simplifies working with URLs, purl module provides common URL
|
||||
# parsing and processing
|
||||
|
||||
from urlparse import urlparse
|
||||
from os.path import basename
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
class URI(object):
|
||||
"""URI class provides a URL parser and simplifies working with
|
||||
URLs."""
|
||||
|
||||
def __init__(self, uri=None):
|
||||
if uri:
|
||||
self.set_uri(str(uri))
|
||||
else:
|
||||
self.__scheme = None
|
||||
self.__location = None
|
||||
self.__path = None
|
||||
self.__filename = None
|
||||
self.__params = None
|
||||
self.__query = None
|
||||
self.__fragment = None
|
||||
self.__uri = None
|
||||
|
||||
self.__authinfo = None
|
||||
|
||||
def get_uri(self):
|
||||
if self.__uri:
|
||||
return self.__uri
|
||||
return None
|
||||
|
||||
def set_uri(self, uri):
|
||||
# (scheme, location, path, params, query, fragment)
|
||||
uri = str(uri)
|
||||
u = urlparse(uri, "file")
|
||||
self.__scheme = u[0]
|
||||
self.__location = u[1]
|
||||
self.__path = u[2]
|
||||
self.__filename = basename(self.__path)
|
||||
self.__params = u[3]
|
||||
self.__query = u[4]
|
||||
self.__fragment = u[5]
|
||||
|
||||
self.__uri = uri
|
||||
|
||||
def is_local_file(self):
|
||||
if self.scheme() == "file":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_remote_file(self):
|
||||
return not self.is_local_file()
|
||||
|
||||
def is_absolute_path(self):
|
||||
return util.absolute_path(self.__path)
|
||||
|
||||
def is_relative_path(self):
|
||||
return not self.is_absolute_path()
|
||||
|
||||
def set_auth_info(self, authTuple):
|
||||
if not isinstance(authTuple, tuple):
|
||||
raise Exception, _("setAuthInfo needs a tuple (user, pass)")
|
||||
self.__authinfo = authTuple
|
||||
|
||||
def auth_info(self):
|
||||
return self.__authinfo
|
||||
|
||||
def scheme(self):
|
||||
return self.__scheme
|
||||
|
||||
def location(self):
|
||||
return self.__location
|
||||
|
||||
def path(self):
|
||||
return self.__path
|
||||
|
||||
def filename(self):
|
||||
return self.__filename
|
||||
|
||||
def params(self):
|
||||
return self.__params
|
||||
|
||||
def query(self):
|
||||
return self.__query
|
||||
|
||||
def fragment(self):
|
||||
return self.__fragment
|
||||
|
||||
def __str__(self):
|
||||
return self.get_uri()
|
||||
|
||||
uri = property(get_uri, set_uri)
|
||||
@@ -1,658 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# misc. utility functions, including process and file utils
|
||||
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr>
|
||||
# S. Caglar Onur <caglar@pardus.org.tr>
|
||||
# A. Murat Eren <meren@pardus.org.tr>
|
||||
|
||||
# standard python modules
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import sha
|
||||
import shutil
|
||||
import string
|
||||
import statvfs
|
||||
import operator
|
||||
import subprocess
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
# pisi modules
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
class FileError(Error):
|
||||
pass
|
||||
|
||||
|
||||
#########################
|
||||
# spec validation utility #
|
||||
#########################
|
||||
|
||||
class Checks:
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
|
||||
def add(self, err):
|
||||
self.list.append(err)
|
||||
|
||||
def join(self, list):
|
||||
self.list.extend(list)
|
||||
|
||||
def has_tag(self, var, section, name):
|
||||
if not var:
|
||||
self.list.append(_("%s section should have a '%s' tag") % (section, name))
|
||||
|
||||
def has_error():
|
||||
return len(self.list)>0
|
||||
|
||||
def print_errors(list):
|
||||
for x in list:
|
||||
ctx.ui.error(x)
|
||||
print_errors = staticmethod(print_errors)
|
||||
|
||||
#########################
|
||||
# string/list/functional#
|
||||
#########################
|
||||
|
||||
def every(pred, seq):
|
||||
return reduce(operator.and_, map(pred, seq))
|
||||
|
||||
def any(pred, seq):
|
||||
return reduce(operator.or_, map(pred, seq))
|
||||
|
||||
def unzip(seq):
|
||||
return zip(*seq)
|
||||
|
||||
def concat(l):
|
||||
'''concatenate a list of lists'''
|
||||
return reduce( operator.concat, l )
|
||||
|
||||
def strlist(l):
|
||||
"""concatenate string reps of l's elements"""
|
||||
return "".join(map(lambda x: str(x) + ' ', l))
|
||||
|
||||
def multisplit(str, chars):
|
||||
""" split str with any of chars"""
|
||||
l = [str]
|
||||
for c in chars:
|
||||
l = concat(map(lambda x:x.split(c), l))
|
||||
return l
|
||||
|
||||
def same(l):
|
||||
'''check if all elements of a sequence are equal'''
|
||||
if len(l)==0:
|
||||
return True
|
||||
else:
|
||||
last = l.pop()
|
||||
for x in l:
|
||||
if x!=last:
|
||||
return False
|
||||
return True
|
||||
|
||||
def prefix(a, b):
|
||||
'''check if sequence a is a prefix of sequence b'''
|
||||
if len(a)>len(b):
|
||||
return False
|
||||
for i in range(0,len(a)):
|
||||
if a[i]!=b[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def remove_prefix(a,b):
|
||||
"remove prefix a from sequence b"
|
||||
assert prefix(a,b)
|
||||
return b[len(a):]
|
||||
|
||||
def human_readable_size(size = 0):
|
||||
symbols, depth = [' B', 'KB', 'MB', 'GB'], 0
|
||||
|
||||
while size > 1000 and depth < 3:
|
||||
size = float(size / 1024)
|
||||
depth += 1
|
||||
|
||||
return size, symbols[depth]
|
||||
|
||||
def human_readable_rate(size = 0):
|
||||
x = human_readable_size(size)
|
||||
return x[0], x[1] + '/s'
|
||||
|
||||
##############################
|
||||
# Process Releated Functions #
|
||||
##############################
|
||||
|
||||
#FIXME: the added flags make out and err defunct, they should be
|
||||
#split to another function if needed, this function runs a command
|
||||
#noninteractively, then returns return value, standard output and
|
||||
# error. the return spec must not change.
|
||||
def run_batch(cmd, realtime = False):
|
||||
"""run command non-interactively/realtime and report return value and output"""
|
||||
ctx.ui.info(_('Running ') + cmd, verbose=True)
|
||||
|
||||
if ctx.stdout:
|
||||
stdout = ctx.stdout
|
||||
else:
|
||||
stdout = subprocess.PIPE
|
||||
|
||||
if ctx.stderr:
|
||||
stderr = ctx.stderr
|
||||
else:
|
||||
stderr = subprocess.STDOUT
|
||||
|
||||
out = err = ""
|
||||
p = subprocess.Popen(cmd, shell=True, stdout=stdout, stderr=stderr)
|
||||
if realtime and not ctx.stdout:
|
||||
while p.poll() == None:
|
||||
line = p.stdout.readline()
|
||||
if line:
|
||||
ctx.ui.debug(line[:-1])
|
||||
else:
|
||||
out, err = p.communicate()
|
||||
|
||||
ctx.ui.debug(_('return value for "%s" is %s') % (cmd, p.returncode))
|
||||
|
||||
return (p.returncode, out, err)
|
||||
|
||||
######################
|
||||
# Terminal functions #
|
||||
######################
|
||||
|
||||
def has_xterm():
|
||||
return os.environ.has_key("TERM") and sys.stderr.isatty()
|
||||
|
||||
def xterm_title(message):
|
||||
"""sets message as a console window's title"""
|
||||
if os.environ.has_key("TERM") and sys.stderr.isatty():
|
||||
terminalType = os.environ["TERM"]
|
||||
for term in ["xterm", "Eterm", "aterm", "rxvt", "screen", "kterm", "rxvt-unicode"]:
|
||||
if terminalType.startswith(term):
|
||||
sys.stderr.write("\x1b]2;"+str(message)+"\x07")
|
||||
sys.stderr.flush()
|
||||
break
|
||||
|
||||
def xterm_title_reset():
|
||||
"""resets console window's title"""
|
||||
if os.environ.has_key("TERM"):
|
||||
terminalType = os.environ["TERM"]
|
||||
xterm_title(os.environ["TERM"])
|
||||
|
||||
|
||||
#############################
|
||||
# Path Processing Functions #
|
||||
#############################
|
||||
|
||||
def splitpath(a):
|
||||
"""split path into components and return as a list
|
||||
os.path.split doesn't do what I want like removing trailing /"""
|
||||
comps = a.split(os.path.sep)
|
||||
if comps[len(comps)-1]=='':
|
||||
comps.pop()
|
||||
return comps
|
||||
|
||||
def makepath(comps, relative = False, sep = os.path.sep):
|
||||
"""reconstruct a path from components"""
|
||||
path = reduce(lambda x,y: x + sep + y, comps, '')
|
||||
if relative:
|
||||
return path[len(sep):]
|
||||
else:
|
||||
return path
|
||||
|
||||
def parentpath(a, sep = os.path.sep):
|
||||
# remove trailing '/'
|
||||
a = a.rstrip(sep)
|
||||
return a[:a.rfind(sep)]
|
||||
|
||||
def parenturi(a):
|
||||
return parentpath(a, '/')
|
||||
|
||||
# I'm not sure how necessary this is. Ahem.
|
||||
def commonprefix(l):
|
||||
"""an improved version of os.path.commonprefix,
|
||||
returns a list of path components"""
|
||||
common = []
|
||||
comps = map(splitpath, l)
|
||||
for i in range(0, min(len,l)):
|
||||
compi = map(lambda x: x[i], comps) # get ith slice
|
||||
if same(compi):
|
||||
common.append(compi[0])
|
||||
return common
|
||||
|
||||
# but this one is necessary
|
||||
def subpath(a, b):
|
||||
"find if path a is before b in the directory tree"
|
||||
return prefix(splitpath(a), splitpath(b))
|
||||
|
||||
def removepathprefix(prefix, path):
|
||||
"remove path prefix a from b, finding the pathname rooted at a"
|
||||
comps = remove_prefix(splitpath(prefix), splitpath(path))
|
||||
if len(comps) > 0:
|
||||
return join_path(*tuple(comps))
|
||||
else:
|
||||
return ""
|
||||
|
||||
def absolute_path(path):
|
||||
"determine if given @path is absolute"
|
||||
comps = splitpath(path)
|
||||
return comps[0] == ''
|
||||
|
||||
def join_path(a, *p):
|
||||
"""Join two or more pathname components, inserting '/' as needed"""
|
||||
"""The python original version has a silly logic"""
|
||||
path = a
|
||||
for b in p:
|
||||
b = b.lstrip('/')
|
||||
if path == '' or path.endswith('/'):
|
||||
path += b
|
||||
else:
|
||||
path += '/' + b
|
||||
return path
|
||||
|
||||
####################################
|
||||
# File/Directory Related Functions #
|
||||
####################################
|
||||
|
||||
def check_file(file, mode = os.F_OK):
|
||||
"shorthand to check if a file exists"
|
||||
if not os.access(file, mode):
|
||||
raise FileError("File " + file + " not found")
|
||||
return True
|
||||
|
||||
def check_dir(dir):
|
||||
"""check if directory exists, and create if it doesn't.
|
||||
works recursively"""
|
||||
dir = dir.strip().rstrip("/")
|
||||
if not os.access(dir, os.F_OK):
|
||||
os.makedirs(dir)
|
||||
|
||||
def clean_dir(path):
|
||||
"Remove all content of a directory (top)"
|
||||
# don't reimplement the wheel
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
def creation_time(file):
|
||||
"""returns the creation time of the given file"""
|
||||
if check_file(file):
|
||||
import time
|
||||
st = os.stat(file)
|
||||
return time.localtime(st.st_ctime)
|
||||
|
||||
def dir_size(dir):
|
||||
""" calculate the size of files under a dir
|
||||
based on the os module example"""
|
||||
# It's really hard to give an approximate value for package's
|
||||
# installed size. Gettin a sum of all files' sizes if far from
|
||||
# being true. Using 'du' command (like Debian does) can be a
|
||||
# better solution :(.
|
||||
# Not really, du calculates size on disk, this is much better -- exa
|
||||
from os.path import getsize, islink, isdir, exists
|
||||
join = join_path
|
||||
|
||||
if exists(dir) and (not isdir(dir) and not islink(dir)):
|
||||
#so, this is not a directory but file..
|
||||
return getsize(dir)
|
||||
|
||||
if islink(dir):
|
||||
return long(len(os.readlink(dir)))
|
||||
|
||||
def sizes():
|
||||
for root, dirs, files in os.walk(dir):
|
||||
yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))])
|
||||
yield sum([long(len(os.readlink((join(root, name))))) for name in files if islink(join(root,name))])
|
||||
return sum( sizes() )
|
||||
|
||||
def copy_file(src,dest):
|
||||
"""copy source file to destination file"""
|
||||
check_file(src)
|
||||
check_dir(os.path.dirname(dest))
|
||||
shutil.copyfile(src, dest)
|
||||
|
||||
def is_ar_file(file_path):
|
||||
return open(file_path).readline().strip() == '!<arch>'
|
||||
|
||||
def clean_ar_timestamps(ar_file):
|
||||
"""clean the timestamps of the ar files"""
|
||||
if not is_ar_file(ar_file):
|
||||
return
|
||||
content = open(ar_file).readlines()
|
||||
fp = open(ar_file, 'w')
|
||||
for line in content:
|
||||
pos = line.rfind(chr(32) + chr(96))
|
||||
if pos > -1 and line[pos - 57:pos + 2].find(chr(47)) > -1:
|
||||
line = line[:pos - 41] + '0000000000' + line[pos - 31:]
|
||||
fp.write(line)
|
||||
fp.close()
|
||||
|
||||
# FIXME: this should be done in a much much simpler way
|
||||
# as it stands, it seems to be a kludge to solve
|
||||
# an unrelated problem
|
||||
def get_file_hashes(top, excludePrefix=None, removePrefix=None):
|
||||
"""Generator function iterates over a toplevel path and returns the
|
||||
(filePath, sha1Hash) tuples for all files. If excludePrefixes list
|
||||
is given as a parameter, function will exclude the filePaths
|
||||
matching those prefixes. The removePrefix string parameter will be
|
||||
used to remove prefix from filePath while matching excludes, if
|
||||
given."""
|
||||
|
||||
def sha1_sum(f, data=False):
|
||||
if not data and f.endswith('.a'):
|
||||
#workaround for .a issue..
|
||||
#don't skip .a files,
|
||||
#but pad their timestamps with '0'..
|
||||
clean_ar_timestamps(f)
|
||||
|
||||
func = None
|
||||
|
||||
if data:
|
||||
func = sha1_data
|
||||
else:
|
||||
func = sha1_file
|
||||
|
||||
try:
|
||||
return func(f)
|
||||
except FileError, e:
|
||||
if os.path.islink(f):
|
||||
ctx.ui.info(_("Including external link '%s'") % f)
|
||||
elif os.path.isdir(f):
|
||||
ctx.ui.info(_("Including directory '%s'") % f)
|
||||
else:
|
||||
raise e
|
||||
return None
|
||||
|
||||
def has_excluded_prefix(filename):
|
||||
if excludePrefix and removePrefix:
|
||||
tempfnam = remove_prefix(removePrefix, filename)
|
||||
for p in excludePrefix:
|
||||
if tempfnam.startswith(p):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# handle single file
|
||||
if os.path.isfile(top):
|
||||
yield (top, sha1_sum(top))
|
||||
return
|
||||
|
||||
# handle single symlink declaration here.
|
||||
if os.path.islink(top):
|
||||
yield (top, sha1_sum(os.readlink(top), True))
|
||||
return
|
||||
|
||||
for root, dirs, files in os.walk(top, topdown=False):
|
||||
#bug 339
|
||||
if os.path.islink(root) and not has_excluded_prefix(root):
|
||||
#yield the symlink..
|
||||
#bug 373
|
||||
yield (root, sha1_sum(os.readlink(root), True))
|
||||
excludePrefix.append(remove_prefix(removePrefix, root) + "/")
|
||||
continue
|
||||
|
||||
#bug 397
|
||||
for dir in dirs:
|
||||
d = join_path(root, dir)
|
||||
if os.path.islink(d) and not has_excluded_prefix(d):
|
||||
yield (d, sha1_sum(os.readlink(d), True))
|
||||
excludePrefix.append(remove_prefix(removePrefix, d) + "/")
|
||||
|
||||
#bug 340
|
||||
if os.path.isdir(root) and not has_excluded_prefix(root):
|
||||
parent, r, d, f = root, '', '', ''
|
||||
for r, d, f in os.walk(parent, topdown=False): pass
|
||||
if not f and not d:
|
||||
yield (parent, sha1_sum(parent))
|
||||
|
||||
for fname in files:
|
||||
f = join_path(root, fname)
|
||||
if has_excluded_prefix(f):
|
||||
continue
|
||||
#bug 373
|
||||
elif os.path.islink(f):
|
||||
yield (f, sha1_sum(os.readlink(f), True))
|
||||
else:
|
||||
yield (f, sha1_sum(f))
|
||||
|
||||
def copy_dir(src, dest):
|
||||
"""copy source dir to destination dir recursively"""
|
||||
shutil.copytree(src, dest)
|
||||
|
||||
def check_file_hash(filename, hash):
|
||||
"""Check the files integrity with a given hash"""
|
||||
return sha1_file(filename) == hash
|
||||
|
||||
def sha1_file(filename):
|
||||
"""calculate sha1 hash of filename"""
|
||||
# Broken links can cause problem!
|
||||
try:
|
||||
m = sha.new()
|
||||
f = file(filename, 'rb')
|
||||
for line in f:
|
||||
m.update(line)
|
||||
return m.hexdigest()
|
||||
except IOError:
|
||||
raise FileError(_("I/O Error: Cannot calculate SHA1 hash of %s") % filename)
|
||||
|
||||
def sha1_data(data):
|
||||
"""calculate sha1 hash of given data"""
|
||||
try:
|
||||
m = sha.new()
|
||||
m.update(data)
|
||||
return m.hexdigest()
|
||||
except:
|
||||
raise Error(_("Cannot calculate SHA1 hash of given data"))
|
||||
|
||||
def uncompress(patchFile, compressType="gz", targetDir=None):
|
||||
"""uncompresses a file and returns the path of the uncompressed
|
||||
file"""
|
||||
if targetDir:
|
||||
filePath = join_path(targetDir,
|
||||
os.path.basename(patchFile))
|
||||
else:
|
||||
filePath = os.path.basename(patchFile)
|
||||
|
||||
if compressType == "gz":
|
||||
from gzip import GzipFile
|
||||
obj = GzipFile(patchFile)
|
||||
elif compressType == "bz2":
|
||||
from bz2 import BZ2File
|
||||
obj = BZ2File(patchFile)
|
||||
|
||||
open(filePath, "w").write(obj.read())
|
||||
return filePath
|
||||
|
||||
|
||||
def do_patch(sourceDir, patchFile, level = 0, target = ''):
|
||||
"""simple function to apply patches.."""
|
||||
cwd = os.getcwd()
|
||||
os.chdir(sourceDir)
|
||||
|
||||
if level == None:
|
||||
level = 0
|
||||
if target == None:
|
||||
target = ''
|
||||
|
||||
check_file(patchFile)
|
||||
(ret, out, err) = run_batch("patch -p%d %s < %s" %
|
||||
(level, target, patchFile))
|
||||
if ret:
|
||||
if out is None and err is None:
|
||||
# Which means stderr and stdout directed so they are None
|
||||
raise Error(_("ERROR: patch (%s) failed") % (patchFile))
|
||||
else:
|
||||
raise Error(_("ERROR: patch (%s) failed: %s") % (patchFile, out))
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def strip_directory(top, excludelist=[]):
|
||||
for root, dirs, files in os.walk(top):
|
||||
for fn in files:
|
||||
frpath = join_path(root, fn)
|
||||
drpath = join_path(os.path.dirname(top),
|
||||
ctx.const.debug_dir_suffix,
|
||||
remove_prefix(top, frpath))
|
||||
|
||||
# Some upstream sources have buggy libtool and ltmain.sh with them,
|
||||
# which causes wrong path entries in *.la files. And these wrong path
|
||||
# entries sometimes triggers compile-time errors or linkage problems.
|
||||
# Instead of patching all these buggy sources and maintain these patches,
|
||||
# PISI removes wrong paths...
|
||||
extension = os.path.splitext(frpath)[1]
|
||||
if extension == ".la":
|
||||
# FIXME: I'm regular expr. idiot, so one can convert this to python...
|
||||
os.system("sed -i -e 's~-L/var/tmp/pisi/[[:graph:]]* ~~g' %s" % frpath)
|
||||
|
||||
# real path in .pisi package
|
||||
p = '/' + removepathprefix(top, frpath)
|
||||
strip = True
|
||||
for exclude in excludelist:
|
||||
if p.startswith(exclude):
|
||||
strip = False
|
||||
ctx.ui.debug("%s [%s]" %(p, "NoStrip"))
|
||||
|
||||
if strip:
|
||||
if strip_file(frpath, drpath):
|
||||
ctx.ui.debug("%s [%s]" %(p, "stripped"))
|
||||
|
||||
|
||||
def strip_file(filepath, outpath):
|
||||
"""strip a file"""
|
||||
p = os.popen("file \"%s\"" % filepath)
|
||||
o = p.read()
|
||||
|
||||
def run_strip(f, flags=""):
|
||||
p = os.popen("strip %s %s" %(flags, f))
|
||||
ret = p.close()
|
||||
if ret:
|
||||
ctx.ui.warning(_("strip command failed for file '%s'!") % f)
|
||||
|
||||
def save_elf_debug(f, o):
|
||||
"""copy debug info into file.debug file"""
|
||||
p = os.popen("objcopy --only-keep-debug %s %s%s" % (f, o, ctx.const.debug_file_suffix))
|
||||
ret = p.close()
|
||||
if ret:
|
||||
ctx.ui.warning(_("objcopy (keep-debug) command failed for file '%s'!") % f)
|
||||
|
||||
"""mark binary/shared objects to use file.debug"""
|
||||
p = os.popen("objcopy --add-gnu-debuglink=%s%s %s" % (o, ctx.const.debug_file_suffix, f))
|
||||
ret = p.close()
|
||||
if ret:
|
||||
ctx.ui.warning(_("objcopy (add-debuglink) command failed for file '%s'!") % f)
|
||||
|
||||
if "current ar archive" in o:
|
||||
run_strip(filepath, "-g")
|
||||
return True
|
||||
|
||||
elif "SB executable" in o:
|
||||
if not ctx.get_option('no_debug'):
|
||||
check_dir(os.path.dirname(outpath))
|
||||
save_elf_debug(filepath, outpath)
|
||||
run_strip(filepath)
|
||||
return True
|
||||
|
||||
elif "SB shared object" in o:
|
||||
if not ctx.get_option('no_debug'):
|
||||
check_dir(os.path.dirname(outpath))
|
||||
save_elf_debug(filepath, outpath)
|
||||
run_strip(filepath, "--strip-unneeded")
|
||||
# FIXME: warn for TEXTREL
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def partition_freespace(directory):
|
||||
""" returns free space of given directory's partition """
|
||||
st = os.statvfs(directory)
|
||||
return st[statvfs.F_BSIZE] * st[statvfs.F_BFREE]
|
||||
|
||||
def clean_locks(top = '.'):
|
||||
for root, dirs, files in os.walk(top):
|
||||
for fn in files:
|
||||
if fn.endswith('.lock'):
|
||||
path = join_path(root, fn)
|
||||
ctx.ui.info(_('Removing lock %s'), path)
|
||||
os.unlink(path)
|
||||
|
||||
########################################
|
||||
# Package/Repository Related Functions #
|
||||
########################################
|
||||
|
||||
def package_name(name, version, release, build, prependSuffix=True):
|
||||
fn = name + '-' + version + '-' + release
|
||||
if build:
|
||||
fn += '-' + str(build)
|
||||
if prependSuffix:
|
||||
fn += ctx.const.package_suffix
|
||||
return fn
|
||||
|
||||
def is_package_name(fn, package_name = None):
|
||||
"check if fn is a valid filename for given package_name"
|
||||
"if not given a package name, see if fn fits the package name rules"
|
||||
if (package_name==None) or fn.startswith(package_name + '-'):
|
||||
if fn.endswith(ctx.const.package_suffix):
|
||||
# get version string, skip separator '-'
|
||||
verstr = fn[len(package_name) + 1:
|
||||
len(fn)-len(ctx.const.package_suffix)]
|
||||
import string
|
||||
for x in verstr.split('-'):
|
||||
# weak rule: version components after '-' start with a digit
|
||||
if x is '' or (not x[0] in string.digits):
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
def env_update():
|
||||
import pisi.environment
|
||||
ctx.ui.info(_('Updating environment...'))
|
||||
|
||||
env_dir = join_path(ctx.config.dest_dir(), "/etc/env.d")
|
||||
if not os.path.exists(env_dir):
|
||||
os.makedirs(env_dir, 0755)
|
||||
|
||||
pisi.environment.update_environment(ctx.config.dest_dir())
|
||||
|
||||
def pure_package_name(package_name):
|
||||
"return pure package name from given string"
|
||||
"ex: package_name=tasma-1.0.3-5-2.pisi, returns tasma"
|
||||
|
||||
if package_name.endswith(ctx.const.package_suffix):
|
||||
package_name = package_name.rstrip(ctx.const.package_suffix)
|
||||
|
||||
return parse_package_name(package_name)[0]
|
||||
|
||||
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)
|
||||
|
||||
def generate_pisi_file(patchFile, fromFile, toFile):
|
||||
if run_batch("xdelta patch %s %s %s" % (patchFile, fromFile, toFile))[0]:
|
||||
raise Error(_("ERROR: xdelta patch %s %s %s failed") % (patchFile, fromFile, toFile))
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2005, TUBITAK/UEKAE
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free
|
||||
# Software Foundation; either version 2 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@pardus.org.tr>
|
||||
# Baris Metin <baris@pardus.org.tr>
|
||||
#
|
||||
# History:
|
||||
# Eray wrote the first version
|
||||
# Baris added support for the fancy keywords and
|
||||
# alphanumeric version string components
|
||||
|
||||
|
||||
"""version structure"""
|
||||
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
import pisi.util as util
|
||||
|
||||
|
||||
# Basic rule is:
|
||||
# p > (no suffix) > m > rc > pre > beta > alpha
|
||||
# m: milestone. this was added for OO.o
|
||||
# p: patch-level
|
||||
keywords = {"alpha": 0,
|
||||
"beta" : 1,
|
||||
"pre" : 2,
|
||||
"rc" : 3,
|
||||
"m" : 4,
|
||||
"NOKEY" : 5,
|
||||
"p" : 6}
|
||||
|
||||
# helper functions
|
||||
def has_keyword(versionitem):
|
||||
if versionitem._keyword != "NOKEY":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class VersionItem:
|
||||
_keyword = "NOKEY"
|
||||
_value = 0
|
||||
|
||||
def __init__(self, itemstring):
|
||||
|
||||
# special-case: 1.5p
|
||||
# here "p" comes as a separate itemstring and conflicts with
|
||||
# keyword "p". But keyword should allways contain an integer prefix.
|
||||
# So, skipping looking for keywords if the length() is 1 makes the
|
||||
# trick.
|
||||
if len(itemstring) > 1:
|
||||
|
||||
for keyword in keywords.keys():
|
||||
if itemstring.startswith(keyword):
|
||||
|
||||
if self._keyword == "NOKEY":
|
||||
self._keyword = keyword
|
||||
else:
|
||||
# longer match is correct
|
||||
if len(keyword) > len(self._keyword):
|
||||
self._keyword = keyword
|
||||
|
||||
if self._keyword == "NOKEY":
|
||||
if len(itemstring) == 1 and itemstring in string.ascii_letters:
|
||||
# single letter version item ('a' to 'Z')
|
||||
self._value = itemstring
|
||||
else:
|
||||
self._value = int(itemstring)
|
||||
else:
|
||||
# rest is the version item's value. And each must have
|
||||
# one!
|
||||
self._value = int(itemstring[len(self._keyword):])
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __lt__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l < r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value < rhs._value
|
||||
else: # l > r
|
||||
return False
|
||||
|
||||
def __le__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l < r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value <= rhs._value
|
||||
else: # l > r
|
||||
return False
|
||||
|
||||
def __gt__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l > r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value > rhs._value
|
||||
else: # l < r
|
||||
return False
|
||||
|
||||
def __ge__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l > r:
|
||||
return True
|
||||
elif l == r:
|
||||
return self._value >= rhs._value
|
||||
else: # l < r
|
||||
return False
|
||||
|
||||
def __eq__(self,rhs):
|
||||
l = keywords[self._keyword]
|
||||
r = keywords[rhs._keyword]
|
||||
if l == r and self._value == rhs._value:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class Version:
|
||||
|
||||
def __init__(self, verstring):
|
||||
self.comps = []
|
||||
for i in util.multisplit(verstring,'.-_'):
|
||||
# some version strings can contain ascii chars at the
|
||||
# back. As an example: 2.11a
|
||||
# We split '11a' as two items like '11' and 'a'
|
||||
s = re.compile("[a-z-A-Z]$").search(i)
|
||||
if s:
|
||||
head = i[:s.start()]
|
||||
tail = s.group()
|
||||
self.comps.append(VersionItem(head))
|
||||
self.comps.append(VersionItem(tail))
|
||||
else:
|
||||
self.comps.append(VersionItem(i))
|
||||
|
||||
self.verstring = verstring
|
||||
|
||||
def string(self):
|
||||
return self.verstring
|
||||
|
||||
def compare(self, rhs):
|
||||
"""this comparison routine is essentially a comparison routine
|
||||
for two rationals in (0,1) interval. we compare two sequences
|
||||
of digits one by one. We start with the leftmost digit
|
||||
in the expansion. If they are equal, we proceed to the next. If
|
||||
not we use the comparison operator. And we iterate to the left.
|
||||
The result is, 0 if two are equal, -1 if self < rhs, and +1
|
||||
if self>rhs"""
|
||||
lhs = self.comps
|
||||
rhs = rhs.comps
|
||||
# pad the short version string with zeros
|
||||
if len(lhs) < len(rhs):
|
||||
lhs.extend( [VersionItem('0')] * (len(rhs) - len(lhs)) )
|
||||
elif len(lhs) > len(rhs):
|
||||
rhs.extend( [VersionItem('0')] * (len(lhs) - len(rhs)) )
|
||||
# now let's iterate from left to right
|
||||
for (litem, ritem) in zip(lhs, rhs):
|
||||
if litem < ritem:
|
||||
return -1
|
||||
elif litem > ritem:
|
||||
return +1
|
||||
return 0
|
||||
|
||||
# premature optimization is the root of all evil
|
||||
|
||||
def __lt__(self,rhs):
|
||||
return self.compare(rhs) < 0
|
||||
|
||||
def __le__(self,rhs):
|
||||
return self.compare(rhs) <= 0
|
||||
|
||||
def __gt__(self,rhs):
|
||||
return self.compare(rhs) > 0
|
||||
|
||||
def __ge__(self,rhs):
|
||||
return self.compare(rhs) >= 0
|
||||
|
||||
def __eq__(self,rhs):
|
||||
return self.compare(rhs) == 0
|
||||
|
||||
def __str__(self):
|
||||
return self.verstring
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,81 +0,0 @@
|
||||
#! /usr/bin/python
|
||||
# a simple tool to find the correct build order of source
|
||||
# packages in the repository. this one works really fast.
|
||||
# just give it the repository directory as an argument
|
||||
# author: exa
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi.context as ctx
|
||||
import pisi
|
||||
import pisi.api
|
||||
import pisi.config
|
||||
import pisi.specfile as specfile
|
||||
import pisi.util
|
||||
import pisi.graph
|
||||
|
||||
options = pisi.config.Options()
|
||||
if len(sys.argv) > 2:
|
||||
options.destdir=sys.argv[2]
|
||||
else:
|
||||
options.destdir = '/'
|
||||
pisi.api.init(database=False, options=options)
|
||||
repo_uri = sys.argv[1]
|
||||
|
||||
specfiles = []
|
||||
|
||||
print "reading pspecfiles, just a moment"
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn == 'pspec.xml':
|
||||
#ctx.ui.info(_('Adding %s to package index') % fn)
|
||||
sf = specfile.SpecFile()
|
||||
sf.read(pisi.util.join_path(root, fn))
|
||||
sf.check()
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
specfiles.append(sf)
|
||||
|
||||
def plan_build(specfiles):
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
|
||||
G_f = pisi.graph.Digraph()
|
||||
pkgtosrc = {}
|
||||
for sf in specfiles:
|
||||
for pkg in sf.packages:
|
||||
pkgtosrc[str(pkg.name)] = str(sf.source.name)
|
||||
|
||||
for x in specfiles:
|
||||
#G_f.add_package(x)
|
||||
src = x.source
|
||||
if not str(src.name) in G_f.vertices():
|
||||
G_f.add_vertex(str(src.name), (src.version, src.release))
|
||||
for builddep in src.buildDependencies:
|
||||
G_f.add_edge(str(src.name), str(builddep.package))
|
||||
for pkg in x.packages:
|
||||
for rtdep in pkg.packageDependencies:
|
||||
tsrc = pkgtosrc[rtdep.package]
|
||||
if not src.name == tsrc: # firefox - firefox-devel thing
|
||||
G_f.add_edge(src.name, tsrc )
|
||||
|
||||
if ctx.config.get_option('debug'):
|
||||
G_f.write_graphviz(sys.stdout)
|
||||
order = G_f.topological_sort()
|
||||
order.reverse()
|
||||
return G_f, order
|
||||
|
||||
print "planning build order"
|
||||
try:
|
||||
G_f, order = plan_build(specfiles)
|
||||
print order
|
||||
except pisi.graph.CycleException, e:
|
||||
print e
|
||||
|
||||
pisi.api.finalize()
|
||||
@@ -1,28 +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.
|
||||
#
|
||||
# Author: Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
import sys
|
||||
import bsddb3.dbshelve as shelve
|
||||
import bsddb3.db as db
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
import pisi
|
||||
import pisi.cli
|
||||
|
||||
d = shelve.open( sys.argv[1], 'r', 0660, filetype = db.DB_BTREE )
|
||||
|
||||
for key, data in d.items():
|
||||
pisi.cli.printu(u'%s: %s\n' % (key, data))
|
||||
|
||||
d.close()
|
||||
@@ -1,32 +0,0 @@
|
||||
#! /usr/bin/python
|
||||
# a simple tool to list stuff in source repository
|
||||
# author: exa
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
import pisi
|
||||
import pisi.api
|
||||
import pisi.config
|
||||
import pisi.specfile as specfile
|
||||
import pisi.context as ctx
|
||||
import pisi.util
|
||||
|
||||
options = pisi.config.Options()
|
||||
if len(sys.argv) > 2:
|
||||
options.destdir=sys.argv[2]
|
||||
else:
|
||||
options.destdir = '/'
|
||||
pisi.api.init(database=False, options=options)
|
||||
repo_uri = sys.argv[1]
|
||||
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn == 'pspec.xml':
|
||||
#ctx.ui.info(_('Adding %s to package index') % fn)
|
||||
sf = specfile.SpecFile()
|
||||
sf.read(pisi.util.join_path(root, fn))
|
||||
sf.check()
|
||||
print '%s: %s' % (sf.source.name, sf.source.version)
|
||||
|
||||
pisi.api.finalize()
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import codecs
|
||||
import xml.dom.minidom as mdom
|
||||
|
||||
def find_pspecs(folder):
|
||||
paks = []
|
||||
for root, dirs, files in os.walk(folder):
|
||||
if "pspec.xml" in files:
|
||||
paks.append(root)
|
||||
# dont walk into the versioned stuff
|
||||
if ".svn" in dirs:
|
||||
dirs.remove(".svn")
|
||||
return paks
|
||||
|
||||
def addText(doc, parent, text):
|
||||
cdata =doc.createTextNode(text)
|
||||
parent.appendChild(cdata)
|
||||
|
||||
def getTags(parent, childName):
|
||||
return [x for x in parent.childNodes if x.nodeType == x.ELEMENT_NODE if x.tagName == childName]
|
||||
|
||||
def getNodeText(node, tag, default=None):
|
||||
try:
|
||||
c = getTags(node, tag)[0].firstChild.data
|
||||
except:
|
||||
c = default
|
||||
return c
|
||||
|
||||
def newNode(doc, tag, text):
|
||||
node = doc.createElement(tag)
|
||||
cdata = doc.createTextNode(text)
|
||||
node.appendChild(cdata)
|
||||
return node
|
||||
|
||||
def fixIndent(doc, node):
|
||||
for x in node.childNodes:
|
||||
if x.nodeType == x.ELEMENT_NODE:
|
||||
if x.tagName == "Update":
|
||||
fixIndent(doc, x)
|
||||
else:
|
||||
x.data = "\n" + x.data[5:]
|
||||
|
||||
def fixTags(doc, hist):
|
||||
for update in hist.childNodes:
|
||||
if update.nodeType == update.ELEMENT_NODE:
|
||||
rno = getNodeText(update, "Release")
|
||||
update.setAttribute("release", rno)
|
||||
if rno == "1":
|
||||
comment = newNode(doc, "Comment", "First release.")
|
||||
paker = getTags(getTags(doc.documentElement, "Source")[0], "Packager")[0]
|
||||
name = newNode(doc, "Name", getNodeText(paker, "Name"))
|
||||
email = newNode(doc, "Email", getNodeText(paker, "Email"))
|
||||
else:
|
||||
comment = newNode(doc, "Comment", "FIXHISTORY")
|
||||
name = newNode(doc, "Name", "FIXHISTORY")
|
||||
email = newNode(doc, "Email", "FIXHISTORY")
|
||||
update.replaceChild(comment, getTags(update, "Release")[0])
|
||||
addText(doc, update, " ")
|
||||
update.appendChild(name)
|
||||
addText(doc, update, "\n ")
|
||||
update.appendChild(email)
|
||||
addText(doc, update, "\n ")
|
||||
|
||||
def fixPspec(path):
|
||||
doc = mdom.parse(path)
|
||||
pisi = doc.documentElement
|
||||
source = getTags(pisi, "Source")[0]
|
||||
history = getTags(source, "History")[0]
|
||||
item = source.removeChild(history)
|
||||
addText(doc, pisi, "\n ")
|
||||
fixIndent(doc, item)
|
||||
fixTags(doc, item)
|
||||
pisi.appendChild(item)
|
||||
addText(doc, pisi, "\n")
|
||||
f = codecs.open(path,'w', "utf-8")
|
||||
f.write(doc.toxml())
|
||||
f.close()
|
||||
|
||||
pakages = find_pspecs(sys.argv[1])
|
||||
for pak in pakages:
|
||||
fixPspec(os.path.join(pak, "pspec.xml"))
|
||||
@@ -1,63 +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 os
|
||||
import sys
|
||||
|
||||
sys.path.append('.')
|
||||
|
||||
import pisi.api
|
||||
import pisi.uri
|
||||
import pisi.context as ctx
|
||||
import pisi.specfile
|
||||
import pisi.util as util
|
||||
from pisi.fetcher import fetch_url
|
||||
|
||||
def scanPSPEC(folder):
|
||||
packages = []
|
||||
for root, dirs, files in os.walk(folder):
|
||||
if "pspec.xml" in files:
|
||||
packages.append(root)
|
||||
# dont walk into the versioned stuff
|
||||
if ".svn" in dirs:
|
||||
dirs.remove(".svn")
|
||||
return packages
|
||||
|
||||
def isCached(file, sha1sum):
|
||||
try:
|
||||
return util.check_file_hash(os.path.join(ctx.config.archives_dir(), file), sha1sum)
|
||||
except:
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
pisi.api.init(database=False, options='')
|
||||
try:
|
||||
packages = scanPSPEC(sys.argv[1])
|
||||
except:
|
||||
print "Usage: fetchAll.py path2repo"
|
||||
sys.exit(1)
|
||||
|
||||
for package in packages:
|
||||
spec = pisi.specfile.SpecFile()
|
||||
spec.read(os.path.join(package, "pspec.xml"))
|
||||
|
||||
URI = pisi.uri.URI(spec.source.archive.uri)
|
||||
|
||||
if not isCached(URI.filename(), spec.source.archive.sha1sum):
|
||||
print URI, " -> " , os.path.join(ctx.config.archives_dir(), URI.filename())
|
||||
try:
|
||||
fetch_url(URI, ctx.config.archives_dir())
|
||||
except pisi.fetcher.FetchError, e:
|
||||
print e
|
||||
pass
|
||||
else:
|
||||
print URI, "already downloaded..."
|
||||
pisi.api.finalize()
|
||||
@@ -1,64 +0,0 @@
|
||||
#! /usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# find library dependencies of a package
|
||||
# author: exa (Eray Özkural)
|
||||
|
||||
import locale
|
||||
import sys
|
||||
import os
|
||||
import fnmatch
|
||||
|
||||
import gettext
|
||||
__trans = gettext.translation('pisi', fallback=True)
|
||||
_ = __trans.ugettext
|
||||
|
||||
import pisi
|
||||
import pisi.api
|
||||
import pisi.config
|
||||
import pisi.specfile as specfile
|
||||
import pisi.context as ctx
|
||||
import pisi.util as util
|
||||
from pisi.package import Package
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
options = pisi.config.Options()
|
||||
if len(sys.argv) > 2:
|
||||
options.destdir=sys.argv[2]
|
||||
else:
|
||||
options.destdir = '/'
|
||||
pisi.api.init(database=True, comar=False, options=options)
|
||||
|
||||
filename = sys.argv[1]
|
||||
package = Package(filename)
|
||||
package.read()
|
||||
util.clean_dir('/tmp/install')
|
||||
package.extract_dir_flat('install', '/tmp/install')
|
||||
deps = set()
|
||||
needed = set()
|
||||
for file in package.files.list:
|
||||
#print file.path, file.type
|
||||
if file.type == 'executable':
|
||||
(ret, lines) = util.run_batch('objdump -p %s' % util.join_path('/tmp/install', file.path))
|
||||
for x in lines:
|
||||
if x.startswith(' NEEDED'):
|
||||
needed.add(x[8:].strip())
|
||||
|
||||
#print 'needed guys', needed
|
||||
|
||||
for lib in needed:
|
||||
|
||||
(ret, out) = util.run_batch('locate %s' % lib)
|
||||
path = out[0].strip().lstrip('/')
|
||||
try:
|
||||
(pkg_name, file_info) = ctx.filesdb.get_file(path)
|
||||
ctx.ui.info("Library dependency: '%s' due to library '%s'" % (pkg_name, file_info.path))
|
||||
deps.add(pkg_name)
|
||||
except:
|
||||
ctx.ui.warning('Cannot find an installed package for library %s' % lib)
|
||||
|
||||
ctx.ui.info('Found deps:')
|
||||
for x in deps:
|
||||
ctx.ui.info(x)
|
||||
|
||||
pisi.api.finalize()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user